aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--INSTALL8
-rw-r--r--Makefile.am443
-rw-r--r--NEWS14
-rw-r--r--configure.ac122
-rw-r--r--doc/user.xml131
-rw-r--r--m4/ax_append_link_flags.m461
-rw-r--r--m4/ax_check_link_flag.m471
-rw-r--r--m4/ax_cxx_compile_stdcxx_0x.m4107
-rw-r--r--src/AllCommands.cxx387
-rw-r--r--src/AllCommands.hxx (renamed from src/db_internal.h)21
-rw-r--r--src/Client.cxx (renamed from src/client.c)12
-rw-r--r--src/Client.hxx (renamed from src/client.h)37
-rw-r--r--src/ClientEvent.cxx (renamed from src/client_event.c)15
-rw-r--r--src/ClientExpire.cxx (renamed from src/client_expire.c)8
-rw-r--r--src/ClientFile.cxx (renamed from src/client_file.c)12
-rw-r--r--src/ClientFile.hxx (renamed from src/client_file.h)13
-rw-r--r--src/ClientGlobal.cxx (renamed from src/client_global.c)9
-rw-r--r--src/ClientIdle.cxx (renamed from src/client_idle.c)17
-rw-r--r--src/ClientIdle.hxx (renamed from src/client_idle.h)14
-rw-r--r--src/ClientInternal.hxx (renamed from src/client_internal.h)79
-rw-r--r--src/ClientList.cxx (renamed from src/client_list.c)12
-rw-r--r--src/ClientMessage.cxx (renamed from src/db/simple_db_plugin.h)36
-rw-r--r--src/ClientMessage.hxx (renamed from src/exclude.h)45
-rw-r--r--src/ClientNew.cxx (renamed from src/client_new.c)121
-rw-r--r--src/ClientProcess.cxx (renamed from src/client_process.c)43
-rw-r--r--src/ClientRead.cxx (renamed from src/client_read.c)27
-rw-r--r--src/ClientSubscribe.cxx95
-rw-r--r--src/ClientSubscribe.hxx (renamed from src/client_subscribe.h)26
-rw-r--r--src/ClientWrite.cxx (renamed from src/client_write.c)37
-rw-r--r--src/CommandError.cxx134
-rw-r--r--src/CommandError.hxx39
-rw-r--r--src/CommandLine.cxx (renamed from src/cmdline.c)14
-rw-r--r--src/CommandLine.hxx (renamed from src/cmdline.h)8
-rw-r--r--src/CommandListBuilder.cxx42
-rw-r--r--src/CommandListBuilder.hxx109
-rw-r--r--src/CrossFade.cxx (renamed from src/crossfade.c)6
-rw-r--r--src/CrossFade.hxx (renamed from src/crossfade.h)6
-rw-r--r--src/DatabaseCommands.cxx220
-rw-r--r--src/DatabaseCommands.hxx57
-rw-r--r--src/DatabaseGlue.cxx (renamed from src/database.c)126
-rw-r--r--src/DatabaseGlue.hxx59
-rw-r--r--src/DatabaseHelpers.cxx134
-rw-r--r--src/DatabaseHelpers.hxx41
-rw-r--r--src/DatabaseLock.cxx (renamed from src/db_lock.c)4
-rw-r--r--src/DatabaseLock.hxx (renamed from src/db_lock.h)22
-rw-r--r--src/DatabasePlaylist.cxx50
-rw-r--r--src/DatabasePlaylist.hxx (renamed from src/db_save.h)19
-rw-r--r--src/DatabasePlugin.hxx148
-rw-r--r--src/DatabasePrint.cxx231
-rw-r--r--src/DatabasePrint.hxx (renamed from src/db_print.h)42
-rw-r--r--src/DatabaseQueue.cxx58
-rw-r--r--src/DatabaseQueue.hxx (renamed from src/directory_save.h)23
-rw-r--r--src/DatabaseRegistry.cxx43
-rw-r--r--src/DatabaseRegistry.hxx37
-rw-r--r--src/DatabaseSave.cxx (renamed from src/db_save.c)30
-rw-r--r--src/DatabaseSave.hxx36
-rw-r--r--src/DatabaseSelection.cxx27
-rw-r--r--src/DatabaseSelection.hxx (renamed from src/db_selection.h)32
-rw-r--r--src/DatabaseSimple.hxx (renamed from src/database.h)61
-rw-r--r--src/DatabaseVisitor.hxx (renamed from src/song_print.h)21
-rw-r--r--src/DecoderAPI.cxx (renamed from src/decoder_api.c)50
-rw-r--r--src/DecoderControl.cxx (renamed from src/decoder_control.c)39
-rw-r--r--src/DecoderControl.hxx (renamed from src/decoder_control.h)100
-rw-r--r--src/DecoderInternal.cxx (renamed from src/decoder_internal.c)36
-rw-r--r--src/DecoderInternal.hxx (renamed from src/decoder_internal.h)21
-rw-r--r--src/DecoderPrint.cxx (renamed from src/decoder_print.c)10
-rw-r--r--src/DecoderPrint.hxx28
-rw-r--r--src/DecoderThread.cxx (renamed from src/decoder_thread.c)78
-rw-r--r--src/DecoderThread.hxx (renamed from src/decoder_thread.h)6
-rw-r--r--src/Directory.cxx336
-rw-r--r--src/Directory.hxx264
-rw-r--r--src/DirectorySave.cxx (renamed from src/directory_save.c)77
-rw-r--r--src/DirectorySave.hxx36
-rw-r--r--src/ExcludeList.cxx (renamed from src/exclude.c)41
-rw-r--r--src/ExcludeList.hxx78
-rw-r--r--src/InotifyQueue.cxx (renamed from src/inotify_queue.c)54
-rw-r--r--src/InotifyQueue.hxx (renamed from src/inotify_queue.h)8
-rw-r--r--src/InotifySource.cxx (renamed from src/inotify_source.c)18
-rw-r--r--src/InotifySource.hxx (renamed from src/inotify_source.h)8
-rw-r--r--src/InotifyUpdate.cxx (renamed from src/inotify_update.c)29
-rw-r--r--src/InotifyUpdate.hxx (renamed from src/inotify_update.h)6
-rw-r--r--src/Listen.cxx (renamed from src/listen.c)15
-rw-r--r--src/Listen.hxx (renamed from src/listen.h)8
-rw-r--r--src/Main.cxx (renamed from src/main.c)106
-rw-r--r--src/Main.hxx (renamed from src/main.h)8
-rw-r--r--src/Mapper.cxx (renamed from src/mapper.c)38
-rw-r--r--src/Mapper.hxx (renamed from src/mapper.h)50
-rw-r--r--src/MessageCommands.cxx168
-rw-r--r--src/MessageCommands.hxx42
-rw-r--r--src/MusicBuffer.cxx79
-rw-r--r--src/MusicBuffer.hxx (renamed from src/buffer.h)6
-rw-r--r--src/MusicChunk.cxx87
-rw-r--r--src/MusicChunk.hxx (renamed from src/chunk.h)104
-rw-r--r--src/MusicPipe.cxx (renamed from src/pipe.c)77
-rw-r--r--src/MusicPipe.hxx (renamed from src/pipe.h)11
-rw-r--r--src/OtherCommands.cxx311
-rw-r--r--src/OtherCommands.hxx69
-rw-r--r--src/OutputAll.cxx (renamed from src/output_all.c)51
-rw-r--r--src/OutputCommand.cxx (renamed from src/output_command.c)7
-rw-r--r--src/OutputCommand.hxx (renamed from src/output_command.h)8
-rw-r--r--src/OutputCommands.cxx74
-rw-r--r--src/OutputCommands.hxx36
-rw-r--r--src/OutputControl.cxx (renamed from src/output_control.c)21
-rw-r--r--src/OutputControl.hxx (renamed from src/output_control.h)13
-rw-r--r--src/OutputError.hxx35
-rw-r--r--src/OutputFinish.cxx (renamed from src/output_finish.c)5
-rw-r--r--src/OutputInit.cxx (renamed from src/output_init.c)16
-rw-r--r--src/OutputList.cxx (renamed from src/output_list.c)4
-rw-r--r--src/OutputList.hxx (renamed from src/output_list.h)6
-rw-r--r--src/OutputPlugin.cxx (renamed from src/output_plugin.c)6
-rw-r--r--src/OutputPrint.cxx (renamed from src/output_print.c)13
-rw-r--r--src/OutputPrint.hxx (renamed from src/output_print.h)10
-rw-r--r--src/OutputState.cxx (renamed from src/output_state.c)7
-rw-r--r--src/OutputState.hxx (renamed from src/output_state.h)7
-rw-r--r--src/OutputThread.cxx (renamed from src/output_thread.c)44
-rw-r--r--src/OutputThread.hxx (renamed from src/output_thread.h)6
-rw-r--r--src/Partition.hxx43
-rw-r--r--src/Permission.cxx (renamed from src/permission.c)37
-rw-r--r--src/Permission.hxx (renamed from src/permission.h)8
-rw-r--r--src/PlayerCommands.cxx403
-rw-r--r--src/PlayerCommands.hxx90
-rw-r--r--src/PlayerControl.cxx (renamed from src/player_control.c)167
-rw-r--r--src/PlayerControl.hxx (renamed from src/player_control.h)110
-rw-r--r--src/PlayerThread.cxx (renamed from src/player_thread.c)133
-rw-r--r--src/PlayerThread.hxx (renamed from src/player_thread.h)6
-rw-r--r--src/Playlist.cxx (renamed from src/playlist.c)107
-rw-r--r--src/Playlist.hxx (renamed from src/playlist.h)27
-rw-r--r--src/PlaylistAny.cxx (renamed from src/playlist_any.c)9
-rw-r--r--src/PlaylistAny.hxx (renamed from src/playlist_any.h)6
-rw-r--r--src/PlaylistCommands.cxx224
-rw-r--r--src/PlaylistCommands.hxx60
-rw-r--r--src/PlaylistControl.cxx (renamed from src/playlist_control.c)52
-rw-r--r--src/PlaylistDatabase.cxx (renamed from src/playlist_database.c)31
-rw-r--r--src/PlaylistDatabase.hxx (renamed from src/playlist_database.h)16
-rw-r--r--src/PlaylistEdit.cxx (renamed from src/playlist_edit.c)166
-rw-r--r--src/PlaylistFile.cxx (renamed from src/stored_playlist.c)252
-rw-r--r--src/PlaylistFile.hxx (renamed from src/stored_playlist.h)36
-rw-r--r--src/PlaylistGlobal.cxx (renamed from src/playlist_global.c)28
-rw-r--r--src/PlaylistInfo.hxx63
-rw-r--r--src/PlaylistInternal.hxx (renamed from src/playlist_internal.h)8
-rw-r--r--src/PlaylistMapper.cxx (renamed from src/playlist_mapper.c)11
-rw-r--r--src/PlaylistMapper.hxx (renamed from src/playlist_mapper.h)6
-rw-r--r--src/PlaylistPrint.cxx (renamed from src/playlist_print.c)111
-rw-r--r--src/PlaylistPrint.hxx (renamed from src/playlist_print.h)38
-rw-r--r--src/PlaylistQueue.cxx (renamed from src/playlist_queue.c)19
-rw-r--r--src/PlaylistQueue.hxx (renamed from src/playlist_queue.h)8
-rw-r--r--src/PlaylistSave.cxx (renamed from src/playlist_save.c)45
-rw-r--r--src/PlaylistSave.hxx (renamed from src/playlist_save.h)3
-rw-r--r--src/PlaylistSong.cxx (renamed from src/playlist_song.c)29
-rw-r--r--src/PlaylistSong.hxx (renamed from src/playlist_song.h)8
-rw-r--r--src/PlaylistState.cxx (renamed from src/playlist_state.c)45
-rw-r--r--src/PlaylistState.hxx (renamed from src/playlist_state.h)10
-rw-r--r--src/PlaylistVector.cxx68
-rw-r--r--src/PlaylistVector.hxx56
-rw-r--r--src/Queue.cxx (renamed from src/queue.c)374
-rw-r--r--src/Queue.hxx332
-rw-r--r--src/QueueCommands.cxx400
-rw-r--r--src/QueueCommands.hxx84
-rw-r--r--src/QueuePrint.cxx107
-rw-r--r--src/QueuePrint.hxx (renamed from src/queue_print.h)26
-rw-r--r--src/QueueSave.cxx (renamed from src/queue_save.c)65
-rw-r--r--src/QueueSave.hxx (renamed from src/queue_save.h)10
-rw-r--r--src/ReplayGainConfig.cxx (renamed from src/replay_gain_config.c)18
-rw-r--r--src/ReplayGainInfo.cxx (renamed from src/replay_gain_info.c)5
-rw-r--r--src/Song.cxx (renamed from src/song.c)102
-rw-r--r--src/SongFilter.cxx170
-rw-r--r--src/SongFilter.hxx107
-rw-r--r--src/SongPrint.cxx (renamed from src/song_print.c)52
-rw-r--r--src/SongPrint.hxx32
-rw-r--r--src/SongSave.cxx (renamed from src/song_save.c)19
-rw-r--r--src/SongSave.hxx (renamed from src/song_save.h)13
-rw-r--r--src/SongSticker.cxx (renamed from src/song_sticker.c)37
-rw-r--r--src/SongSticker.hxx (renamed from src/song_sticker.h)11
-rw-r--r--src/SongUpdate.cxx (renamed from src/song_update.c)17
-rw-r--r--src/StateFile.cxx (renamed from src/state_file.c)61
-rw-r--r--src/StateFile.hxx (renamed from src/state_file.h)12
-rw-r--r--src/Stats.cxx89
-rw-r--r--src/StickerCommands.cxx176
-rw-r--r--src/StickerCommands.hxx30
-rw-r--r--src/StickerDatabase.cxx (renamed from src/sticker.c)96
-rw-r--r--src/StickerDatabase.hxx (renamed from src/sticker.h)18
-rw-r--r--src/StickerPrint.cxx (renamed from src/sticker_print.c)16
-rw-r--r--src/StickerPrint.hxx (renamed from src/sticker_print.h)13
-rw-r--r--src/TagPrint.cxx (renamed from src/tag_print.c)10
-rw-r--r--src/TagPrint.hxx (renamed from src/tag_print.h)12
-rw-r--r--src/TagSave.cxx (renamed from src/tag_save.c)4
-rw-r--r--src/TagSave.hxx (renamed from src/tag_save.h)6
-rw-r--r--src/TextFile.cxx (renamed from src/text_file.c)15
-rw-r--r--src/TextFile.hxx66
-rw-r--r--src/TimePrint.cxx47
-rw-r--r--src/TimePrint.hxx33
-rw-r--r--src/UpdateArchive.cxx (renamed from src/update_archive.c)35
-rw-r--r--src/UpdateArchive.hxx (renamed from src/update_archive.h)20
-rw-r--r--src/UpdateContainer.cxx (renamed from src/update_container.c)35
-rw-r--r--src/UpdateContainer.hxx (renamed from src/update_container.h)11
-rw-r--r--src/UpdateDatabase.cxx (renamed from src/update_db.c)30
-rw-r--r--src/UpdateDatabase.hxx (renamed from src/update_db.h)16
-rw-r--r--src/UpdateGlue.cxx (renamed from src/update.c)30
-rw-r--r--src/UpdateGlue.hxx (renamed from src/update.h)8
-rw-r--r--src/UpdateIO.cxx (renamed from src/update_io.c)16
-rw-r--r--src/UpdateIO.hxx (renamed from src/update_io.h)19
-rw-r--r--src/UpdateInternal.hxx (renamed from src/update_internal.h)2
-rw-r--r--src/UpdateQueue.cxx (renamed from src/update_queue.c)4
-rw-r--r--src/UpdateQueue.hxx (renamed from src/update_queue.h)8
-rw-r--r--src/UpdateRemove.cxx (renamed from src/update_remove.c)19
-rw-r--r--src/UpdateRemove.hxx (renamed from src/update_remove.h)6
-rw-r--r--src/UpdateSong.cxx (renamed from src/update_song.c)42
-rw-r--r--src/UpdateSong.hxx (renamed from src/update_song.h)11
-rw-r--r--src/UpdateWalk.cxx (renamed from src/update_walk.c)109
-rw-r--r--src/UpdateWalk.hxx (renamed from src/update_walk.h)8
-rw-r--r--src/Win32Main.cxx (renamed from src/main_win32.c)5
-rw-r--r--src/audio_check.h2
-rw-r--r--src/audio_format.h19
-rw-r--r--src/audio_parser.h2
-rw-r--r--src/buffer.c137
-rw-r--r--src/chunk.c102
-rw-r--r--src/client_message.c96
-rw-r--r--src/client_message.h72
-rw-r--r--src/client_subscribe.c123
-rw-r--r--src/clock.h6
-rw-r--r--src/command.c2298
-rw-r--r--src/command.h49
-rw-r--r--src/conf.c1
-rw-r--r--src/db/ProxyDatabasePlugin.cxx476
-rw-r--r--src/db/ProxyDatabasePlugin.hxx27
-rw-r--r--src/db/SimpleDatabasePlugin.cxx347
-rw-r--r--src/db/SimpleDatabasePlugin.hxx98
-rw-r--r--src/db/simple_db_plugin.c357
-rw-r--r--src/dbUtils.c209
-rw-r--r--src/dbUtils.h56
-rw-r--r--src/db_plugin.h156
-rw-r--r--src/db_print.c393
-rw-r--r--src/db_visitor.h54
-rw-r--r--src/decoder/AdPlugDecoderPlugin.cxx148
-rw-r--r--src/decoder/AdPlugDecoderPlugin.h25
-rw-r--r--src/decoder/FLACCommon.cxx (renamed from src/decoder/_flac_common.c)67
-rw-r--r--src/decoder/FLACCommon.hxx (renamed from src/decoder/_flac_common.h)30
-rw-r--r--src/decoder/FLACDecoderPlugin.cxx (renamed from src/decoder/flac_decoder_plugin.c)292
-rw-r--r--src/decoder/FLACDecoderPlugin.h26
-rw-r--r--src/decoder/FLACIOHandle.cxx114
-rw-r--r--src/decoder/FLACIOHandle.hxx48
-rw-r--r--src/decoder/FLACInput.cxx152
-rw-r--r--src/decoder/FLACInput.hxx72
-rw-r--r--src/decoder/FLACMetaData.cxx (renamed from src/decoder/flac_metadata.c)134
-rw-r--r--src/decoder/FLACMetaData.hxx140
-rw-r--r--src/decoder/FLAC_PCM.cxx (renamed from src/decoder/flac_pcm.c)4
-rw-r--r--src/decoder/FLAC_PCM.hxx (renamed from src/decoder/flac_pcm.h)6
-rw-r--r--src/decoder/OggUtil.cxx56
-rw-r--r--src/decoder/OggUtil.hxx48
-rw-r--r--src/decoder/OpusDecoderPlugin.cxx366
-rw-r--r--src/decoder/OpusDecoderPlugin.h25
-rw-r--r--src/decoder/OpusHead.cxx44
-rw-r--r--src/decoder/OpusHead.hxx30
-rw-r--r--src/decoder/OpusReader.hxx97
-rw-r--r--src/decoder/OpusTags.cxx77
-rw-r--r--src/decoder/OpusTags.hxx31
-rw-r--r--src/decoder/XiphTags.c28
-rw-r--r--src/decoder/XiphTags.h28
-rw-r--r--src/decoder/dsdiff_decoder_plugin.c138
-rw-r--r--src/decoder/dsdlib.c56
-rw-r--r--src/decoder/dsdlib.h5
-rw-r--r--src/decoder/dsf_decoder_plugin.c29
-rw-r--r--src/decoder/flac_compat.h114
-rw-r--r--src/decoder/flac_metadata.h64
-rw-r--r--src/decoder/mad_decoder_plugin.c9
-rw-r--r--src/decoder/ogg_codec.c (renamed from src/decoder/_ogg_common.c)18
-rw-r--r--src/decoder/ogg_codec.h (renamed from src/decoder/_ogg_common.h)14
-rw-r--r--src/decoder/sidplay_decoder_plugin.cxx8
-rw-r--r--src/decoder/vorbis_comments.c10
-rw-r--r--src/decoder/vorbis_decoder_plugin.c115
-rw-r--r--src/decoder_api.h6
-rw-r--r--src/decoder_error.h35
-rw-r--r--src/decoder_list.c11
-rw-r--r--src/directory.c314
-rw-r--r--src/directory.h262
-rw-r--r--src/dsd2pcm/dsd2pcm.hpp6
-rw-r--r--src/dsd2pcm/noiseshape.hpp9
-rw-r--r--src/encoder/OggStream.hxx128
-rw-r--r--src/encoder/OpusEncoderPlugin.cxx432
-rw-r--r--src/encoder/OpusEncoderPlugin.hxx25
-rw-r--r--src/encoder/VorbisEncoderPlugin.cxx (renamed from src/encoder/vorbis_encoder.c)123
-rw-r--r--src/encoder/VorbisEncoderPlugin.hxx25
-rw-r--r--src/encoder/flac_encoder.c31
-rw-r--r--src/encoder_list.c8
-rw-r--r--src/encoder_plugin.h2
-rw-r--r--src/event_pipe.h2
-rw-r--r--src/fd_util.h8
-rw-r--r--src/filter/null_filter_plugin.c1
-rw-r--r--src/filter/replay_gain_filter_plugin.c28
-rw-r--r--src/filter/replay_gain_filter_plugin.h3
-rw-r--r--src/filter_plugin.h2
-rw-r--r--src/gcc.h32
-rw-r--r--src/gerror.h (renamed from src/decoder_print.h)9
-rw-r--r--src/input/file_input_plugin.c17
-rw-r--r--src/input_init.h3
-rw-r--r--src/io_error.h44
-rw-r--r--src/locate.c239
-rw-r--r--src/locate.h92
-rw-r--r--src/ls.cxx (renamed from src/ls.c)14
-rw-r--r--src/ls.hxx (renamed from src/ls.h)11
-rw-r--r--src/mixer_control.h2
-rw-r--r--src/mixer_plugin.h2
-rw-r--r--src/mpd_error.h1
-rw-r--r--src/output/httpd_client.h3
-rw-r--r--src/output/pulse_output_plugin.h4
-rw-r--r--src/output/shout_output_plugin.c71
-rw-r--r--src/output_all.h11
-rw-r--r--src/output_internal.h7
-rw-r--r--src/output_plugin.h7
-rw-r--r--src/pcm_channels.c71
-rw-r--r--src/pcm_channels.h17
-rw-r--r--src/pcm_convert.c79
-rw-r--r--src/pcm_convert.h8
-rw-r--r--src/pcm_mix.h3
-rw-r--r--src/pcm_volume.h1
-rw-r--r--src/playlist_vector.c114
-rw-r--r--src/playlist_vector.h80
-rw-r--r--src/protocol/ArgParser.cxx (renamed from src/protocol/argparser.c)18
-rw-r--r--src/protocol/ArgParser.hxx (renamed from src/protocol/argparser.h)20
-rw-r--r--src/protocol/Result.cxx (renamed from src/protocol/result.c)12
-rw-r--r--src/protocol/Result.hxx (renamed from src/protocol/result.h)19
-rw-r--r--src/queue.h379
-rw-r--r--src/queue_print.c122
-rw-r--r--src/replay_gain_config.h2
-rw-r--r--src/resolver.h6
-rw-r--r--src/server_socket.h5
-rw-r--r--src/sig_handlers.c2
-rw-r--r--src/socket_util.c2
-rw-r--r--src/socket_util.h4
-rw-r--r--src/song.h71
-rw-r--r--src/stats.c128
-rw-r--r--src/stats.h5
-rw-r--r--src/string_util.h12
-rw-r--r--src/strset.c148
-rw-r--r--src/strset.h50
-rw-r--r--src/tag.c20
-rw-r--r--src/tag_id3.c5
-rw-r--r--src/tag_id3.h18
-rw-r--r--src/tag_pool.c25
-rw-r--r--src/tag_pool.h6
-rw-r--r--src/text_file.h39
-rw-r--r--src/thread/CriticalSection.hxx63
-rw-r--r--src/thread/Mutex.hxx54
-rw-r--r--src/tokenizer.c2
-rw-r--r--src/tokenizer.h2
-rw-r--r--src/uri.h10
-rw-r--r--src/util/HugeAllocator.cxx87
-rw-r--r--src/util/HugeAllocator.hxx82
-rw-r--r--src/util/SliceBuffer.hxx161
-rw-r--r--src/util/bit_reverse.h5
-rw-r--r--src/util/list.h49
-rw-r--r--src/utils.h3
-rw-r--r--src/zeroconf-avahi.c2
-rw-r--r--src/zeroconf.c2
-rw-r--r--test/DumpDatabase.cxx151
-rw-r--r--test/FakeSong.cxx33
-rw-r--r--test/RunInotify.cxx (renamed from test/run_inotify.c)5
-rw-r--r--test/TestQueuePriority.cxx186
-rw-r--r--test/dump_playlist.cxx (renamed from test/dump_playlist.c)19
-rw-r--r--test/dump_text_file.c3
-rw-r--r--test/read_tags.c23
-rw-r--r--test/run_decoder.c10
-rw-r--r--test/run_filter.c5
-rw-r--r--test/run_input.cxx (renamed from test/run_input.c)13
-rw-r--r--test/run_output.cxx (renamed from test/run_output.c)23
-rw-r--r--test/test_queue_priority.c175
-rw-r--r--valgrind.suppressions23
367 files changed, 14813 insertions, 10078 deletions
diff --git a/INSTALL b/INSTALL
index fdebbe71d..aa889828c 100644
--- a/INSTALL
+++ b/INSTALL
@@ -81,8 +81,11 @@ Alternative for MP3 support.
Ogg Vorbis - http://www.xiph.org/ogg/vorbis/
For Ogg Vorbis support. You will need libogg and libvorbis.
+libopus - http://www.opus-codec.org/
+Opus codec support
+
FLAC - http://flac.sourceforge.net/
-For FLAC support. You will need version 1.1.0 or higher of libflac.
+For FLAC support. You will need version 1.2 or higher of libFLAC.
Audio File - http://www.68k.org/~michael/audiofile/
For WAVE, AIFF, and AU support. You will need libaudiofile.
@@ -114,6 +117,9 @@ WAVE, AIFF, and many others.
libwavpack - http://www.wavpack.com/
For WavPack playback.
+libadplug - http://adplug.sourceforge.net/
+For AdLib playback.
+
despotify - https://github.com/SimonKagstrom/despotify
For Spotify playback.
diff --git a/Makefile.am b/Makefile.am
index 89b643530..d4761c950 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,6 +12,7 @@ noinst_LIBRARIES = \
libpcm.a \
libtag.a \
libinput.a \
+ libdb_plugins.a \
libplaylist_plugins.a \
libdecoder_plugins.a \
libfilter_plugins.a \
@@ -19,10 +20,12 @@ noinst_LIBRARIES = \
liboutput_plugins.a
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
+ $(LIBMPDCLIENT_CFLAGS) \
$(AVAHI_CFLAGS) \
$(LIBWRAP_CFLAGS) \
$(SQLITE_CFLAGS)
src_mpd_LDADD = \
+ $(DB_LIBS) \
$(PLAYLIST_LIBS) \
$(AVAHI_LIBS) \
$(LIBWRAP_LDFLAGS) \
@@ -49,13 +52,7 @@ mpd_headers = \
src/audio_parser.h \
src/output_internal.h \
src/output_api.h \
- src/output_list.h \
src/output_all.h \
- src/output_thread.h \
- src/output_control.h \
- src/output_state.h \
- src/output_print.h \
- src/output_command.h \
src/filter_internal.h \
src/filter_config.h \
src/filter_plugin.h \
@@ -67,39 +64,20 @@ mpd_headers = \
src/filter/volume_filter_plugin.h \
src/command.h \
src/idle.h \
- src/cmdline.h \
src/conf.h \
- src/crossfade.h \
- src/dbUtils.h \
- src/decoder_thread.h \
- src/decoder_control.h \
src/decoder_plugin.h \
src/decoder_command.h \
src/decoder_buffer.h \
src/decoder_api.h \
src/decoder_plugin.h \
- src/decoder_internal.h \
- src/directory.h \
- src/directory_save.h \
- src/database.h \
src/encoder_plugin.h \
src/encoder_list.h \
src/encoder_api.h \
- src/exclude.h \
src/fd_util.h \
+ src/gerror.h \
src/glib_compat.h \
- src/update.h \
- src/inotify_source.h \
- src/inotify_queue.h \
- src/inotify_update.h \
src/gcc.h \
src/decoder_list.h \
- src/decoder_print.h \
- src/decoder/flac_compat.h \
- src/decoder/flac_metadata.h \
- src/decoder/flac_pcm.h \
- src/decoder/_flac_common.h \
- src/decoder/_ogg_common.h \
src/decoder/pcm_decoder_plugin.h \
src/input_init.h \
src/input_plugin.h \
@@ -113,17 +91,12 @@ mpd_headers = \
src/input/despotify_input_plugin.h \
src/input/cdio_paranoia_input_plugin.h \
src/despotify_utils.h \
- src/text_file.h \
src/text_input_stream.h \
src/icy_server.h \
src/icy_metadata.h \
- src/client.h \
- src/client_internal.h \
src/server_socket.h \
- src/listen.h \
src/log.h \
src/ls.h \
- src/main.h \
src/mixer_all.h \
src/mixer_api.h \
src/mixer_control.h \
@@ -136,32 +109,16 @@ mpd_headers = \
src/daemon.h \
src/AudioCompress/config.h \
src/AudioCompress/compress.h \
- src/buffer.h \
- src/pipe.h \
- src/chunk.h \
src/path.h \
- src/mapper.h \
src/open.h \
src/output/httpd_client.h \
src/output/httpd_internal.h \
src/page.h \
- src/permission.h \
- src/player_thread.h \
- src/player_control.h \
- src/playlist.h \
+ src/Playlist.hxx \
src/playlist_error.h \
- src/playlist_internal.h \
- src/playlist_print.h \
- src/playlist_save.h \
- src/playlist_state.h \
+ src/PlaylistInternal.hxx \
src/playlist_plugin.h \
src/playlist_list.h \
- src/playlist_mapper.h \
- src/playlist_any.h \
- src/playlist_song.h \
- src/playlist_queue.h \
- src/playlist_vector.h \
- src/playlist_database.h \
src/playlist/extm3u_playlist_plugin.h \
src/playlist/m3u_playlist_plugin.h \
src/playlist/pls_playlist_plugin.h \
@@ -174,24 +131,16 @@ mpd_headers = \
src/poison.h \
src/riff.h \
src/aiff.h \
- src/queue.h \
- src/queue_print.h \
- src/queue_save.h \
src/refcount.h \
src/replay_gain_config.h \
src/replay_gain_info.h \
src/replay_gain_ape.h \
src/sig_handlers.h \
+ src/TimePrint.cxx src/TimePrint.hxx \
src/song.h \
- src/song_print.h \
- src/song_save.h \
- src/song_sticker.h \
src/song_sort.c src/song_sort.h \
src/socket_util.h \
- src/state_file.h \
src/stats.h \
- src/sticker.h \
- src/sticker_print.h \
src/tag.h \
src/tag_internal.h \
src/tag_pool.h \
@@ -199,17 +148,12 @@ mpd_headers = \
src/tag_ape.h \
src/tag_id3.h \
src/tag_rva2.h \
- src/tag_print.h \
- src/tag_save.h \
src/tokenizer.h \
- src/strset.h \
src/uri.h \
src/utils.h \
src/string_util.h \
src/volume.h \
src/zeroconf.h src/zeroconf-internal.h \
- src/locate.h \
- src/stored_playlist.h \
src/timer.h \
src/archive_api.h \
src/archive_internal.h \
@@ -226,6 +170,8 @@ src_mpd_SOURCES = \
$(DECODER_SRC) \
$(OUTPUT_API_SRC) \
$(MIXER_API_SRC) \
+ src/thread/Mutex.hxx \
+ src/thread/CriticalSection.hxx \
src/glib_socket.h \
src/clock.c src/clock.h \
src/notify.c \
@@ -233,126 +179,136 @@ src_mpd_SOURCES = \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
- src/protocol/argparser.c src/protocol/argparser.h \
- src/protocol/result.c src/protocol/result.h \
- src/command.c \
+ src/protocol/ArgParser.cxx src/protocol/ArgParser.hxx \
+ src/protocol/Result.cxx src/protocol/Result.hxx \
+ src/CommandError.cxx src/CommandError.hxx \
+ src/AllCommands.cxx src/AllCommands.hxx \
+ src/QueueCommands.cxx src/QueueCommands.hxx \
+ src/PlayerCommands.cxx src/PlayerCommands.hxx \
+ src/PlaylistCommands.cxx src/PlaylistCommands.hxx \
+ src/DatabaseCommands.cxx src/DatabaseCommands.hxx \
+ src/OutputCommands.cxx src/OutputCommands.hxx \
+ src/MessageCommands.cxx src/MessageCommands.hxx \
+ src/OtherCommands.cxx src/OtherCommands.hxx \
src/idle.c \
- src/cmdline.c \
+ src/CommandLine.cxx src/CommandLine.hxx \
src/conf.c \
- src/crossfade.c \
+ src/CrossFade.cxx src/CrossFade.hxx \
src/cue/cue_parser.c src/cue/cue_parser.h \
- src/dbUtils.c \
- src/decoder_thread.c \
- src/decoder_control.c \
- src/decoder_api.c \
- src/decoder_internal.c \
- src/decoder_print.c \
- src/directory.c \
- src/directory_save.c \
- src/database.c \
- src/db_internal.h \
+ src/decoder_error.h \
+ src/DecoderThread.cxx src/DecoderThread.hxx \
+ src/DecoderControl.cxx src/DecoderControl.hxx \
+ src/DecoderAPI.cxx \
+ src/DecoderInternal.cxx src/DecoderInternal.hxx \
+ src/DecoderPrint.cxx src/DecoderPrint.hxx \
+ src/Directory.cxx src/Directory.hxx \
+ src/DirectorySave.cxx src/DirectorySave.hxx \
+ src/DatabaseSimple.hxx \
+ src/DatabaseGlue.cxx src/DatabaseGlue.hxx \
+ src/DatabasePrint.cxx src/DatabasePrint.hxx \
+ src/DatabaseQueue.cxx src/DatabaseQueue.hxx \
+ src/DatabasePlaylist.cxx src/DatabasePlaylist.hxx \
src/db_error.h \
- src/db_lock.c src/db_lock.h \
- src/db_save.c src/db_save.h \
- src/db_print.c src/db_print.h \
- src/db_plugin.h \
- src/db_visitor.h \
- src/db_selection.h \
- src/db/simple_db_plugin.c src/db/simple_db_plugin.h \
- src/exclude.c \
+ src/DatabaseLock.cxx src/DatabaseLock.hxx \
+ src/DatabaseSave.cxx src/DatabaseSave.hxx \
+ src/DatabasePlugin.hxx \
+ src/DatabaseVisitor.hxx \
+ src/DatabaseSelection.cxx src/DatabaseSelection.hxx \
+ src/ExcludeList.cxx src/ExcludeList.hxx \
src/fd_util.c \
src/fifo_buffer.c src/fifo_buffer.h \
src/growing_fifo.c src/growing_fifo.h \
src/filter_config.c \
src/filter_plugin.c \
src/filter_registry.c \
- src/update.c \
- src/update_queue.c src/update_queue.h \
- src/update_io.c src/update_io.h \
- src/update_db.c src/update_db.h \
- src/update_walk.c src/update_walk.h \
- src/update_song.c src/update_song.h \
- src/update_container.c src/update_container.h \
- src/update_internal.h \
- src/update_remove.c src/update_remove.h \
- src/client.c \
- src/client_event.c \
- src/client_expire.c \
- src/client_global.c \
- src/client_idle.h \
- src/client_idle.c \
- src/client_list.c \
- src/client_new.c \
- src/client_process.c \
- src/client_read.c \
- src/client_write.c \
- src/client_message.h \
- src/client_message.c \
- src/client_subscribe.h \
- src/client_subscribe.c \
- src/client_file.c src/client_file.h \
+ src/UpdateGlue.cxx src/UpdateGlue.hxx \
+ src/UpdateQueue.cxx src/UpdateQueue.hxx \
+ src/UpdateIO.cxx src/UpdateIO.hxx \
+ src/UpdateDatabase.cxx src/UpdateDatabase.hxx \
+ src/UpdateWalk.cxx src/UpdateWalk.hxx \
+ src/UpdateSong.cxx src/UpdateSong.hxx \
+ src/UpdateContainer.cxx src/UpdateContainer.hxx \
+ src/UpdateInternal.hxx \
+ src/UpdateRemove.cxx src/UpdateRemove.hxx \
+ src/CommandListBuilder.cxx src/CommandListBuilder.hxx \
+ src/Client.cxx src/Client.hxx \
+ src/ClientInternal.hxx \
+ src/ClientEvent.cxx \
+ src/ClientExpire.cxx \
+ src/ClientGlobal.cxx \
+ src/ClientIdle.cxx src/ClientIdle.hxx \
+ src/ClientList.cxx \
+ src/ClientNew.cxx \
+ src/ClientProcess.cxx \
+ src/ClientRead.cxx \
+ src/ClientWrite.cxx \
+ src/ClientMessage.cxx src/ClientMessage.hxx \
+ src/ClientSubscribe.cxx src/ClientSubscribe.hxx \
+ src/ClientFile.cxx src/ClientFile.hxx \
src/server_socket.c \
- src/listen.c \
+ src/Listen.cxx src/Listen.hxx \
src/log.c \
- src/ls.c \
+ src/ls.cxx \
+ src/io_error.h \
src/io_thread.c src/io_thread.h \
- src/main.c \
- src/main_win32.c \
+ src/Main.cxx src/Main.hxx \
+ src/Win32Main.cxx \
src/event_pipe.c \
src/daemon.c \
src/AudioCompress/compress.c \
- src/buffer.c \
- src/pipe.c \
- src/chunk.c \
+ src/MusicBuffer.cxx src/MusicBuffer.hxx \
+ src/MusicPipe.cxx src/MusicPipe.hxx \
+ src/MusicChunk.cxx src/MusicChunk.hxx \
src/path.c \
- src/mapper.c \
+ src/Mapper.cxx src/Mapper.hxx \
src/page.c \
- src/permission.c \
- src/player_thread.c \
- src/player_control.c \
- src/playlist.c \
- src/playlist_global.c \
- src/playlist_control.c \
- src/playlist_edit.c \
- src/playlist_print.c \
- src/playlist_save.c \
- src/playlist_mapper.c \
- src/playlist_any.c \
- src/playlist_song.c \
- src/playlist_state.c \
- src/playlist_queue.c \
- src/playlist_vector.c \
- src/playlist_database.c \
- src/queue.c \
- src/queue_print.c \
- src/queue_save.c \
- src/replay_gain_config.c \
- src/replay_gain_info.c \
+ src/Partition.hxx \
+ src/Permission.cxx src/Permission.hxx \
+ src/PlayerThread.cxx src/PlayerThread.hxx \
+ src/PlayerControl.cxx src/PlayerControl.hxx \
+ src/Playlist.cxx \
+ src/PlaylistGlobal.cxx \
+ src/PlaylistControl.cxx \
+ src/PlaylistEdit.cxx \
+ src/PlaylistPrint.cxx src/PlaylistPrint.hxx \
+ src/PlaylistSave.cxx src/PlaylistSave.hxx \
+ src/PlaylistMapper.cxx src/PlaylistMapper.hxx \
+ src/PlaylistAny.cxx src/PlaylistAny.hxx \
+ src/PlaylistSong.cxx src/PlaylistSong.hxx \
+ src/PlaylistState.cxx src/PlaylistState.hxx \
+ src/PlaylistQueue.cxx src/PlaylistQueue.hxx \
+ src/PlaylistVector.cxx src/PlaylistVector.hxx \
+ src/PlaylistInfo.hxx \
+ src/PlaylistDatabase.cxx \
+ src/Queue.cxx src/Queue.hxx \
+ src/QueuePrint.cxx src/QueuePrint.hxx \
+ src/QueueSave.cxx src/QueueSave.hxx \
+ src/ReplayGainConfig.cxx \
+ src/ReplayGainInfo.cxx \
src/sig_handlers.c \
- src/song.c \
- src/song_update.c \
- src/song_print.c \
- src/song_save.c \
+ src/Song.cxx \
+ src/SongUpdate.cxx \
+ src/SongPrint.cxx src/SongPrint.hxx \
+ src/SongSave.cxx src/SongSave.hxx \
src/resolver.c src/resolver.h \
src/socket_util.c \
- src/state_file.c \
- src/stats.c \
+ src/StateFile.cxx src/StateFile.hxx \
+ src/Stats.cxx \
src/tag.c \
src/tag_pool.c \
- src/tag_print.c \
- src/tag_save.c \
+ src/TagPrint.cxx src/TagPrint.hxx \
+ src/TagSave.cxx src/TagSave.hxx \
src/tag_handler.c src/tag_handler.h \
src/tag_file.c src/tag_file.h \
src/tokenizer.c \
- src/text_file.c \
+ src/TextFile.cxx src/TextFile.hxx \
src/text_input_stream.c \
- src/strset.c \
src/uri.c \
src/utils.c \
src/string_util.c \
src/volume.c \
- src/locate.c \
- src/stored_playlist.c \
+ src/SongFilter.cxx src/SongFilter.hxx \
+ src/PlaylistFile.cxx src/PlaylistFile.hxx \
src/timer.c
#
@@ -376,21 +332,24 @@ endif
if ENABLE_INOTIFY
src_mpd_SOURCES += \
- src/inotify_source.c \
- src/inotify_queue.c \
- src/inotify_update.c
+ src/InotifySource.cxx src/InotifySource.hxx \
+ src/InotifyQueue.cxx src/InotifyQueue.hxx \
+ src/InotifyUpdate.cxx src/InotifyUpdate.hxx
endif
if ENABLE_SQLITE
src_mpd_SOURCES += \
- src/sticker.c \
- src/sticker_print.c \
- src/song_sticker.c
+ src/StickerCommands.cxx src/StickerCommands.hxx \
+ src/StickerDatabase.cxx src/StickerDatabase.hxx \
+ src/StickerPrint.cxx src/StickerPrint.hxx \
+ src/SongSticker.cxx src/SongSticker.hxx
endif
# Generic utility library
libutil_a_SOURCES = \
+ src/util/SliceBuffer.hxx \
+ src/util/HugeAllocator.cxx src/util/HugeAllocator.hxx \
src/util/list.h \
src/util/list_sort.c src/util/list_sort.h \
src/util/byte_reverse.c src/util/byte_reverse.h \
@@ -427,6 +386,22 @@ if HAVE_LIBSAMPLERATE
libpcm_a_SOURCES += src/pcm_resample_libsamplerate.c
endif
+# database plugins
+
+libdb_plugins_a_SOURCES = \
+ src/DatabaseRegistry.cxx src/DatabaseRegistry.hxx \
+ src/DatabaseHelpers.cxx src/DatabaseHelpers.hxx \
+ src/db/SimpleDatabasePlugin.cxx src/db/SimpleDatabasePlugin.hxx
+
+if HAVE_LIBMPDCLIENT
+libdb_plugins_a_SOURCES += \
+ src/db/ProxyDatabasePlugin.cxx src/db/ProxyDatabasePlugin.hxx
+endif
+
+DB_LIBS = \
+ libdb_plugins.a \
+ $(LIBMPDCLIENT_LIBS)
+
# archive plugins
if ENABLE_ARCHIVE
@@ -434,7 +409,7 @@ if ENABLE_ARCHIVE
noinst_LIBRARIES += libarchive.a
src_mpd_SOURCES += \
- src/update_archive.c src/update_archive.h
+ src/UpdateArchive.cxx src/UpdateArchive.hxx
libarchive_a_SOURCES = \
src/archive_api.c \
@@ -515,8 +490,10 @@ libdecoder_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(WAVPACK_CFLAGS) \
$(MAD_CFLAGS) \
$(MPG123_CFLAGS) \
+ $(OPUS_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(MPCDEC_CFLAGS) \
+ $(ADPLUG_CFLAGS) \
$(FAAD_CFLAGS)
DECODER_LIBS = \
@@ -532,9 +509,11 @@ DECODER_LIBS = \
$(WAVPACK_LIBS) \
$(MAD_LIBS) \
$(MPG123_LIBS) \
+ $(OPUS_LIBS) \
$(MP4FF_LIBS) \
$(FFMPEG_LIBS) \
$(MPCDEC_LIBS) \
+ $(ADPLUG_LIBS) \
$(FAAD_LIBS)
DECODER_SRC =
@@ -551,10 +530,29 @@ if HAVE_MPCDEC
libdecoder_plugins_a_SOURCES += src/decoder/mpcdec_decoder_plugin.c
endif
+if HAVE_OPUS
+libdecoder_plugins_a_SOURCES += \
+ src/decoder/OggUtil.cxx \
+ src/decoder/OggUtil.hxx \
+ src/decoder/OpusReader.hxx \
+ src/decoder/OpusHead.hxx \
+ src/decoder/OpusHead.cxx \
+ src/decoder/OpusTags.cxx \
+ src/decoder/OpusTags.hxx \
+ src/decoder/OpusDecoderPlugin.cxx \
+ src/decoder/OpusDecoderPlugin.h
+endif
+
if HAVE_WAVPACK
libdecoder_plugins_a_SOURCES += src/decoder/wavpack_decoder_plugin.c
endif
+if HAVE_ADPLUG
+libdecoder_plugins_a_SOURCES += \
+ src/decoder/AdPlugDecoderPlugin.cxx \
+ src/decoder/AdPlugDecoderPlugin.h
+endif
+
if HAVE_FAAD
libdecoder_plugins_a_SOURCES += src/decoder/faad_decoder_plugin.c
endif
@@ -563,15 +561,10 @@ if HAVE_MP4
libdecoder_plugins_a_SOURCES += src/decoder/mp4ff_decoder_plugin.c
endif
-if HAVE_OGG_COMMON
-libdecoder_plugins_a_SOURCES += src/decoder/_ogg_common.c
-endif
-
-if HAVE_FLAC_COMMON
+if HAVE_XIPH
libdecoder_plugins_a_SOURCES += \
- src/decoder/flac_metadata.c \
- src/decoder/flac_pcm.c \
- src/decoder/_flac_common.c
+ src/decoder/XiphTags.c src/decoder/XiphTags.h \
+ src/decoder/ogg_codec.c src/decoder/ogg_codec.h
endif
if ENABLE_VORBIS_DECODER
@@ -582,7 +575,14 @@ libdecoder_plugins_a_SOURCES += \
endif
if HAVE_FLAC
-libdecoder_plugins_a_SOURCES += src/decoder/flac_decoder_plugin.c
+libdecoder_plugins_a_SOURCES += \
+ src/decoder/FLACInput.cxx src/decoder/FLACInput.hxx \
+ src/decoder/FLACIOHandle.cxx src/decoder/FLACIOHandle.hxx \
+ src/decoder/FLACMetaData.cxx src/decoder/FLACMetaData.hxx \
+ src/decoder/FLAC_PCM.cxx src/decoder/FLAC_PCM.hxx \
+ src/decoder/FLACCommon.cxx src/decoder/FLACCommon.hxx \
+ src/decoder/FLACDecoderPlugin.cxx \
+ src/decoder/FLACDecoderPlugin.h
endif
if HAVE_AUDIOFILE
@@ -639,6 +639,7 @@ libencoder_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(LAME_CFLAGS) \
$(TWOLAME_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
+ $(OPUS_CFLAGS) \
$(VORBISENC_CFLAGS)
ENCODER_LIBS = \
@@ -646,19 +647,28 @@ ENCODER_LIBS = \
$(LAME_LIBS) \
$(TWOLAME_LIBS) \
$(FLAC_LIBS) \
+ $(OPUS_LIBS) \
$(VORBISENC_LIBS)
-libencoder_plugins_a_SOURCES =
-
-libencoder_plugins_a_SOURCES += src/encoder_list.c
-libencoder_plugins_a_SOURCES += src/encoder/null_encoder.c
+libencoder_plugins_a_SOURCES = \
+ src/encoder/OggStream.hxx \
+ src/encoder/null_encoder.c \
+ src/encoder_list.c
if ENABLE_WAVE_ENCODER
libencoder_plugins_a_SOURCES += src/encoder/wave_encoder.c
endif
if ENABLE_VORBIS_ENCODER
-libencoder_plugins_a_SOURCES += src/encoder/vorbis_encoder.c
+libencoder_plugins_a_SOURCES += \
+ src/encoder/VorbisEncoderPlugin.cxx \
+ src/encoder/VorbisEncoderPlugin.hxx
+endif
+
+if HAVE_OPUS
+libencoder_plugins_a_SOURCES += \
+ src/encoder/OpusEncoderPlugin.cxx \
+ src/encoder/OpusEncoderPlugin.hxx
endif
if ENABLE_LAME_ENCODER
@@ -770,16 +780,17 @@ OUTPUT_LIBS = \
$(SHOUT_LIBS)
OUTPUT_API_SRC = \
- src/output_list.c \
- src/output_all.c \
- src/output_thread.c \
- src/output_control.c \
- src/output_state.c \
- src/output_print.c \
- src/output_command.c \
- src/output_plugin.c src/output_plugin.h \
- src/output_finish.c \
- src/output_init.c
+ src/OutputList.cxx src/OutputList.hxx \
+ src/OutputAll.cxx \
+ src/OutputThread.cxx src/OutputThread.hxx \
+ src/OutputError.hxx \
+ src/OutputControl.cxx src/OutputControl.hxx \
+ src/OutputState.cxx src/OutputState.hxx \
+ src/OutputPrint.cxx src/OutputPrint.hxx \
+ src/OutputCommand.cxx src/OutputCommand.hxx \
+ src/OutputPlugin.cxx src/output_plugin.h \
+ src/OutputFinish.cxx \
+ src/OutputInit.cxx
liboutput_plugins_a_SOURCES = \
src/output/null_output_plugin.h \
@@ -990,7 +1001,7 @@ if ENABLE_TEST
C_TESTS = \
test/test_byte_reverse \
test/test_pcm \
- test/test_queue_priority
+ test/TestQueuePriority
TESTS = $(C_TESTS)
@@ -998,6 +1009,7 @@ noinst_PROGRAMS = \
$(C_TESTS) \
test/read_conf \
test/run_resolver \
+ test/DumpDatabase \
test/run_input \
test/dump_text_file \
test/dump_playlist \
@@ -1028,6 +1040,23 @@ test_run_resolver_LDADD = \
test_run_resolver_SOURCES = test/run_resolver.c \
src/resolver.c
+test_DumpDatabase_LDADD = \
+ $(DB_LIBS) \
+ libutil.a \
+ $(GLIB_LIBS)
+test_DumpDatabase_SOURCES = test/DumpDatabase.cxx \
+ src/DatabaseRegistry.cxx \
+ src/DatabaseSelection.cxx \
+ src/Directory.cxx src/DirectorySave.cxx \
+ src/PlaylistVector.cxx src/PlaylistDatabase.cxx \
+ src/DatabaseLock.cxx src/DatabaseSave.cxx \
+ src/Song.cxx src/song_sort.c src/SongSave.cxx \
+ src/tag.c src/tag_pool.c src/TagSave.cxx \
+ src/path.c \
+ src/SongFilter.cxx \
+ src/TextFile.cxx \
+ src/conf.c src/tokenizer.c src/utils.c src/string_util.c
+
test_run_input_LDADD = \
$(INPUT_LIBS) \
$(ARCHIVE_LIBS) \
@@ -1036,7 +1065,7 @@ test_run_input_SOURCES = test/run_input.c \
test/stdbin.h \
src/io_thread.c src/io_thread.h \
src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
- src/tag.c src/tag_pool.c src/tag_save.c \
+ src/tag.c src/tag_pool.c src/TagSave.cxx \
src/fd_util.c
test_dump_text_file_LDADD = \
@@ -1060,12 +1089,12 @@ test_dump_playlist_LDADD = \
$(TAG_LIBS) \
libutil.a \
$(GLIB_LIBS)
-test_dump_playlist_SOURCES = test/dump_playlist.c \
+test_dump_playlist_SOURCES = test/dump_playlist.cxx \
$(DECODER_SRC) \
src/io_thread.c src/io_thread.h \
src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
src/uri.c \
- src/song.c src/tag.c src/tag_pool.c src/tag_save.c \
+ src/Song.cxx src/tag.c src/tag_pool.c src/TagSave.cxx \
src/tag_handler.c src/tag_file.c \
src/audio_check.c src/pcm_buffer.c \
src/text_input_stream.c src/fifo_buffer.c \
@@ -1074,8 +1103,8 @@ test_dump_playlist_SOURCES = test/dump_playlist.c \
if HAVE_FLAC
test_dump_playlist_SOURCES += \
- src/replay_gain_info.c \
- src/decoder/flac_metadata.c
+ src/ReplayGainInfo.cxx \
+ src/decoder/FLACMetaData.cxx
endif
test_run_decoder_LDADD = \
@@ -1091,7 +1120,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \
src/io_thread.c src/io_thread.h \
src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
src/tag.c src/tag_pool.c src/tag_handler.c \
- src/replay_gain_info.c \
+ src/ReplayGainInfo.cxx \
src/uri.c \
src/fd_util.c \
src/audio_check.c \
@@ -1113,7 +1142,7 @@ test_read_tags_SOURCES = test/read_tags.c \
src/io_thread.c src/io_thread.h \
src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
src/tag.c src/tag_pool.c src/tag_handler.c \
- src/replay_gain_info.c \
+ src/ReplayGainInfo.cxx \
src/uri.c \
src/fd_util.c \
src/audio_check.c \
@@ -1141,8 +1170,8 @@ test_run_filter_SOURCES = test/run_filter.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
- src/replay_gain_config.c \
- src/replay_gain_info.c \
+ src/ReplayGainConfig.cxx \
+ src/ReplayGainInfo.cxx \
src/AudioCompress/compress.c
if ENABLE_DESPOTIFY
@@ -1231,7 +1260,7 @@ test_run_output_LDADD = $(MPD_LIBS) \
$(FILTER_LIBS) \
libutil.a \
$(GLIB_LIBS)
-test_run_output_SOURCES = test/run_output.c \
+test_run_output_SOURCES = test/run_output.cxx \
test/stdbin.h \
src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
src/io_thread.c src/io_thread.h \
@@ -1244,16 +1273,16 @@ test_run_output_SOURCES = test/run_output.c \
src/page.c \
src/socket_util.c \
src/resolver.c \
- src/output_init.c src/output_finish.c src/output_list.c \
- src/output_plugin.c \
+ src/OutputInit.cxx src/OutputFinish.cxx src/OutputList.cxx \
+ src/OutputPlugin.cxx \
src/mixer_api.c \
src/mixer_control.c \
src/mixer_type.c \
src/filter_plugin.c \
src/filter_config.c \
src/AudioCompress/compress.c \
- src/replay_gain_info.c \
- src/replay_gain_config.c \
+ src/ReplayGainInfo.cxx \
+ src/ReplayGainConfig.cxx \
src/fd_util.c \
src/server_socket.c
@@ -1282,12 +1311,12 @@ TESTS += test/test_archive_iso9660.sh
endif
if ENABLE_INOTIFY
-noinst_PROGRAMS += test/run_inotify
-test_run_inotify_SOURCES = test/run_inotify.c \
+noinst_PROGRAMS += test/RunInotify
+test_RunInotify_SOURCES = test/RunInotify.c \
src/fd_util.c \
src/fifo_buffer.c \
- src/inotify_source.c
-test_run_inotify_LDADD = $(GLIB_LIBS)
+ src/InotifySource.cxx
+test_RunInotify_LDADD = $(GLIB_LIBS)
endif
test_test_byte_reverse_SOURCES = \
@@ -1308,13 +1337,12 @@ test_test_pcm_LDADD = \
libutil.a \
$(GLIB_LIBS)
-test_test_queue_priority_SOURCES = \
- src/queue.c \
- test/test_queue_priority.c
-test_test_queue_priority_LDADD = \
+test_TestQueuePriority_SOURCES = \
+ src/Queue.cxx \
+ test/TestQueuePriority.cxx
+test_TestQueuePriority_LDADD = \
$(GLIB_LIBS)
-if HAVE_CXX
noinst_PROGRAMS += src/dsd2pcm/dsd2pcm
src_dsd2pcm_dsd2pcm_SOURCES = \
@@ -1322,7 +1350,6 @@ src_dsd2pcm_dsd2pcm_SOURCES = \
src/dsd2pcm/noiseshape.c src/dsd2pcm/noiseshape.h \
src/dsd2pcm/main.cpp
src_dsd2pcm_dsd2pcm_LDADD = libutil.a
-endif
endif
diff --git a/NEWS b/NEWS
index 65e9b4c8c..822dc2fd5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,17 @@
+ver 0.18 (2012/??/??)
+* decoder:
+ - adplug: new decoder plugin using libadplug
+ - flac: require libFLAC 1.2 or newer
+ - flac: support FLAC files inside archives
+ - opus: new decoder plugin for the Opus codec
+ - vorbis: skip 16 bit quantisation, provide float samples
+* encoder:
+ - opus: new encoder plugin for the Opus codec
+ - vorbis: accept floating point input samples
+* output:
+ - new option "tags" may be used to disable sending tags to output
+* improved decoder/output error reporting
+
ver 0.17.3 (2013/01/06)
* output:
- osx: fix pops during playback
diff --git a/configure.ac b/configure.ac
index c246ddbb3..9ade4bdf2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,13 +1,13 @@
AC_PREREQ(2.60)
-AC_INIT(mpd, 0.17.3, musicpd-dev-team@lists.sourceforge.net)
+AC_INIT(mpd, 0.18~git, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
-VERSION_MINOR=17
+VERSION_MINOR=18
VERSION_REVISION=0
VERSION_EXTRA=0
-AC_CONFIG_SRCDIR([src/main.c])
+AC_CONFIG_SRCDIR([src/Main.cxx])
AM_INIT_AUTOMAKE([foreign 1.11 dist-bzip2 subdir-objects])
AM_SILENT_RULES
AC_CONFIG_HEADERS(config.h)
@@ -23,22 +23,6 @@ AC_PROG_CC_C99
AC_PROG_CXX
AC_PROG_RANLIB
-HAVE_CXX=yes
-if test x$CXX = xg++; then
- # CXX=g++ probably means that autoconf hasn't found any C++
- # compiler; to be sure, we check again
- AC_PATH_PROG(CXX, $CXX, no)
- if test x$CXX = xno; then
- # no, we don't have C++ - the following hack is
- # required because automake insists on using $(CXX)
- # for linking the MPD binary
- AC_MSG_NOTICE([Disabling C++ support])
- CXX="$CC"
- HAVE_CXX=no
- fi
-fi
-AM_CONDITIONAL(HAVE_CXX, test x$HAVE_CXX = xyes)
-
AC_PROG_INSTALL
AC_PROG_MAKE_SET
PKG_PROG_PKG_CONFIG
@@ -126,6 +110,19 @@ if test -z "$prefix" || test "x$prefix" = xNONE; then
fi
dnl ---------------------------------------------------------------------------
+dnl Language Checks
+dnl ---------------------------------------------------------------------------
+
+AC_CXX_COMPILE_STDCXX_0X
+if test "$ax_cv_cxx_compile_cxx0x_native" != yes; then
+ if test "$ax_cv_cxx_compile_cxx0x_gxx" = yes; then
+ AM_CXXFLAGS="$AM_CXXFLAGS -std=gnu++0x"
+ elif test "$ax_cv_cxx_compile_cxx0x_cxx" = yes; then
+ AM_CXXFLAGS="$AM_CXXFLAGS -std=c++0x"
+ fi
+fi
+
+dnl ---------------------------------------------------------------------------
dnl Header/Library Checks
dnl ---------------------------------------------------------------------------
AC_CHECK_FUNCS(daemon fork)
@@ -147,6 +144,17 @@ AC_CHECK_HEADERS(valgrind/memcheck.h)
dnl ---------------------------------------------------------------------------
dnl Allow tools to be specifically built
dnl ---------------------------------------------------------------------------
+
+AC_ARG_ENABLE(mpdclient,
+ AS_HELP_STRING([--enable-libmpdclient],
+ [enable support for the MPD client]),,
+ enable_libmpdclient=auto)
+
+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=auto])
@@ -326,6 +334,11 @@ AC_ARG_ENABLE(openal,
[enable OpenAL support (default: disable)]),,
enable_openal=no)
+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)]),,
@@ -537,6 +550,15 @@ dnl ---------------------------------------------------------------------------
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)
+
dnl --------------------------------- inotify ---------------------------------
AC_CHECK_FUNCS(inotify_init inotify_init1)
@@ -806,6 +828,14 @@ dnl ---------------------------------------------------------------------------
dnl Decoder Plugins
dnl ---------------------------------------------------------------------------
+dnl -------------------------------- libadplug --------------------------------
+MPD_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.1.7],
[audiofile decoder plugin], [libaudiofile not found])
@@ -832,7 +862,7 @@ AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes)
dnl ----------------------------------- FLAC ----------------------------------
-MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1],
+MPD_AUTO_PKG(flac, FLAC, [flac >= 1.2],
[FLAC decoder], [libFLAC not found])
if test x$enable_flac = xyes; then
@@ -899,9 +929,6 @@ fi
AM_CONDITIONAL(ENABLE_MIKMOD_DECODER, test x$enable_mikmod = xyes)
dnl -------------------------------- libmodplug -------------------------------
-found_modplug=$HAVE_CXX
-MPD_AUTO_PRE(modplug, [modplug decoder plugin], [No C++ compiler found])
-
MPD_AUTO_PKG(modplug, MODPLUG, [libmodplug],
[modplug decoder plugin], [libmodplug not found])
@@ -910,6 +937,14 @@ if test x$enable_modplug = xyes; then
fi
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
+dnl -------------------------------- libopus ----------------------------------
+MPD_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],
@@ -1015,9 +1050,6 @@ fi
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes)
dnl --------------------------------- sidplay ---------------------------------
-found_sidplay=$HAVE_CXX
-MPD_AUTO_PRE(sidplay, [sidplay decoder plugin], [No C++ compiler found])
-
if test x$enable_sidplay != xno; then
# we're not using pkg-config here
# because libsidplay2's .pc file requires libtool
@@ -1095,6 +1127,7 @@ if
test x$enable_mp4 = xno &&
test x$enable_mpc = xno &&
test x$enable_mpg123 = xno &&
+ test x$enable_opus = xno &&
test x$enable_sidplay = xno &&
test x$enable_tremor = xno &&
test x$enable_vorbis = xno &&
@@ -1104,11 +1137,8 @@ if
AC_MSG_ERROR([No input plugins supported!])
fi
-AM_CONDITIONAL(HAVE_OGG_COMMON,
- test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes)
-
-AM_CONDITIONAL(HAVE_FLAC_COMMON,
- test x$enable_flac = xyes)
+AM_CONDITIONAL(HAVE_XIPH,
+ test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes || test x$enable_opus = xyes)
dnl ---------------------------------------------------------------------------
dnl Encoders for Streaming Audio Output Plugins
@@ -1196,6 +1226,7 @@ fi
dnl --------------------------- encoder plugins test --------------------------
if test x$enable_vorbis_encoder != xno ||
+ test x$enable_opus != xno ||
test x$enable_lame_encoder != xno ||
test x$enable_twolame_encoder != xno ||
test x$enable_flac_encoder != xno ||
@@ -1499,6 +1530,22 @@ dnl ---------------------------------------------------------------------------
dnl ---------------------------------- debug ----------------------------------
if test "x$enable_debug" = xno; then
AM_CPPFLAGS="$AM_CPPFLAGS -DNDEBUG"
+
+ AX_APPEND_COMPILE_FLAGS([-ffunction-sections])
+ AX_APPEND_COMPILE_FLAGS([-fdata-sections])
+ AX_APPEND_COMPILE_FLAGS([-fvisibility=hidden])
+
+ AC_LANG_PUSH([C++])
+ AX_APPEND_COMPILE_FLAGS([-ffunction-sections])
+ AX_APPEND_COMPILE_FLAGS([-fdata-sections])
+ AX_APPEND_COMPILE_FLAGS([-fvisibility=hidden])
+ AX_APPEND_COMPILE_FLAGS([-fno-threadsafe-statics])
+ AX_APPEND_COMPILE_FLAGS([-fmerge-all-constants])
+ AX_APPEND_COMPILE_FLAGS([-fno-exceptions])
+ AX_APPEND_COMPILE_FLAGS([-fno-rtti])
+ AC_LANG_POP
+
+ AX_APPEND_LINK_FLAGS([-Wl,--gc-sections])
fi
dnl ----------------------------------- GCC -----------------------------------
@@ -1513,6 +1560,17 @@ then
AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
AX_APPEND_COMPILE_FLAGS([-pedantic])
+
+ AC_LANG_PUSH([C++])
+ AX_APPEND_COMPILE_FLAGS([-Wall])
+ AX_APPEND_COMPILE_FLAGS([-Wextra])
+ AX_APPEND_COMPILE_FLAGS([-Wmissing-declarations])
+ AX_APPEND_COMPILE_FLAGS([-Wshadow])
+ AX_APPEND_COMPILE_FLAGS([-Wpointer-arith])
+ AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
+ AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
+ AX_APPEND_COMPILE_FLAGS([-Wsign-compare])
+ AC_LANG_POP
fi
dnl ---------------------------- warnings as errors ---------------------------
@@ -1559,6 +1617,7 @@ results(mpg123, [MPG123])
results(mp4, [MP4])
results(mpc, [Musepack])
printf '\n\t'
+results(opus, [OPUS])
results(tremor, [OggTremor])
results(vorbis, [OggVorbis])
results(audiofile, [WAVE])
@@ -1602,6 +1661,7 @@ if
results(flac_encoder, [FLAC])
results(lame_encoder, [LAME])
results(vorbis_encoder, [Ogg Vorbis])
+ results(opus, [Opus])
results(twolame_encoder, [TwoLAME])
results(wave_encoder, [WAVE])
fi
diff --git a/doc/user.xml b/doc/user.xml
index 38d8a9d85..c83c625a2 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -165,6 +165,53 @@ systemctl start mpd.socket</programlisting>
</section>
<section>
+ <title>Configuring database plugins</title>
+
+ <para>
+ If a music directory is configured, one database plugin is
+ used. To configure this plugin, add a
+ <varname>database</varname> block to
+ <filename>mpd.conf</filename>:
+ </para>
+
+ <programlisting>database {
+ plugin "simple"
+ path "/var/lib/mpd/db"
+}
+ </programlisting>
+
+ <para>
+ The following table lists the <varname>database</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>
+
+ <section>
<title>Configuring input plugins</title>
<para>
@@ -391,6 +438,18 @@ systemctl start mpd.socket</programlisting>
</row>
<row>
<entry>
+ <varname>tags</varname>
+ <parameter>yes|no</parameter>
+ </entry>
+ <entry>
+ If set to "no", then MPD will not send tags to this
+ output. This is only useful for output plugins that
+ can receive tags, for example the
+ <varname>httpd</varname> output plugin.
+ </entry>
+ </row>
+ <row>
+ <entry>
<varname>always_on</varname>
<parameter>yes|no</parameter>
</entry>
@@ -618,6 +677,78 @@ systemctl start mpd.socket</programlisting>
<title>Plugin reference</title>
<section>
+ <title>Database plugins</title>
+
+ <section>
+ <title><varname>simple</varname></title>
+
+ <para>
+ The default plugin. Stores a copy of the database in
+ memory. A file is used for permanent storage.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>path</varname>
+ </entry>
+ <entry>
+ The path of the database file.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+
+ <section>
+ <title><varname>proxy</varname></title>
+
+ <para>
+ Provides access to the database of another MPD instance
+ using <filename>libmpdclient</filename>. Experimental!
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>host</varname>
+ </entry>
+ <entry>
+ The host name of the "master" MPD instance.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>port</varname>
+ </entry>
+ <entry>
+ The port number of the "master" MPD instance.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+ </section>
+
+ <section>
<title>Input plugins</title>
<section>
diff --git a/m4/ax_append_link_flags.m4 b/m4/ax_append_link_flags.m4
new file mode 100644
index 000000000..4fc433700
--- /dev/null
+++ b/m4/ax_append_link_flags.m4
@@ -0,0 +1,61 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS])
+#
+# DESCRIPTION
+#
+# For every FLAG1, FLAG2 it is checked whether the linker works with the
+# flag. If it does, the flag is added FLAGS-VARIABLE
+#
+# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is
+# used. During the check the flag is always added to the linker's flags.
+#
+# If EXTRA-FLAGS is defined, it is added to the linker's default flags
+# when the check is done. The check is thus made with the flags: "LDFLAGS
+# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
+# issue an error when a bad flag is given.
+#
+# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG.
+# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS.
+#
+# LICENSE
+#
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 2
+
+AC_DEFUN([AX_APPEND_LINK_FLAGS],
+[for flag in $1; do
+ AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3])
+done
+])dnl AX_APPEND_LINK_FLAGS
diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4
new file mode 100644
index 000000000..e2d0d363e
--- /dev/null
+++ b/m4/ax_check_link_flag.m4
@@ -0,0 +1,71 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the linker or gives an error.
+# (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the linker's default flags
+# when the check is done. The check is thus made with the flags: "LDFLAGS
+# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
+# issue an error when a bad flag is given.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 2
+
+AC_DEFUN([AX_CHECK_LINK_FLAG],
+[AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
+AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
+ ax_check_save_flags=$LDFLAGS
+ LDFLAGS="$LDFLAGS $4 $1"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ LDFLAGS=$ax_check_save_flags])
+AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_LINK_FLAGS
diff --git a/m4/ax_cxx_compile_stdcxx_0x.m4 b/m4/ax_cxx_compile_stdcxx_0x.m4
new file mode 100644
index 000000000..a4e556ff9
--- /dev/null
+++ b/m4/ax_cxx_compile_stdcxx_0x.m4
@@ -0,0 +1,107 @@
+# ============================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_0x.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX_0X
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the C++0x
+# standard.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 7
+
+AU_ALIAS([AC_CXX_COMPILE_STDCXX_0X], [AX_CXX_COMPILE_STDCXX_0X])
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_0X], [
+ AC_CACHE_CHECK(if g++ supports C++0x features without additional flags,
+ ax_cv_cxx_compile_cxx0x_native,
+ [AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ AC_TRY_COMPILE([
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);],,
+ ax_cv_cxx_compile_cxx0x_native=yes, ax_cv_cxx_compile_cxx0x_native=no)
+ AC_LANG_RESTORE
+ ])
+
+ AC_CACHE_CHECK(if g++ supports C++0x features with -std=c++0x,
+ ax_cv_cxx_compile_cxx0x_cxx,
+ [AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ ac_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS -std=c++0x"
+ AC_TRY_COMPILE([
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);],,
+ ax_cv_cxx_compile_cxx0x_cxx=yes, ax_cv_cxx_compile_cxx0x_cxx=no)
+ CXXFLAGS="$ac_save_CXXFLAGS"
+ AC_LANG_RESTORE
+ ])
+
+ AC_CACHE_CHECK(if g++ supports C++0x features with -std=gnu++0x,
+ ax_cv_cxx_compile_cxx0x_gxx,
+ [AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ ac_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS -std=gnu++0x"
+ AC_TRY_COMPILE([
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);],,
+ ax_cv_cxx_compile_cxx0x_gxx=yes, ax_cv_cxx_compile_cxx0x_gxx=no)
+ CXXFLAGS="$ac_save_CXXFLAGS"
+ AC_LANG_RESTORE
+ ])
+
+ if test "$ax_cv_cxx_compile_cxx0x_native" = yes ||
+ test "$ax_cv_cxx_compile_cxx0x_cxx" = yes ||
+ test "$ax_cv_cxx_compile_cxx0x_gxx" = yes; then
+ AC_DEFINE(HAVE_STDCXX_0X,,[Define if g++ supports C++0x features. ])
+ fi
+])
diff --git a/src/AllCommands.cxx b/src/AllCommands.cxx
new file mode 100644
index 000000000..58dcf4dba
--- /dev/null
+++ b/src/AllCommands.cxx
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2003-2012 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 "AllCommands.hxx"
+#include "command.h"
+#include "QueueCommands.hxx"
+#include "PlayerCommands.hxx"
+#include "PlaylistCommands.hxx"
+#include "DatabaseCommands.hxx"
+#include "OutputCommands.hxx"
+#include "MessageCommands.hxx"
+#include "OtherCommands.hxx"
+#include "Permission.hxx"
+#include "tag.h"
+#include "protocol/Result.hxx"
+#include "Client.hxx"
+
+extern "C" {
+#include "tokenizer.h"
+}
+
+#ifdef ENABLE_SQLITE
+#include "StickerCommands.hxx"
+#include "StickerDatabase.hxx"
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+/*
+ * The most we ever use is for search/find, and that limits it to the
+ * number of tags we can have. Add one for the command, and one extra
+ * to catch errors clients may send us
+ */
+#define COMMAND_ARGV_MAX (2+(TAG_NUM_OF_ITEM_TYPES*2))
+
+/* if min: -1 don't check args *
+ * if max: -1 no max args */
+struct command {
+ const char *cmd;
+ unsigned permission;
+ int min;
+ int max;
+ enum command_return (*handler)(Client *client, int argc, char **argv);
+};
+
+/* don't be fooled, this is the command handler for "commands" command */
+static enum command_return
+handle_commands(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
+
+static enum command_return
+handle_not_commands(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
+
+/**
+ * The command registry.
+ *
+ * This array must be sorted!
+ */
+static const struct command commands[] = {
+ { "add", PERMISSION_ADD, 1, 1, handle_add },
+ { "addid", PERMISSION_ADD, 1, 2, handle_addid },
+ { "channels", PERMISSION_READ, 0, 0, handle_channels },
+ { "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
+ { "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
+ { "close", PERMISSION_NONE, -1, -1, handle_close },
+ { "commands", PERMISSION_NONE, 0, 0, handle_commands },
+ { "config", PERMISSION_ADMIN, 0, 0, handle_config },
+ { "consume", PERMISSION_CONTROL, 1, 1, handle_consume },
+ { "count", PERMISSION_READ, 2, -1, handle_count },
+ { "crossfade", PERMISSION_CONTROL, 1, 1, handle_crossfade },
+ { "currentsong", PERMISSION_READ, 0, 0, handle_currentsong },
+ { "decoders", PERMISSION_READ, 0, 0, handle_decoders },
+ { "delete", PERMISSION_CONTROL, 1, 1, handle_delete },
+ { "deleteid", PERMISSION_CONTROL, 1, 1, handle_deleteid },
+ { "disableoutput", PERMISSION_ADMIN, 1, 1, handle_disableoutput },
+ { "enableoutput", PERMISSION_ADMIN, 1, 1, handle_enableoutput },
+ { "find", PERMISSION_READ, 2, -1, handle_find },
+ { "findadd", PERMISSION_READ, 2, -1, handle_findadd},
+ { "idle", PERMISSION_READ, 0, -1, handle_idle },
+ { "kill", PERMISSION_ADMIN, -1, -1, handle_kill },
+ { "list", PERMISSION_READ, 1, -1, handle_list },
+ { "listall", PERMISSION_READ, 0, 1, handle_listall },
+ { "listallinfo", PERMISSION_READ, 0, 1, handle_listallinfo },
+ { "listplaylist", PERMISSION_READ, 1, 1, handle_listplaylist },
+ { "listplaylistinfo", PERMISSION_READ, 1, 1, handle_listplaylistinfo },
+ { "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },
+ { "load", PERMISSION_ADD, 1, 2, handle_load },
+ { "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },
+ { "mixrampdb", PERMISSION_CONTROL, 1, 1, handle_mixrampdb },
+ { "mixrampdelay", PERMISSION_CONTROL, 1, 1, handle_mixrampdelay },
+ { "move", PERMISSION_CONTROL, 2, 2, handle_move },
+ { "moveid", PERMISSION_CONTROL, 2, 2, handle_moveid },
+ { "next", PERMISSION_CONTROL, 0, 0, handle_next },
+ { "notcommands", PERMISSION_NONE, 0, 0, handle_not_commands },
+ { "outputs", PERMISSION_READ, 0, 0, handle_devices },
+ { "password", PERMISSION_NONE, 1, 1, handle_password },
+ { "pause", PERMISSION_CONTROL, 0, 1, handle_pause },
+ { "ping", PERMISSION_NONE, 0, 0, handle_ping },
+ { "play", PERMISSION_CONTROL, 0, 1, handle_play },
+ { "playid", PERMISSION_CONTROL, 0, 1, handle_playid },
+ { "playlist", PERMISSION_READ, 0, 0, handle_playlist },
+ { "playlistadd", PERMISSION_CONTROL, 2, 2, handle_playlistadd },
+ { "playlistclear", PERMISSION_CONTROL, 1, 1, handle_playlistclear },
+ { "playlistdelete", PERMISSION_CONTROL, 2, 2, handle_playlistdelete },
+ { "playlistfind", PERMISSION_READ, 2, -1, handle_playlistfind },
+ { "playlistid", PERMISSION_READ, 0, 1, handle_playlistid },
+ { "playlistinfo", PERMISSION_READ, 0, 1, handle_playlistinfo },
+ { "playlistmove", PERMISSION_CONTROL, 3, 3, handle_playlistmove },
+ { "playlistsearch", PERMISSION_READ, 2, -1, handle_playlistsearch },
+ { "plchanges", PERMISSION_READ, 1, 1, handle_plchanges },
+ { "plchangesposid", PERMISSION_READ, 1, 1, handle_plchangesposid },
+ { "previous", PERMISSION_CONTROL, 0, 0, handle_previous },
+ { "prio", PERMISSION_CONTROL, 2, -1, handle_prio },
+ { "prioid", PERMISSION_CONTROL, 2, -1, handle_prioid },
+ { "random", PERMISSION_CONTROL, 1, 1, handle_random },
+ { "readmessages", PERMISSION_READ, 0, 0, handle_read_messages },
+ { "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
+ { "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
+ { "replay_gain_mode", PERMISSION_CONTROL, 1, 1,
+ handle_replay_gain_mode },
+ { "replay_gain_status", PERMISSION_READ, 0, 0,
+ handle_replay_gain_status },
+ { "rescan", PERMISSION_CONTROL, 0, 1, handle_rescan },
+ { "rm", PERMISSION_CONTROL, 1, 1, handle_rm },
+ { "save", PERMISSION_CONTROL, 1, 1, handle_save },
+ { "search", PERMISSION_READ, 2, -1, handle_search },
+ { "searchadd", PERMISSION_ADD, 2, -1, handle_searchadd },
+ { "searchaddpl", PERMISSION_CONTROL, 3, -1, handle_searchaddpl },
+ { "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
+ { "seekcur", PERMISSION_CONTROL, 1, 1, handle_seekcur },
+ { "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
+ { "sendmessage", PERMISSION_CONTROL, 2, 2, handle_send_message },
+ { "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
+ { "shuffle", PERMISSION_CONTROL, 0, 1, handle_shuffle },
+ { "single", PERMISSION_CONTROL, 1, 1, handle_single },
+ { "stats", PERMISSION_READ, 0, 0, handle_stats },
+ { "status", PERMISSION_READ, 0, 0, handle_status },
+#ifdef ENABLE_SQLITE
+ { "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
+#endif
+ { "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
+ { "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
+ { "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
+ { "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
+ { "tagtypes", PERMISSION_READ, 0, 0, handle_tagtypes },
+ { "unsubscribe", PERMISSION_READ, 1, 1, handle_unsubscribe },
+ { "update", PERMISSION_CONTROL, 0, 1, handle_update },
+ { "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
+};
+
+static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]);
+
+static bool
+command_available(G_GNUC_UNUSED const struct command *cmd)
+{
+#ifdef ENABLE_SQLITE
+ if (strcmp(cmd->cmd, "sticker") == 0)
+ return sticker_enabled();
+#endif
+
+ return true;
+}
+
+/* don't be fooled, this is the command handler for "commands" command */
+static enum command_return
+handle_commands(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ const unsigned permission = client_get_permission(client);
+ const struct command *cmd;
+
+ for (unsigned i = 0; i < num_commands; ++i) {
+ cmd = &commands[i];
+
+ if (cmd->permission == (permission & cmd->permission) &&
+ command_available(cmd))
+ client_printf(client, "command: %s\n", cmd->cmd);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_not_commands(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ const unsigned permission = client_get_permission(client);
+ const struct command *cmd;
+
+ for (unsigned i = 0; i < num_commands; ++i) {
+ cmd = &commands[i];
+
+ if (cmd->permission != (permission & cmd->permission))
+ client_printf(client, "command: %s\n", cmd->cmd);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+void command_init(void)
+{
+#ifndef NDEBUG
+ /* ensure that the command list is sorted */
+ for (unsigned i = 0; i < num_commands - 1; ++i)
+ assert(strcmp(commands[i].cmd, commands[i + 1].cmd) < 0);
+#endif
+}
+
+void command_finish(void)
+{
+}
+
+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);
+ if (cmp == 0)
+ return &commands[i];
+ else if (cmp < 0)
+ b = i;
+ else if (cmp > 0)
+ a = i + 1;
+ } while (a < b);
+
+ return NULL;
+}
+
+static bool
+command_check_request(const struct command *cmd, Client *client,
+ unsigned permission, int argc, char *argv[])
+{
+ int min = cmd->min + 1;
+ int max = cmd->max + 1;
+
+ if (cmd->permission != (permission & cmd->permission)) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_PERMISSION,
+ "you don't have permission for \"%s\"",
+ cmd->cmd);
+ return false;
+ }
+
+ if (min == 0)
+ return true;
+
+ if (min == max && max != argc) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_ARG,
+ "wrong number of arguments for \"%s\"",
+ argv[0]);
+ return false;
+ } else if (argc < min) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_ARG,
+ "too few arguments for \"%s\"", argv[0]);
+ return false;
+ } else if (argc > max && max /* != 0 */ ) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_ARG,
+ "too many arguments for \"%s\"", argv[0]);
+ return false;
+ } else
+ return true;
+}
+
+static const struct command *
+command_checked_lookup(Client *client, unsigned permission,
+ int argc, char *argv[])
+{
+ const struct command *cmd;
+
+ current_command = "";
+
+ if (argc == 0)
+ return NULL;
+
+ cmd = command_lookup(argv[0]);
+ if (cmd == NULL) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "unknown command \"%s\"", argv[0]);
+ return NULL;
+ }
+
+ current_command = cmd->cmd;
+
+ if (!command_check_request(cmd, client, permission, argc, argv))
+ return NULL;
+
+ return cmd;
+}
+
+enum command_return
+command_process(Client *client, unsigned num, char *line)
+{
+ GError *error = NULL;
+ int argc;
+ char *argv[COMMAND_ARGV_MAX] = { NULL };
+ const struct command *cmd;
+ enum command_return ret = COMMAND_RETURN_ERROR;
+
+ command_list_num = num;
+
+ /* get the command name (first word on the line) */
+
+ argv[0] = tokenizer_next_word(&line, &error);
+ if (argv[0] == NULL) {
+ current_command = "";
+ if (*line == 0)
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "No command given");
+ else {
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "%s", error->message);
+ g_error_free(error);
+ }
+ current_command = NULL;
+
+ return COMMAND_RETURN_ERROR;
+ }
+
+ argc = 1;
+
+ /* now parse the arguments (quoted or unquoted) */
+
+ while (argc < (int)G_N_ELEMENTS(argv) &&
+ (argv[argc] =
+ tokenizer_next_param(&line, &error)) != NULL)
+ ++argc;
+
+ /* some error checks; we have to set current_command because
+ command_error() expects it to be set */
+
+ current_command = argv[0];
+
+ if (argc >= (int)G_N_ELEMENTS(argv)) {
+ command_error(client, ACK_ERROR_ARG, "Too many arguments");
+ current_command = NULL;
+ return COMMAND_RETURN_ERROR;
+ }
+
+ if (*line != 0) {
+ command_error(client, ACK_ERROR_ARG,
+ "%s", error->message);
+ current_command = NULL;
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ /* look up and invoke the command handler */
+
+ cmd = command_checked_lookup(client, client_get_permission(client),
+ argc, argv);
+ if (cmd)
+ ret = cmd->handler(client, argc, argv);
+
+ current_command = NULL;
+ command_list_num = 0;
+
+ return ret;
+}
diff --git a/src/db_internal.h b/src/AllCommands.hxx
index a33351524..a55eb5a3b 100644
--- a/src/db_internal.h
+++ b/src/AllCommands.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,19 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DB_INTERNAL_H
-#define MPD_DB_INTERNAL_H
+#ifndef MPD_ALL_COMMANDS_HXX
+#define MPD_ALL_COMMANDS_HXX
-#include "db_plugin.h"
+#include "command.h"
-#include <assert.h>
+class Client;
-static inline void
-db_base_init(struct db *db, const struct db_plugin *plugin)
-{
- assert(plugin != NULL);
+void command_init(void);
- db->plugin = plugin;
-}
+void command_finish(void);
+
+enum command_return
+command_process(Client *client, unsigned num, char *line);
#endif
diff --git a/src/client.c b/src/Client.cxx
index 3fa2c9be4..efb4125ac 100644
--- a/src/client.c
+++ b/src/Client.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,24 +18,24 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
-bool client_is_expired(const struct client *client)
+bool client_is_expired(const Client *client)
{
return client->channel == NULL;
}
-int client_get_uid(const struct client *client)
+int client_get_uid(const Client *client)
{
return client->uid;
}
-unsigned client_get_permission(const struct client *client)
+unsigned client_get_permission(const Client *client)
{
return client->permission;
}
-void client_set_permission(struct client *client, unsigned permission)
+void client_set_permission(Client *client, unsigned permission)
{
client->permission = permission;
}
diff --git a/src/client.h b/src/Client.hxx
index 0302a2e0a..d442d89b2 100644
--- a/src/client.h
+++ b/src/Client.hxx
@@ -20,60 +20,65 @@
#ifndef MPD_CLIENT_H
#define MPD_CLIENT_H
-#include <glib.h>
+#include "gcc.h"
+
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h>
-struct client;
struct sockaddr;
+struct playlist;
struct player_control;
+class Client;
void client_manager_init(void);
void client_manager_deinit(void);
-void client_new(struct player_control *player_control,
- int fd, const struct sockaddr *sa, size_t sa_length, int uid);
+void
+client_new(struct playlist &playlist, struct player_control *player_control,
+ int fd, const struct sockaddr *sa, size_t sa_length, int uid);
-G_GNUC_PURE
-bool client_is_expired(const struct client *client);
+gcc_pure
+bool client_is_expired(const Client *client);
/**
* returns the uid of the client process, or a negative value if the
* uid is unknown
*/
-G_GNUC_PURE
-int client_get_uid(const struct client *client);
+gcc_pure
+int client_get_uid(const Client *client);
/**
* Is this client running on the same machine, connected with a local
* (UNIX domain) socket?
*/
-G_GNUC_PURE
+gcc_pure
static inline bool
-client_is_local(const struct client *client)
+client_is_local(const Client *client)
{
return client_get_uid(client) > 0;
}
-G_GNUC_PURE
-unsigned client_get_permission(const struct client *client);
+gcc_pure
+unsigned client_get_permission(const Client *client);
-void client_set_permission(struct client *client, unsigned permission);
+void client_set_permission(Client *client, unsigned permission);
/**
* Write a C string to the client.
*/
-void client_puts(struct client *client, const char *s);
+void client_puts(Client *client, const char *s);
/**
* Write a printf-like formatted string to the client.
*/
-void client_vprintf(struct client *client, const char *fmt, va_list args);
+void client_vprintf(Client *client, const char *fmt, va_list args);
/**
* Write a printf-like formatted string to the client.
*/
-G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...);
+gcc_fprintf
+void
+client_printf(Client *client, const char *fmt, ...);
#endif
diff --git a/src/client_event.c b/src/ClientEvent.cxx
index 4f54ae0a7..aacd01cfe 100644
--- a/src/client_event.c
+++ b/src/ClientEvent.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "client_internal.h"
-#include "main.h"
+#include "ClientInternal.hxx"
+#include "Main.hxx"
#include <assert.h>
@@ -27,7 +27,7 @@ static gboolean
client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
gpointer data)
{
- struct client *client = data;
+ Client *client = (Client *)data;
assert(!client_is_expired(client));
@@ -49,7 +49,7 @@ client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
/* done sending deferred buffers exist: schedule
read */
client->source_id = g_io_add_watch(client->channel,
- G_IO_IN|G_IO_ERR|G_IO_HUP,
+ GIOCondition(G_IO_IN|G_IO_ERR|G_IO_HUP),
client_in_event, client);
return false;
}
@@ -62,7 +62,7 @@ gboolean
client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
gpointer data)
{
- struct client *client = data;
+ Client *client = (Client *)data;
enum command_return ret;
assert(!client_is_expired(client));
@@ -77,6 +77,7 @@ client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
ret = client_read(client);
switch (ret) {
case COMMAND_RETURN_OK:
+ case COMMAND_RETURN_IDLE:
case COMMAND_RETURN_ERROR:
break;
@@ -98,7 +99,7 @@ client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
if (!g_queue_is_empty(client->deferred_send)) {
/* deferred buffers exist: schedule write */
client->source_id = g_io_add_watch(client->channel,
- G_IO_OUT|G_IO_ERR|G_IO_HUP,
+ GIOCondition(G_IO_OUT|G_IO_ERR|G_IO_HUP),
client_out_event, client);
return false;
}
diff --git a/src/client_expire.c b/src/ClientExpire.cxx
index 1ca32ebcc..e24770ea1 100644
--- a/src/client_expire.c
+++ b/src/ClientExpire.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,12 +18,12 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
static guint expire_source_id;
void
-client_set_expired(struct client *client)
+client_set_expired(Client *client)
{
if (!client_is_expired(client))
client_schedule_expire();
@@ -42,7 +42,7 @@ client_set_expired(struct client *client)
static void
client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
- struct client *client = data;
+ Client *client = (Client *)data;
if (client_is_expired(client)) {
g_debug("[%u] expired", client->num);
diff --git a/src/client_file.c b/src/ClientFile.cxx
index 2ee433308..ca5acb229 100644
--- a/src/client_file.c
+++ b/src/ClientFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,9 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "client_file.h"
-#include "client.h"
+#include "ClientFile.hxx"
+#include "Client.hxx"
#include "ack.h"
+#include "io_error.h"
#include <sys/stat.h>
#include <sys/types.h>
@@ -27,7 +28,7 @@
#include <unistd.h>
bool
-client_allow_file(const struct client *client, const char *path_fs,
+client_allow_file(const Client *client, const char *path_fs,
GError **error_r)
{
#ifdef WIN32
@@ -53,8 +54,7 @@ client_allow_file(const struct client *client, const char *path_fs,
struct stat st;
if (stat(path_fs, &st) < 0) {
- g_set_error(error_r, g_file_error_quark(), errno,
- "%s", g_strerror(errno));
+ set_error_errno(error_r);
return false;
}
diff --git a/src/client_file.h b/src/ClientFile.hxx
index bc64bd041..48e00c44f 100644
--- a/src/client_file.h
+++ b/src/ClientFile.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CLIENT_FILE_H
-#define MPD_CLIENT_FILE_H
+#ifndef MPD_CLIENT_FILE_HXX
+#define MPD_CLIENT_FILE_HXX
+
+#include "gerror.h"
-#include <glib.h>
#include <stdbool.h>
-struct client;
+class Client;
/**
* Is this client allowed to use the specified local file?
@@ -36,7 +37,7 @@ struct client;
* @return true if access is allowed
*/
bool
-client_allow_file(const struct client *client, const char *path_fs,
+client_allow_file(const Client *client, const char *path_fs,
GError **error_r);
#endif
diff --git a/src/client_global.c b/src/ClientGlobal.cxx
index adf3b2f9e..10c78f1f1 100644
--- a/src/client_global.c
+++ b/src/ClientGlobal.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,11 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+
+extern "C" {
#include "conf.h"
+}
#include <assert.h>
@@ -55,7 +58,7 @@ void client_manager_init(void)
static void client_close_all(void)
{
while (!client_list_is_empty()) {
- struct client *client = client_list_get_first();
+ Client *client = client_list_get_first();
client_close(client);
}
diff --git a/src/client_idle.c b/src/ClientIdle.cxx
index 930911d6e..c2fa5dde5 100644
--- a/src/client_idle.c
+++ b/src/ClientIdle.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,12 @@
*/
#include "config.h"
-#include "client_idle.h"
-#include "client_internal.h"
+#include "ClientIdle.hxx"
+#include "ClientInternal.hxx"
+
+extern "C" {
#include "idle.h"
+}
#include <assert.h>
@@ -28,7 +31,7 @@
* Send "idle" response to this client.
*/
static void
-client_idle_notify(struct client *client)
+client_idle_notify(Client *client)
{
unsigned flags, i;
const char *const* idle_names;
@@ -52,7 +55,7 @@ client_idle_notify(struct client *client)
}
void
-client_idle_add(struct client *client, unsigned flags)
+client_idle_add(Client *client, unsigned flags)
{
if (client_is_expired(client))
return;
@@ -68,7 +71,7 @@ client_idle_add(struct client *client, unsigned flags)
static void
client_idle_callback(gpointer data, gpointer user_data)
{
- struct client *client = data;
+ Client *client = (Client *)data;
unsigned flags = GPOINTER_TO_UINT(user_data);
client_idle_add(client, flags);
@@ -81,7 +84,7 @@ void client_manager_idle_add(unsigned flags)
client_list_foreach(client_idle_callback, GUINT_TO_POINTER(flags));
}
-bool client_idle_wait(struct client *client, unsigned flags)
+bool client_idle_wait(Client *client, unsigned flags)
{
assert(!client->idle_waiting);
diff --git a/src/client_idle.h b/src/ClientIdle.hxx
index c56fd014c..a8c08b7aa 100644
--- a/src/client_idle.h
+++ b/src/ClientIdle.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,15 +17,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CLIENT_IDLE_H
-#define MPD_CLIENT_IDLE_H
+#ifndef MPD_CLIENT_IDLE_HXX
+#define MPD_CLIENT_IDLE_HXX
-#include <stdbool.h>
-
-struct client;
+class Client;
void
-client_idle_add(struct client *client, unsigned flags);
+client_idle_add(Client *client, unsigned flags);
/**
* Adds the specified idle flags to all clients and immediately sends
@@ -40,6 +38,6 @@ client_manager_idle_add(unsigned flags);
* client into waiting mode and returns false.
*/
bool
-client_idle_wait(struct client *client, unsigned flags);
+client_idle_wait(Client *client, unsigned flags);
#endif
diff --git a/src/client_internal.h b/src/ClientInternal.hxx
index ba97e4b8f..6f3035671 100644
--- a/src/client_internal.h
+++ b/src/ClientInternal.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CLIENT_INTERNAL_H
-#define MPD_CLIENT_INTERNAL_H
+#ifndef MPD_CLIENT_INTERNAL_HXX
+#define MPD_CLIENT_INTERNAL_HXX
-#include "client.h"
-#include "client_message.h"
+#include "Client.hxx"
+#include "ClientMessage.hxx"
+#include "CommandListBuilder.hxx"
#include "command.h"
+#include <set>
+#include <string>
+#include <list>
+
+#include <glib.h>
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "client"
@@ -37,7 +44,9 @@ struct deferred_buffer {
char data[sizeof(long)];
};
-struct client {
+class Client {
+public:
+ struct playlist &playlist;
struct player_control *player_control;
GIOChannel *channel;
@@ -56,9 +65,8 @@ struct client {
*/
GTimer *last_activity;
- GSList *cmd_list; /* for when in list mode */
- int cmd_list_OK; /* print OK after each command execution */
- size_t cmd_list_size; /* mem cmd_list consumes */
+ CommandListBuilder cmd_list;
+
GQueue *deferred_send; /* for output if client is slow */
size_t deferred_bytes; /* mem deferred_send consumes */
unsigned int num; /* client number */
@@ -79,7 +87,7 @@ struct client {
/**
* A list of channel names this client is subscribed to.
*/
- GSList *subscriptions;
+ std::set<std::string> subscriptions;
/**
* The number of subscriptions in #subscriptions. Used to
@@ -88,15 +96,19 @@ struct client {
unsigned num_subscriptions;
/**
- * A list of messages this client has received in reverse
- * order (latest first).
+ * A list of messages this client has received.
*/
- GSList *messages;
+ std::list<ClientMessage> messages;
- /**
- * The number of messages in #messages.
- */
- unsigned num_messages;
+ Client(struct playlist &playlist,
+ struct player_control *player_control,
+ int fd, int uid, int num);
+ ~Client();
+
+ gcc_pure
+ bool IsSubscribed(const char *channel_name) const {
+ return subscriptions.find(channel_name) != subscriptions.end();
+ }
};
extern unsigned int client_max_connections;
@@ -110,38 +122,23 @@ client_list_is_empty(void);
bool
client_list_is_full(void);
-struct client *
+Client *
client_list_get_first(void);
void
-client_list_add(struct client *client);
+client_list_add(Client *client);
void
client_list_foreach(GFunc func, gpointer user_data);
void
-client_list_remove(struct client *client);
+client_list_remove(Client *client);
void
-client_close(struct client *client);
-
-static inline void
-new_cmd_list_ptr(struct client *client, const char *s)
-{
- client->cmd_list = g_slist_prepend(client->cmd_list, g_strdup(s));
-}
-
-static inline void
-free_cmd_list(GSList *list)
-{
- for (GSList *tmp = list; tmp != NULL; tmp = g_slist_next(tmp))
- g_free(tmp->data);
-
- g_slist_free(list);
-}
+client_close(Client *client);
void
-client_set_expired(struct client *client);
+client_set_expired(Client *client);
/**
* Schedule an "expired" check for all clients: permanently delete
@@ -157,16 +154,16 @@ void
client_deinit_expire(void);
enum command_return
-client_read(struct client *client);
+client_read(Client *client);
enum command_return
-client_process_line(struct client *client, char *line);
+client_process_line(Client *client, char *line);
void
-client_write_deferred(struct client *client);
+client_write_deferred(Client *client);
void
-client_write_output(struct client *client);
+client_write_output(Client *client);
gboolean
client_in_event(GIOChannel *source, GIOCondition condition,
diff --git a/src/client_list.c b/src/ClientList.cxx
index 2c7f37aff..e0fa533d9 100644
--- a/src/client_list.c
+++ b/src/ClientList.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
#include <assert.h>
@@ -37,16 +37,16 @@ client_list_is_full(void)
return num_clients >= client_max_connections;
}
-struct client *
+Client *
client_list_get_first(void)
{
assert(clients != NULL);
- return clients->data;
+ return (Client *)clients->data;
}
void
-client_list_add(struct client *client)
+client_list_add(Client *client)
{
clients = g_list_prepend(clients, client);
++num_clients;
@@ -59,7 +59,7 @@ client_list_foreach(GFunc func, gpointer user_data)
}
void
-client_list_remove(struct client *client)
+client_list_remove(Client *client)
{
assert(num_clients > 0);
assert(clients != NULL);
diff --git a/src/db/simple_db_plugin.h b/src/ClientMessage.cxx
index 511505846..6fbcf3371 100644
--- a/src/db/simple_db_plugin.h
+++ b/src/ClientMessage.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,26 +17,26 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SIMPLE_DB_PLUGIN_H
-#define MPD_SIMPLE_DB_PLUGIN_H
+#include "ClientMessage.hxx"
+#include <assert.h>
#include <glib.h>
-#include <stdbool.h>
-#include <time.h>
-
-extern const struct db_plugin simple_db_plugin;
-
-struct db;
G_GNUC_PURE
-struct directory *
-simple_db_get_root(struct db *db);
+static bool
+valid_channel_char(const char ch)
+{
+ return g_ascii_isalnum(ch) ||
+ ch == '_' || ch == '-' || ch == '.' || ch == ':';
+}
bool
-simple_db_save(struct db *db, GError **error_r);
-
-G_GNUC_PURE
-time_t
-simple_db_get_mtime(const struct db *db);
-
-#endif
+client_message_valid_channel_name(const char *name)
+{
+ do {
+ if (!valid_channel_char(*name))
+ return false;
+ } while (*++name != 0);
+
+ return true;
+}
diff --git a/src/exclude.h b/src/ClientMessage.hxx
index 5b1229e29..2a929d445 100644
--- a/src/exclude.h
+++ b/src/ClientMessage.hxx
@@ -17,35 +17,36 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-/*
- * The .mpdignore backend code.
- *
- */
+#ifndef MPD_CLIENT_MESSAGE_H
+#define MPD_CLIENT_MESSAGE_H
-#ifndef MPD_EXCLUDE_H
-#define MPD_EXCLUDE_H
+#include "gcc.h"
-#include <glib.h>
-
-#include <stdbool.h>
+#include <string>
/**
- * Loads and parses a .mpdignore file.
+ * A client-to-client message.
*/
-GSList *
-exclude_list_load(const char *path_fs);
+class ClientMessage {
+ std::string channel, message;
-/**
- * Frees a list returned by exclude_list_load().
- */
-void
-exclude_list_free(GSList *list);
+public:
+ template<typename T, typename U>
+ ClientMessage(T &&_channel, U &&_message)
+ :channel(std::forward<T>(_channel)),
+ message(std::forward<U>(_message)) {}
-/**
- * Checks whether one of the patterns in the .mpdignore file matches
- * the specified file name.
- */
+ const char *GetChannel() const {
+ return channel.c_str();
+ }
+
+ const char *GetMessage() const {
+ return message.c_str();
+ }
+};
+
+gcc_pure
bool
-exclude_list_check(GSList *list, const char *name_fs);
+client_message_valid_channel_name(const char *name);
#endif
diff --git a/src/client_new.c b/src/ClientNew.cxx
index cf28c43c5..1579b9a53 100644
--- a/src/client_new.c
+++ b/src/ClientNew.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,13 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
#include "fd_util.h"
+extern "C" {
#include "fifo_buffer.h"
#include "resolver.h"
-#include "permission.h"
+}
+#include "Permission.hxx"
#include "glib_socket.h"
#include <assert.h>
@@ -43,12 +45,59 @@
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
+Client::Client(struct playlist &_playlist,
+ struct player_control *_player_control,
+ int fd, int _uid, int _num)
+ :playlist(_playlist), player_control(_player_control),
+ input(fifo_buffer_new(4096)),
+ permission(getDefaultPermissions()),
+ uid(_uid),
+ last_activity(g_timer_new()),
+ deferred_send(g_queue_new()), deferred_bytes(0),
+ num(_num),
+ send_buf_used(0),
+ idle_waiting(false), idle_flags(0),
+ num_subscriptions(0)
+{
+ assert(fd >= 0);
+
+ channel = g_io_channel_new_socket(fd);
+ /* GLib is responsible for closing the file descriptor */
+ g_io_channel_set_close_on_unref(channel, true);
+ /* NULL encoding means the stream is binary safe; the MPD
+ protocol is UTF-8 only, but we are doing this call anyway
+ to prevent GLib from messing around with the stream */
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ /* we prefer to do buffering */
+ g_io_channel_set_buffered(channel, false);
+
+ source_id = g_io_add_watch(channel,
+ GIOCondition(G_IO_IN|G_IO_ERR|G_IO_HUP),
+ client_in_event, this);
+}
+
+static void
+deferred_buffer_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
+{
+ struct deferred_buffer *buffer = (struct deferred_buffer *)data;
+ g_free(buffer);
+}
+
+Client::~Client()
+{
+ g_timer_destroy(last_activity);
+
+ g_queue_foreach(deferred_send, deferred_buffer_free, NULL);
+ g_queue_free(deferred_send);
+
+ fifo_buffer_free(input);
+}
+
void
-client_new(struct player_control *player_control,
+client_new(struct playlist &playlist, struct player_control *player_control,
int fd, const struct sockaddr *sa, size_t sa_length, int uid)
{
static unsigned int next_client_num;
- struct client *client;
char *remote;
assert(player_control != NULL);
@@ -85,43 +134,8 @@ client_new(struct player_control *player_control,
return;
}
- client = g_new0(struct client, 1);
- client->player_control = player_control;
-
- client->channel = g_io_channel_new_socket(fd);
- /* GLib is responsible for closing the file descriptor */
- g_io_channel_set_close_on_unref(client->channel, true);
- /* NULL encoding means the stream is binary safe; the MPD
- protocol is UTF-8 only, but we are doing this call anyway
- to prevent GLib from messing around with the stream */
- g_io_channel_set_encoding(client->channel, NULL, NULL);
- /* we prefer to do buffering */
- g_io_channel_set_buffered(client->channel, false);
-
- client->source_id = g_io_add_watch(client->channel,
- G_IO_IN|G_IO_ERR|G_IO_HUP,
- client_in_event, client);
-
- client->input = fifo_buffer_new(4096);
-
- client->permission = getDefaultPermissions();
- client->uid = uid;
-
- client->last_activity = g_timer_new();
-
- client->cmd_list = NULL;
- client->cmd_list_OK = -1;
- client->cmd_list_size = 0;
-
- client->deferred_send = g_queue_new();
- client->deferred_bytes = 0;
- client->num = next_client_num++;
-
- client->send_buf_used = 0;
-
- client->subscriptions = NULL;
- client->messages = NULL;
- client->num_messages = 0;
+ Client *client = new Client(playlist, player_control, fd, uid,
+ next_client_num++);
(void)send(fd, GREETING, sizeof(GREETING) - 1, 0);
@@ -133,33 +147,14 @@ client_new(struct player_control *player_control,
g_free(remote);
}
-static void
-deferred_buffer_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct deferred_buffer *buffer = data;
- g_free(buffer);
-}
-
void
-client_close(struct client *client)
+client_close(Client *client)
{
client_list_remove(client);
client_set_expired(client);
- g_timer_destroy(client->last_activity);
-
- if (client->cmd_list) {
- free_cmd_list(client->cmd_list);
- client->cmd_list = NULL;
- }
-
- g_queue_foreach(client->deferred_send, deferred_buffer_free, NULL);
- g_queue_free(client->deferred_send);
-
- fifo_buffer_free(client->input);
-
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"[%u] closed", client->num);
- g_free(client);
+ delete client;
}
diff --git a/src/client_process.c b/src/ClientProcess.cxx
index 57a8a7824..69b23e868 100644
--- a/src/client_process.c
+++ b/src/ClientProcess.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,9 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+#include "protocol/Result.hxx"
+#include "AllCommands.hxx"
#include <string.h>
@@ -27,13 +29,14 @@
#define CLIENT_LIST_MODE_END "command_list_end"
static enum command_return
-client_process_command_list(struct client *client, bool list_ok, GSList *list)
+client_process_command_list(Client *client, bool list_ok,
+ std::list<std::string> &&list)
{
enum command_return ret = COMMAND_RETURN_OK;
unsigned num = 0;
- for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) {
- char *cmd = cur->data;
+ for (auto &&i : list) {
+ char *cmd = &*i.begin();
g_debug("command_process_list: process command \"%s\"",
cmd);
@@ -49,7 +52,7 @@ client_process_command_list(struct client *client, bool list_ok, GSList *list)
}
enum command_return
-client_process_line(struct client *client, char *line)
+client_process_line(Client *client, char *line)
{
enum command_return ret;
@@ -74,19 +77,16 @@ client_process_line(struct client *client, char *line)
return COMMAND_RETURN_CLOSE;
}
- if (client->cmd_list_OK >= 0) {
+ if (client->cmd_list.IsActive()) {
if (strcmp(line, CLIENT_LIST_MODE_END) == 0) {
g_debug("[%u] process command list",
client->num);
- /* for scalability reasons, we have prepended
- each new command; now we have to reverse it
- to restore the correct order */
- client->cmd_list = g_slist_reverse(client->cmd_list);
+ auto &&cmd_list = client->cmd_list.Commit();
ret = client_process_command_list(client,
- client->cmd_list_OK,
- client->cmd_list);
+ client->cmd_list.IsOKMode(),
+ std::move(cmd_list));
g_debug("[%u] process command "
"list returned %i", client->num, ret);
@@ -98,31 +98,24 @@ client_process_line(struct client *client, char *line)
command_success(client);
client_write_output(client);
- free_cmd_list(client->cmd_list);
- client->cmd_list = NULL;
- client->cmd_list_OK = -1;
+ client->cmd_list.Reset();
} else {
- size_t len = strlen(line) + 1;
- client->cmd_list_size += len;
- if (client->cmd_list_size >
- client_max_command_list_size) {
- g_warning("[%u] command list size (%lu) "
+ if (!client->cmd_list.Add(line)) {
+ g_warning("[%u] command list size "
"is larger than the max (%lu)",
client->num,
- (unsigned long)client->cmd_list_size,
(unsigned long)client_max_command_list_size);
return COMMAND_RETURN_CLOSE;
}
- new_cmd_list_ptr(client, line);
ret = COMMAND_RETURN_OK;
}
} else {
if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) {
- client->cmd_list_OK = 0;
+ client->cmd_list.Begin(false);
ret = COMMAND_RETURN_OK;
} else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) {
- client->cmd_list_OK = 1;
+ client->cmd_list.Begin(true);
ret = COMMAND_RETURN_OK;
} else {
g_debug("[%u] process command \"%s\"",
diff --git a/src/client_read.c b/src/ClientRead.cxx
index 26ade264e..aab4cae33 100644
--- a/src/client_read.c
+++ b/src/ClientRead.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,35 +18,35 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+
+extern "C" {
#include "fifo_buffer.h"
+}
#include <assert.h>
#include <string.h>
static char *
-client_read_line(struct client *client)
+client_read_line(Client *client)
{
- const char *p, *newline;
size_t length;
- char *line;
-
- p = fifo_buffer_read(client->input, &length);
+ const char *p = (const char *)fifo_buffer_read(client->input, &length);
if (p == NULL)
return NULL;
- newline = memchr(p, '\n', length);
+ const char *newline = (const char *)memchr(p, '\n', length);
if (newline == NULL)
return NULL;
- line = g_strndup(p, newline - p);
+ char *line = g_strndup(p, newline - p);
fifo_buffer_consume(client->input, newline - p + 1);
return g_strchomp(line);
}
static enum command_return
-client_input_received(struct client *client, size_t bytesRead)
+client_input_received(Client *client, size_t bytesRead)
{
char *line;
@@ -69,10 +69,8 @@ client_input_received(struct client *client, size_t bytesRead)
}
enum command_return
-client_read(struct client *client)
+client_read(Client *client)
{
- char *p;
- size_t max_length;
GError *error = NULL;
GIOStatus status;
gsize bytes_read;
@@ -80,7 +78,8 @@ client_read(struct client *client)
assert(client != NULL);
assert(client->channel != NULL);
- p = fifo_buffer_write(client->input, &max_length);
+ size_t max_length;
+ char *p = (char *)fifo_buffer_write(client->input, &max_length);
if (p == NULL) {
g_warning("[%u] buffer overflow", client->num);
return COMMAND_RETURN_CLOSE;
diff --git a/src/ClientSubscribe.cxx b/src/ClientSubscribe.cxx
new file mode 100644
index 000000000..a1ad6e68e
--- /dev/null
+++ b/src/ClientSubscribe.cxx
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ClientSubscribe.hxx"
+#include "ClientIdle.hxx"
+#include "ClientInternal.hxx"
+
+extern "C" {
+#include "idle.h"
+}
+
+#include <assert.h>
+#include <string.h>
+
+enum client_subscribe_result
+client_subscribe(Client *client, const char *channel)
+{
+ assert(client != NULL);
+ assert(channel != NULL);
+
+ if (!client_message_valid_channel_name(channel))
+ return CLIENT_SUBSCRIBE_INVALID;
+
+ if (client->num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS)
+ return CLIENT_SUBSCRIBE_FULL;
+
+ auto r = client->subscriptions.insert(channel);
+ if (!r.second)
+ return CLIENT_SUBSCRIBE_ALREADY;
+
+ ++client->num_subscriptions;
+
+ idle_add(IDLE_SUBSCRIPTION);
+
+ return CLIENT_SUBSCRIBE_OK;
+}
+
+bool
+client_unsubscribe(Client *client, const char *channel)
+{
+ const auto i = client->subscriptions.find(channel);
+ if (i == client->subscriptions.end())
+ return false;
+
+ assert(client->num_subscriptions > 0);
+
+ client->subscriptions.erase(i);
+ --client->num_subscriptions;
+
+ idle_add(IDLE_SUBSCRIPTION);
+
+ assert((client->num_subscriptions == 0) ==
+ client->subscriptions.empty());
+
+ return true;
+}
+
+void
+client_unsubscribe_all(Client *client)
+{
+ client->subscriptions.clear();
+ client->num_subscriptions = 0;
+}
+
+bool
+client_push_message(Client *client, const ClientMessage &msg)
+{
+ assert(client != NULL);
+
+ if (client->messages.size() >= CLIENT_MAX_MESSAGES ||
+ !client->IsSubscribed(msg.GetChannel()))
+ return false;
+
+ if (client->messages.empty())
+ client_idle_add(client, IDLE_MESSAGE);
+
+ client->messages.push_back(msg);
+ return true;
+}
diff --git a/src/client_subscribe.h b/src/ClientSubscribe.hxx
index 09f864417..e9d3b8a68 100644
--- a/src/client_subscribe.h
+++ b/src/ClientSubscribe.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CLIENT_SUBSCRIBE_H
-#define MPD_CLIENT_SUBSCRIBE_H
+#ifndef MPD_CLIENT_SUBSCRIBE_HXX
+#define MPD_CLIENT_SUBSCRIBE_HXX
-#include <stdbool.h>
-#include <glib.h>
+#include "gcc.h"
-struct client;
-struct client_message;
+typedef struct _GSList GSList;
+class Client;
+class ClientMessage;
enum client_subscribe_result {
/** success */
@@ -41,19 +41,15 @@ enum client_subscribe_result {
};
enum client_subscribe_result
-client_subscribe(struct client *client, const char *channel);
+client_subscribe(Client *client, const char *channel);
bool
-client_unsubscribe(struct client *client, const char *channel);
+client_unsubscribe(Client *client, const char *channel);
void
-client_unsubscribe_all(struct client *client);
+client_unsubscribe_all(Client *client);
bool
-client_push_message(struct client *client, const struct client_message *msg);
-
-G_GNUC_MALLOC
-GSList *
-client_read_messages(struct client *client);
+client_push_message(Client *client, const ClientMessage &msg);
#endif
diff --git a/src/client_write.c b/src/ClientWrite.cxx
index 78cfca8a1..825029fb9 100644
--- a/src/client_write.c
+++ b/src/ClientWrite.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,14 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
#include <assert.h>
#include <string.h>
#include <stdio.h>
static size_t
-client_write_deferred_buffer(struct client *client,
+client_write_deferred_buffer(Client *client,
const struct deferred_buffer *buffer)
{
GError *error = NULL;
@@ -67,12 +67,13 @@ client_write_deferred_buffer(struct client *client,
}
void
-client_write_deferred(struct client *client)
+client_write_deferred(Client *client)
{
size_t ret;
while (!g_queue_is_empty(client->deferred_send)) {
struct deferred_buffer *buf =
+ (struct deferred_buffer *)
g_queue_peek_head(client->deferred_send);
assert(buf->size > 0);
@@ -108,8 +109,8 @@ client_write_deferred(struct client *client)
}
}
-static void client_defer_output(struct client *client,
- const void *data, size_t length)
+static void
+client_defer_output(Client *client, const void *data, size_t length)
{
size_t alloc;
struct deferred_buffer *buf;
@@ -129,15 +130,15 @@ static void client_defer_output(struct client *client,
return;
}
- buf = g_malloc(alloc);
+ buf = (struct deferred_buffer *)g_malloc(alloc);
buf->size = length;
memcpy(buf->data, data, length);
g_queue_push_tail(client->deferred_send, buf);
}
-static void client_write_direct(struct client *client,
- const char *data, size_t length)
+static void
+client_write_direct(Client *client, const char *data, size_t length)
{
GError *error = NULL;
GIOStatus status;
@@ -181,7 +182,7 @@ static void client_write_direct(struct client *client,
}
void
-client_write_output(struct client *client)
+client_write_output(Client *client)
{
if (client_is_expired(client) || !client->send_buf_used)
return;
@@ -211,7 +212,8 @@ client_write_output(struct client *client)
/**
* Write a block of data to the client.
*/
-static void client_write(struct client *client, const char *buffer, size_t buflen)
+static void
+client_write(Client *client, const char *buffer, size_t buflen)
{
/* if the client is going to be closed, do nothing */
if (client_is_expired(client))
@@ -236,17 +238,18 @@ static void client_write(struct client *client, const char *buffer, size_t bufle
}
}
-void client_puts(struct client *client, const char *s)
+void
+client_puts(Client *client, const char *s)
{
client_write(client, s, strlen(s));
}
-void client_vprintf(struct client *client, const char *fmt, va_list args)
+void
+client_vprintf(Client *client, const char *fmt, va_list args)
{
#ifndef G_OS_WIN32
va_list tmp;
int length;
- char *buffer;
va_copy(tmp, args);
length = vsnprintf(NULL, 0, fmt, tmp);
@@ -256,7 +259,7 @@ void client_vprintf(struct client *client, const char *fmt, va_list args)
/* wtf.. */
return;
- buffer = g_malloc(length + 1);
+ char *buffer = (char *)g_malloc(length + 1);
vsnprintf(buffer, length + 1, fmt, args);
client_write(client, buffer, length);
g_free(buffer);
@@ -274,7 +277,9 @@ void client_vprintf(struct client *client, const char *fmt, va_list args)
#endif
}
-G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...)
+G_GNUC_PRINTF(2, 3)
+void
+client_printf(Client *client, const char *fmt, ...)
{
va_list args;
diff --git a/src/CommandError.cxx b/src/CommandError.cxx
new file mode 100644
index 000000000..7e777d82a
--- /dev/null
+++ b/src/CommandError.cxx
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2003-2012 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 "CommandError.hxx"
+#include "db_error.h"
+#include "io_error.h"
+#include "protocol/Result.hxx"
+
+#include <assert.h>
+#include <errno.h>
+
+enum command_return
+print_playlist_result(Client *client, enum playlist_result result)
+{
+ switch (result) {
+ case PLAYLIST_RESULT_SUCCESS:
+ return COMMAND_RETURN_OK;
+
+ case PLAYLIST_RESULT_ERRNO:
+ command_error(client, ACK_ERROR_SYSTEM, "%s",
+ g_strerror(errno));
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_DENIED:
+ command_error(client, ACK_ERROR_PERMISSION, "Access denied");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_NO_SUCH_SONG:
+ command_error(client, ACK_ERROR_NO_EXIST, "No such song");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_NO_SUCH_LIST:
+ command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_LIST_EXISTS:
+ command_error(client, ACK_ERROR_EXIST,
+ "Playlist already exists");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_BAD_NAME:
+ command_error(client, ACK_ERROR_ARG,
+ "playlist name is invalid: "
+ "playlist names may not contain slashes,"
+ " newlines or carriage returns");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_BAD_RANGE:
+ command_error(client, ACK_ERROR_ARG, "Bad song index");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_NOT_PLAYING:
+ command_error(client, ACK_ERROR_PLAYER_SYNC, "Not playing");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_TOO_LARGE:
+ command_error(client, ACK_ERROR_PLAYLIST_MAX,
+ "playlist is at the max size");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_DISABLED:
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "stored playlist support is disabled");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ assert(0);
+ return COMMAND_RETURN_ERROR;
+}
+
+/**
+ * Send the GError to the client and free the GError.
+ */
+enum command_return
+print_error(Client *client, GError *error)
+{
+ assert(client != NULL);
+ assert(error != NULL);
+
+ g_warning("%s", error->message);
+
+ if (error->domain == playlist_quark()) {
+ enum playlist_result result = (playlist_result)error->code;
+ g_error_free(error);
+ return print_playlist_result(client, result);
+ } else if (error->domain == ack_quark()) {
+ command_error(client, (ack)error->code, "%s", error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ } else if (error->domain == db_quark()) {
+ switch ((enum db_error)error->code) {
+ case DB_DISABLED:
+ command_error(client, ACK_ERROR_NO_EXIST, "%s",
+ error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+
+ case DB_NOT_FOUND:
+ g_error_free(error);
+ command_error(client, ACK_ERROR_NO_EXIST, "Not found");
+ return COMMAND_RETURN_ERROR;
+ }
+ } else if (error->domain == errno_quark()) {
+ command_error(client, ACK_ERROR_SYSTEM, "%s",
+ g_strerror(error->code));
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ } else if (error->domain == g_file_error_quark()) {
+ command_error(client, ACK_ERROR_SYSTEM, "%s", error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ g_error_free(error);
+ command_error(client, ACK_ERROR_UNKNOWN, "error");
+ return COMMAND_RETURN_ERROR;
+}
diff --git a/src/CommandError.hxx b/src/CommandError.hxx
new file mode 100644
index 000000000..739e982e9
--- /dev/null
+++ b/src/CommandError.hxx
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003-2012 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_COMMAND_ERROR_HXX
+#define MPD_COMMAND_ERROR_HXX
+
+#include "command.h"
+#include "playlist_error.h"
+
+#include <glib.h>
+
+class Client;
+
+enum command_return
+print_playlist_result(Client *client, enum playlist_result result);
+
+/**
+ * Send the GError to the client and free the GError.
+ */
+enum command_return
+print_error(Client *client, GError *error);
+
+#endif
diff --git a/src/cmdline.c b/src/CommandLine.cxx
index cb7eff36a..4d9d2ca9d 100644
--- a/src/cmdline.c
+++ b/src/CommandLine.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,19 +18,23 @@
*/
#include "config.h"
-#include "cmdline.h"
+#include "CommandLine.hxx"
#include "path.h"
+#include "ls.hxx"
+
+extern "C" {
#include "log.h"
#include "conf.h"
+}
+
#include "decoder_list.h"
#include "decoder_plugin.h"
-#include "output_list.h"
+#include "OutputList.hxx"
#include "output_plugin.h"
#include "input_registry.h"
#include "input_plugin.h"
#include "playlist_list.h"
#include "playlist_plugin.h"
-#include "ls.h"
#include "mpd_error.h"
#include "glib_compat.h"
@@ -159,7 +163,7 @@ parse_cmdline(int argc, char **argv, struct options *options,
"verbose logging", NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &option_version,
"print version number", NULL },
- { .long_name = NULL }
+ { nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr }
};
options->kill = false;
diff --git a/src/cmdline.h b/src/CommandLine.hxx
index 68f625a6c..7a8731f82 100644
--- a/src/cmdline.h
+++ b/src/CommandLine.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef CMDLINE_H
-#define CMDLINE_H
+#ifndef MPD_COMMAND_LINE_HXX
+#define MPD_COMMAND_LINE_HXX
#include <glib.h>
-#include <stdbool.h>
-
struct options {
gboolean kill;
gboolean daemon;
diff --git a/src/CommandListBuilder.cxx b/src/CommandListBuilder.cxx
new file mode 100644
index 000000000..e58afccd1
--- /dev/null
+++ b/src/CommandListBuilder.cxx
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "CommandListBuilder.hxx"
+#include "ClientInternal.hxx"
+
+#include <string.h>
+
+void
+CommandListBuilder::Reset()
+{
+ list.clear();
+ mode = Mode::DISABLED;
+}
+
+bool
+CommandListBuilder::Add(const char *cmd)
+{
+ size_t len = strlen(cmd) + 1;
+ size += len;
+ if (size > client_max_command_list_size)
+ return false;
+
+ list.emplace_back(cmd);
+ return true;
+}
diff --git a/src/CommandListBuilder.hxx b/src/CommandListBuilder.hxx
new file mode 100644
index 000000000..a112ac33b
--- /dev/null
+++ b/src/CommandListBuilder.hxx
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_COMMAND_LIST_BUILDER_HXX
+#define MPD_COMMAND_LIST_BUILDER_HXX
+
+#include <list>
+#include <string>
+
+#include <assert.h>
+
+class CommandListBuilder {
+ /**
+ * print OK after each command execution
+ */
+ enum class Mode {
+ /**
+ * Not active.
+ */
+ DISABLED = -1,
+
+ /**
+ * Enabled in normal list mode.
+ */
+ ENABLED = false,
+
+ /**
+ * Enabled in "list_OK" mode.
+ */
+ OK = true,
+ } mode;
+
+ /**
+ * for when in list mode
+ */
+ std::list<std::string> list;
+
+ /**
+ * Memory consumed by the list.
+ */
+ size_t size;
+
+public:
+ CommandListBuilder()
+ :mode(Mode::DISABLED), size(0) {}
+
+ /**
+ * Is a command list currently being built?
+ */
+ bool IsActive() const {
+ return mode != Mode::DISABLED;
+ }
+
+ /**
+ * Is the object in "list_OK" mode?
+ */
+ bool IsOKMode() const {
+ assert(IsActive());
+
+ return (bool)mode;
+ }
+
+ /**
+ * Reset the object: delete the list and clear the mode.
+ */
+ void Reset();
+
+ /**
+ * Begin building a command list.
+ */
+ void Begin(bool ok) {
+ assert(list.empty());
+ assert(mode == Mode::DISABLED);
+
+ mode = (Mode)ok;
+ }
+
+ /**
+ * @return false if the list is full
+ */
+ bool Add(const char *cmd);
+
+ /**
+ * Finishes the list and returns it.
+ */
+ std::list<std::string> &&Commit() {
+ assert(IsActive());
+
+ return std::move(list);
+ }
+};
+
+#endif
diff --git a/src/crossfade.c b/src/CrossFade.cxx
index 46a0dff30..fe937e4ca 100644
--- a/src/crossfade.c
+++ b/src/CrossFade.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "crossfade.h"
-#include "chunk.h"
+#include "CrossFade.hxx"
+#include "MusicChunk.hxx"
#include "audio_format.h"
#include "tag.h"
diff --git a/src/crossfade.h b/src/CrossFade.hxx
index d581dbfe0..1c4670758 100644
--- a/src/crossfade.h
+++ b/src/CrossFade.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CROSSFADE_H
-#define MPD_CROSSFADE_H
+#ifndef MPD_CROSSFADE_HXX
+#define MPD_CROSSFADE_HXX
struct audio_format;
struct music_chunk;
diff --git a/src/DatabaseCommands.cxx b/src/DatabaseCommands.cxx
new file mode 100644
index 000000000..b52d72f39
--- /dev/null
+++ b/src/DatabaseCommands.cxx
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabaseCommands.hxx"
+#include "DatabaseQueue.hxx"
+#include "DatabasePlaylist.hxx"
+#include "DatabasePrint.hxx"
+#include "DatabaseSelection.hxx"
+#include "CommandError.hxx"
+#include "ClientInternal.hxx"
+#include "tag.h"
+#include "uri.h"
+#include "SongFilter.hxx"
+#include "protocol/Result.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+enum command_return
+handle_lsinfo2(Client *client, int argc, char *argv[])
+{
+ const char *uri;
+
+ if (argc == 2)
+ uri = argv[1];
+ else
+ /* default is root directory */
+ uri = "";
+
+ const DatabaseSelection selection(uri, false);
+
+ GError *error = NULL;
+ if (!db_selection_print(client, selection, true, &error))
+ return print_error(client, error);
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_match(Client *client, int argc, char *argv[], bool fold_case)
+{
+ SongFilter filter;
+ if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ const DatabaseSelection selection("", true, &filter);
+
+ GError *error = NULL;
+ return db_selection_print(client, selection, true, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_find(Client *client, int argc, char *argv[])
+{
+ return handle_match(client, argc, argv, false);
+}
+
+enum command_return
+handle_search(Client *client, int argc, char *argv[])
+{
+ return handle_match(client, argc, argv, true);
+}
+
+static enum command_return
+handle_match_add(Client *client, int argc, char *argv[], bool fold_case)
+{
+ SongFilter filter;
+ if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ GError *error = NULL;
+ return findAddIn(client->playlist, client->player_control,
+ "", &filter, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_findadd(Client *client, int argc, char *argv[])
+{
+ return handle_match_add(client, argc, argv, false);
+}
+
+enum command_return
+handle_searchadd(Client *client, int argc, char *argv[])
+{
+ return handle_match_add(client, argc, argv, true);
+}
+
+enum command_return
+handle_searchaddpl(Client *client, int argc, char *argv[])
+{
+ const char *playlist = argv[1];
+
+ SongFilter filter;
+ if (!filter.Parse(argc - 2, argv + 2, true)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ GError *error = NULL;
+ return search_add_to_playlist("", playlist, &filter, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_count(Client *client, int argc, char *argv[])
+{
+ SongFilter filter;
+ if (!filter.Parse(argc - 1, argv + 1, false)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ GError *error = NULL;
+ return searchStatsForSongsIn(client, "", &filter, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_listall(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *directory = "";
+
+ if (argc == 2)
+ directory = argv[1];
+
+ GError *error = NULL;
+ return printAllIn(client, directory, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_list(Client *client, int argc, char *argv[])
+{
+ unsigned tagType = locate_parse_type(argv[1]);
+
+ if (tagType == TAG_NUM_OF_ITEM_TYPES) {
+ command_error(client, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ if (tagType == LOCATE_TAG_ANY_TYPE) {
+ command_error(client, ACK_ERROR_ARG,
+ "\"any\" is not a valid return tag type");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ /* for compatibility with < 0.12.0 */
+ SongFilter *filter;
+ if (argc == 3) {
+ if (tagType != TAG_ALBUM) {
+ command_error(client, ACK_ERROR_ARG,
+ "should be \"%s\" for 3 arguments",
+ tag_item_names[TAG_ALBUM]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ filter = new SongFilter((unsigned)TAG_ARTIST, argv[2]);
+ } else if (argc > 2) {
+ filter = new SongFilter();
+ if (!filter->Parse(argc - 2, argv + 2, false)) {
+ delete filter;
+ command_error(client, ACK_ERROR_ARG,
+ "not able to parse args");
+ return COMMAND_RETURN_ERROR;
+ }
+ } else
+ filter = nullptr;
+
+ GError *error = NULL;
+ enum command_return ret =
+ listAllUniqueTags(client, tagType, filter, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+
+ delete filter;
+
+ return ret;
+}
+
+enum command_return
+handle_listallinfo(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *directory = "";
+
+ if (argc == 2)
+ directory = argv[1];
+
+ GError *error = NULL;
+ return printInfoForAllIn(client, directory, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
diff --git a/src/DatabaseCommands.hxx b/src/DatabaseCommands.hxx
new file mode 100644
index 000000000..335adc4d6
--- /dev/null
+++ b/src/DatabaseCommands.hxx
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2003-2012 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_DATABASE_COMMANDS_HXX
+#define MPD_DATABASE_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_lsinfo2(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_find(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_findadd(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_search(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_searchadd(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_searchaddpl(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_count(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listall(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_list(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listallinfo(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/database.c b/src/DatabaseGlue.cxx
index 8c903bb45..283ef9943 100644
--- a/src/database.c
+++ b/src/DatabaseGlue.cxx
@@ -18,17 +18,21 @@
*/
#include "config.h"
-#include "database.h"
+#include "DatabaseGlue.hxx"
+#include "DatabaseSimple.hxx"
+#include "DatabaseRegistry.hxx"
+#include "DatabaseSave.hxx"
+#include "Directory.hxx"
+
+extern "C" {
#include "db_error.h"
-#include "db_save.h"
-#include "db_selection.h"
-#include "db_visitor.h"
-#include "db_plugin.h"
-#include "db/simple_db_plugin.h"
-#include "directory.h"
#include "stats.h"
#include "conf.h"
#include "glib_compat.h"
+}
+
+#include "DatabasePlugin.hxx"
+#include "db/SimpleDatabasePlugin.hxx"
#include <glib.h>
@@ -42,97 +46,89 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "database"
-static struct db *db;
+static Database *db;
static bool db_is_open;
+static bool is_simple;
bool
-db_init(const struct config_param *path, GError **error_r)
+DatabaseGlobalInit(const config_param *param, GError **error_r)
{
assert(db == NULL);
assert(!db_is_open);
- if (path == NULL)
- return true;
-
- struct config_param *param = config_new_param("database", path->line);
- config_add_block_param(param, "path", path->value, path->line);
-
- db = db_plugin_new(&simple_db_plugin, param, error_r);
+ const char *plugin_name =
+ config_get_block_string(param, "plugin", "simple");
+ is_simple = strcmp(plugin_name, "simple") == 0;
- config_param_free(param);
+ const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
+ if (plugin == NULL) {
+ g_set_error(error_r, db_quark(), 0,
+ "No such database plugin: %s", plugin_name);
+ return false;
+ }
+ db = plugin->create(param, error_r);
return db != NULL;
}
void
-db_finish(void)
+DatabaseGlobalDeinit(void)
{
if (db_is_open)
- db_plugin_close(db);
+ db->Close();
if (db != NULL)
- db_plugin_free(db);
+ delete db;
}
-struct directory *
-db_get_root(void)
+const Database *
+GetDatabase()
{
- assert(db != NULL);
+ assert(db == NULL || db_is_open);
- return simple_db_get_root(db);
+ return db;
}
-struct directory *
-db_get_directory(const char *name)
+const Database *
+GetDatabase(GError **error_r)
{
- if (db == NULL)
- return NULL;
+ assert(db == nullptr || db_is_open);
- struct directory *music_root = db_get_root();
- if (name == NULL)
- return music_root;
+ if (db == nullptr)
+ g_set_error_literal(error_r, db_quark(), DB_DISABLED,
+ "No database");
- struct directory *directory =
- directory_lookup_directory(music_root, name);
- return directory;
+ return db;
}
-struct song *
-db_get_song(const char *file)
+bool
+db_is_simple(void)
{
- assert(file != NULL);
-
- g_debug("get song: %s", file);
+ assert(db == NULL || db_is_open);
- if (db == NULL)
- return NULL;
-
- return db_plugin_get_song(db, file, NULL);
+ return is_simple;
}
-bool
-db_visit(const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
+Directory *
+db_get_root(void)
{
- if (db == NULL) {
- g_set_error_literal(error_r, db_quark(), DB_DISABLED,
- "No database");
- return false;
- }
+ assert(db != NULL);
+ assert(db_is_simple());
- return db_plugin_visit(db, selection, visitor, ctx, error_r);
+ return ((SimpleDatabase *)db)->GetRoot();
}
-bool
-db_walk(const char *uri,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
+Directory *
+db_get_directory(const char *name)
{
- struct db_selection selection;
- db_selection_init(&selection, uri, true);
+ if (db == NULL)
+ return NULL;
+
+ Directory *music_root = db_get_root();
+ if (name == NULL)
+ return music_root;
- return db_visit(&selection, visitor, ctx, error_r);
+ return music_root->LookupDirectory(name);
}
bool
@@ -140,17 +136,18 @@ db_save(GError **error_r)
{
assert(db != NULL);
assert(db_is_open);
+ assert(db_is_simple());
- return simple_db_save(db, error_r);
+ return ((SimpleDatabase *)db)->Save(error_r);
}
bool
-db_load(GError **error)
+DatabaseGlobalOpen(GError **error)
{
assert(db != NULL);
assert(!db_is_open);
- if (!db_plugin_open(db, error))
+ if (!db->Open(error))
return false;
db_is_open = true;
@@ -165,6 +162,7 @@ db_get_mtime(void)
{
assert(db != NULL);
assert(db_is_open);
+ assert(db_is_simple());
- return simple_db_get_mtime(db);
+ return ((SimpleDatabase *)db)->GetLastModified();
}
diff --git a/src/DatabaseGlue.hxx b/src/DatabaseGlue.hxx
new file mode 100644
index 000000000..ea26f3242
--- /dev/null
+++ b/src/DatabaseGlue.hxx
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2003-2011 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_DATABASE_GLUE_HXX
+#define MPD_DATABASE_GLUE_HXX
+
+#include "gcc.h"
+#include "gerror.h"
+
+struct config_param;
+class Database;
+
+/**
+ * Initialize the database library.
+ *
+ * @param param the database configuration block
+ */
+bool
+DatabaseGlobalInit(const config_param *param, GError **error_r);
+
+void
+DatabaseGlobalDeinit(void);
+
+bool
+DatabaseGlobalOpen(GError **error);
+
+/**
+ * Returns the global #Database instance. May return NULL if this MPD
+ * configuration has no database (no music_directory was configured).
+ */
+gcc_pure
+const Database *
+GetDatabase();
+
+/**
+ * Returns the global #Database instance. May return NULL if this MPD
+ * configuration has no database (no music_directory was configured).
+ */
+gcc_pure
+const Database *
+GetDatabase(GError **error_r);
+
+#endif
diff --git a/src/DatabaseHelpers.cxx b/src/DatabaseHelpers.cxx
new file mode 100644
index 000000000..dc31a4bc2
--- /dev/null
+++ b/src/DatabaseHelpers.cxx
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2003-2012 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 "DatabaseHelpers.hxx"
+#include "DatabasePlugin.hxx"
+#include "song.h"
+#include "tag.h"
+
+#include <functional>
+#include <set>
+
+#include <string.h>
+
+struct StringLess {
+ gcc_pure
+ bool operator()(const char *a, const char *b) const {
+ return strcmp(a, b) < 0;
+ }
+};
+
+typedef std::set<const char *, StringLess> StringSet;
+
+static bool
+CollectTags(StringSet &set, enum tag_type tag_type, song &song)
+{
+ struct tag *tag = song.tag;
+ if (tag == nullptr)
+ return true;
+
+ bool found = false;
+ for (unsigned i = 0; i < tag->num_items; ++i) {
+ if (tag->items[i]->type == tag_type) {
+ set.insert(tag->items[i]->value);
+ found = true;
+ }
+ }
+
+ if (!found)
+ set.insert("");
+
+ return true;
+}
+
+bool
+VisitUniqueTags(const Database &db, const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r)
+{
+ StringSet set;
+
+ using namespace std::placeholders;
+ const auto f = std::bind(CollectTags, std::ref(set), tag_type, _1);
+ if (!db.Visit(selection, f, error_r))
+ return false;
+
+ for (auto value : set)
+ if (!visit_string(value, error_r))
+ return false;
+
+ return true;
+}
+
+static void
+StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
+ const struct tag &tag)
+{
+ if (tag.time > 0)
+ stats.total_duration += tag.time;
+
+ for (unsigned i = 0; i < tag.num_items; ++i) {
+ const struct tag_item &item = *tag.items[i];
+
+ switch (item.type) {
+ case TAG_ARTIST:
+ artists.insert(item.value);
+ break;
+
+ case TAG_ALBUM:
+ albums.insert(item.value);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static bool
+StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums,
+ song &song)
+{
+ ++stats.song_count;
+
+ if (song.tag != nullptr)
+ StatsVisitTag(stats, artists, albums, *song.tag);
+
+ return true;
+}
+
+bool
+GetStats(const Database &db, const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r)
+{
+ stats.Clear();
+
+ StringSet artists, albums;
+ using namespace std::placeholders;
+ const auto f = std::bind(StatsVisitSong,
+ std::ref(stats), std::ref(artists),
+ std::ref(albums), _1);
+ if (!db.Visit(selection, f, error_r))
+ return false;
+
+ stats.artist_count = artists.size();
+ stats.album_count = albums.size();
+ return true;
+}
diff --git a/src/DatabaseHelpers.hxx b/src/DatabaseHelpers.hxx
new file mode 100644
index 000000000..cfcc94ac7
--- /dev/null
+++ b/src/DatabaseHelpers.hxx
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2012 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_MEMORY_DATABASE_PLUGIN_HXX
+#define MPD_MEMORY_DATABASE_PLUGIN_HXX
+
+#include "DatabaseVisitor.hxx"
+#include "tag.h"
+#include "gcc.h"
+
+class Database;
+struct DatabaseSelection;
+struct DatabaseStats;
+
+bool
+VisitUniqueTags(const Database &db, const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r);
+
+bool
+GetStats(const Database &db, const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r);
+
+#endif
diff --git a/src/db_lock.c b/src/DatabaseLock.cxx
index 53759673d..872b9bfd3 100644
--- a/src/db_lock.c
+++ b/src/DatabaseLock.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "db_lock.h"
+#include "DatabaseLock.hxx"
#include "gcc.h"
#if GCC_CHECK_VERSION(4, 2)
diff --git a/src/db_lock.h b/src/DatabaseLock.hxx
index 4640502f3..6646fb43d 100644
--- a/src/db_lock.h
+++ b/src/DatabaseLock.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,14 +23,13 @@
* multi-threading.
*/
-#ifndef MPD_DB_LOCK_H
-#define MPD_DB_LOCK_H
+#ifndef MPD_DB_LOCK_HXX
+#define MPD_DB_LOCK_HXX
#include "check.h"
#include <glib.h>
#include <assert.h>
-#include <stdbool.h>
extern GStaticMutex db_mutex;
@@ -81,4 +80,19 @@ db_unlock(void)
g_static_mutex_unlock(&db_mutex);
}
+#ifdef __cplusplus
+
+class ScopeDatabaseLock {
+public:
+ ScopeDatabaseLock() {
+ db_lock();
+ }
+
+ ~ScopeDatabaseLock() {
+ db_unlock();
+ }
+};
+
+#endif
+
#endif
diff --git a/src/DatabasePlaylist.cxx b/src/DatabasePlaylist.cxx
new file mode 100644
index 000000000..fb477e83b
--- /dev/null
+++ b/src/DatabasePlaylist.cxx
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2003-2012 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 "DatabasePlaylist.hxx"
+#include "DatabaseSelection.hxx"
+#include "PlaylistFile.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+
+#include <functional>
+
+static bool
+AddSong(const char *playlist_path_utf8,
+ song &song, GError **error_r)
+{
+ return spl_append_song(playlist_path_utf8, &song, error_r);
+}
+
+bool
+search_add_to_playlist(const char *uri, const char *playlist_path_utf8,
+ const SongFilter *filter,
+ GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ const DatabaseSelection selection(uri, true, filter);
+
+ using namespace std::placeholders;
+ const auto f = std::bind(AddSong, playlist_path_utf8, _1, _2);
+ return db->Visit(selection, f, error_r);
+}
diff --git a/src/db_save.h b/src/DatabasePlaylist.hxx
index e760ec881..7c6952ffa 100644
--- a/src/db_save.h
+++ b/src/DatabasePlaylist.hxx
@@ -17,19 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DB_SAVE_H
-#define MPD_DB_SAVE_H
+#ifndef MPD_DATABASE_PLAYLIST_HXX
+#define MPD_DATABASE_PLAYLIST_HXX
-#include <glib.h>
-#include <stdbool.h>
-#include <stdio.h>
+#include "gcc.h"
+#include "gerror.h"
-struct directory;
-
-void
-db_save_internal(FILE *file, const struct directory *root);
+class SongFilter;
+gcc_nonnull(1,2)
bool
-db_load_internal(FILE *file, struct directory *root, GError **error);
+search_add_to_playlist(const char *uri, const char *path_utf8,
+ const SongFilter *filter,
+ GError **error_r);
#endif
diff --git a/src/DatabasePlugin.hxx b/src/DatabasePlugin.hxx
new file mode 100644
index 000000000..a175b3cd9
--- /dev/null
+++ b/src/DatabasePlugin.hxx
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2011 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
+ *
+ * This header declares the db_plugin class. It describes a
+ * plugin API for databases of song metadata.
+ */
+
+#ifndef MPD_DATABASE_PLUGIN_HXX
+#define MPD_DATABASE_PLUGIN_HXX
+
+#include "DatabaseVisitor.hxx"
+#include "gcc.h"
+
+extern "C" {
+#include "tag.h"
+}
+
+struct config_param;
+struct DatabaseSelection;
+struct db_visitor;
+
+struct DatabaseStats {
+ /**
+ * Number of songs.
+ */
+ unsigned song_count;
+
+ /**
+ * Total duration of all songs (in seconds).
+ */
+ unsigned long total_duration;
+
+ /**
+ * Number of distinct artist names.
+ */
+ unsigned artist_count;
+
+ /**
+ * Number of distinct album names.
+ */
+ unsigned album_count;
+
+ void Clear() {
+ song_count = 0;
+ total_duration = 0;
+ artist_count = album_count = 0;
+ }
+};
+
+class Database {
+public:
+ /**
+ * Free instance data.
+ */
+ virtual ~Database() {}
+
+ /**
+ * Open the database. Read it into memory if applicable.
+ */
+ virtual bool Open(gcc_unused GError **error_r) {
+ return true;
+ }
+
+ /**
+ * Close the database, free allocated memory.
+ */
+ virtual void Close() {}
+
+ /**
+ * Look up a song (including tag data) in the database. When
+ * you don't need this anymore, call ReturnSong().
+ *
+ * @param uri_utf8 the URI of the song within the music
+ * directory (UTF-8)
+ */
+ virtual struct song *GetSong(const char *uri_utf8,
+ GError **error_r) const = 0;
+
+ /**
+ * Mark the song object as "unused". Call this on objects
+ * returned by GetSong().
+ */
+ virtual void ReturnSong(struct song *song) const = 0;
+
+ /**
+ * Visit the selected entities.
+ */
+ virtual bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const = 0;
+
+ bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ GError **error_r) const {
+ return Visit(selection, visit_directory, visit_song,
+ VisitPlaylist(), error_r);
+ }
+
+ bool Visit(const DatabaseSelection &selection, VisitSong visit_song,
+ GError **error_r) const {
+ return Visit(selection, VisitDirectory(), visit_song, error_r);
+ }
+
+ /**
+ * Visit all unique tag values.
+ */
+ virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const = 0;
+
+ virtual bool GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats,
+ GError **error_r) const = 0;
+};
+
+struct DatabasePlugin {
+ const char *name;
+
+ /**
+ * Allocates and configures a database.
+ */
+ Database *(*create)(const struct config_param *param,
+ GError **error_r);
+};
+
+#endif
diff --git a/src/DatabasePrint.cxx b/src/DatabasePrint.cxx
new file mode 100644
index 000000000..2c3fcc9f8
--- /dev/null
+++ b/src/DatabasePrint.cxx
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabasePrint.hxx"
+#include "DatabaseSelection.hxx"
+#include "SongFilter.hxx"
+#include "PlaylistVector.hxx"
+#include "SongPrint.hxx"
+#include "TimePrint.hxx"
+#include "Directory.hxx"
+#include "Client.hxx"
+
+extern "C" {
+#include "song.h"
+#include "tag.h"
+}
+
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+
+#include <functional>
+
+static bool
+PrintDirectory(Client *client, const Directory &directory)
+{
+ if (!directory.IsRoot())
+ client_printf(client, "directory: %s\n", directory.GetPath());
+
+ return true;
+}
+
+static void
+print_playlist_in_directory(Client *client,
+ const Directory &directory,
+ const char *name_utf8)
+{
+ if (directory.IsRoot())
+ client_printf(client, "playlist: %s\n", name_utf8);
+ else
+ client_printf(client, "playlist: %s/%s\n",
+ directory.GetPath(), name_utf8);
+}
+
+static bool
+PrintSongBrief(Client *client, song &song)
+{
+ assert(song.parent != NULL);
+
+ song_print_uri(client, &song);
+
+ if (song.tag != NULL && song.tag->has_playlist)
+ /* this song file has an embedded CUE sheet */
+ print_playlist_in_directory(client, *song.parent, song.uri);
+
+ return true;
+}
+
+static bool
+PrintSongFull(Client *client, song &song)
+{
+ assert(song.parent != NULL);
+
+ song_print_info(client, &song);
+
+ if (song.tag != NULL && song.tag->has_playlist)
+ /* this song file has an embedded CUE sheet */
+ print_playlist_in_directory(client, *song.parent, song.uri);
+
+ return true;
+}
+
+static bool
+PrintPlaylistBrief(Client *client,
+ const PlaylistInfo &playlist,
+ const Directory &directory)
+{
+ print_playlist_in_directory(client, directory, playlist.name.c_str());
+ return true;
+}
+
+static bool
+PrintPlaylistFull(Client *client,
+ const PlaylistInfo &playlist,
+ const Directory &directory)
+{
+ print_playlist_in_directory(client, directory, playlist.name.c_str());
+
+ if (playlist.mtime > 0)
+ time_print(client, "Last-Modified", playlist.mtime);
+
+ return true;
+}
+
+bool
+db_selection_print(Client *client, const DatabaseSelection &selection,
+ bool full, GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ using namespace std::placeholders;
+ const auto d = selection.filter == nullptr
+ ? std::bind(PrintDirectory, client, _1)
+ : VisitDirectory();
+ const auto s = std::bind(full ? PrintSongFull : PrintSongBrief,
+ client, _1);
+ const auto p = selection.filter == nullptr
+ ? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
+ client, _1, _2)
+ : VisitPlaylist();
+
+ return db->Visit(selection, d, s, p, error_r);
+}
+
+struct SearchStats {
+ int numberOfSongs;
+ unsigned long playTime;
+};
+
+static void printSearchStats(Client *client, SearchStats *stats)
+{
+ client_printf(client, "songs: %i\n", stats->numberOfSongs);
+ client_printf(client, "playtime: %li\n", stats->playTime);
+}
+
+static bool
+stats_visitor_song(SearchStats &stats, song &song)
+{
+ stats.numberOfSongs++;
+ stats.playTime += song_get_duration(&song);
+
+ return true;
+}
+
+bool
+searchStatsForSongsIn(Client *client, const char *name,
+ const SongFilter *filter,
+ GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ const DatabaseSelection selection(name, true, filter);
+
+ SearchStats stats;
+ stats.numberOfSongs = 0;
+ stats.playTime = 0;
+
+ using namespace std::placeholders;
+ const auto f = std::bind(stats_visitor_song, std::ref(stats),
+ _1);
+ if (!db->Visit(selection, f, error_r))
+ return false;
+
+ printSearchStats(client, &stats);
+ return true;
+}
+
+bool
+printAllIn(Client *client, const char *uri_utf8, GError **error_r)
+{
+ const DatabaseSelection selection(uri_utf8, true);
+ return db_selection_print(client, selection, false, error_r);
+}
+
+bool
+printInfoForAllIn(Client *client, const char *uri_utf8,
+ GError **error_r)
+{
+ const DatabaseSelection selection(uri_utf8, true);
+ return db_selection_print(client, selection, true, error_r);
+}
+
+static bool
+PrintSongURIVisitor(Client *client, song &song)
+{
+ song_print_uri(client, &song);
+
+ return true;
+}
+
+static bool
+PrintUniqueTag(Client *client, enum tag_type tag_type,
+ const char *value)
+{
+ client_printf(client, "%s: %s\n", tag_item_names[tag_type], value);
+ return true;
+}
+
+bool
+listAllUniqueTags(Client *client, int type,
+ const SongFilter *filter,
+ GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ const DatabaseSelection selection("", true, filter);
+
+ if (type == LOCATE_TAG_FILE_TYPE) {
+ using namespace std::placeholders;
+ const auto f = std::bind(PrintSongURIVisitor, client, _1);
+ return db->Visit(selection, f, error_r);
+ } else {
+ using namespace std::placeholders;
+ const auto f = std::bind(PrintUniqueTag, client,
+ (enum tag_type)type, _1);
+ return db->VisitUniqueTags(selection, (enum tag_type)type,
+ f, error_r);
+ }
+}
diff --git a/src/db_print.h b/src/DatabasePrint.hxx
index 1b957da18..68551b63c 100644
--- a/src/db_print.h
+++ b/src/DatabasePrint.hxx
@@ -21,51 +21,37 @@
#define MPD_DB_PRINT_H
#include "gcc.h"
+#include "gerror.h"
-#include <glib.h>
-#include <stdbool.h>
-
-struct client;
-struct locate_item_list;
-struct db_selection;
+class SongFilter;
+struct DatabaseSelection;
struct db_visitor;
+class Client;
-gcc_nonnull(1,2)
+gcc_nonnull(1)
bool
-db_selection_print(struct client *client, const struct db_selection *selection,
+db_selection_print(Client *client, const DatabaseSelection &selection,
bool full, GError **error_r);
gcc_nonnull(1,2)
bool
-printAllIn(struct client *client, const char *uri_utf8, GError **error_r);
+printAllIn(Client *client, const char *uri_utf8, GError **error_r);
gcc_nonnull(1,2)
bool
-printInfoForAllIn(struct client *client, const char *uri_utf8,
+printInfoForAllIn(Client *client, const char *uri_utf8,
GError **error_r);
-gcc_nonnull(1,2,3)
-bool
-searchForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r);
-
-gcc_nonnull(1,2,3)
-bool
-findSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r);
-
-gcc_nonnull(1,2,3)
+gcc_nonnull(1,2)
bool
-searchStatsForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
+searchStatsForSongsIn(Client *client, const char *name,
+ const SongFilter *filter,
GError **error_r);
-gcc_nonnull(1,3)
+gcc_nonnull(1)
bool
-listAllUniqueTags(struct client *client, int type,
- const struct locate_item_list *criteria,
+listAllUniqueTags(Client *client, int type,
+ const SongFilter *filter,
GError **error_r);
#endif
diff --git a/src/DatabaseQueue.cxx b/src/DatabaseQueue.cxx
new file mode 100644
index 000000000..21103fe37
--- /dev/null
+++ b/src/DatabaseQueue.cxx
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2003-2012 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 "DatabaseQueue.hxx"
+#include "DatabaseSelection.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+#include "Playlist.hxx"
+
+#include <functional>
+
+static bool
+AddToQueue(struct playlist &playlist, struct player_control *pc,
+ song &song, GError **error_r)
+{
+ enum playlist_result result =
+ playlist_append_song(&playlist, pc, &song, NULL);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ g_set_error(error_r, playlist_quark(), result,
+ "Playlist error");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+findAddIn(struct playlist &playlist, struct player_control *pc,
+ const char *uri,
+ const SongFilter *filter, GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ const DatabaseSelection selection(uri, true, filter);
+
+ using namespace std::placeholders;
+ const auto f = std::bind(AddToQueue, std::ref(playlist), pc, _1, _2);
+ return db->Visit(selection, f, error_r);
+}
diff --git a/src/directory_save.h b/src/DatabaseQueue.hxx
index 2d0056830..50ed7d03a 100644
--- a/src/directory_save.h
+++ b/src/DatabaseQueue.hxx
@@ -17,21 +17,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DIRECTORY_SAVE_H
-#define MPD_DIRECTORY_SAVE_H
+#ifndef MPD_DATABASE_QUEUE_HXX
+#define MPD_DATABASE_QUEUE_HXX
-#include <glib.h>
+#include "gcc.h"
+#include "gerror.h"
-#include <stdbool.h>
-#include <stdio.h>
-
-struct directory;
-
-void
-directory_save(FILE *fp, const struct directory *directory);
+class SongFilter;
+struct playlist;
+struct player_control;
+gcc_nonnull(2,3)
bool
-directory_load(FILE *fp, struct directory *directory,
- GString *buffer, GError **error);
+findAddIn(struct playlist &playlist, struct player_control *pc,
+ const char *name,
+ const SongFilter *filter, GError **error_r);
#endif
diff --git a/src/DatabaseRegistry.cxx b/src/DatabaseRegistry.cxx
new file mode 100644
index 000000000..cf01decdd
--- /dev/null
+++ b/src/DatabaseRegistry.cxx
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003-2012 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 "DatabaseRegistry.hxx"
+#include "db/SimpleDatabasePlugin.hxx"
+#include "db/ProxyDatabasePlugin.hxx"
+
+#include <string.h>
+
+const DatabasePlugin *const database_plugins[] = {
+ &simple_db_plugin,
+#ifdef HAVE_LIBMPDCLIENT
+ &proxy_db_plugin,
+#endif
+ NULL
+};
+
+const DatabasePlugin *
+GetDatabasePluginByName(const char *name)
+{
+ for (auto i = database_plugins; *i != nullptr; ++i)
+ if (strcmp((*i)->name, name) == 0)
+ return *i;
+
+ return nullptr;
+}
diff --git a/src/DatabaseRegistry.hxx b/src/DatabaseRegistry.hxx
new file mode 100644
index 000000000..4be581573
--- /dev/null
+++ b/src/DatabaseRegistry.hxx
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2003-2012 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_DATABASE_REGISTRY_HXX
+#define MPD_DATABASE_REGISTRY_HXX
+
+#include "gcc.h"
+
+struct DatabasePlugin;
+
+/**
+ * NULL terminated list of all database plugins which were enabled at
+ * compile time.
+ */
+extern const DatabasePlugin *const database_plugins[];
+
+gcc_pure
+const DatabasePlugin *
+GetDatabasePluginByName(const char *name);
+
+#endif
diff --git a/src/db_save.c b/src/DatabaseSave.cxx
index 4af9d58b8..78a2c4939 100644
--- a/src/db_save.c
+++ b/src/DatabaseSave.cxx
@@ -18,15 +18,18 @@
*/
#include "config.h"
-#include "db_save.h"
-#include "db_lock.h"
-#include "directory.h"
-#include "directory_save.h"
+#include "DatabaseSave.hxx"
+#include "DatabaseLock.hxx"
+#include "Directory.hxx"
+#include "DirectorySave.hxx"
#include "song.h"
+#include "TextFile.hxx"
+
+extern "C" {
#include "path.h"
-#include "text_file.h"
#include "tag.h"
#include "tag_internal.h"
+}
#include <glib.h>
@@ -55,7 +58,7 @@ db_quark(void)
}
void
-db_save_internal(FILE *fp, const struct directory *music_root)
+db_save_internal(FILE *fp, const Directory *music_root)
{
assert(music_root != NULL);
@@ -74,9 +77,8 @@ db_save_internal(FILE *fp, const struct directory *music_root)
}
bool
-db_load_internal(FILE *fp, struct directory *music_root, GError **error)
+db_load_internal(TextFile &file, Directory *music_root, GError **error)
{
- GString *buffer = g_string_sized_new(1024);
char *line;
int format = 0;
bool found_charset = false, found_version = false;
@@ -86,16 +88,15 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
assert(music_root != NULL);
/* get initial info */
- line = read_text_line(fp, buffer);
+ line = file.ReadLine();
if (line == NULL || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) {
g_set_error(error, db_quark(), 0, "Database corrupted");
- g_string_free(buffer, true);
return false;
}
memset(tags, false, sizeof(tags));
- while ((line = read_text_line(fp, buffer)) != NULL &&
+ while ((line = file.ReadLine()) != NULL &&
strcmp(line, DIRECTORY_INFO_END) != 0) {
if (g_str_has_prefix(line, DB_FORMAT_PREFIX)) {
format = atoi(line + sizeof(DB_FORMAT_PREFIX) - 1);
@@ -103,7 +104,6 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
if (found_version) {
g_set_error(error, db_quark(), 0,
"Duplicate version line");
- g_string_free(buffer, true);
return false;
}
@@ -114,7 +114,6 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
if (found_charset) {
g_set_error(error, db_quark(), 0,
"Duplicate charset line");
- g_string_free(buffer, true);
return false;
}
@@ -129,7 +128,6 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
"\"%s\" instead of \"%s\"; "
"discarding database file",
new_charset, old_charset);
- g_string_free(buffer, true);
return false;
}
} else if (g_str_has_prefix(line, DB_TAG_PREFIX)) {
@@ -147,7 +145,6 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
} else {
g_set_error(error, db_quark(), 0,
"Malformed line: %s", line);
- g_string_free(buffer, true);
return false;
}
}
@@ -171,9 +168,8 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
g_debug("reading DB");
db_lock();
- success = directory_load(fp, music_root, buffer, error);
+ success = directory_load(file, music_root, error);
db_unlock();
- g_string_free(buffer, true);
return success;
}
diff --git a/src/DatabaseSave.hxx b/src/DatabaseSave.hxx
new file mode 100644
index 000000000..40048f261
--- /dev/null
+++ b/src/DatabaseSave.hxx
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DATABASE_SAVE_HXX
+#define MPD_DATABASE_SAVE_HXX
+
+#include "gerror.h"
+
+#include <stdio.h>
+
+struct Directory;
+class TextFile;
+
+void
+db_save_internal(FILE *file, const Directory *root);
+
+bool
+db_load_internal(TextFile &file, Directory *root, GError **error);
+
+#endif
diff --git a/src/DatabaseSelection.cxx b/src/DatabaseSelection.cxx
new file mode 100644
index 000000000..bd756f5f9
--- /dev/null
+++ b/src/DatabaseSelection.cxx
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2003-2011 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 "DatabaseSelection.hxx"
+#include "SongFilter.hxx"
+
+bool
+DatabaseSelection::Match(const song &song) const
+{
+ return filter == nullptr || filter->Match(song);
+}
diff --git a/src/db_selection.h b/src/DatabaseSelection.hxx
index 2cebb4907..3a81c01ec 100644
--- a/src/db_selection.h
+++ b/src/DatabaseSelection.hxx
@@ -17,17 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DB_SELECTION_H
-#define MPD_DB_SELECTION_H
+#ifndef MPD_DATABASE_SELECTION_HXX
+#define MPD_DATABASE_SELECTION_HXX
#include "gcc.h"
#include <assert.h>
+#include <stddef.h>
-struct directory;
+class SongFilter;
struct song;
-struct db_selection {
+struct DatabaseSelection {
/**
* The base URI of the search (UTF-8). Must not begin or end
* with a slash. NULL or an empty string searches the whole
@@ -39,18 +40,17 @@ struct db_selection {
* Recursively search all sub directories?
*/
bool recursive;
-};
-gcc_nonnull(1,2)
-static inline void
-db_selection_init(struct db_selection *selection,
- const char *uri, bool recursive)
-{
- assert(selection != NULL);
- assert(uri != NULL);
-
- selection->uri = uri;
- selection->recursive = recursive;
-}
+ const SongFilter *filter;
+
+ DatabaseSelection(const char *_uri, bool _recursive,
+ const SongFilter *_filter=nullptr)
+ :uri(_uri), recursive(_recursive), filter(_filter) {
+ assert(uri != NULL);
+ }
+
+ gcc_pure
+ bool Match(const song &song) const;
+};
#endif
diff --git a/src/database.h b/src/DatabaseSimple.hxx
index f877b74d3..e57ae41bf 100644
--- a/src/database.h
+++ b/src/DatabaseSimple.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DATABASE_H
-#define MPD_DATABASE_H
+#ifndef MPD_DATABASE_SIMPLE_HXX
+#define MPD_DATABASE_SIMPLE_HXX
#include "gcc.h"
@@ -28,68 +28,55 @@
#include <stdbool.h>
struct config_param;
-struct directory;
+struct Directory;
struct db_selection;
struct db_visitor;
/**
- * Initialize the database library.
- *
- * @param path the absolute path of the database file
+ * Check whether the default #SimpleDatabasePlugin is used. This
+ * allows using db_get_root(), db_save(), db_get_mtime() and
+ * db_exists().
*/
bool
-db_init(const struct config_param *path, GError **error_r);
-
-void
-db_finish(void);
+db_is_simple(void);
/**
* Returns the root directory object. Returns NULL if there is no
* configured music directory.
+ *
+ * May only be used if db_is_simple() returns true.
*/
-G_GNUC_PURE
-struct directory *
+gcc_pure
+Directory *
db_get_root(void);
/**
* Caller must lock the #db_mutex.
*/
gcc_nonnull(1)
-G_GNUC_PURE
-struct directory *
+gcc_pure
+Directory *
db_get_directory(const char *name);
-gcc_nonnull(1)
-G_GNUC_PURE
-struct song *
-db_get_song(const char *file);
-
-gcc_nonnull(1,2)
-bool
-db_visit(const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r);
-
-gcc_nonnull(1,2)
-bool
-db_walk(const char *uri,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r);
-
+/**
+ * May only be used if db_is_simple() returns true.
+ */
bool
db_save(GError **error_r);
-bool
-db_load(GError **error);
-
-G_GNUC_PURE
+/**
+ * May only be used if db_is_simple() returns true.
+ */
+gcc_pure
time_t
db_get_mtime(void);
/**
* Returns true if there is a valid database file on the disk.
+ *
+ * May only be used if db_is_simple() returns true.
*/
-G_GNUC_PURE
+gcc_pure
static inline bool
db_exists(void)
{
diff --git a/src/song_print.h b/src/DatabaseVisitor.hxx
index 8f1f0cc65..c90441415 100644
--- a/src/song_print.h
+++ b/src/DatabaseVisitor.hxx
@@ -17,17 +17,22 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SONG_PRINT_H
-#define MPD_SONG_PRINT_H
+#ifndef MPD_DATABASE_VISITOR_HXX
+#define MPD_DATABASE_VISITOR_HXX
-struct client;
+#include "gerror.h"
+
+#include <functional>
+
+struct Directory;
struct song;
-struct songvec;
+struct PlaylistInfo;
-void
-song_print_info(struct client *client, struct song *song);
+typedef std::function<bool(const Directory &, GError **)> VisitDirectory;
+typedef std::function<bool(struct song &, GError **)> VisitSong;
+typedef std::function<bool(const PlaylistInfo &, const Directory &,
+ GError **)> VisitPlaylist;
-void
-song_print_uri(struct client *client, struct song *song);
+typedef std::function<bool(const char *, GError **)> VisitString;
#endif
diff --git a/src/decoder_api.c b/src/DecoderAPI.cxx
index a45d0f1e6..6e0460a8c 100644
--- a/src/decoder_api.c
+++ b/src/DecoderAPI.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,19 @@
*/
#include "config.h"
+
+extern "C" {
#include "decoder_api.h"
-#include "decoder_internal.h"
-#include "decoder_control.h"
#include "audio_config.h"
-#include "song.h"
-#include "buffer.h"
-#include "pipe.h"
-#include "chunk.h"
#include "replay_gain_config.h"
+}
+
+#include "MusicChunk.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicPipe.hxx"
+#include "DecoderControl.hxx"
+#include "DecoderInternal.hxx"
+#include "song.h"
#include <glib.h>
@@ -362,11 +366,10 @@ update_stream_tag(struct decoder *decoder, struct input_stream *is)
enum decoder_command
decoder_data(struct decoder *decoder,
struct input_stream *is,
- const void *_data, size_t length,
+ const void *data, size_t length,
uint16_t kbit_rate)
{
struct decoder_control *dc = decoder->dc;
- const char *data = _data;
GError *error = NULL;
enum decoder_command cmd;
@@ -417,7 +420,6 @@ decoder_data(struct decoder *decoder,
while (length > 0) {
struct music_chunk *chunk;
- char *dest;
size_t nbytes;
bool full;
@@ -427,10 +429,10 @@ decoder_data(struct decoder *decoder,
return dc->command;
}
- dest = music_chunk_write(chunk, &dc->out_audio_format,
- decoder->timestamp -
- dc->song->start_ms / 1000.0,
- kbit_rate, &nbytes);
+ void *dest = chunk->Write(dc->out_audio_format,
+ decoder->timestamp -
+ dc->song->start_ms / 1000.0,
+ kbit_rate, &nbytes);
if (dest == NULL) {
/* the chunk is full, flush it */
decoder_flush_chunk(decoder);
@@ -449,14 +451,14 @@ decoder_data(struct decoder *decoder,
/* expand the music pipe chunk */
- full = music_chunk_expand(chunk, &dc->out_audio_format, nbytes);
+ full = chunk->Expand(dc->out_audio_format, nbytes);
if (full) {
/* the chunk is full, flush it */
decoder_flush_chunk(decoder);
g_cond_signal(dc->client_cond);
}
- data += nbytes;
+ data = (const uint8_t *)data + nbytes;
length -= nbytes;
decoder->timestamp += (double)nbytes /
@@ -517,11 +519,10 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
return cmd;
}
-float
+void
decoder_replay_gain(struct decoder *decoder,
const struct replay_gain_info *replay_gain_info)
{
- float return_db = 0;
assert(decoder != NULL);
if (replay_gain_info != NULL) {
@@ -530,9 +531,13 @@ decoder_replay_gain(struct decoder *decoder,
serial = 1;
if (REPLAY_GAIN_OFF != replay_gain_mode) {
- return_db = 20.0 * log10f(
+ enum replay_gain_mode rgm = replay_gain_mode;
+ if (rgm != REPLAY_GAIN_ALBUM)
+ rgm = REPLAY_GAIN_TRACK;
+
+ decoder->dc->replay_gain_db = 20.0 * log10f(
replay_gain_tuple_scale(
- &replay_gain_info->tuples[replay_gain_get_real_mode()],
+ &replay_gain_info->tuples[rgm],
replay_gain_preamp, replay_gain_missing_preamp,
replay_gain_limit));
}
@@ -549,19 +554,16 @@ decoder_replay_gain(struct decoder *decoder,
}
} else
decoder->replay_gain_serial = 0;
-
- return return_db;
}
void
-decoder_mixramp(struct decoder *decoder, float replay_gain_db,
+decoder_mixramp(struct decoder *decoder,
char *mixramp_start, char *mixramp_end)
{
assert(decoder != NULL);
struct decoder_control *dc = decoder->dc;
assert(dc != NULL);
- dc->replay_gain_db = replay_gain_db;
dc_mixramp_start(dc, mixramp_start);
dc_mixramp_end(dc, mixramp_end);
}
diff --git a/src/decoder_control.c b/src/DecoderControl.cxx
index 2ce03b666..58d109483 100644
--- a/src/decoder_control.c
+++ b/src/DecoderControl.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,9 @@
*/
#include "config.h"
-#include "decoder_control.h"
-#include "pipe.h"
+#include "DecoderControl.hxx"
+#include "MusicPipe.hxx"
+#include "song.h"
#include <assert.h>
@@ -40,6 +41,8 @@ dc_new(GCond *client_cond)
dc->state = DECODE_STATE_STOP;
dc->command = DECODE_COMMAND_NONE;
+ dc->song = NULL;
+
dc->replay_gain_db = 0;
dc->replay_gain_prev_db = 0;
dc->mixramp_start = NULL;
@@ -52,6 +55,11 @@ dc_new(GCond *client_cond)
void
dc_free(struct decoder_control *dc)
{
+ dc_clear_error(dc);
+
+ if (dc->song != NULL)
+ song_free(dc->song);
+
g_cond_free(dc->cond);
g_mutex_free(dc->mutex);
g_free(dc->mixramp_start);
@@ -79,6 +87,7 @@ static void
dc_command(struct decoder_control *dc, enum decoder_command cmd)
{
decoder_lock(dc);
+ dc_clear_error(dc);
dc_command_locked(dc, cmd);
decoder_unlock(dc);
}
@@ -94,6 +103,27 @@ dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
decoder_unlock(dc);
}
+bool
+decoder_is_current_song(const struct decoder_control *dc,
+ const struct song *song)
+{
+ assert(dc != NULL);
+ assert(song != NULL);
+
+ switch (dc->state) {
+ case DECODE_STATE_STOP:
+ case DECODE_STATE_ERROR:
+ return false;
+
+ case DECODE_STATE_START:
+ case DECODE_STATE_DECODE:
+ return song_equals(dc->song, song);
+ }
+
+ assert(false);
+ return false;
+}
+
void
dc_start(struct decoder_control *dc, struct song *song,
unsigned start_ms, unsigned end_ms,
@@ -104,6 +134,9 @@ dc_start(struct decoder_control *dc, struct song *song,
assert(pipe != NULL);
assert(music_pipe_empty(pipe));
+ if (dc->song != NULL)
+ song_free(dc->song);
+
dc->song = song;
dc->start_ms = start_ms;
dc->end_ms = end_ms;
diff --git a/src/decoder_control.h b/src/DecoderControl.hxx
index 566b153ee..f98a604fa 100644
--- a/src/decoder_control.h
+++ b/src/DecoderControl.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_CONTROL_H
-#define MPD_DECODER_CONTROL_H
+#ifndef MPD_DECODER_CONTROL_HXX
+#define MPD_DECODER_CONTROL_HXX
#include "decoder_command.h"
#include "audio_format.h"
@@ -67,6 +67,14 @@ struct decoder_control {
enum decoder_state state;
enum decoder_command command;
+ /**
+ * The error that occurred in the decoder thread. This
+ * attribute is only valid if #state is #DECODE_STATE_ERROR.
+ * The object must be freed when this object transitions to
+ * any other state (usually #DECODE_STATE_START).
+ */
+ GError *error;
+
bool quit;
bool seek_error;
bool seekable;
@@ -82,8 +90,11 @@ struct decoder_control {
* The song currently being decoded. This attribute is set by
* the player thread, when it sends the #DECODE_COMMAND_START
* command.
+ *
+ * This is a duplicate, and must be freed when this attribute
+ * is cleared.
*/
- const struct song *song;
+ struct song *song;
/**
* The initial seek position (in milliseconds), e.g. to the
@@ -188,6 +199,50 @@ decoder_has_failed(const struct decoder_control *dc)
return dc->state == DECODE_STATE_ERROR;
}
+/**
+ * Checks whether an error has occurred, and if so, returns a newly
+ * allocated copy of the #GError object.
+ *
+ * Caller must lock the object.
+ */
+static inline GError *
+dc_get_error(const struct decoder_control *dc)
+{
+ assert(dc != NULL);
+ assert(dc->command == DECODE_COMMAND_NONE);
+ assert(dc->state != DECODE_STATE_ERROR || dc->error != NULL);
+
+ return dc->state == DECODE_STATE_ERROR
+ ? g_error_copy(dc->error)
+ : NULL;
+}
+
+/**
+ * Like dc_get_error(), but locks and unlocks the object.
+ */
+static inline GError *
+dc_lock_get_error(struct decoder_control *dc)
+{
+ decoder_lock(dc);
+ GError *error = dc_get_error(dc);
+ decoder_unlock(dc);
+ return error;
+}
+
+/**
+ * Clear the error condition and free the #GError object (if any).
+ *
+ * Caller must lock the object.
+ */
+static inline void
+dc_clear_error(struct decoder_control *dc)
+{
+ if (dc->state == DECODE_STATE_ERROR) {
+ g_error_free(dc->error);
+ dc->state = DECODE_STATE_STOP;
+ }
+}
+
static inline bool
decoder_lock_is_idle(struct decoder_control *dc)
{
@@ -224,28 +279,35 @@ decoder_lock_has_failed(struct decoder_control *dc)
return ret;
}
-static inline const struct song *
-decoder_current_song(const struct decoder_control *dc)
-{
- switch (dc->state) {
- case DECODE_STATE_STOP:
- case DECODE_STATE_ERROR:
- return NULL;
-
- case DECODE_STATE_START:
- case DECODE_STATE_DECODE:
- return dc->song;
- }
+/**
+ * Check if the specified song is currently being decoded. If the
+ * decoder is not running currently (or being started), then this
+ * function returns false in any case.
+ *
+ * Caller must lock the object.
+ */
+gcc_pure
+bool
+decoder_is_current_song(const struct decoder_control *dc,
+ const struct song *song);
- assert(false);
- return NULL;
+gcc_pure
+static inline bool
+decoder_lock_is_current_song(struct decoder_control *dc,
+ const struct song *song)
+{
+ decoder_lock(dc);
+ const bool result = decoder_is_current_song(dc, song);
+ decoder_unlock(dc);
+ return result;
}
/**
* Start the decoder.
*
* @param the decoder
- * @param song the song to be decoded
+ * @param song the song to be decoded; the given instance will be
+ * owned and freed by the decoder
* @param start_ms see #decoder_control
* @param end_ms see #decoder_control
* @param pipe the pipe which receives the decoded chunks (owned by
diff --git a/src/decoder_internal.c b/src/DecoderInternal.cxx
index bc349f2ff..c5884d357 100644
--- a/src/decoder_internal.c
+++ b/src/DecoderInternal.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,37 @@
*/
#include "config.h"
-#include "decoder_internal.h"
-#include "decoder_control.h"
-#include "pipe.h"
+#include "DecoderInternal.hxx"
+#include "DecoderControl.hxx"
+#include "MusicPipe.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
+
+extern "C" {
+#include "tag.h"
+}
+
#include "input_stream.h"
-#include "buffer.h"
-#include "chunk.h"
#include <assert.h>
+decoder::~decoder()
+{
+ /* caller must flush the chunk */
+ assert(chunk == nullptr);
+
+ if (song_tag != nullptr)
+ tag_free(song_tag);
+
+ if (stream_tag != nullptr)
+ tag_free(stream_tag);
+
+ if (decoder_tag != nullptr)
+ tag_free(decoder_tag);
+
+ pcm_convert_deinit(&conv_state);
+}
+
/**
* All chunks are full of decoded data; wait for the player to free
* one.
@@ -87,7 +109,7 @@ decoder_flush_chunk(struct decoder *decoder)
assert(decoder != NULL);
assert(decoder->chunk != NULL);
- if (music_chunk_is_empty(decoder->chunk))
+ if (decoder->chunk->IsEmpty())
music_buffer_return(dc->buffer, decoder->chunk);
else
music_pipe_push(dc->pipe, decoder->chunk);
diff --git a/src/decoder_internal.h b/src/DecoderInternal.hxx
index d89e68cfc..ae50a62e2 100644
--- a/src/decoder_internal.h
+++ b/src/DecoderInternal.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_INTERNAL_H
-#define MPD_DECODER_INTERNAL_H
+#ifndef MPD_DECODER_INTERNAL_HXX
+#define MPD_DECODER_INTERNAL_HXX
#include "decoder_command.h"
#include "pcm_convert.h"
@@ -80,6 +80,21 @@ struct decoder {
* has changed since the last check.
*/
unsigned replay_gain_serial;
+
+ decoder(decoder_control *_dc, bool _initial_seek_pending,
+ struct tag *_tag)
+ :dc(_dc),
+ timestamp(0),
+ initial_seek_pending(_initial_seek_pending),
+ initial_seek_running(false),
+ seeking(false),
+ song_tag(_tag), stream_tag(nullptr), decoder_tag(nullptr),
+ chunk(nullptr),
+ replay_gain_serial(0) {
+ pcm_convert_init(&conv_state);
+ }
+
+ ~decoder();
};
/**
diff --git a/src/decoder_print.c b/src/DecoderPrint.cxx
index e14477ed8..70c713e9c 100644
--- a/src/decoder_print.c
+++ b/src/DecoderPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,15 @@
*/
#include "config.h"
-#include "decoder_print.h"
+#include "DecoderPrint.hxx"
#include "decoder_list.h"
#include "decoder_plugin.h"
-#include "client.h"
+#include "Client.hxx"
#include <assert.h>
static void
-decoder_plugin_print(struct client *client,
+decoder_plugin_print(Client *client,
const struct decoder_plugin *plugin)
{
const char *const*p;
@@ -46,7 +46,7 @@ decoder_plugin_print(struct client *client,
}
void
-decoder_list_print(struct client *client)
+decoder_list_print(Client *client)
{
decoder_plugins_for_each_enabled(plugin)
decoder_plugin_print(client, plugin);
diff --git a/src/DecoderPrint.hxx b/src/DecoderPrint.hxx
new file mode 100644
index 000000000..d94ba2cef
--- /dev/null
+++ b/src/DecoderPrint.hxx
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_PRINT_HXX
+#define MPD_DECODER_PRINT_HXX
+
+class Client;
+
+void
+decoder_list_print(Client *client);
+
+#endif
diff --git a/src/decoder_thread.c b/src/DecoderThread.cxx
index af80ed45b..18228f381 100644
--- a/src/decoder_thread.c
+++ b/src/DecoderThread.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,21 +18,23 @@
*/
#include "config.h"
-#include "decoder_thread.h"
-#include "decoder_control.h"
-#include "decoder_internal.h"
-#include "decoder_list.h"
+#include "DecoderThread.hxx"
+#include "DecoderControl.hxx"
+#include "DecoderInternal.hxx"
+#include "decoder_error.h"
#include "decoder_plugin.h"
+#include "song.h"
+#include "mpd_error.h"
+#include "Mapper.hxx"
+
+extern "C" {
+#include "decoder_list.h"
#include "decoder_api.h"
#include "replay_gain_ape.h"
#include "input_stream.h"
-#include "pipe.h"
-#include "song.h"
#include "tag.h"
-#include "mapper.h"
-#include "path.h"
#include "uri.h"
-#include "mpd_error.h"
+}
#include <glib.h>
@@ -182,12 +184,7 @@ decoder_file_decode(const struct decoder_plugin *plugin,
static inline gpointer
deconst_plugin(const struct decoder_plugin *plugin)
{
- union {
- const struct decoder_plugin *in;
- gpointer out;
- } u = { .in = plugin };
-
- return u.out;
+ return const_cast<struct decoder_plugin *>(plugin);
}
/**
@@ -383,57 +380,49 @@ static void
decoder_run_song(struct decoder_control *dc,
const struct song *song, const char *uri)
{
- struct decoder decoder = {
- .dc = dc,
- .initial_seek_pending = dc->start_ms > 0,
- .initial_seek_running = false,
- };
+ decoder decoder(dc, dc->start_ms > 0,
+ song->tag != NULL && song_is_file(song)
+ ? tag_dup(song->tag) : nullptr);
int ret;
- decoder.timestamp = 0.0;
- decoder.seeking = false;
- decoder.song_tag = song->tag != NULL && song_is_file(song)
- ? tag_dup(song->tag) : NULL;
- decoder.stream_tag = NULL;
- decoder.decoder_tag = NULL;
- decoder.chunk = NULL;
-
dc->state = DECODE_STATE_START;
decoder_command_finished_locked(dc);
- pcm_convert_init(&decoder.conv_state);
-
ret = song_is_file(song)
? decoder_run_file(&decoder, uri)
: decoder_run_stream(&decoder, uri);
decoder_unlock(dc);
- pcm_convert_deinit(&decoder.conv_state);
-
/* flush the last chunk */
if (decoder.chunk != NULL)
decoder_flush_chunk(&decoder);
- if (decoder.song_tag != NULL)
- tag_free(decoder.song_tag);
+ decoder_lock(dc);
- if (decoder.stream_tag != NULL)
- tag_free(decoder.stream_tag);
+ if (ret)
+ dc->state = DECODE_STATE_STOP;
+ else {
+ dc->state = DECODE_STATE_ERROR;
- if (decoder.decoder_tag != NULL)
- tag_free(decoder.decoder_tag);
+ const char *error_uri = song->uri;
+ char *allocated = uri_remove_auth(error_uri);
+ if (allocated != NULL)
+ error_uri = allocated;
- decoder_lock(dc);
-
- dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR;
+ dc->error = g_error_new(decoder_quark(), 0,
+ "Failed to decode %s", error_uri);
+ g_free(allocated);
+ }
}
static void
decoder_run(struct decoder_control *dc)
{
+ dc_clear_error(dc);
+
const struct song *song = dc->song;
char *uri;
@@ -446,6 +435,9 @@ decoder_run(struct decoder_control *dc)
if (uri == NULL) {
dc->state = DECODE_STATE_ERROR;
+ dc->error = g_error_new(decoder_quark(), 0,
+ "Failed to map song");
+
decoder_command_finished_locked(dc);
return;
}
@@ -458,7 +450,7 @@ decoder_run(struct decoder_control *dc)
static gpointer
decoder_task(gpointer arg)
{
- struct decoder_control *dc = arg;
+ struct decoder_control *dc = (struct decoder_control *)arg;
decoder_lock(dc);
diff --git a/src/decoder_thread.h b/src/DecoderThread.hxx
index 78f12a54a..8efaa2fca 100644
--- a/src/decoder_thread.h
+++ b/src/DecoderThread.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_THREAD_H
-#define MPD_DECODER_THREAD_H
+#ifndef MPD_DECODER_THREAD_HXX
+#define MPD_DECODER_THREAD_HXX
struct decoder_control;
diff --git a/src/Directory.cxx b/src/Directory.cxx
new file mode 100644
index 000000000..f27b3d474
--- /dev/null
+++ b/src/Directory.cxx
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2003-2011 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 "Directory.hxx"
+#include "SongFilter.hxx"
+#include "PlaylistVector.hxx"
+#include "DatabaseLock.hxx"
+
+extern "C" {
+#include "song.h"
+#include "song_sort.h"
+#include "path.h"
+#include "util/list_sort.h"
+}
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+inline Directory *
+Directory::Allocate(const char *path)
+{
+ assert(path != NULL);
+
+ const size_t path_size = strlen(path) + 1;
+ Directory *directory =
+ (Directory *)g_malloc0(sizeof(*directory)
+ - sizeof(directory->path)
+ + path_size);
+ new(directory) Directory(path);
+
+ return directory;
+}
+
+Directory::Directory()
+{
+ INIT_LIST_HEAD(&children);
+ INIT_LIST_HEAD(&songs);
+
+ path[0] = 0;
+}
+
+Directory::Directory(const char *_path)
+{
+ INIT_LIST_HEAD(&children);
+ INIT_LIST_HEAD(&songs);
+
+ strcpy(path, _path);
+}
+
+Directory::~Directory()
+{
+ struct song *song, *ns;
+ directory_for_each_song_safe(song, ns, this)
+ song_free(song);
+
+ Directory *child, *n;
+ directory_for_each_child_safe(child, n, this)
+ child->Free();
+}
+
+Directory *
+Directory::NewGeneric(const char *path, Directory *parent)
+{
+ assert(path != NULL);
+ assert((*path == 0) == (parent == NULL));
+
+ Directory *directory = Allocate(path);
+
+ directory->parent = parent;
+
+ return directory;
+}
+
+void
+Directory::Free()
+{
+ this->Directory::~Directory();
+ g_free(this);
+}
+
+void
+Directory::Delete()
+{
+ assert(holding_db_lock());
+ assert(parent != nullptr);
+
+ list_del(&siblings);
+ Free();
+}
+
+const char *
+Directory::GetName() const
+{
+ assert(!IsRoot());
+ assert(path != nullptr);
+
+ const char *slash = strrchr(path, '/');
+ assert((slash == nullptr) == parent->IsRoot());
+
+ return slash != NULL
+ ? slash + 1
+ : path;
+}
+
+Directory *
+Directory::CreateChild(const char *name_utf8)
+{
+ assert(holding_db_lock());
+ assert(name_utf8 != NULL);
+ assert(*name_utf8 != 0);
+
+ char *allocated;
+ const char *path_utf8;
+ if (IsRoot()) {
+ allocated = NULL;
+ path_utf8 = name_utf8;
+ } else {
+ allocated = g_strconcat(GetPath(),
+ "/", name_utf8, NULL);
+ path_utf8 = allocated;
+ }
+
+ Directory *child = NewGeneric(path_utf8, this);
+ g_free(allocated);
+
+ list_add_tail(&child->siblings, &children);
+ return child;
+}
+
+const Directory *
+Directory::FindChild(const char *name) const
+{
+ assert(holding_db_lock());
+
+ const Directory *child;
+ directory_for_each_child(child, this)
+ if (strcmp(child->GetName(), name) == 0)
+ return child;
+
+ return NULL;
+}
+
+void
+Directory::PruneEmpty()
+{
+ assert(holding_db_lock());
+
+ Directory *child, *n;
+ directory_for_each_child_safe(child, n, this) {
+ child->PruneEmpty();
+
+ if (child->IsEmpty())
+ child->Delete();
+ }
+}
+
+Directory *
+Directory::LookupDirectory(const char *uri)
+{
+ assert(holding_db_lock());
+ assert(uri != NULL);
+
+ if (isRootDirectory(uri))
+ return this;
+
+ char *duplicated = g_strdup(uri), *name = duplicated;
+
+ Directory *d = this;
+ while (1) {
+ char *slash = strchr(name, '/');
+ if (slash == name) {
+ d = NULL;
+ break;
+ }
+
+ if (slash != NULL)
+ *slash = '\0';
+
+ d = d->FindChild(name);
+ if (d == NULL || slash == NULL)
+ break;
+
+ name = slash + 1;
+ }
+
+ g_free(duplicated);
+
+ return d;
+}
+
+void
+Directory::AddSong(struct song *song)
+{
+ assert(holding_db_lock());
+ assert(song != NULL);
+ assert(song->parent == this);
+
+ list_add_tail(&song->siblings, &songs);
+}
+
+void
+Directory::RemoveSong(struct song *song)
+{
+ assert(holding_db_lock());
+ assert(song != NULL);
+ assert(song->parent == this);
+
+ list_del(&song->siblings);
+}
+
+const song *
+Directory::FindSong(const char *name_utf8) const
+{
+ assert(holding_db_lock());
+ assert(name_utf8 != NULL);
+
+ struct song *song;
+ directory_for_each_song(song, this) {
+ assert(song->parent == this);
+
+ if (strcmp(song->uri, name_utf8) == 0)
+ return song;
+ }
+
+ return NULL;
+}
+
+struct song *
+Directory::LookupSong(const char *uri)
+{
+ char *duplicated, *base;
+
+ assert(holding_db_lock());
+ assert(uri != NULL);
+
+ duplicated = g_strdup(uri);
+ base = strrchr(duplicated, '/');
+
+ Directory *d = this;
+ if (base != NULL) {
+ *base++ = 0;
+ d = d->LookupDirectory(duplicated);
+ if (d == nullptr) {
+ g_free(duplicated);
+ return NULL;
+ }
+ } else
+ base = duplicated;
+
+ struct song *song = d->FindSong(base);
+ assert(song == NULL || song->parent == d);
+
+ g_free(duplicated);
+ return song;
+
+}
+
+static int
+directory_cmp(G_GNUC_UNUSED void *priv,
+ struct list_head *_a, struct list_head *_b)
+{
+ const Directory *a = (const Directory *)_a;
+ const Directory *b = (const Directory *)_b;
+ return g_utf8_collate(a->path, b->path);
+}
+
+void
+Directory::Sort()
+{
+ assert(holding_db_lock());
+
+ list_sort(NULL, &children, directory_cmp);
+ song_list_sort(&songs);
+
+ Directory *child;
+ directory_for_each_child(child, this)
+ child->Sort();
+}
+
+bool
+Directory::Walk(bool recursive, const SongFilter *filter,
+ VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const
+{
+ assert(error_r == NULL || *error_r == NULL);
+
+ if (visit_song) {
+ struct song *song;
+ directory_for_each_song(song, this)
+ if ((filter == nullptr || filter->Match(*song)) &&
+ !visit_song(*song, error_r))
+ return false;
+ }
+
+ if (visit_playlist) {
+ for (const PlaylistInfo &p : playlists)
+ if (!visit_playlist(p, *this, error_r))
+ return false;
+ }
+
+ Directory *child;
+ directory_for_each_child(child, this) {
+ if (visit_directory &&
+ !visit_directory(*child, error_r))
+ return false;
+
+ if (recursive &&
+ !child->Walk(recursive, filter,
+ visit_directory, visit_song, visit_playlist,
+ error_r))
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/Directory.hxx b/src/Directory.hxx
new file mode 100644
index 000000000..710f47921
--- /dev/null
+++ b/src/Directory.hxx
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DIRECTORY_HXX
+#define MPD_DIRECTORY_HXX
+
+#include "check.h"
+#include "util/list.h"
+#include "gcc.h"
+#include "DatabaseVisitor.hxx"
+#include "PlaylistVector.hxx"
+
+#include <glib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#define DEVICE_INARCHIVE (dev_t)(-1)
+#define DEVICE_CONTAINER (dev_t)(-2)
+
+#define directory_for_each_child(pos, directory) \
+ list_for_each_entry(pos, &directory->children, siblings)
+
+#define directory_for_each_child_safe(pos, n, directory) \
+ list_for_each_entry_safe(pos, n, &directory->children, siblings)
+
+#define directory_for_each_song(pos, directory) \
+ list_for_each_entry(pos, &directory->songs, siblings)
+
+#define directory_for_each_song_safe(pos, n, directory) \
+ list_for_each_entry_safe(pos, n, &directory->songs, siblings)
+
+struct song;
+struct db_visitor;
+class SongFilter;
+
+struct Directory {
+ /**
+ * Pointers to the siblings of this directory within the
+ * parent directory. It is unused (undefined) in the root
+ * directory.
+ *
+ * This attribute is protected with the global #db_mutex.
+ * Read access in the update thread does not need protection.
+ */
+ struct list_head siblings;
+
+ /**
+ * A doubly linked list of child directories.
+ *
+ * This attribute is protected with the global #db_mutex.
+ * Read access in the update thread does not need protection.
+ */
+ struct list_head children;
+
+ /**
+ * A doubly linked list of songs within this directory.
+ *
+ * This attribute is protected with the global #db_mutex.
+ * Read access in the update thread does not need protection.
+ */
+ struct list_head songs;
+
+ PlaylistVector playlists;
+
+ Directory *parent;
+ time_t mtime;
+ ino_t inode;
+ dev_t device;
+ bool have_stat; /* not needed if ino_t == dev_t == 0 is impossible */
+ char path[sizeof(long)];
+
+protected:
+ Directory(const char *path);
+
+ gcc_malloc gcc_nonnull_all
+ static Directory *Allocate(const char *path);
+
+public:
+ /**
+ * Default constructor, needed for #detached_root.
+ */
+ Directory();
+ ~Directory();
+
+ /**
+ * Generic constructor for #Directory object.
+ */
+ gcc_malloc
+ static Directory *NewGeneric(const char *path_utf8, Directory *parent);
+
+ /**
+ * Create a new root #Directory object.
+ */
+ gcc_malloc
+ static Directory *NewRoot() {
+ return NewGeneric("", nullptr);
+ }
+
+ /**
+ * Free this #Directory object (and the whole object tree within it),
+ * assuming it was already removed from the parent.
+ */
+ void Free();
+
+ /**
+ * Remove this #Directory object from its parent and free it. This
+ * must not be called with the root Directory.
+ *
+ * Caller must lock the #db_mutex.
+ */
+ void Delete();
+
+ /**
+ * Create a new #Directory object as a child of the given one.
+ *
+ * Caller must lock the #db_mutex.
+ *
+ * @param name_utf8 the UTF-8 encoded name of the new sub directory
+ */
+ gcc_malloc
+ Directory *CreateChild(const char *name_utf8);
+
+ /**
+ * Caller must lock the #db_mutex.
+ */
+ gcc_pure
+ const Directory *FindChild(const char *name) const;
+
+ gcc_pure
+ Directory *FindChild(const char *name) {
+ const Directory *cthis = this;
+ return const_cast<Directory *>(cthis->FindChild(name));
+ }
+
+ /**
+ * Look up a sub directory, and create the object if it does not
+ * exist.
+ *
+ * Caller must lock the #db_mutex.
+ */
+ Directory *MakeChild(const char *name_utf8) {
+ Directory *child = FindChild(name_utf8);
+ if (child == nullptr)
+ child = CreateChild(name_utf8);
+ return child;
+ }
+
+ /**
+ * Looks up a directory by its relative URI.
+ *
+ * @param uri the relative URI
+ * @return the Directory, or NULL if none was found
+ */
+ gcc_pure
+ Directory *LookupDirectory(const char *uri);
+
+ gcc_pure
+ bool IsEmpty() const {
+ return list_empty(&children) &&
+ list_empty(&songs) &&
+ playlists.empty();
+ }
+
+ gcc_pure
+ const char *GetPath() const {
+ return path;
+ }
+
+ /**
+ * Returns the base name of the directory.
+ */
+ gcc_pure
+ const char *GetName() const;
+
+ /**
+ * Is this the root directory of the music database?
+ */
+ gcc_pure
+ bool IsRoot() const {
+ return parent == NULL;
+ }
+
+ /**
+ * Look up a song in this directory by its name.
+ *
+ * Caller must lock the #db_mutex.
+ */
+ gcc_pure
+ const song *FindSong(const char *name_utf8) const;
+
+ gcc_pure
+ song *FindSong(const char *name_utf8) {
+ const Directory *cthis = this;
+ return const_cast<song *>(cthis->FindSong(name_utf8));
+ }
+
+ /**
+ * Looks up a song by its relative URI.
+ *
+ * Caller must lock the #db_mutex.
+ *
+ * @param uri the relative URI
+ * @return the song, or NULL if none was found
+ */
+ gcc_pure
+ song *LookupSong(const char *uri);
+
+ /**
+ * Add a song object to this directory. Its "parent" attribute must
+ * be set already.
+ */
+ void AddSong(song *song);
+
+ /**
+ * Remove a song object from this directory (which effectively
+ * invalidates the song object, because the "parent" attribute becomes
+ * stale), but does not free it.
+ */
+ void RemoveSong(song *song);
+
+ /**
+ * Caller must lock the #db_mutex.
+ */
+ void PruneEmpty();
+
+ /**
+ * Sort all directory entries recursively.
+ *
+ * Caller must lock the #db_mutex.
+ */
+ void Sort();
+
+ /**
+ * Caller must lock #db_mutex.
+ */
+ bool Walk(bool recursive, const SongFilter *match,
+ VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const;
+};
+
+static inline bool
+isRootDirectory(const char *name)
+{
+ return name[0] == 0 || (name[0] == '/' && name[1] == 0);
+}
+
+#endif
diff --git a/src/directory_save.c b/src/DirectorySave.cxx
index de1df050a..6a5efb058 100644
--- a/src/directory_save.c
+++ b/src/DirectorySave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,12 +18,12 @@
*/
#include "config.h"
-#include "directory_save.h"
-#include "directory.h"
+#include "DirectorySave.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "text_file.h"
-#include "song_save.h"
-#include "playlist_database.h"
+#include "SongSave.hxx"
+#include "PlaylistDatabase.hxx"
+#include "TextFile.hxx"
#include <assert.h>
#include <string.h>
@@ -43,17 +43,16 @@ directory_quark(void)
}
void
-directory_save(FILE *fp, const struct directory *directory)
+directory_save(FILE *fp, const Directory *directory)
{
- if (!directory_is_root(directory)) {
+ if (!directory->IsRoot()) {
fprintf(fp, DIRECTORY_MTIME "%lu\n",
(unsigned long)directory->mtime);
- fprintf(fp, "%s%s\n", DIRECTORY_BEGIN,
- directory_get_path(directory));
+ fprintf(fp, "%s%s\n", DIRECTORY_BEGIN, directory->GetPath());
}
- struct directory *cur;
+ Directory *cur;
directory_for_each_child(cur, directory) {
char *base = g_path_get_basename(cur->path);
@@ -70,33 +69,31 @@ directory_save(FILE *fp, const struct directory *directory)
directory_for_each_song(song, directory)
song_save(fp, song);
- playlist_vector_save(fp, &directory->playlists);
+ playlist_vector_save(fp, directory->playlists);
- if (!directory_is_root(directory))
- fprintf(fp, DIRECTORY_END "%s\n",
- directory_get_path(directory));
+ if (!directory->IsRoot())
+ fprintf(fp, DIRECTORY_END "%s\n", directory->GetPath());
}
-static struct directory *
-directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
- GString *buffer, GError **error_r)
+static Directory *
+directory_load_subdir(TextFile &file, Directory *parent, const char *name,
+ GError **error_r)
{
- const char *line;
bool success;
- if (directory_get_child(parent, name) != NULL) {
+ if (parent->FindChild(name) != nullptr) {
g_set_error(error_r, directory_quark(), 0,
"Duplicate subdirectory '%s'", name);
return NULL;
}
- struct directory *directory = directory_new_child(parent, name);
+ Directory *directory = parent->CreateChild(name);
- line = read_text_line(fp, buffer);
+ const char *line = file.ReadLine();
if (line == NULL) {
g_set_error(error_r, directory_quark(), 0,
"Unexpected end of file");
- directory_delete(directory);
+ directory->Delete();
return NULL;
}
@@ -105,11 +102,11 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
g_ascii_strtoull(line + sizeof(DIRECTORY_MTIME) - 1,
NULL, 10);
- line = read_text_line(fp, buffer);
+ line = file.ReadLine();
if (line == NULL) {
g_set_error(error_r, directory_quark(), 0,
"Unexpected end of file");
- directory_delete(directory);
+ directory->Delete();
return NULL;
}
}
@@ -117,13 +114,13 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
if (!g_str_has_prefix(line, DIRECTORY_BEGIN)) {
g_set_error(error_r, directory_quark(), 0,
"Malformed line: %s", line);
- directory_delete(directory);
+ directory->Delete();
return NULL;
}
- success = directory_load(fp, directory, buffer, error_r);
+ success = directory_load(file, directory, error_r);
if (!success) {
- directory_delete(directory);
+ directory->Delete();
return NULL;
}
@@ -131,44 +128,42 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
}
bool
-directory_load(FILE *fp, struct directory *directory,
- GString *buffer, GError **error)
+directory_load(TextFile &file, Directory *directory, GError **error)
{
const char *line;
- while ((line = read_text_line(fp, buffer)) != NULL &&
+ while ((line = file.ReadLine()) != NULL &&
!g_str_has_prefix(line, DIRECTORY_END)) {
if (g_str_has_prefix(line, DIRECTORY_DIR)) {
- struct directory *subdir =
- directory_load_subdir(fp, directory,
+ Directory *subdir =
+ directory_load_subdir(file, directory,
line + sizeof(DIRECTORY_DIR) - 1,
- buffer, error);
+ error);
if (subdir == NULL)
return false;
} else if (g_str_has_prefix(line, SONG_BEGIN)) {
const char *name = line + sizeof(SONG_BEGIN) - 1;
struct song *song;
- if (directory_get_song(directory, name) != NULL) {
+ if (directory->FindSong(name) != nullptr) {
g_set_error(error, directory_quark(), 0,
"Duplicate song '%s'", name);
- return NULL;
+ return false;
}
- song = song_load(fp, directory, name,
- buffer, error);
+ song = song_load(file, directory, name, error);
if (song == NULL)
return false;
- directory_add_song(directory, song);
+ directory->AddSong(song);
} else if (g_str_has_prefix(line, PLAYLIST_META_BEGIN)) {
/* duplicate the name, because
playlist_metadata_load() will overwrite the
buffer */
char *name = g_strdup(line + sizeof(PLAYLIST_META_BEGIN) - 1);
- if (!playlist_metadata_load(fp, &directory->playlists,
- name, buffer, error)) {
+ if (!playlist_metadata_load(file, directory->playlists,
+ name, error)) {
g_free(name);
return false;
}
diff --git a/src/DirectorySave.hxx b/src/DirectorySave.hxx
new file mode 100644
index 000000000..a7f3034a7
--- /dev/null
+++ b/src/DirectorySave.hxx
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DIRECTORY_SAVE_HXX
+#define MPD_DIRECTORY_SAVE_HXX
+
+#include <glib.h>
+
+#include <stdio.h>
+
+struct Directory;
+class TextFile;
+
+void
+directory_save(FILE *fp, const Directory *directory);
+
+bool
+directory_load(TextFile &file, Directory *directory, GError **error);
+
+#endif
diff --git a/src/exclude.c b/src/ExcludeList.cxx
index 438039d30..4beb7afc3 100644
--- a/src/exclude.c
+++ b/src/ExcludeList.cxx
@@ -23,24 +23,23 @@
*/
#include "config.h"
-#include "exclude.h"
+#include "ExcludeList.hxx"
+
+extern "C" {
#include "path.h"
+}
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
-GSList *
-exclude_list_load(const char *path_fs)
+bool
+ExcludeList::LoadFile(const char *path_fs)
{
- FILE *file;
- char line[1024];
- GSList *list = NULL;
-
assert(path_fs != NULL);
- file = fopen(path_fs, "r");
+ FILE *file = fopen(path_fs, "r");
if (file == NULL) {
if (errno != ENOENT) {
char *path_utf8 = fs_charset_to_utf8(path_fs);
@@ -49,9 +48,10 @@ exclude_list_load(const char *path_fs)
g_free(path_utf8);
}
- return NULL;
+ return false;
}
+ char line[1024];
while (fgets(line, sizeof(line), file) != NULL) {
char *p = strchr(line, '#');
if (p != NULL)
@@ -59,37 +59,24 @@ exclude_list_load(const char *path_fs)
p = g_strstrip(line);
if (*p != 0)
- list = g_slist_prepend(list, g_pattern_spec_new(p));
+ patterns.emplace_front(p);
}
fclose(file);
- return list;
-}
-
-void
-exclude_list_free(GSList *list)
-{
- while (list != NULL) {
- GPatternSpec *pattern = list->data;
- g_pattern_spec_free(pattern);
- list = g_slist_remove(list, list->data);
- }
+ return true;
}
bool
-exclude_list_check(GSList *list, const char *name_fs)
+ExcludeList::Check(const char *name_fs) const
{
assert(name_fs != NULL);
/* XXX include full path name in check */
- for (; list != NULL; list = list->next) {
- GPatternSpec *pattern = list->data;
-
- if (g_pattern_match_string(pattern, name_fs))
+ for (const auto &i : patterns)
+ if (i.Check(name_fs))
return true;
- }
return false;
}
diff --git a/src/ExcludeList.hxx b/src/ExcludeList.hxx
new file mode 100644
index 000000000..4d678b085
--- /dev/null
+++ b/src/ExcludeList.hxx
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2003-2011 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.
+ */
+
+/*
+ * The .mpdignore backend code.
+ *
+ */
+
+#ifndef MPD_EXCLUDE_H
+#define MPD_EXCLUDE_H
+
+#include "gcc.h"
+
+#include <forward_list>
+
+#include <glib.h>
+
+class ExcludeList {
+ 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);
+ }
+
+ gcc_pure
+ bool Check(const char *name_fs) const {
+ return g_pattern_match_string(pattern, name_fs);
+ }
+ };
+
+ std::forward_list<Pattern> patterns;
+
+public:
+ gcc_pure
+ bool IsEmpty() const {
+ return patterns.empty();
+ }
+
+ /**
+ * Loads and parses a .mpdignore file.
+ */
+ bool LoadFile(const char *path_fs);
+
+ /**
+ * Checks whether one of the patterns in the .mpdignore file matches
+ * the specified file name.
+ */
+ bool Check(const char *name_fs) const;
+};
+
+
+#endif
diff --git a/src/inotify_queue.c b/src/InotifyQueue.cxx
index d5e2228c3..0d19ff083 100644
--- a/src/inotify_queue.c
+++ b/src/InotifyQueue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,11 @@
*/
#include "config.h"
-#include "inotify_queue.h"
-#include "update.h"
+#include "InotifyQueue.hxx"
+#include "UpdateGlue.hxx"
+
+#include <list>
+#include <string>
#include <glib.h>
@@ -37,7 +40,7 @@ enum {
INOTIFY_UPDATE_DELAY_S = 5,
};
-static GSList *inotify_queue;
+static std::list<std::string> inotify_queue;
static guint queue_source_id;
void
@@ -45,20 +48,11 @@ mpd_inotify_queue_init(void)
{
}
-static void
-free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- g_free(data);
-}
-
void
mpd_inotify_queue_finish(void)
{
if (queue_source_id != 0)
g_source_remove(queue_source_id);
-
- g_slist_foreach(inotify_queue, free_callback, NULL);
- g_slist_free(inotify_queue);
}
static gboolean
@@ -66,8 +60,8 @@ mpd_inotify_run_update(G_GNUC_UNUSED gpointer data)
{
unsigned id;
- while (inotify_queue != NULL) {
- char *uri_utf8 = inotify_queue->data;
+ while (!inotify_queue.empty()) {
+ const char *uri_utf8 = inotify_queue.front().c_str();
id = update_enqueue(uri_utf8, false);
if (id == 0)
@@ -76,9 +70,7 @@ mpd_inotify_run_update(G_GNUC_UNUSED gpointer data)
g_debug("updating '%s' job=%u", uri_utf8, id);
- g_free(uri_utf8);
- inotify_queue = g_slist_delete_link(inotify_queue,
- inotify_queue);
+ inotify_queue.pop_front();
}
/* done, remove the timer event by returning false */
@@ -97,39 +89,29 @@ path_in(const char *path, const char *possible_parent)
}
void
-mpd_inotify_enqueue(char *uri_utf8)
+mpd_inotify_enqueue(const char *uri_utf8)
{
- GSList *old_queue = inotify_queue;
-
if (queue_source_id != 0)
g_source_remove(queue_source_id);
queue_source_id = g_timeout_add_seconds(INOTIFY_UPDATE_DELAY_S,
mpd_inotify_run_update, NULL);
- inotify_queue = NULL;
- while (old_queue != NULL) {
- char *current_uri = old_queue->data;
+ for (auto i = inotify_queue.begin(), end = inotify_queue.end();
+ i != end;) {
+ const char *current_uri = i->c_str();
- if (path_in(uri_utf8, current_uri)) {
+ if (path_in(uri_utf8, current_uri))
/* already enqueued */
- g_free(uri_utf8);
- inotify_queue = g_slist_concat(inotify_queue,
- old_queue);
return;
- }
-
- old_queue = g_slist_delete_link(old_queue, old_queue);
if (path_in(current_uri, uri_utf8))
/* existing path is a sub-path of the new
path; we can dequeue the existing path and
update the new path instead */
- g_free(current_uri);
+ i = inotify_queue.erase(i);
else
- /* move the existing path to the new queue */
- inotify_queue = g_slist_prepend(inotify_queue,
- current_uri);
+ ++i;
}
- inotify_queue = g_slist_prepend(inotify_queue, uri_utf8);
+ inotify_queue.emplace_back(uri_utf8);
}
diff --git a/src/inotify_queue.h b/src/InotifyQueue.hxx
index cfc28ebfe..158a5dbb5 100644
--- a/src/inotify_queue.h
+++ b/src/InotifyQueue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INOTIFY_QUEUE_H
-#define MPD_INOTIFY_QUEUE_H
+#ifndef MPD_INOTIFY_QUEUE_HXX
+#define MPD_INOTIFY_QUEUE_HXX
void
mpd_inotify_queue_init(void);
@@ -27,6 +27,6 @@ void
mpd_inotify_queue_finish(void);
void
-mpd_inotify_enqueue(char *uri_utf8);
+mpd_inotify_enqueue(const char *uri_utf8);
#endif
diff --git a/src/inotify_source.c b/src/InotifySource.cxx
index e415f5e72..16fd62511 100644
--- a/src/inotify_source.c
+++ b/src/InotifySource.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,20 @@
*/
#include "config.h"
-#include "inotify_source.h"
+#include "InotifySource.hxx"
+
+extern "C" {
#include "fifo_buffer.h"
+}
+
#include "fd_util.h"
#include "mpd_error.h"
+#include <glib.h>
+
#include <sys/inotify.h>
#include <unistd.h>
#include <errno.h>
-#include <stdbool.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "inotify"
@@ -61,11 +66,10 @@ mpd_inotify_in_event(G_GNUC_UNUSED GIOChannel *_source,
G_GNUC_UNUSED GIOCondition condition,
gpointer data)
{
- struct mpd_inotify_source *source = data;
+ struct mpd_inotify_source *source = (struct mpd_inotify_source *)data;
void *dest;
size_t length;
ssize_t nbytes;
- const struct inotify_event *event;
dest = fifo_buffer_write(source->buffer, &length);
if (dest == NULL)
@@ -83,7 +87,9 @@ mpd_inotify_in_event(G_GNUC_UNUSED GIOChannel *_source,
while (true) {
const char *name;
- event = fifo_buffer_read(source->buffer, &length);
+ const struct inotify_event *event =
+ (const struct inotify_event *)
+ fifo_buffer_read(source->buffer, &length);
if (event == NULL || length < sizeof(*event) ||
length < sizeof(*event) + event->len)
break;
diff --git a/src/inotify_source.h b/src/InotifySource.hxx
index f92e18e39..cde2bc269 100644
--- a/src/inotify_source.h
+++ b/src/InotifySource.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INOTIFY_SOURCE_H
-#define MPD_INOTIFY_SOURCE_H
+#ifndef MPD_INOTIFY_SOURCE_HXX
+#define MPD_INOTIFY_SOURCE_HXX
-#include <glib.h>
+#include "gerror.h"
typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask,
const char *name, void *ctx);
diff --git a/src/inotify_update.c b/src/InotifyUpdate.cxx
index 3f4a8c0c4..b7bb3af2b 100644
--- a/src/inotify_update.c
+++ b/src/InotifyUpdate.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,19 @@
*/
#include "config.h" /* must be first for large file support */
-#include "inotify_update.h"
-#include "inotify_source.h"
-#include "inotify_queue.h"
-#include "database.h"
-#include "mapper.h"
+#include "InotifyUpdate.hxx"
+#include "InotifySource.hxx"
+#include "InotifyQueue.hxx"
+#include "Mapper.hxx"
+
+extern "C" {
#include "path.h"
+}
+#include <glib.h>
#include <assert.h>
#include <sys/inotify.h>
#include <sys/stat.h>
-#include <stdbool.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
@@ -90,7 +92,8 @@ tree_remove_watch_directory(struct watch_directory *directory)
static struct watch_directory *
tree_find_watch_directory(int wd)
{
- return g_tree_lookup(inotify_directories, GINT_TO_POINTER(wd));
+ return (struct watch_directory *)
+ g_tree_lookup(inotify_directories, GINT_TO_POINTER(wd));
}
static void
@@ -109,7 +112,7 @@ remove_watch_directory(struct watch_directory *directory)
tree_remove_watch_directory(directory);
while (directory->children != NULL)
- remove_watch_directory(directory->children->data);
+ remove_watch_directory((struct watch_directory *)directory->children->data);
directory->parent->children =
g_list_remove(directory->parent->children, directory);
@@ -292,10 +295,10 @@ mpd_inotify_callback(int wd, unsigned mask,
? fs_charset_to_utf8(uri_fs)
: g_strdup("");
- if (uri_utf8 != NULL)
- /* this function will take care of freeing
- uri_utf8 */
+ if (uri_utf8 != NULL) {
mpd_inotify_enqueue(uri_utf8);
+ g_free(uri_utf8);
+ }
}
g_free(uri_fs);
@@ -349,7 +352,7 @@ static gboolean
free_watch_directory(G_GNUC_UNUSED gpointer key, gpointer value,
G_GNUC_UNUSED gpointer data)
{
- struct watch_directory *directory = value;
+ struct watch_directory *directory = (struct watch_directory *)value;
g_free(directory->name);
g_list_free(directory->children);
diff --git a/src/inotify_update.h b/src/InotifyUpdate.hxx
index ca75c0f45..ceb421553 100644
--- a/src/inotify_update.h
+++ b/src/InotifyUpdate.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INOTIFY_UPDATE_H
-#define MPD_INOTIFY_UPDATE_H
+#ifndef MPD_INOTIFY_UPDATE_HXX
+#define MPD_INOTIFY_UPDATE_HXX
#include "check.h"
diff --git a/src/listen.c b/src/Listen.cxx
index 90e13b9c1..3ea9bae9a 100644
--- a/src/listen.c
+++ b/src/Listen.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,15 @@
*/
#include "config.h"
-#include "listen.h"
+#include "Listen.hxx"
+#include "Main.hxx"
+#include "Partition.hxx"
+#include "Client.hxx"
+
+extern "C" {
#include "server_socket.h"
-#include "client.h"
#include "conf.h"
-#include "main.h"
+}
#include <string.h>
#include <assert.h>
@@ -43,7 +47,8 @@ static void
listen_callback(int fd, const struct sockaddr *address,
size_t address_length, int uid, G_GNUC_UNUSED void *ctx)
{
- client_new(global_player_control, fd, address, address_length, uid);
+ client_new(global_partition->playlist, &global_partition->pc,
+ fd, address, address_length, uid);
}
static bool
diff --git a/src/listen.h b/src/Listen.hxx
index 246e83706..fd553477b 100644
--- a/src/listen.h
+++ b/src/Listen.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_LISTEN_H
-#define MPD_LISTEN_H
+#ifndef MPD_LISTEN_HXX
+#define MPD_LISTEN_HXX
-#include <glib.h>
+#include "gerror.h"
#include <stdbool.h>
diff --git a/src/main.c b/src/Main.cxx
index 12f8d86f6..371602247 100644
--- a/src/main.c
+++ b/src/Main.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,56 +18,60 @@
*/
#include "config.h"
-#include "main.h"
+#include "Main.hxx"
+#include "CommandLine.hxx"
+#include "PlaylistFile.hxx"
+#include "UpdateGlue.hxx"
+#include "MusicChunk.hxx"
+#include "StateFile.hxx"
+#include "PlayerThread.hxx"
+#include "Mapper.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabaseSimple.hxx"
+#include "Permission.hxx"
+#include "Listen.hxx"
+#include "ClientIdle.hxx"
+#include "Client.hxx"
+#include "AllCommands.hxx"
+#include "Partition.hxx"
+
+extern "C" {
#include "daemon.h"
#include "io_thread.h"
-#include "client.h"
-#include "client_idle.h"
#include "idle.h"
-#include "command.h"
-#include "playlist.h"
-#include "stored_playlist.h"
-#include "database.h"
-#include "update.h"
-#include "player_thread.h"
-#include "listen.h"
-#include "cmdline.h"
#include "conf.h"
#include "path.h"
-#include "mapper.h"
-#include "chunk.h"
-#include "player_control.h"
#include "stats.h"
#include "sig_handlers.h"
#include "audio_config.h"
#include "output_all.h"
#include "volume.h"
#include "log.h"
-#include "permission.h"
#include "pcm_resample.h"
#include "replay_gain_config.h"
#include "decoder_list.h"
#include "input_init.h"
#include "playlist_list.h"
-#include "state_file.h"
#include "tag.h"
-#include "dbUtils.h"
#include "zeroconf.h"
#include "event_pipe.h"
-#include "tag_pool.h"
+}
+
#include "mpd_error.h"
#ifdef ENABLE_INOTIFY
-#include "inotify_update.h"
+#include "InotifyUpdate.hxx"
#endif
#ifdef ENABLE_SQLITE
-#include "sticker.h"
+#include "StickerDatabase.hxx"
#endif
+extern "C" {
#ifdef ENABLE_ARCHIVE
#include "archive_list.h"
#endif
+}
#include <glib.h>
@@ -95,7 +99,7 @@ GMainLoop *main_loop;
GCond *main_cond;
-struct player_control *global_player_control;
+Partition *global_partition;
static bool
glue_daemonize_init(const struct options *options, GError **error_r)
@@ -153,31 +157,47 @@ glue_mapper_init(GError **error_r)
static bool
glue_db_init_and_load(void)
{
+ const struct config_param *param = config_get_param("database");
const struct config_param *path = config_get_param(CONF_DB_FILE);
+ if (param != NULL && path != NULL)
+ g_message("Found both 'database' and '" CONF_DB_FILE
+ "' setting - ignoring the latter");
+
GError *error = NULL;
bool ret;
if (!mapper_has_music_directory()) {
+ if (param != NULL)
+ g_message("Found database setting without "
+ CONF_MUSIC_DIR " - disabling database");
if (path != NULL)
g_message("Found " CONF_DB_FILE " setting without "
CONF_MUSIC_DIR " - disabling database");
- db_init(NULL, NULL);
return true;
}
- if (path == NULL)
- MPD_ERROR(CONF_DB_FILE " setting missing");
+ struct config_param *allocated = NULL;
- if (!db_init(path, &error))
+ if (param == NULL && path != NULL) {
+ allocated = config_new_param("database", path->line);
+ config_add_block_param(allocated, "path",
+ path->value, path->line);
+ param = allocated;
+ }
+
+ if (!DatabaseGlobalInit(param, &error))
MPD_ERROR("%s", error->message);
- ret = db_load(&error);
+ if (allocated != NULL)
+ config_param_free(allocated);
+
+ ret = DatabaseGlobalOpen(&error);
if (!ret)
MPD_ERROR("%s", error->message);
/* run database update after daemonization? */
- return db_exists();
+ return !db_is_simple() || db_exists();
}
/**
@@ -210,7 +230,7 @@ glue_state_file_init(GError **error_r)
return false;
}
- state_file_init(path, global_player_control);
+ state_file_init(path, *global_partition);
g_free(path);
return true;
@@ -285,7 +305,13 @@ initialize_decoder_and_player(void)
if (buffered_before_play > buffered_chunks)
buffered_before_play = buffered_chunks;
- global_player_control = pc_new(buffered_chunks, buffered_before_play);
+ const unsigned max_length =
+ config_get_positive(CONF_MAX_PLAYLIST_LENGTH,
+ DEFAULT_PLAYLIST_MAX_LENGTH);
+
+ global_partition = new Partition(max_length,
+ buffered_chunks,
+ buffered_before_play);
}
/**
@@ -342,7 +368,6 @@ int mpd_main(int argc, char *argv[])
io_thread_init();
winsock_init();
idle_init();
- tag_pool_init();
config_global_init();
success = parse_cmdline(argc, argv, &options, &error);
@@ -416,7 +441,7 @@ int mpd_main(int argc, char *argv[])
initialize_decoder_and_player();
volume_init();
initAudioConfig();
- audio_output_all_init(global_player_control);
+ audio_output_all_init(&global_partition->pc);
client_manager_init();
replay_gain_global_init();
@@ -442,7 +467,7 @@ int mpd_main(int argc, char *argv[])
initZeroconf();
- player_create(global_player_control);
+ player_create(&global_partition->pc);
if (create_db) {
/* the database failed to load: recreate the
@@ -458,6 +483,8 @@ int mpd_main(int argc, char *argv[])
return EXIT_FAILURE;
}
+ audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(global_partition->playlist.queue.random));
+
success = config_get_bool(CONF_AUTO_UPDATE, false);
#ifdef ENABLE_INOTIFY
if (success && mapper_has_music_directory())
@@ -472,7 +499,7 @@ int mpd_main(int argc, char *argv[])
/* enable all audio outputs (if not already done by
playlist_state_restore() */
- pc_update_audio(global_player_control);
+ pc_update_audio(&global_partition->pc);
#ifdef WIN32
win32_app_started();
@@ -493,15 +520,14 @@ int mpd_main(int argc, char *argv[])
mpd_inotify_finish();
#endif
- state_file_finish(global_player_control);
- pc_kill(global_player_control);
+ state_file_finish(*global_partition);
+ pc_kill(&global_partition->pc);
finishZeroconf();
client_manager_deinit();
listen_global_finish();
- playlist_global_finish();
start = clock();
- db_finish();
+ DatabaseGlobalDeinit();
g_debug("db_finish took %f seconds",
((float)(clock()-start))/CLOCKS_PER_SEC);
@@ -518,8 +544,7 @@ int mpd_main(int argc, char *argv[])
volume_finish();
mapper_finish();
path_global_finish();
- finishPermissions();
- pc_free(global_player_control);
+ delete global_partition;
command_finish();
update_global_finish();
decoder_plugin_deinit_all();
@@ -527,7 +552,6 @@ int mpd_main(int argc, char *argv[])
archive_plugin_deinit_all();
#endif
config_global_finish();
- tag_pool_deinit();
idle_deinit();
stats_global_finish();
io_thread_deinit();
diff --git a/src/main.h b/src/Main.hxx
index 2a7d75910..6d61843a8 100644
--- a/src/main.h
+++ b/src/Main.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 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 MAIN_H
-#define MAIN_H
+#ifndef MPD_MAIN_HXX
+#define MPD_MAIN_HXX
#include <glib.h>
@@ -28,7 +28,7 @@ extern GMainLoop *main_loop;
extern GCond *main_cond;
-extern struct player_control *global_player_control;
+extern struct Partition *global_partition;
/**
* A entry point for application.
diff --git a/src/mapper.c b/src/Mapper.cxx
index 7db74b1af..4d863418b 100644
--- a/src/mapper.c
+++ b/src/Mapper.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,10 +22,13 @@
*/
#include "config.h"
-#include "mapper.h"
-#include "directory.h"
+#include "Mapper.hxx"
+#include "Directory.hxx"
#include "song.h"
+
+extern "C" {
#include "path.h"
+}
#include <glib.h>
@@ -178,19 +181,19 @@ map_uri_fs(const char *uri)
}
char *
-map_directory_fs(const struct directory *directory)
+map_directory_fs(const Directory *directory)
{
assert(music_dir_utf8 != NULL);
assert(music_dir_fs != NULL);
- if (directory_is_root(directory))
+ if (directory->IsRoot())
return g_strdup(music_dir_fs);
- return map_uri_fs(directory_get_path(directory));
+ return map_uri_fs(directory->GetPath());
}
char *
-map_directory_child_fs(const struct directory *directory, const char *name)
+map_directory_child_fs(const Directory *directory, const char *name)
{
assert(music_dir_utf8 != NULL);
assert(music_dir_fs != NULL);
@@ -219,13 +222,32 @@ map_directory_child_fs(const struct directory *directory, const char *name)
return path;
}
+/**
+ * Map a song object that was created by song_dup_detached(). It does
+ * not have a real parent directory, only the dummy object
+ * #detached_root.
+ */
+static char *
+map_detached_song_fs(const char *uri_utf8)
+{
+ char *uri_fs = utf8_to_fs_charset(uri_utf8);
+ if (uri_fs == NULL)
+ return NULL;
+
+ char *path = g_build_filename(music_dir_fs, uri_fs, NULL);
+ g_free(uri_fs);
+ return path;
+}
+
char *
map_song_fs(const struct song *song)
{
assert(song_is_file(song));
if (song_in_database(song))
- return map_directory_child_fs(song->parent, song->uri);
+ return song_is_detached(song)
+ ? map_detached_song_fs(song->uri)
+ : map_directory_child_fs(song->parent, song->uri);
else
return utf8_to_fs_charset(song->uri);
}
diff --git a/src/mapper.h b/src/Mapper.hxx
index d6184a175..2ced38a10 100644
--- a/src/mapper.h
+++ b/src/Mapper.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,15 +21,15 @@
* Maps directory and song objects to file system paths.
*/
-#ifndef MPD_MAPPER_H
-#define MPD_MAPPER_H
+#ifndef MPD_MAPPER_HXX
+#define MPD_MAPPER_HXX
-#include <glib.h>
-#include <stdbool.h>
+#include "gcc.h"
+#include "gerror.h"
#define PLAYLIST_FILE_SUFFIX ".m3u"
-struct directory;
+struct Directory;
struct song;
void mapper_init(const char *_music_dir, const char *_playlist_dir);
@@ -39,7 +39,7 @@ void mapper_finish(void);
/**
* Return the absolute path of the music directory encoded in UTF-8.
*/
-G_GNUC_CONST
+gcc_const
const char *
mapper_get_music_directory_utf8(void);
@@ -47,18 +47,18 @@ mapper_get_music_directory_utf8(void);
* Return the absolute path of the music directory encoded in the
* filesystem character set.
*/
-G_GNUC_CONST
+gcc_const
const char *
mapper_get_music_directory_fs(void);
/**
* Returns true if a music directory was configured.
*/
-G_GNUC_CONST
+gcc_const
static inline bool
mapper_has_music_directory(void)
{
- return mapper_get_music_directory_utf8() != NULL;
+ return mapper_get_music_directory_utf8() != nullptr;
}
/**
@@ -66,7 +66,7 @@ mapper_has_music_directory(void)
* this function converts it to a relative path. If not, it returns
* the unmodified string pointer.
*/
-G_GNUC_PURE
+gcc_pure
const char *
map_to_relative_path(const char *path_utf8);
@@ -75,7 +75,7 @@ map_to_relative_path(const char *path_utf8);
* is basically done by converting the URI to the file system charset
* and prepending the music directory.
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
map_uri_fs(const char *uri);
@@ -83,11 +83,11 @@ map_uri_fs(const char *uri);
* Determines the file system path of a directory object.
*
* @param directory the directory object
- * @return the path in file system encoding, or NULL if mapping failed
+ * @return the path in file system encoding, or nullptr if mapping failed
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
-map_directory_fs(const struct directory *directory);
+map_directory_fs(const Directory *directory);
/**
* Determines the file system path of a directory's child (may be a
@@ -95,20 +95,20 @@ map_directory_fs(const struct directory *directory);
*
* @param directory the parent directory object
* @param name the child's name in UTF-8
- * @return the path in file system encoding, or NULL if mapping failed
+ * @return the path in file system encoding, or nullptr if mapping failed
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
-map_directory_child_fs(const struct directory *directory, const char *name);
+map_directory_child_fs(const Directory *directory, const char *name);
/**
* Determines the file system path of a song. This must not be a
* remote song.
*
* @param song the song object
- * @return the path in file system encoding, or NULL if mapping failed
+ * @return the path in file system encoding, or nullptr if mapping failed
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
map_song_fs(const struct song *song);
@@ -117,16 +117,16 @@ map_song_fs(const struct song *song);
* absolute) to a relative path in UTF-8 encoding.
*
* @param path_fs a path in file system encoding
- * @return the relative path in UTF-8, or NULL if mapping failed
+ * @return the relative path in UTF-8, or nullptr if mapping failed
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
map_fs_to_utf8(const char *path_fs);
/**
* Returns the playlist directory.
*/
-G_GNUC_CONST
+gcc_const
const char *
map_spl_path(void);
@@ -135,9 +135,9 @@ map_spl_path(void);
* path. The return value is allocated on the heap and must be freed
* with g_free().
*
- * @return the path in file system encoding, or NULL if mapping failed
+ * @return the path in file system encoding, or nullptr if mapping failed
*/
-G_GNUC_PURE
+gcc_pure
char *
map_spl_utf8_to_fs(const char *name);
diff --git a/src/MessageCommands.cxx b/src/MessageCommands.cxx
new file mode 100644
index 000000000..c5f4d79b0
--- /dev/null
+++ b/src/MessageCommands.cxx
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "MessageCommands.hxx"
+#include "ClientSubscribe.hxx"
+#include "ClientInternal.hxx"
+#include "protocol/Result.hxx"
+#include "protocol/ArgParser.hxx"
+
+#include <set>
+#include <string>
+
+#include <assert.h>
+
+enum command_return
+handle_subscribe(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ assert(argc == 2);
+
+ switch (client_subscribe(client, argv[1])) {
+ case CLIENT_SUBSCRIBE_OK:
+ return COMMAND_RETURN_OK;
+
+ case CLIENT_SUBSCRIBE_INVALID:
+ command_error(client, ACK_ERROR_ARG,
+ "invalid channel name");
+ return COMMAND_RETURN_ERROR;
+
+ case CLIENT_SUBSCRIBE_ALREADY:
+ command_error(client, ACK_ERROR_EXIST,
+ "already subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+
+ case CLIENT_SUBSCRIBE_FULL:
+ command_error(client, ACK_ERROR_EXIST,
+ "subscription list is full");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ /* unreachable */
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_unsubscribe(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ assert(argc == 2);
+
+ if (client_unsubscribe(client, argv[1]))
+ return COMMAND_RETURN_OK;
+ else {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "not subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+struct channels_context {
+ std::set<std::string> channels;
+};
+
+static void
+collect_channels(gpointer data, gpointer user_data)
+{
+ struct channels_context *context =
+ (struct channels_context *)user_data;
+ const Client *client = (const Client *)data;
+
+ context->channels.insert(client->subscriptions.begin(),
+ client->subscriptions.end());
+}
+
+enum command_return
+handle_channels(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 1);
+
+ struct channels_context context;
+
+ client_list_foreach(collect_channels, &context);
+
+ for (const auto &channel : context.channels)
+ client_printf(client, "channel: %s\n", channel.c_str());
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_read_messages(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 1);
+
+ while (!client->messages.empty()) {
+ const ClientMessage &msg = client->messages.front();
+
+ client_printf(client, "channel: %s\nmessage: %s\n",
+ msg.GetChannel(), msg.GetMessage());
+ client->messages.pop_front();
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+struct send_message_context {
+ ClientMessage msg;
+
+ bool sent;
+
+ template<typename T, typename U>
+ send_message_context(T &&_channel, U &&_message)
+ :msg(std::forward<T>(_channel), std::forward<U>(_message)),
+ sent(false) {}
+};
+
+static void
+send_message(gpointer data, gpointer user_data)
+{
+ struct send_message_context *context =
+ (struct send_message_context *)user_data;
+ Client *client = (Client *)data;
+
+ if (client_push_message(client, context->msg))
+ context->sent = true;
+}
+
+enum command_return
+handle_send_message(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 3);
+
+ if (!client_message_valid_channel_name(argv[1])) {
+ command_error(client, ACK_ERROR_ARG,
+ "invalid channel name");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ struct send_message_context context(argv[1], argv[2]);
+
+ client_list_foreach(send_message, &context);
+
+ if (context.sent)
+ return COMMAND_RETURN_OK;
+ else {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "nobody is subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+ }
+}
diff --git a/src/MessageCommands.hxx b/src/MessageCommands.hxx
new file mode 100644
index 000000000..b10f3d8e8
--- /dev/null
+++ b/src/MessageCommands.hxx
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2003-2012 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_MESSAGE_COMMANDS_HXX
+#define MPD_MESSAGE_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_subscribe(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_unsubscribe(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_channels(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_read_messages(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_send_message(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/MusicBuffer.cxx b/src/MusicBuffer.cxx
new file mode 100644
index 000000000..ea03fc0b9
--- /dev/null
+++ b/src/MusicBuffer.cxx
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
+#include "thread/Mutex.hxx"
+#include "util/SliceBuffer.hxx"
+#include "mpd_error.h"
+
+#include <assert.h>
+
+struct music_buffer : public SliceBuffer<music_chunk> {
+ /** a mutex which protects #available */
+ Mutex mutex;
+
+ music_buffer(unsigned num_chunks)
+ :SliceBuffer(num_chunks) {
+ if (IsOOM())
+ MPD_ERROR("Failed to allocate buffer");
+ }
+};
+
+struct music_buffer *
+music_buffer_new(unsigned num_chunks)
+{
+ return new music_buffer(num_chunks);
+}
+
+void
+music_buffer_free(struct music_buffer *buffer)
+{
+ delete buffer;
+}
+
+unsigned
+music_buffer_size(const struct music_buffer *buffer)
+{
+ return buffer->GetCapacity();
+}
+
+struct music_chunk *
+music_buffer_allocate(struct music_buffer *buffer)
+{
+ const ScopeLock protect(buffer->mutex);
+ return buffer->Allocate();
+}
+
+void
+music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk)
+{
+ assert(buffer != NULL);
+ assert(chunk != NULL);
+
+ const ScopeLock protect(buffer->mutex);
+
+ if (chunk->other != nullptr) {
+ assert(chunk->other->other == nullptr);
+ buffer->Free(chunk->other);
+ }
+
+ buffer->Free(chunk);
+}
diff --git a/src/buffer.h b/src/MusicBuffer.hxx
index f860231e7..cc03dfcb3 100644
--- a/src/buffer.h
+++ b/src/MusicBuffer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_MUSIC_BUFFER_H
-#define MPD_MUSIC_BUFFER_H
+#ifndef MPD_MUSIC_BUFFER_HXX
+#define MPD_MUSIC_BUFFER_HXX
/**
* An allocator for #music_chunk objects.
diff --git a/src/MusicChunk.cxx b/src/MusicChunk.cxx
new file mode 100644
index 000000000..e79cc3ee9
--- /dev/null
+++ b/src/MusicChunk.cxx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "MusicChunk.hxx"
+#include "audio_format.h"
+
+extern "C" {
+#include "tag.h"
+}
+
+#include <assert.h>
+
+music_chunk::~music_chunk()
+{
+ if (tag != NULL)
+ tag_free(tag);
+}
+
+#ifndef NDEBUG
+bool
+music_chunk::CheckFormat(const struct audio_format &other_format) const
+{
+ assert(audio_format_valid(&other_format));
+
+ return length == 0 ||
+ audio_format_equals(&audio_format, &other_format);
+}
+#endif
+
+void *
+music_chunk::Write(const struct audio_format &af,
+ float data_time, uint16_t _bit_rate,
+ size_t *max_length_r)
+{
+ assert(CheckFormat(af));
+ assert(length == 0 || audio_format_valid(&audio_format));
+
+ if (length == 0) {
+ /* if the chunk is empty, nobody has set bitRate and
+ times yet */
+
+ bit_rate = _bit_rate;
+ times = data_time;
+ }
+
+ const size_t frame_size = audio_format_frame_size(&af);
+ size_t num_frames = (sizeof(data) - length) / frame_size;
+ if (num_frames == 0)
+ return NULL;
+
+#ifndef NDEBUG
+ audio_format = af;
+#endif
+
+ *max_length_r = num_frames * frame_size;
+ return data + length;
+}
+
+bool
+music_chunk::Expand(const struct audio_format &af, size_t _length)
+{
+ const size_t frame_size = audio_format_frame_size(&af);
+
+ assert(length + _length <= sizeof(data));
+ assert(audio_format_equals(&audio_format, &af));
+
+ length += _length;
+
+ return length + frame_size > sizeof(data);
+}
diff --git a/src/chunk.h b/src/MusicChunk.hxx
index a06a203eb..c03e45517 100644
--- a/src/chunk.h
+++ b/src/MusicChunk.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CHUNK_H
-#define MPD_CHUNK_H
+#ifndef MPD_MUSIC_CHUNK_HXX
+#define MPD_MUSIC_CHUNK_HXX
#include "replay_gain_info.h"
@@ -26,7 +26,6 @@
#include "audio_format.h"
#endif
-#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
@@ -92,60 +91,63 @@ struct music_chunk {
#ifndef NDEBUG
struct audio_format audio_format;
#endif
-};
-void
-music_chunk_init(struct music_chunk *chunk);
+ music_chunk()
+ :other(nullptr),
+ length(0),
+ tag(nullptr),
+ replay_gain_serial(0) {}
-void
-music_chunk_free(struct music_chunk *chunk);
+ ~music_chunk();
-static inline bool
-music_chunk_is_empty(const struct music_chunk *chunk)
-{
- return chunk->length == 0 && chunk->tag == NULL;
-}
+ bool IsEmpty() const {
+ return length == 0 && tag == nullptr;
+ }
#ifndef NDEBUG
-/**
- * Checks if the audio format if the chunk is equal to the specified
- * audio_format.
- */
-bool
-music_chunk_check_format(const struct music_chunk *chunk,
- const struct audio_format *audio_format);
+ /**
+ * Checks if the audio format if the chunk is equal to the
+ * specified audio_format.
+ */
+ gcc_pure
+ bool CheckFormat(const struct audio_format &audio_format) const;
#endif
-/**
- * Prepares appending to the music chunk. Returns a buffer where you
- * may write into. After you are finished, call music_chunk_expand().
- *
- * @param chunk the music_chunk object
- * @param audio_format 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 NULL if the chunk is full
- */
-void *
-music_chunk_write(struct music_chunk *chunk,
- const struct audio_format *audio_format,
- float data_time, uint16_t bit_rate,
- size_t *max_length_r);
+ /**
+ * Prepares appending to the music chunk. Returns a buffer
+ * where you may write into. After you are finished, call
+ * music_chunk_expand().
+ *
+ * @param chunk the music_chunk object
+ * @param audio_format 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 NULL if the chunk is full
+ */
+ void *Write(const struct audio_format &af,
+ float data_time, uint16_t bit_rate,
+ size_t *max_length_r);
-/**
- * Increases the length of the chunk after the caller has written to
- * the buffer returned by music_chunk_write().
- *
- * @param chunk the music_chunk object
- * @param audio_format 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
- */
-bool
-music_chunk_expand(struct music_chunk *chunk,
- const struct audio_format *audio_format, size_t length);
+ /**
+ * Increases the length of the chunk after the caller has written to
+ * the buffer returned by music_chunk_write().
+ *
+ * @param chunk the music_chunk object
+ * @param audio_format 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
+ */
+ bool Expand(const struct audio_format &af, size_t length);
+};
+
+void
+music_chunk_init(struct music_chunk *chunk);
+
+void
+music_chunk_free(struct music_chunk *chunk);
#endif
diff --git a/src/pipe.c b/src/MusicPipe.cxx
index d8131432f..6f25eff82 100644
--- a/src/pipe.c
+++ b/src/MusicPipe.cxx
@@ -18,9 +18,10 @@
*/
#include "config.h"
-#include "pipe.h"
-#include "buffer.h"
-#include "chunk.h"
+#include "MusicPipe.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
+#include "thread/Mutex.hxx"
#include <glib.h>
@@ -37,38 +38,35 @@ struct music_pipe {
unsigned size;
/** a mutex which protects #head and #tail_r */
- GMutex *mutex;
+ mutable Mutex mutex;
#ifndef NDEBUG
struct audio_format audio_format;
#endif
+
+ music_pipe()
+ :head(nullptr), tail_r(&head), size(0) {
+#ifndef NDEBUG
+ audio_format_clear(&audio_format);
+#endif
+ }
+
+ ~music_pipe() {
+ assert(head == nullptr);
+ assert(tail_r == &head);
+ }
};
struct music_pipe *
music_pipe_new(void)
{
- struct music_pipe *mp = g_new(struct music_pipe, 1);
-
- mp->head = NULL;
- mp->tail_r = &mp->head;
- mp->size = 0;
- mp->mutex = g_mutex_new();
-
-#ifndef NDEBUG
- audio_format_clear(&mp->audio_format);
-#endif
-
- return mp;
+ return new music_pipe();
}
void
music_pipe_free(struct music_pipe *mp)
{
- assert(mp->head == NULL);
- assert(mp->tail_r == &mp->head);
-
- g_mutex_free(mp->mutex);
- g_free(mp);
+ delete mp;
}
#ifndef NDEBUG
@@ -88,17 +86,12 @@ bool
music_pipe_contains(const struct music_pipe *mp,
const struct music_chunk *chunk)
{
- g_mutex_lock(mp->mutex);
+ const ScopeLock protect(mp->mutex);
for (const struct music_chunk *i = mp->head;
- i != NULL; i = i->next) {
- if (i == chunk) {
- g_mutex_unlock(mp->mutex);
+ i != NULL; i = i->next)
+ if (i == chunk)
return true;
- }
- }
-
- g_mutex_unlock(mp->mutex);
return false;
}
@@ -114,13 +107,11 @@ music_pipe_peek(const struct music_pipe *mp)
struct music_chunk *
music_pipe_shift(struct music_pipe *mp)
{
- struct music_chunk *chunk;
-
- g_mutex_lock(mp->mutex);
+ const ScopeLock protect(mp->mutex);
- chunk = mp->head;
+ struct music_chunk *chunk = mp->head;
if (chunk != NULL) {
- assert(!music_chunk_is_empty(chunk));
+ assert(!chunk->IsEmpty());
mp->head = chunk->next;
--mp->size;
@@ -137,15 +128,13 @@ music_pipe_shift(struct music_pipe *mp)
#ifndef NDEBUG
/* poison the "next" reference */
- chunk->next = (void*)0x01010101;
+ chunk->next = (struct music_chunk *)(void *)0x01010101;
if (mp->size == 0)
audio_format_clear(&mp->audio_format);
#endif
}
- g_mutex_unlock(mp->mutex);
-
return chunk;
}
@@ -161,14 +150,14 @@ music_pipe_clear(struct music_pipe *mp, struct music_buffer *buffer)
void
music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
{
- assert(!music_chunk_is_empty(chunk));
+ assert(!chunk->IsEmpty());
assert(chunk->length == 0 || audio_format_valid(&chunk->audio_format));
- g_mutex_lock(mp->mutex);
+ const ScopeLock protect(mp->mutex);
assert(mp->size > 0 || !audio_format_defined(&mp->audio_format));
assert(!audio_format_defined(&mp->audio_format) ||
- music_chunk_check_format(chunk, &mp->audio_format));
+ chunk->CheckFormat(mp->audio_format));
#ifndef NDEBUG
if (!audio_format_defined(&mp->audio_format) && chunk->length > 0)
@@ -180,15 +169,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
mp->tail_r = &chunk->next;
++mp->size;
-
- g_mutex_unlock(mp->mutex);
}
unsigned
music_pipe_size(const struct music_pipe *mp)
{
- g_mutex_lock(mp->mutex);
- unsigned size = mp->size;
- g_mutex_unlock(mp->mutex);
- return size;
+ const ScopeLock protect(mp->mutex);
+ return mp->size;
}
diff --git a/src/pipe.h b/src/MusicPipe.hxx
index 84b9869e0..99561ca62 100644
--- a/src/pipe.h
+++ b/src/MusicPipe.hxx
@@ -20,8 +20,7 @@
#ifndef MPD_PIPE_H
#define MPD_PIPE_H
-#include <glib.h>
-#include <stdbool.h>
+#include "gcc.h"
#ifndef NDEBUG
struct audio_format;
@@ -39,7 +38,7 @@ struct music_pipe;
/**
* Creates a new #music_pipe object. It is empty.
*/
-G_GNUC_MALLOC
+gcc_malloc
struct music_pipe *
music_pipe_new(void);
@@ -72,7 +71,7 @@ music_pipe_contains(const struct music_pipe *mp,
* Returns the first #music_chunk from the pipe. Returns NULL if the
* pipe is empty.
*/
-G_GNUC_PURE
+gcc_pure
const struct music_chunk *
music_pipe_peek(const struct music_pipe *mp);
@@ -99,11 +98,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
/**
* Returns the number of chunks currently in this pipe.
*/
-G_GNUC_PURE
+gcc_pure
unsigned
music_pipe_size(const struct music_pipe *mp);
-G_GNUC_PURE
+gcc_pure
static inline bool
music_pipe_empty(const struct music_pipe *mp)
{
diff --git a/src/OtherCommands.cxx b/src/OtherCommands.cxx
new file mode 100644
index 000000000..9de616759
--- /dev/null
+++ b/src/OtherCommands.cxx
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "OtherCommands.hxx"
+#include "DatabaseCommands.hxx"
+#include "CommandError.hxx"
+#include "UpdateGlue.hxx"
+#include "Directory.hxx"
+#include "song.h"
+#include "SongPrint.hxx"
+#include "TagPrint.hxx"
+#include "TimePrint.hxx"
+#include "Mapper.hxx"
+#include "DecoderPrint.hxx"
+#include "protocol/ArgParser.hxx"
+#include "protocol/Result.hxx"
+#include "ls.hxx"
+
+extern "C" {
+#include "uri.h"
+#include "volume.h"
+#include "stats.h"
+}
+
+#include "Permission.hxx"
+#include "PlaylistFile.hxx"
+#include "ClientIdle.hxx"
+#include "ClientFile.hxx"
+#include "Client.hxx"
+
+extern "C" {
+#include "idle.h"
+}
+
+#ifdef ENABLE_SQLITE
+#include "StickerDatabase.hxx"
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+static void
+print_spl_list(Client *client, const PlaylistVector &list)
+{
+ for (const auto &i : list) {
+ client_printf(client, "playlist: %s\n", i.name.c_str());
+
+ if (i.mtime > 0)
+ time_print(client, "Last-Modified", i.mtime);
+ }
+}
+
+enum command_return
+handle_urlhandlers(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ if (client_is_local(client))
+ client_puts(client, "handler: file://\n");
+ print_supported_uri_schemes(client);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_decoders(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ decoder_list_print(client);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_tagtypes(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ tag_print_types(client);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_kill(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ return COMMAND_RETURN_KILL;
+}
+
+enum command_return
+handle_close(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ return COMMAND_RETURN_CLOSE;
+}
+
+enum command_return
+handle_lsinfo(Client *client, int argc, char *argv[])
+{
+ const char *uri;
+
+ if (argc == 2)
+ uri = argv[1];
+ else
+ /* default is root directory */
+ uri = "";
+
+ if (strncmp(uri, "file:///", 8) == 0) {
+ /* print information about an arbitrary local file */
+ const char *path = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path, &error))
+ return print_error(client, error);
+
+ struct song *song = song_file_load(path, NULL);
+ if (song == NULL) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "No such file");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ song_print_info(client, song);
+ song_free(song);
+ return COMMAND_RETURN_OK;
+ }
+
+ enum command_return result = handle_lsinfo2(client, argc, argv);
+ if (result != COMMAND_RETURN_OK)
+ return result;
+
+ if (isRootDirectory(uri)) {
+ const auto &list = ListPlaylistFiles(NULL);
+ print_spl_list(client, list);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_update(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *path = NULL;
+ unsigned ret;
+
+ assert(argc <= 2);
+ if (argc == 2) {
+ path = argv[1];
+
+ if (*path == 0 || strcmp(path, "/") == 0)
+ /* backwards compatibility with MPD 0.15 */
+ path = NULL;
+ else if (!uri_safe_local(path)) {
+ command_error(client, ACK_ERROR_ARG,
+ "Malformed path");
+ return COMMAND_RETURN_ERROR;
+ }
+ }
+
+ ret = update_enqueue(path, false);
+ if (ret > 0) {
+ client_printf(client, "updating_db: %i\n", ret);
+ return COMMAND_RETURN_OK;
+ } else {
+ command_error(client, ACK_ERROR_UPDATE_ALREADY,
+ "already updating");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+enum command_return
+handle_rescan(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *path = NULL;
+ unsigned ret;
+
+ assert(argc <= 2);
+ if (argc == 2) {
+ path = argv[1];
+
+ if (!uri_safe_local(path)) {
+ command_error(client, ACK_ERROR_ARG,
+ "Malformed path");
+ return COMMAND_RETURN_ERROR;
+ }
+ }
+
+ ret = update_enqueue(path, true);
+ if (ret > 0) {
+ client_printf(client, "updating_db: %i\n", ret);
+ return COMMAND_RETURN_OK;
+ } else {
+ command_error(client, ACK_ERROR_UPDATE_ALREADY,
+ "already updating");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+enum command_return
+handle_setvol(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned level;
+ bool success;
+
+ if (!check_unsigned(client, &level, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (level > 100) {
+ command_error(client, ACK_ERROR_ARG, "Invalid volume value");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ success = volume_level_change(level);
+ if (!success) {
+ command_error(client, ACK_ERROR_SYSTEM,
+ "problems setting volume");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_stats(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ stats_print(client);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_ping(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_password(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned permission = 0;
+
+ if (getPermissionFromPassword(argv[1], &permission) < 0) {
+ command_error(client, ACK_ERROR_PASSWORD, "incorrect password");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ client_set_permission(client, permission);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_config(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ if (!client_is_local(client)) {
+ command_error(client, ACK_ERROR_PERMISSION,
+ "Command only permitted to local clients");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ const char *path = mapper_get_music_directory_utf8();
+ if (path != NULL)
+ client_printf(client, "music_directory: %s\n", path);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_idle(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ unsigned flags = 0, j;
+ int i;
+ const char *const* idle_names;
+
+ idle_names = idle_get_names();
+ for (i = 1; i < argc; ++i) {
+ if (!argv[i])
+ continue;
+
+ for (j = 0; idle_names[j]; ++j) {
+ if (!g_ascii_strcasecmp(argv[i], idle_names[j])) {
+ flags |= (1 << j);
+ }
+ }
+ }
+
+ /* No argument means that the client wants to receive everything */
+ if (flags == 0)
+ flags = ~0;
+
+ /* enable "idle" mode on this client */
+ client_idle_wait(client, flags);
+
+ return COMMAND_RETURN_IDLE;
+}
diff --git a/src/OtherCommands.hxx b/src/OtherCommands.hxx
new file mode 100644
index 000000000..564ad38e7
--- /dev/null
+++ b/src/OtherCommands.hxx
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2003-2012 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_OTHER_COMMANDS_HXX
+#define MPD_OTHER_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_urlhandlers(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_decoders(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_tagtypes(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_kill(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_close(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_lsinfo(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_update(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_rescan(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_setvol(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_stats(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_ping(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_password(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_config(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_idle(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/output_all.c b/src/OutputAll.cxx
index f56cd04ee..9c65ddcae 100644
--- a/src/output_all.c
+++ b/src/OutputAll.cxx
@@ -18,20 +18,24 @@
*/
#include "config.h"
+
+extern "C" {
#include "output_all.h"
#include "output_internal.h"
-#include "output_control.h"
-#include "chunk.h"
-#include "conf.h"
-#include "pipe.h"
-#include "buffer.h"
-#include "player_control.h"
+}
+
+#include "PlayerControl.hxx"
+#include "OutputControl.hxx"
+#include "OutputError.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicPipe.hxx"
+#include "MusicChunk.hxx"
#include "mpd_error.h"
-#include "notify.h"
-#ifndef NDEBUG
-#include "chunk.h"
-#endif
+extern "C" {
+#include "conf.h"
+#include "notify.h"
+}
#include <assert.h>
#include <string.h>
@@ -269,8 +273,15 @@ audio_output_all_update(void)
return ret;
}
+void
+audio_output_all_set_replay_gain_mode(enum replay_gain_mode mode)
+{
+ for (unsigned i = 0; i < num_audio_outputs; ++i)
+ audio_output_set_replay_gain_mode(audio_outputs[i], mode);
+}
+
bool
-audio_output_all_play(struct music_chunk *chunk)
+audio_output_all_play(struct music_chunk *chunk, GError **error_r)
{
bool ret;
unsigned int i;
@@ -278,11 +289,15 @@ audio_output_all_play(struct music_chunk *chunk)
assert(g_music_buffer != NULL);
assert(g_mp != NULL);
assert(chunk != NULL);
- assert(music_chunk_check_format(chunk, &input_audio_format));
+ assert(chunk->CheckFormat(input_audio_format));
ret = audio_output_all_update();
- if (!ret)
+ if (!ret) {
+ /* TODO: obtain real error */
+ g_set_error(error_r, output_quark(), 0,
+ "Failed to open audio output");
return false;
+ }
music_pipe_push(g_mp, chunk);
@@ -294,7 +309,8 @@ audio_output_all_play(struct music_chunk *chunk)
bool
audio_output_all_open(const struct audio_format *audio_format,
- struct music_buffer *buffer)
+ struct music_buffer *buffer,
+ GError **error_r)
{
bool ret = false, enabled = false;
unsigned int i;
@@ -334,7 +350,12 @@ audio_output_all_open(const struct audio_format *audio_format,
}
if (!enabled)
- g_warning("All audio outputs are disabled");
+ g_set_error(error_r, output_quark(), 0,
+ "All audio outputs are disabled");
+ else if (!ret)
+ /* TODO: obtain real error */
+ g_set_error(error_r, output_quark(), 0,
+ "Failed to open audio output");
if (!ret)
/* close all devices if there was an error */
diff --git a/src/output_command.c b/src/OutputCommand.cxx
index 3988f350a..cce1a5b04 100644
--- a/src/output_command.c
+++ b/src/OutputCommand.cxx
@@ -25,13 +25,16 @@
*/
#include "config.h"
-#include "output_command.h"
+#include "OutputCommand.hxx"
+#include "PlayerControl.hxx"
+
+extern "C" {
#include "output_all.h"
#include "output_internal.h"
#include "output_plugin.h"
#include "mixer_control.h"
-#include "player_control.h"
#include "idle.h"
+}
extern unsigned audio_output_state_version;
diff --git a/src/output_command.h b/src/OutputCommand.hxx
index eda30acc8..74eaf8f1c 100644
--- a/src/output_command.h
+++ b/src/OutputCommand.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,10 +24,8 @@
*
*/
-#ifndef OUTPUT_COMMAND_H
-#define OUTPUT_COMMAND_H
-
-#include <stdbool.h>
+#ifndef MPD_OUTPUT_COMMAND_HXX
+#define MPD_OUTPUT_COMMAND_HXX
/**
* Enables an audio output. Returns false if the specified output
diff --git a/src/OutputCommands.cxx b/src/OutputCommands.cxx
new file mode 100644
index 000000000..7d626477a
--- /dev/null
+++ b/src/OutputCommands.cxx
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2003-2012 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 "OutputCommands.hxx"
+#include "OutputPrint.hxx"
+#include "OutputCommand.hxx"
+#include "protocol/Result.hxx"
+#include "protocol/ArgParser.hxx"
+
+#include <string.h>
+
+enum command_return
+handle_enableoutput(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned device;
+ bool ret;
+
+ if (!check_unsigned(client, &device, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ ret = audio_output_enable_index(device);
+ if (!ret) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "No such audio output");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_disableoutput(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned device;
+ bool ret;
+
+ if (!check_unsigned(client, &device, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ ret = audio_output_disable_index(device);
+ if (!ret) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "No such audio output");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_devices(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ printAudioDevices(client);
+
+ return COMMAND_RETURN_OK;
+}
diff --git a/src/OutputCommands.hxx b/src/OutputCommands.hxx
new file mode 100644
index 000000000..4f7082bfb
--- /dev/null
+++ b/src/OutputCommands.hxx
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2003-2012 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_COMMANDS_HXX
+#define MPD_OUTPUT_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_enableoutput(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_disableoutput(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_devices(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/output_control.c b/src/OutputControl.cxx
index 7b95be49b..7cc2814de 100644
--- a/src/output_control.c
+++ b/src/OutputControl.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,19 @@
*/
#include "config.h"
-#include "output_control.h"
+#include "OutputControl.hxx"
+#include "OutputThread.hxx"
+
+extern "C" {
#include "output_api.h"
#include "output_internal.h"
-#include "output_thread.h"
#include "mixer_control.h"
#include "mixer_plugin.h"
-#include "filter_plugin.h"
#include "notify.h"
+#include "filter/replay_gain_filter_plugin.h"
+}
+
+#include "filter_plugin.h"
#include <assert.h>
#include <stdlib.h>
@@ -92,6 +97,14 @@ ao_lock_command(struct audio_output *ao, enum audio_output_command cmd)
}
void
+audio_output_set_replay_gain_mode(struct audio_output *ao,
+ enum replay_gain_mode mode)
+{
+ if (ao->replay_gain_filter != NULL)
+ replay_gain_filter_set_mode(ao->replay_gain_filter, mode);
+}
+
+void
audio_output_enable(struct audio_output *ao)
{
if (ao->thread == NULL) {
diff --git a/src/output_control.h b/src/OutputControl.hxx
index 874a53518..cf906d2f0 100644
--- a/src/output_control.h
+++ b/src/OutputControl.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OUTPUT_CONTROL_H
-#define MPD_OUTPUT_CONTROL_H
+#ifndef MPD_OUTPUT_CONTROL_HXX
+#define MPD_OUTPUT_CONTROL_HXX
+
+#include "replay_gain_info.h"
#include <glib.h>
#include <stddef.h>
-#include <stdbool.h>
struct audio_output;
struct audio_format;
@@ -37,6 +38,10 @@ audio_output_quark(void)
return g_quark_from_static_string("audio_output");
}
+void
+audio_output_set_replay_gain_mode(struct audio_output *ao,
+ enum replay_gain_mode mode);
+
/**
* Enables the device.
*/
diff --git a/src/OutputError.hxx b/src/OutputError.hxx
new file mode 100644
index 000000000..451df9857
--- /dev/null
+++ b/src/OutputError.hxx
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OUTPUT_ERROR_HXX
+#define MPD_OUTPUT_ERROR_HXX
+
+#include <glib.h>
+
+/**
+ * Quark for GError.domain.
+ */
+G_GNUC_CONST
+static inline GQuark
+output_quark(void)
+{
+ return g_quark_from_static_string("output");
+}
+
+#endif
diff --git a/src/output_finish.c b/src/OutputFinish.cxx
index e11b43675..ac6ad6977 100644
--- a/src/output_finish.c
+++ b/src/OutputFinish.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,13 @@
*/
#include "config.h"
+
+extern "C" {
#include "output_internal.h"
#include "output_plugin.h"
#include "mixer_control.h"
#include "filter_plugin.h"
+}
#include <assert.h>
diff --git a/src/output_init.c b/src/OutputInit.cxx
index c3b808e94..5fc800d19 100644
--- a/src/output_init.c
+++ b/src/OutputInit.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,12 @@
*/
#include "config.h"
-#include "output_control.h"
+#include "OutputControl.hxx"
+#include "OutputList.hxx"
+
+extern "C" {
#include "output_api.h"
#include "output_internal.h"
-#include "output_list.h"
#include "audio_parser.h"
#include "mixer_control.h"
#include "mixer_type.h"
@@ -33,6 +35,7 @@
#include "filter/chain_filter_plugin.h"
#include "filter/autoconvert_filter_plugin.h"
#include "filter/replay_gain_filter_plugin.h"
+}
#include <glib.h>
@@ -165,6 +168,7 @@ ao_base_init(struct audio_output *ao,
}
ao->plugin = plugin;
+ ao->tags = config_get_block_bool(param, "tags", true);
ao->always_on = config_get_block_bool(param, "always_on", false);
ao->enabled = config_get_block_bool(param, "enabled", true);
ao->really_enabled = false;
@@ -297,14 +301,14 @@ audio_output_new(const struct config_param *param,
if (p == NULL) {
g_set_error(error_r, audio_output_quark(), 0,
"Missing \"type\" configuration");
- return false;
+ return nullptr;
}
plugin = audio_output_plugin_get(p);
if (plugin == NULL) {
g_set_error(error_r, audio_output_quark(), 0,
"No such audio output plugin: %s", p);
- return false;
+ return nullptr;
}
} else {
g_warning("No \"%s\" defined in config file\n",
@@ -312,7 +316,7 @@ audio_output_new(const struct config_param *param,
plugin = audio_output_detect(error_r);
if (plugin == NULL)
- return false;
+ return nullptr;
g_message("Successfully detected a %s audio device",
plugin->name);
diff --git a/src/output_list.c b/src/OutputList.cxx
index 835c02bba..3e469385f 100644
--- a/src/output_list.c
+++ b/src/OutputList.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "output_list.h"
+#include "OutputList.hxx"
#include "output_api.h"
#include "output/alsa_output_plugin.h"
#include "output/ao_output_plugin.h"
diff --git a/src/output_list.h b/src/OutputList.hxx
index 185ada716..b7716c67e 100644
--- a/src/output_list.h
+++ b/src/OutputList.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OUTPUT_LIST_H
-#define MPD_OUTPUT_LIST_H
+#ifndef MPD_OUTPUT_LIST_HXX
+#define MPD_OUTPUT_LIST_HXX
extern const struct audio_output_plugin *const audio_output_plugins[];
diff --git a/src/output_plugin.c b/src/OutputPlugin.cxx
index 221570c1c..9aa0f7792 100644
--- a/src/output_plugin.c
+++ b/src/OutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,11 @@
*/
#include "config.h"
+
+extern "C" {
#include "output_plugin.h"
+}
+
#include "output_internal.h"
struct audio_output *
diff --git a/src/output_print.c b/src/OutputPrint.cxx
index 483648ca2..ed4391547 100644
--- a/src/output_print.c
+++ b/src/OutputPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,15 +23,18 @@
*/
#include "config.h"
-#include "output_print.h"
+#include "OutputPrint.hxx"
#include "output_internal.h"
+#include "Client.hxx"
+
+extern "C" {
#include "output_all.h"
-#include "client.h"
+}
void
-printAudioDevices(struct client *client)
+printAudioDevices(Client *client)
{
- unsigned n = audio_output_count();
+ const unsigned n = audio_output_count();
for (unsigned i = 0; i < n; ++i) {
const struct audio_output *ao = audio_output_get(i);
diff --git a/src/output_print.h b/src/OutputPrint.hxx
index e02f4e9f5..78717d0af 100644
--- a/src/output_print.h
+++ b/src/OutputPrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,12 +22,12 @@
*
*/
-#ifndef OUTPUT_PRINT_H
-#define OUTPUT_PRINT_H
+#ifndef MPD_OUTPUT_PRINT_HXX
+#define MPD_OUTPUT_PRINT_HXX
-struct client;
+class Client;
void
-printAudioDevices(struct client *client);
+printAudioDevices(Client *client);
#endif
diff --git a/src/output_state.c b/src/OutputState.cxx
index 7bcafb36b..95aeacbca 100644
--- a/src/output_state.c
+++ b/src/OutputState.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,9 +23,12 @@
*/
#include "config.h"
-#include "output_state.h"
+#include "OutputState.hxx"
#include "output_internal.h"
+
+extern "C" {
#include "output_all.h"
+}
#include <glib.h>
diff --git a/src/output_state.h b/src/OutputState.hxx
index 320a3520a..5ab765ba8 100644
--- a/src/output_state.h
+++ b/src/OutputState.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,10 +22,9 @@
*
*/
-#ifndef OUTPUT_STATE_H
-#define OUTPUT_STATE_H
+#ifndef MPD_OUTPUT_STATE_HXX
+#define MPD_OUTPUT_STATE_HXX
-#include <stdbool.h>
#include <stdio.h>
bool
diff --git a/src/output_thread.c b/src/OutputThread.cxx
index 4eef2ccdd..3d0d96f7a 100644
--- a/src/output_thread.c
+++ b/src/OutputThread.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,18 +18,23 @@
*/
#include "config.h"
-#include "output_thread.h"
+#include "OutputThread.hxx"
+
+extern "C" {
#include "output_api.h"
#include "output_internal.h"
-#include "chunk.h"
-#include "pipe.h"
-#include "player_control.h"
#include "pcm_mix.h"
#include "filter_plugin.h"
#include "filter/convert_filter_plugin.h"
#include "filter/replay_gain_filter_plugin.h"
-#include "mpd_error.h"
#include "notify.h"
+}
+
+#include "PlayerControl.hxx"
+#include "MusicPipe.hxx"
+#include "MusicChunk.hxx"
+
+#include "mpd_error.h"
#include "gcc.h"
#include <glib.h>
@@ -315,17 +320,17 @@ ao_wait(struct audio_output *ao)
}
}
-static const char *
+static const void *
ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
struct filter *replay_gain_filter,
unsigned *replay_gain_serial_p,
size_t *length_r)
{
assert(chunk != NULL);
- assert(!music_chunk_is_empty(chunk));
- assert(music_chunk_check_format(chunk, &ao->in_audio_format));
+ assert(!chunk->IsEmpty());
+ assert(chunk->CheckFormat(ao->in_audio_format));
- const char *data = chunk->data;
+ const void *data = chunk->data;
size_t length = chunk->length;
(void)ao;
@@ -356,14 +361,14 @@ ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
return data;
}
-static const char *
+static const void *
ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
size_t *length_r)
{
GError *error = NULL;
size_t length;
- const char *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter,
+ const void *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter,
&ao->replay_gain_serial, &length);
if (data == NULL)
return NULL;
@@ -378,7 +383,7 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
if (chunk->other != NULL) {
size_t other_length;
- const char *other_data =
+ const void *other_data =
ao_chunk_data(ao, chunk->other,
ao->other_replay_gain_filter,
&ao->other_replay_gain_serial,
@@ -399,13 +404,14 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
if (length > other_length)
length = other_length;
- char *dest = pcm_buffer_get(&ao->cross_fade_buffer,
+ void *dest = pcm_buffer_get(&ao->cross_fade_buffer,
other_length);
memcpy(dest, other_data, other_length);
- if (!pcm_mix(dest, data, length, ao->in_audio_format.format,
+ if (!pcm_mix(dest, data, length,
+ sample_format(ao->in_audio_format.format),
1.0 - chunk->mix_ratio)) {
g_warning("Cannot cross-fade format %s",
- sample_format_to_string(ao->in_audio_format.format));
+ sample_format_to_string(sample_format(ao->in_audio_format.format)));
return NULL;
}
@@ -435,7 +441,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
assert(ao != NULL);
assert(ao->filter != NULL);
- if (chunk->tag != NULL) {
+ if (ao->tags && gcc_unlikely(chunk->tag != NULL)) {
g_mutex_unlock(ao->mutex);
ao_plugin_send_tag(ao, chunk->tag);
g_mutex_lock(ao->mutex);
@@ -446,7 +452,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
/* workaround -Wmaybe-uninitialized false positive */
size = 0;
#endif
- const char *data = ao_filter_chunk(ao, chunk, &size);
+ const char *data = (const char *)ao_filter_chunk(ao, chunk, &size);
if (data == NULL) {
ao_close(ao, false);
@@ -578,7 +584,7 @@ static void ao_pause(struct audio_output *ao)
static gpointer audio_output_task(gpointer arg)
{
- struct audio_output *ao = arg;
+ struct audio_output *ao = (struct audio_output *)arg;
g_mutex_lock(ao->mutex);
diff --git a/src/output_thread.h b/src/OutputThread.hxx
index 5ad9a7527..1a7932162 100644
--- a/src/output_thread.h
+++ b/src/OutputThread.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OUTPUT_THREAD_H
-#define MPD_OUTPUT_THREAD_H
+#ifndef MPD_OUTPUT_THREAD_HXX
+#define MPD_OUTPUT_THREAD_HXX
struct audio_output;
diff --git a/src/Partition.hxx b/src/Partition.hxx
new file mode 100644
index 000000000..a3b9e4625
--- /dev/null
+++ b/src/Partition.hxx
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PARTITION_HXX
+#define MPD_PARTITION_HXX
+
+#include "Playlist.hxx"
+#include "PlayerControl.hxx"
+
+/**
+ * A partition of the Music Player Daemon. It is a separate unit with
+ * a playlist, a player, outputs etc.
+ */
+struct Partition {
+ struct playlist playlist;
+
+ player_control pc;
+
+ Partition(unsigned max_length,
+ unsigned buffer_chunks,
+ unsigned buffered_before_play)
+ :playlist(max_length),
+ pc(buffer_chunks, buffered_before_play) {
+ }
+};
+
+#endif
diff --git a/src/permission.c b/src/Permission.cxx
index cd52b9c86..0b76e3364 100644
--- a/src/permission.c
+++ b/src/Permission.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,16 @@
*/
#include "config.h"
-#include "permission.h"
-#include "conf.h"
+#include "Permission.hxx"
#include "mpd_error.h"
+extern "C" {
+#include "conf.h"
+}
+
+#include <map>
+#include <string>
+
#include <glib.h>
#include <stdbool.h>
@@ -35,7 +41,7 @@
#define PERMISSION_CONTROL_STRING "control"
#define PERMISSION_ADMIN_STRING "admin"
-static GHashTable *permission_passwords;
+static std::map<std::string, unsigned> permission_passwords;
static unsigned permission_default;
@@ -75,9 +81,6 @@ void initPermissions(void)
unsigned permission;
const struct config_param *param;
- permission_passwords = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, NULL);
-
permission_default = PERMISSION_READ | PERMISSION_ADD |
PERMISSION_CONTROL | PERMISSION_ADMIN;
@@ -101,9 +104,8 @@ void initPermissions(void)
permission = parsePermissions(separator + 1);
- g_hash_table_replace(permission_passwords,
- password,
- GINT_TO_POINTER(permission));
+ permission_passwords.insert(std::make_pair(password,
+ permission));
} while ((param = config_get_next_param(CONF_PASSWORD, param)));
}
@@ -115,23 +117,14 @@ void initPermissions(void)
int getPermissionFromPassword(char const* password, unsigned* permission)
{
- bool found;
- gpointer key, value;
-
- found = g_hash_table_lookup_extended(permission_passwords,
- password, &key, &value);
- if (!found)
+ auto i = permission_passwords.find(password);
+ if (i == permission_passwords.end())
return -1;
- *permission = GPOINTER_TO_INT(value);
+ *permission = i->second;
return 0;
}
-void finishPermissions(void)
-{
- g_hash_table_destroy(permission_passwords);
-}
-
unsigned getDefaultPermissions(void)
{
return permission_default;
diff --git a/src/permission.h b/src/Permission.hxx
index 6c3771362..4ff3850e0 100644
--- a/src/permission.h
+++ b/src/Permission.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PERMISSION_H
-#define MPD_PERMISSION_H
+#ifndef MPD_PERMISSION_HXX
+#define MPD_PERMISSION_HXX
#define PERMISSION_NONE 0
#define PERMISSION_READ 1
@@ -29,8 +29,6 @@
int getPermissionFromPassword(char const* password, unsigned* permission);
-void finishPermissions(void);
-
unsigned getDefaultPermissions(void);
void initPermissions(void);
diff --git a/src/PlayerCommands.cxx b/src/PlayerCommands.cxx
new file mode 100644
index 000000000..2bd6fc4e6
--- /dev/null
+++ b/src/PlayerCommands.cxx
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PlayerCommands.hxx"
+#include "CommandError.hxx"
+#include "Playlist.hxx"
+#include "PlaylistPrint.hxx"
+#include "UpdateGlue.hxx"
+#include "ClientInternal.hxx"
+#include "protocol/Result.hxx"
+#include "protocol/ArgParser.hxx"
+
+extern "C" {
+#include "audio_format.h"
+#include "volume.h"
+#include "replay_gain_config.h"
+#include "output_all.h"
+}
+
+#include "PlayerControl.hxx"
+
+#include <errno.h>
+
+#define COMMAND_STATUS_STATE "state"
+#define COMMAND_STATUS_REPEAT "repeat"
+#define COMMAND_STATUS_SINGLE "single"
+#define COMMAND_STATUS_CONSUME "consume"
+#define COMMAND_STATUS_RANDOM "random"
+#define COMMAND_STATUS_PLAYLIST "playlist"
+#define COMMAND_STATUS_PLAYLIST_LENGTH "playlistlength"
+#define COMMAND_STATUS_SONG "song"
+#define COMMAND_STATUS_SONGID "songid"
+#define COMMAND_STATUS_NEXTSONG "nextsong"
+#define COMMAND_STATUS_NEXTSONGID "nextsongid"
+#define COMMAND_STATUS_TIME "time"
+#define COMMAND_STATUS_BITRATE "bitrate"
+#define COMMAND_STATUS_ERROR "error"
+#define COMMAND_STATUS_CROSSFADE "xfade"
+#define COMMAND_STATUS_MIXRAMPDB "mixrampdb"
+#define COMMAND_STATUS_MIXRAMPDELAY "mixrampdelay"
+#define COMMAND_STATUS_AUDIO "audio"
+#define COMMAND_STATUS_UPDATING_DB "updating_db"
+
+enum command_return
+handle_play(Client *client, int argc, char *argv[])
+{
+ int song = -1;
+ enum playlist_result result;
+
+ if (argc == 2 && !check_int(client, &song, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ result = playlist_play(&client->playlist, client->player_control,
+ song);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_playid(Client *client, int argc, char *argv[])
+{
+ int id = -1;
+ enum playlist_result result;
+
+ if (argc == 2 && !check_int(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ result = playlist_play_id(&client->playlist, client->player_control,
+ id);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_stop(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist_stop(&client->playlist, client->player_control);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_currentsong(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist_print_current(client, &client->playlist);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_pause(Client *client,
+ int argc, char *argv[])
+{
+ if (argc == 2) {
+ bool pause_flag;
+ if (!check_bool(client, &pause_flag, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ pc_set_pause(client->player_control, pause_flag);
+ } else
+ pc_pause(client->player_control);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_status(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ const char *state = NULL;
+ struct player_status player_status;
+ int updateJobId;
+ char *error;
+ int song;
+
+ pc_get_status(client->player_control, &player_status);
+
+ switch (player_status.state) {
+ case PLAYER_STATE_STOP:
+ state = "stop";
+ break;
+ case PLAYER_STATE_PAUSE:
+ state = "pause";
+ break;
+ case PLAYER_STATE_PLAY:
+ state = "play";
+ break;
+ }
+
+ 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_CROSSFADE ": %i\n"
+ COMMAND_STATUS_MIXRAMPDB ": %f\n"
+ COMMAND_STATUS_MIXRAMPDELAY ": %f\n"
+ COMMAND_STATUS_STATE ": %s\n",
+ volume_level_get(),
+ playlist_get_repeat(&playlist),
+ playlist_get_random(&playlist),
+ playlist_get_single(&playlist),
+ playlist_get_consume(&playlist),
+ playlist_get_version(&playlist),
+ playlist_get_length(&playlist),
+ (int)(pc_get_cross_fade(client->player_control) + 0.5),
+ pc_get_mixramp_db(client->player_control),
+ pc_get_mixramp_delay(client->player_control),
+ state);
+
+ song = playlist_get_current_song(&playlist);
+ if (song >= 0) {
+ client_printf(client,
+ COMMAND_STATUS_SONG ": %i\n"
+ COMMAND_STATUS_SONGID ": %u\n",
+ song, playlist_get_song_id(&playlist, song));
+ }
+
+ if (player_status.state != PLAYER_STATE_STOP) {
+ struct audio_format_string af_string;
+
+ client_printf(client,
+ COMMAND_STATUS_TIME ": %i:%i\n"
+ "elapsed: %1.3f\n"
+ COMMAND_STATUS_BITRATE ": %u\n"
+ COMMAND_STATUS_AUDIO ": %s\n",
+ (int)(player_status.elapsed_time + 0.5),
+ (int)(player_status.total_time + 0.5),
+ player_status.elapsed_time,
+ player_status.bit_rate,
+ audio_format_to_string(&player_status.audio_format,
+ &af_string));
+ }
+
+ if ((updateJobId = isUpdatingDB())) {
+ client_printf(client,
+ COMMAND_STATUS_UPDATING_DB ": %i\n",
+ updateJobId);
+ }
+
+ error = pc_get_error_message(client->player_control);
+ if (error != NULL) {
+ client_printf(client,
+ COMMAND_STATUS_ERROR ": %s\n",
+ error);
+ g_free(error);
+ }
+
+ song = playlist_get_next_song(&playlist);
+ if (song >= 0) {
+ client_printf(client,
+ COMMAND_STATUS_NEXTSONG ": %i\n"
+ COMMAND_STATUS_NEXTSONGID ": %u\n",
+ song, playlist_get_song_id(&playlist, song));
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_next(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist &playlist = client->playlist;
+
+ /* single mode is not considered when this is user who
+ * wants to change song. */
+ const bool single = playlist.queue.single;
+ playlist.queue.single = false;
+
+ playlist_next(&playlist, client->player_control);
+
+ playlist.queue.single = single;
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_previous(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist_previous(&client->playlist, client->player_control);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_repeat(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_set_repeat(&client->playlist, client->player_control, status);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_single(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_set_single(&client->playlist, client->player_control, status);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_consume(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_set_consume(&client->playlist, status);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_random(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_set_random(&client->playlist, client->player_control, status);
+ audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(client->playlist.queue.random));
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_clearerror(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ pc_clear_error(client->player_control);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_seek(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned song, seek_time;
+ enum playlist_result result;
+
+ if (!check_unsigned(client, &song, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &seek_time, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ result = playlist_seek_song(&client->playlist, client->player_control,
+ song, seek_time);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_seekid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id, seek_time;
+ enum playlist_result result;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &seek_time, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ result = playlist_seek_song_id(&client->playlist,
+ client->player_control,
+ id, seek_time);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_seekcur(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *p = argv[1];
+ bool relative = *p == '+' || *p == '-';
+ int seek_time;
+ if (!check_int(client, &seek_time, p))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ playlist_seek_current(&client->playlist,
+ client->player_control,
+ seek_time, relative);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_crossfade(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned xfade_time;
+
+ if (!check_unsigned(client, &xfade_time, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ pc_set_cross_fade(client->player_control, xfade_time);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_mixrampdb(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ float db;
+
+ if (!check_float(client, &db, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ pc_set_mixramp_db(client->player_control, db);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_mixrampdelay(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ float delay_secs;
+
+ if (!check_float(client, &delay_secs, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ pc_set_mixramp_delay(client->player_control, delay_secs);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_replay_gain_mode(Client *client,
+ G_GNUC_UNUSED int argc, char *argv[])
+{
+ if (!replay_gain_set_mode_string(argv[1])) {
+ command_error(client, ACK_ERROR_ARG,
+ "Unrecognized replay gain mode");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(client->playlist.queue.random));
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_replay_gain_status(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ client_printf(client, "replay_gain_mode: %s\n",
+ replay_gain_get_mode_string());
+ return COMMAND_RETURN_OK;
+}
diff --git a/src/PlayerCommands.hxx b/src/PlayerCommands.hxx
new file mode 100644
index 000000000..a2fed5853
--- /dev/null
+++ b/src/PlayerCommands.hxx
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2003-2012 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_PLAYER_COMMANDS_HXX
+#define MPD_PLAYER_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_play(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_stop(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_currentsong(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_pause(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_status(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_next(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_previous(Client *client, int argc, char *avg[]);
+
+enum command_return
+handle_repeat(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_single(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_consume(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_random(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_clearerror(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_seek(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_seekid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_seekcur(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_crossfade(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_mixrampdb(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_mixrampdelay(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_replay_gain_mode(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_replay_gain_status(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/player_control.c b/src/PlayerControl.cxx
index 90f616d77..275556fe8 100644
--- a/src/player_control.c
+++ b/src/PlayerControl.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,15 @@
*/
#include "config.h"
-#include "player_control.h"
-#include "decoder_control.h"
-#include "path.h"
-#include "log.h"
-#include "tag.h"
-#include "song.h"
+#include "PlayerControl.hxx"
+
+extern "C" {
#include "idle.h"
-#include "pcm_volume.h"
-#include "main.h"
+}
+
+#include "song.h"
+#include "DecoderControl.hxx"
+#include "Main.hxx"
#include <assert.h>
#include <stdio.h>
@@ -35,33 +35,32 @@
static void
pc_enqueue_song_locked(struct player_control *pc, struct song *song);
-struct player_control *
-pc_new(unsigned buffer_chunks, unsigned int buffered_before_play)
+player_control::player_control(unsigned _buffer_chunks,
+ unsigned _buffered_before_play)
+ :buffer_chunks(_buffer_chunks),
+ buffered_before_play(_buffered_before_play),
+ thread(nullptr),
+ mutex(g_mutex_new()),
+ cond(g_cond_new()),
+ command(PLAYER_COMMAND_NONE),
+ state(PLAYER_STATE_STOP),
+ error_type(PLAYER_ERROR_NONE),
+ error(nullptr),
+ next_song(nullptr),
+ cross_fade_seconds(0),
+ mixramp_db(0),
+ mixramp_delay_seconds(nanf("")),
+ total_play_time(0)
{
- struct player_control *pc = g_new0(struct player_control, 1);
-
- pc->buffer_chunks = buffer_chunks;
- pc->buffered_before_play = buffered_before_play;
-
- pc->mutex = g_mutex_new();
- pc->cond = g_cond_new();
-
- pc->command = PLAYER_COMMAND_NONE;
- pc->error = PLAYER_ERROR_NOERROR;
- pc->state = PLAYER_STATE_STOP;
- pc->cross_fade_seconds = 0;
- pc->mixramp_db = 0;
- pc->mixramp_delay_seconds = nanf("");
-
- return pc;
}
-void
-pc_free(struct player_control *pc)
+player_control::~player_control()
{
- g_cond_free(pc->cond);
- g_mutex_free(pc->mutex);
- g_free(pc);
+ if (next_song != nullptr)
+ song_free(next_song);
+
+ g_cond_free(cond);
+ g_mutex_free(mutex);
}
void
@@ -76,15 +75,6 @@ player_wait_decoder(struct player_control *pc, struct decoder_control *dc)
g_cond_wait(pc->cond, dc->mutex);
}
-void
-pc_song_deleted(struct player_control *pc, const struct song *song)
-{
- if (pc->errored_song == song) {
- pc->error = PLAYER_ERROR_NOERROR;
- pc->errored_song = NULL;
- }
-}
-
static void
player_command_wait_locked(struct player_control *pc)
{
@@ -236,70 +226,43 @@ pc_get_status(struct player_control *pc, struct player_status *status)
player_unlock(pc);
}
-enum player_state
-pc_get_state(struct player_control *pc)
+void
+pc_set_error(struct player_control *pc, enum player_error type,
+ GError *error)
{
- return pc->state;
+ assert(pc != NULL);
+ assert(type != PLAYER_ERROR_NONE);
+ assert(error != NULL);
+
+ if (pc->error_type != PLAYER_ERROR_NONE)
+ g_error_free(pc->error);
+
+ pc->error_type = type;
+ pc->error = error;
}
void
pc_clear_error(struct player_control *pc)
{
player_lock(pc);
- pc->error = PLAYER_ERROR_NOERROR;
- pc->errored_song = NULL;
- player_unlock(pc);
-}
-enum player_error
-pc_get_error(struct player_control *pc)
-{
- return pc->error;
-}
+ if (pc->error_type != PLAYER_ERROR_NONE) {
+ pc->error_type = PLAYER_ERROR_NONE;
+ g_error_free(pc->error);
+ }
-static char *
-pc_errored_song_uri(struct player_control *pc)
-{
- return song_get_uri(pc->errored_song);
+ player_unlock(pc);
}
char *
pc_get_error_message(struct player_control *pc)
{
- char *error;
- char *uri;
-
- switch (pc->error) {
- case PLAYER_ERROR_NOERROR:
- return NULL;
-
- case PLAYER_ERROR_FILENOTFOUND:
- uri = pc_errored_song_uri(pc);
- error = g_strdup_printf("file \"%s\" does not exist or is inaccessible", uri);
- g_free(uri);
- return error;
-
- case PLAYER_ERROR_FILE:
- uri = pc_errored_song_uri(pc);
- error = g_strdup_printf("problems decoding \"%s\"", uri);
- g_free(uri);
- return error;
-
- case PLAYER_ERROR_AUDIO:
- return g_strdup("problems opening audio device");
-
- case PLAYER_ERROR_SYSTEM:
- return g_strdup("system error occurred");
-
- case PLAYER_ERROR_UNKTYPE:
- uri = pc_errored_song_uri(pc);
- error = g_strdup_printf("file type of \"%s\" is unknown", uri);
- g_free(uri);
- return error;
- }
-
- assert(false);
- return NULL;
+ player_lock(pc);
+ char *message = pc->error_type != PLAYER_ERROR_NONE
+ ? g_strdup(pc->error->message)
+ : NULL;
+ player_unlock(pc);
+ return message;
}
static void
@@ -328,6 +291,10 @@ pc_seek(struct player_control *pc, struct song *song, float seek_time)
assert(song != NULL);
player_lock(pc);
+
+ if (pc->next_song != NULL)
+ song_free(pc->next_song);
+
pc->next_song = song;
pc->seek_where = seek_time;
player_command_locked(pc, PLAYER_COMMAND_SEEK);
@@ -356,12 +323,6 @@ pc_set_cross_fade(struct player_control *pc, float cross_fade_seconds)
idle_add(IDLE_OPTIONS);
}
-float
-pc_get_mixramp_db(const struct player_control *pc)
-{
- return pc->mixramp_db;
-}
-
void
pc_set_mixramp_db(struct player_control *pc, float mixramp_db)
{
@@ -370,12 +331,6 @@ pc_set_mixramp_db(struct player_control *pc, float mixramp_db)
idle_add(IDLE_OPTIONS);
}
-float
-pc_get_mixramp_delay(const struct player_control *pc)
-{
- return pc->mixramp_delay_seconds;
-}
-
void
pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds)
{
@@ -383,9 +338,3 @@ pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds)
idle_add(IDLE_OPTIONS);
}
-
-double
-pc_get_total_play_time(const struct player_control *pc)
-{
- return pc->total_play_time;
-}
diff --git a/src/player_control.h b/src/PlayerControl.hxx
index a77d31ec5..1a0503209 100644
--- a/src/player_control.h
+++ b/src/PlayerControl.hxx
@@ -66,12 +66,17 @@ enum player_command {
};
enum player_error {
- PLAYER_ERROR_NOERROR = 0,
- PLAYER_ERROR_FILE,
- PLAYER_ERROR_AUDIO,
- PLAYER_ERROR_SYSTEM,
- PLAYER_ERROR_UNKTYPE,
- PLAYER_ERROR_FILENOTFOUND,
+ PLAYER_ERROR_NONE = 0,
+
+ /**
+ * The decoder has failed to decode the song.
+ */
+ PLAYER_ERROR_DECODER,
+
+ /**
+ * The audio output has failed.
+ */
+ PLAYER_ERROR_OUTPUT,
};
struct player_status {
@@ -103,13 +108,30 @@ struct player_control {
enum player_command command;
enum player_state state;
- enum player_error error;
+
+ enum player_error error_type;
+
+ /**
+ * The error that occurred in the player thread. This
+ * attribute is only valid if #error is not
+ * #PLAYER_ERROR_NONE. The object must be freed when this
+ * object transitions back to #PLAYER_ERROR_NONE.
+ */
+ GError *error;
+
uint16_t bit_rate;
struct audio_format audio_format;
float total_time;
float elapsed_time;
+
+ /**
+ * The next queued song.
+ *
+ * This is a duplicate, and must be freed when this attribute
+ * is cleared.
+ */
struct song *next_song;
- const struct song *errored_song;
+
double seek_where;
float cross_fade_seconds;
float mixramp_db;
@@ -124,13 +146,11 @@ struct player_control {
* time.
*/
bool border_pause;
-};
-
-struct player_control *
-pc_new(unsigned buffer_chunks, unsigned buffered_before_play);
-void
-pc_free(struct player_control *pc);
+ player_control(unsigned buffer_chunks,
+ unsigned buffered_before_play);
+ ~player_control();
+};
/**
* Locks the #player_control object.
@@ -194,14 +214,10 @@ player_lock_signal(struct player_control *pc)
}
/**
- * Call this function when the specified song pointer is about to be
- * invalidated. This makes sure that player_control.errored_song does
- * not point to an invalid pointer.
+ * @param song the song to be queued; the given instance will be owned
+ * and freed by the player
*/
void
-pc_song_deleted(struct player_control *pc, const struct song *song);
-
-void
pc_play(struct player_control *pc, struct song *song);
/**
@@ -228,8 +244,24 @@ pc_kill(struct player_control *pc);
void
pc_get_status(struct player_control *pc, struct player_status *status);
-enum player_state
-pc_get_state(struct player_control *pc);
+static inline enum player_state
+pc_get_state(struct player_control *pc)
+{
+ return pc->state;
+}
+
+/**
+ * Set the error. Discards any previous error condition.
+ *
+ * Caller must lock the object.
+ *
+ * @param type the error type; must not be #PLAYER_ERROR_NONE
+ * @param error detailed error information; must not be NULL; the
+ * #player_control takes over ownership of this #GError instance
+ */
+void
+pc_set_error(struct player_control *pc, enum player_error type,
+ GError *error);
void
pc_clear_error(struct player_control *pc);
@@ -242,8 +274,11 @@ pc_clear_error(struct player_control *pc);
char *
pc_get_error_message(struct player_control *pc);
-enum player_error
-pc_get_error(struct player_control *pc);
+static inline enum player_error
+pc_get_error_type(struct player_control *pc)
+{
+ return pc->error_type;
+}
void
pc_stop(struct player_control *pc);
@@ -251,12 +286,18 @@ pc_stop(struct player_control *pc);
void
pc_update_audio(struct player_control *pc);
+/**
+ * @param song the song to be queued; the given instance will be owned
+ * and freed by the player
+ */
void
pc_enqueue_song(struct player_control *pc, struct song *song);
/**
* Makes the player thread seek the specified song to a position.
*
+ * @param song the song to be queued; the given instance will be owned
+ * and freed by the player
* @return true on success, false on failure (e.g. if MPD isn't
* playing currently)
*/
@@ -272,16 +313,25 @@ pc_get_cross_fade(const struct player_control *pc);
void
pc_set_mixramp_db(struct player_control *pc, float mixramp_db);
-float
-pc_get_mixramp_db(const struct player_control *pc);
+static inline float
+pc_get_mixramp_db(const struct player_control *pc)
+{
+ return pc->mixramp_db;
+}
void
pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds);
-float
-pc_get_mixramp_delay(const struct player_control *pc);
+static inline float
+pc_get_mixramp_delay(const struct player_control *pc)
+{
+ return pc->mixramp_delay_seconds;
+}
-double
-pc_get_total_play_time(const struct player_control *pc);
+static inline double
+pc_get_total_play_time(const struct player_control *pc)
+{
+ return pc->total_play_time;
+}
#endif
diff --git a/src/player_thread.c b/src/PlayerThread.cxx
index 707fb27ae..0343b9fcd 100644
--- a/src/player_thread.c
+++ b/src/PlayerThread.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,23 +18,24 @@
*/
#include "config.h"
-#include "player_thread.h"
-#include "player_control.h"
-#include "decoder_control.h"
-#include "decoder_thread.h"
+#include "PlayerThread.hxx"
+#include "DecoderThread.hxx"
+#include "DecoderControl.hxx"
+#include "MusicPipe.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
+#include "song.h"
+#include "Main.hxx"
+#include "mpd_error.h"
+#include "CrossFade.hxx"
+#include "PlayerControl.hxx"
+
+extern "C" {
#include "output_all.h"
-#include "pcm_volume.h"
-#include "path.h"
#include "event_pipe.h"
-#include "crossfade.h"
-#include "song.h"
#include "tag.h"
-#include "pipe.h"
-#include "chunk.h"
#include "idle.h"
-#include "main.h"
-#include "buffer.h"
-#include "mpd_error.h"
+}
#include <glib.h>
@@ -123,6 +124,20 @@ struct player {
* precisely.
*/
float elapsed_time;
+
+ player(player_control *_pc, decoder_control *_dc)
+ :pc(_pc), dc(_dc),
+ buffering(false),
+ decoder_starting(false),
+ paused(false),
+ queued(true),
+ output_open(false),
+ song(NULL),
+ xfade(XFADE_UNKNOWN),
+ cross_fading(false),
+ cross_fade_chunks(0),
+ cross_fade_tag(NULL),
+ elapsed_time(0.0) {}
};
static struct music_buffer *player_buffer;
@@ -162,7 +177,7 @@ player_dc_start(struct player *player, struct music_pipe *pipe)
if (pc->command == PLAYER_COMMAND_SEEK)
start_ms += (unsigned)(pc->seek_where * 1000);
- dc_start(dc, pc->next_song,
+ dc_start(dc, song_dup_detached(pc->next_song),
start_ms, pc->next_song->end_ms,
player_buffer, pipe);
}
@@ -235,16 +250,22 @@ player_wait_for_decoder(struct player *player)
player->queued = false;
- if (decoder_lock_has_failed(dc)) {
+ GError *error = dc_lock_get_error(dc);
+ if (error != NULL) {
player_lock(pc);
- pc->errored_song = dc->song;
- pc->error = PLAYER_ERROR_FILE;
+ pc_set_error(pc, PLAYER_ERROR_DECODER, error);
+
+ song_free(pc->next_song);
pc->next_song = NULL;
+
player_unlock(pc);
return false;
}
+ if (player->song != NULL)
+ song_free(player->song);
+
player->song = pc->next_song;
player->elapsed_time = 0.0;
@@ -305,7 +326,9 @@ player_open_output(struct player *player)
assert(pc->state == PLAYER_STATE_PLAY ||
pc->state == PLAYER_STATE_PAUSE);
- if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
+ GError *error = NULL;
+ if (audio_output_all_open(&player->play_audio_format, player_buffer,
+ &error)) {
player->output_open = true;
player->paused = false;
@@ -315,6 +338,8 @@ player_open_output(struct player *player)
return true;
} else {
+ g_warning("%s", error->message);
+
player->output_open = false;
/* pause: the user may resume playback as soon as an
@@ -322,7 +347,7 @@ player_open_output(struct player *player)
player->paused = true;
player_lock(pc);
- pc->error = PLAYER_ERROR_AUDIO;
+ pc_set_error(pc, PLAYER_ERROR_OUTPUT, error);
pc->state = PLAYER_STATE_PAUSE;
player_unlock(pc);
@@ -347,13 +372,13 @@ player_check_decoder_startup(struct player *player)
decoder_lock(dc);
- if (decoder_has_failed(dc)) {
+ GError *error = dc_get_error(dc);
+ if (error != NULL) {
/* the decoder failed */
decoder_unlock(dc);
player_lock(pc);
- pc->errored_song = dc->song;
- pc->error = PLAYER_ERROR_FILE;
+ pc_set_error(pc, PLAYER_ERROR_DECODER, error);
player_unlock(pc);
return false;
@@ -429,7 +454,11 @@ player_send_silence(struct player *player)
chunk->length = num_frames * frame_size;
memset(chunk->data, 0, chunk->length);
- if (!audio_output_all_play(chunk)) {
+ GError *error = NULL;
+ if (!audio_output_all_play(chunk, &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+
music_buffer_return(player_buffer, chunk);
return false;
}
@@ -452,7 +481,7 @@ static bool player_seek_decoder(struct player *player)
const unsigned start_ms = song->start_ms;
- if (decoder_current_song(dc) != song) {
+ if (!decoder_lock_is_current_song(dc, song)) {
/* the decoder is already decoding the "next" song -
stop it and start the previous song again */
@@ -478,6 +507,7 @@ static bool player_seek_decoder(struct player *player)
player->pipe = dc->pipe;
}
+ song_free(pc->next_song);
pc->next_song = NULL;
player->queued = false;
}
@@ -598,6 +628,7 @@ static void player_process_command(struct player *player)
player_lock(pc);
}
+ song_free(pc->next_song);
pc->next_song = NULL;
player->queued = false;
player_command_finished_locked(pc);
@@ -652,9 +683,10 @@ update_song_tag(struct song *song, const struct tag *new_tag)
static bool
play_chunk(struct player_control *pc,
struct song *song, struct music_chunk *chunk,
- const struct audio_format *format)
+ const struct audio_format *format,
+ GError **error_r)
{
- assert(music_chunk_check_format(chunk, format));
+ assert(chunk->CheckFormat(*format));
if (chunk->tag != NULL)
update_song_tag(song, chunk->tag);
@@ -670,7 +702,7 @@ play_chunk(struct player_control *pc,
/* send the chunk to the audio outputs */
- if (!audio_output_all_play(chunk))
+ if (!audio_output_all_play(chunk, error_r))
return false;
pc->total_play_time += (double)chunk->length /
@@ -734,7 +766,7 @@ play_next_chunk(struct player *player)
chunk->mix_ratio = nan("");
}
- if (music_chunk_is_empty(other_chunk)) {
+ if (other_chunk->IsEmpty()) {
/* the "other" chunk was a music_chunk
which had only a tag, but no music
data - we cannot cross-fade that;
@@ -785,13 +817,16 @@ play_next_chunk(struct player *player)
/* play the current chunk */
+ GError *error = NULL;
if (!play_chunk(player->pc, player->song, chunk,
- &player->play_audio_format)) {
+ &player->play_audio_format, &error)) {
+ g_warning("%s", error->message);
+
music_buffer_return(player_buffer, chunk);
player_lock(pc);
- pc->error = PLAYER_ERROR_AUDIO;
+ pc_set_error(pc, PLAYER_ERROR_OUTPUT, error);
/* pause: the user may resume playback as soon as an
audio output becomes available */
@@ -862,21 +897,7 @@ player_song_border(struct player *player)
*/
static void do_play(struct player_control *pc, struct decoder_control *dc)
{
- struct player player = {
- .pc = pc,
- .dc = dc,
- .buffering = true,
- .decoder_starting = false,
- .paused = false,
- .queued = true,
- .output_open = false,
- .song = NULL,
- .xfade = XFADE_UNKNOWN,
- .cross_fading = false,
- .cross_fade_chunks = 0,
- .cross_fade_tag = NULL,
- .elapsed_time = 0.0,
- };
+ player player(pc, dc);
player_unlock(pc);
@@ -884,6 +905,8 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
player_dc_start(&player, player.pipe);
if (!player_wait_for_decoder(&player)) {
+ assert(player.song == NULL);
+
player_dc_stop(&player);
player_command_finished(pc);
music_pipe_free(player.pipe);
@@ -1049,10 +1072,14 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
if (player.cross_fade_tag != NULL)
tag_free(player.cross_fade_tag);
+ if (player.song != NULL)
+ song_free(player.song);
+
player_lock(pc);
if (player.queued) {
assert(pc->next_song != NULL);
+ song_free(pc->next_song);
pc->next_song = NULL;
}
@@ -1068,7 +1095,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
static gpointer
player_task(gpointer arg)
{
- struct player_control *pc = arg;
+ struct player_control *pc = (struct player_control *)arg;
struct decoder_control *dc = dc_new(pc->cond);
decoder_thread_start(dc);
@@ -1094,7 +1121,11 @@ player_task(gpointer arg)
/* fall through */
case PLAYER_COMMAND_PAUSE:
- pc->next_song = NULL;
+ if (pc->next_song != NULL) {
+ song_free(pc->next_song);
+ pc->next_song = NULL;
+ }
+
player_command_finished_locked(pc);
break;
@@ -1135,7 +1166,11 @@ player_task(gpointer arg)
return NULL;
case PLAYER_COMMAND_CANCEL:
- pc->next_song = NULL;
+ if (pc->next_song != NULL) {
+ song_free(pc->next_song);
+ pc->next_song = NULL;
+ }
+
player_command_finished_locked(pc);
break;
diff --git a/src/player_thread.h b/src/PlayerThread.hxx
index 7373eb438..197669cd6 100644
--- a/src/player_thread.h
+++ b/src/PlayerThread.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -34,8 +34,8 @@
* #music_chunk instances around in #music_pipe objects.
*/
-#ifndef MPD_PLAYER_THREAD_H
-#define MPD_PLAYER_THREAD_H
+#ifndef MPD_PLAYER_THREAD_HXX
+#define MPD_PLAYER_THREAD_HXX
struct player_control;
diff --git a/src/playlist.c b/src/Playlist.cxx
index dc6d8c340..7f89e4ea9 100644
--- a/src/playlist.c
+++ b/src/Playlist.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,13 @@
*/
#include "config.h"
-#include "playlist_internal.h"
-#include "playlist_save.h"
-#include "player_control.h"
-#include "command.h"
-#include "tag.h"
+#include "PlaylistInternal.hxx"
+#include "PlayerControl.hxx"
#include "song.h"
-#include "conf.h"
-#include "stored_playlist.h"
+
+extern "C" {
#include "idle.h"
+}
#include <glib.h>
@@ -38,7 +36,7 @@
void
playlist_increment_version_all(struct playlist *playlist)
{
- queue_modify_all(&playlist->queue);
+ playlist->queue.ModifyAll();
idle_add(IDLE_PLAYLIST);
}
@@ -50,27 +48,10 @@ playlist_tag_changed(struct playlist *playlist)
assert(playlist->current >= 0);
- queue_modify(&playlist->queue, playlist->current);
+ playlist->queue.ModifyAtOrder(playlist->current);
idle_add(IDLE_PLAYLIST);
}
-void
-playlist_init(struct playlist *playlist)
-{
- queue_init(&playlist->queue,
- config_get_positive(CONF_MAX_PLAYLIST_LENGTH,
- DEFAULT_PLAYLIST_MAX_LENGTH));
-
- playlist->queued = -1;
- playlist->current = -1;
-}
-
-void
-playlist_finish(struct playlist *playlist)
-{
- queue_finish(&playlist->queue);
-}
-
/**
* Queue a song, addressed by its order number.
*/
@@ -78,14 +59,15 @@ static void
playlist_queue_song_order(struct playlist *playlist, struct player_control *pc,
unsigned order)
{
- struct song *song;
char *uri;
- assert(queue_valid_order(&playlist->queue, order));
+ assert(playlist->queue.IsValidOrder(order));
playlist->queued = order;
- song = queue_get_order(&playlist->queue, order);
+ struct song *song =
+ song_dup_detached(playlist->queue.GetOrder(order));
+
uri = song_get_uri(song);
g_debug("queue song %i:\"%s\"", playlist->queued, uri);
g_free(uri);
@@ -111,8 +93,7 @@ playlist_song_started(struct playlist *playlist, struct player_control *pc)
if(playlist->queue.consume)
playlist_delete(playlist, pc,
- queue_order_to_position(&playlist->queue,
- current));
+ playlist->queue.OrderToPosition(current));
idle_add(IDLE_PLAYER);
}
@@ -123,7 +104,7 @@ playlist_get_queued_song(struct playlist *playlist)
if (!playlist->playing || playlist->queued < 0)
return NULL;
- return queue_get_order(&playlist->queue, playlist->queued);
+ return playlist->queue.GetOrder(playlist->queued);
}
void
@@ -137,11 +118,11 @@ playlist_update_queued_song(struct playlist *playlist,
if (!playlist->playing)
return;
- assert(!queue_is_empty(&playlist->queue));
+ assert(!playlist->queue.IsEmpty());
assert((playlist->queued < 0) == (prev == NULL));
next_order = playlist->current >= 0
- ? queue_next_order(&playlist->queue, playlist->current)
+ ? playlist->queue.GetNextOrder(playlist->current)
: 0;
if (next_order == 0 && playlist->queue.random &&
@@ -150,21 +131,19 @@ playlist_update_queued_song(struct playlist *playlist,
order each time the playlist is played
completely */
unsigned current_position =
- queue_order_to_position(&playlist->queue,
- playlist->current);
+ playlist->queue.OrderToPosition(playlist->current);
- queue_shuffle_order(&playlist->queue);
+ playlist->queue.ShuffleOrder();
/* make sure that the playlist->current still points to
the current song, after the song order has been
shuffled */
playlist->current =
- queue_position_to_order(&playlist->queue,
- current_position);
+ playlist->queue.PositionToOrder(current_position);
}
if (next_order >= 0)
- next_song = queue_get_order(&playlist->queue, next_order);
+ next_song = playlist->queue.GetOrder(next_order);
else
next_song = NULL;
@@ -186,13 +165,13 @@ void
playlist_play_order(struct playlist *playlist, struct player_control *pc,
int orderNum)
{
- struct song *song;
char *uri;
playlist->playing = true;
playlist->queued = -1;
- song = queue_get_order(&playlist->queue, orderNum);
+ struct song *song =
+ song_dup_detached(playlist->queue.GetOrder(orderNum));
uri = song_get_uri(song);
g_debug("play %i:\"%s\"", orderNum, uri);
@@ -257,15 +236,15 @@ playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
assert(playlist->playing);
assert(pc_get_state(pc) == PLAYER_STATE_STOP);
- error = pc_get_error(pc);
- if (error == PLAYER_ERROR_NOERROR)
+ error = pc_get_error_type(pc);
+ if (error == PLAYER_ERROR_NONE)
playlist->error_count = 0;
else
++playlist->error_count;
- if ((playlist->stop_on_error && error != PLAYER_ERROR_NOERROR) ||
- error == PLAYER_ERROR_AUDIO || error == PLAYER_ERROR_SYSTEM ||
- playlist->error_count >= queue_length(&playlist->queue))
+ if ((playlist->stop_on_error && error != PLAYER_ERROR_NONE) ||
+ error == PLAYER_ERROR_OUTPUT ||
+ playlist->error_count >= playlist->queue.GetLength())
/* too many errors, or critical error: stop
playback */
playlist_stop(playlist, pc);
@@ -324,10 +303,9 @@ playlist_order(struct playlist *playlist)
{
if (playlist->current >= 0)
/* update playlist.current, order==position now */
- playlist->current = queue_order_to_position(&playlist->queue,
- playlist->current);
+ playlist->current = playlist->queue.OrderToPosition(playlist->current);
- queue_restore_order(&playlist->queue);
+ playlist->queue.RestoreOrder();
}
void
@@ -380,20 +358,18 @@ playlist_set_random(struct playlist *playlist, struct player_control *pc,
int current_position =
playlist->playing && playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- playlist->current)
+ ? (int)playlist->queue.OrderToPosition(playlist->current)
: -1;
- queue_shuffle_order(&playlist->queue);
+ playlist->queue.ShuffleOrder();
if (current_position >= 0) {
/* make sure the current song is the first in
the order list, so the whole rest of the
playlist is played after that */
unsigned current_order =
- queue_position_to_order(&playlist->queue,
- current_position);
- queue_swap_order(&playlist->queue, 0, current_order);
+ playlist->queue.PositionToOrder(current_position);
+ playlist->queue.SwapOrders(0, current_order);
playlist->current = 0;
} else
playlist->current = -1;
@@ -409,8 +385,7 @@ int
playlist_get_current_song(const struct playlist *playlist)
{
if (playlist->current >= 0)
- return queue_order_to_position(&playlist->queue,
- playlist->current);
+ return playlist->queue.OrderToPosition(playlist->current);
return -1;
}
@@ -421,13 +396,11 @@ playlist_get_next_song(const struct playlist *playlist)
if (playlist->current >= 0)
{
if (playlist->queue.single == 1 && playlist->queue.repeat == 1)
- return queue_order_to_position(&playlist->queue,
- playlist->current);
- else if (playlist->current + 1 < (int)queue_length(&playlist->queue))
- return queue_order_to_position(&playlist->queue,
- playlist->current + 1);
+ return playlist->queue.OrderToPosition(playlist->current);
+ else if (playlist->current + 1 < (int)playlist->queue.GetLength())
+ return playlist->queue.OrderToPosition(playlist->current + 1);
else if (playlist->queue.repeat == 1)
- return queue_order_to_position(&playlist->queue, 0);
+ return playlist->queue.OrderToPosition(0);
}
return -1;
@@ -442,11 +415,11 @@ playlist_get_version(const struct playlist *playlist)
int
playlist_get_length(const struct playlist *playlist)
{
- return queue_length(&playlist->queue);
+ return playlist->queue.GetLength();
}
unsigned
playlist_get_song_id(const struct playlist *playlist, unsigned song)
{
- return queue_position_to_id(&playlist->queue, song);
+ return playlist->queue.PositionToId(song);
}
diff --git a/src/playlist.h b/src/Playlist.hxx
index a21bdf24a..a93c88f05 100644
--- a/src/playlist.h
+++ b/src/Playlist.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_H
-#define MPD_PLAYLIST_H
+#ifndef MPD_PLAYLIST_HXX
+#define MPD_PLAYLIST_HXX
-#include "queue.h"
+#include "Queue.hxx"
#include "playlist_error.h"
#include <stdbool.h>
@@ -68,22 +68,17 @@ struct playlist {
* This variable is only valid if #playing is true.
*/
int queued;
-};
-
-/** the global playlist object */
-extern struct playlist g_playlist;
-void
-playlist_global_init(void);
+ playlist(unsigned max_length)
+ :queue(max_length), current(-1), queued(-1) {
+ }
-void
-playlist_global_finish(void);
-
-void
-playlist_init(struct playlist *playlist);
+ ~playlist() {
+ }
+};
void
-playlist_finish(struct playlist *playlist);
+playlist_global_init();
void
playlist_tag_changed(struct playlist *playlist);
diff --git a/src/playlist_any.c b/src/PlaylistAny.cxx
index 450ca5932..efa975a1f 100644
--- a/src/playlist_any.c
+++ b/src/PlaylistAny.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,14 @@
*/
#include "config.h"
-#include "playlist_any.h"
+#include "PlaylistAny.hxx"
+#include "PlaylistMapper.hxx"
+
+extern "C" {
#include "playlist_list.h"
-#include "playlist_mapper.h"
#include "uri.h"
#include "input_stream.h"
+}
#include <assert.h>
diff --git a/src/playlist_any.h b/src/PlaylistAny.hxx
index 310913de9..fbc325420 100644
--- a/src/playlist_any.h
+++ b/src/PlaylistAny.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_ANY_H
-#define MPD_PLAYLIST_ANY_H
+#ifndef MPD_PLAYLIST_ANY_HXX
+#define MPD_PLAYLIST_ANY_HXX
#include <glib.h>
diff --git a/src/PlaylistCommands.cxx b/src/PlaylistCommands.cxx
new file mode 100644
index 000000000..dc3b3e0de
--- /dev/null
+++ b/src/PlaylistCommands.cxx
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PlaylistCommands.hxx"
+#include "DatabasePlaylist.hxx"
+#include "CommandError.hxx"
+#include "PlaylistPrint.hxx"
+#include "PlaylistSave.hxx"
+#include "PlaylistFile.hxx"
+#include "PlaylistVector.hxx"
+#include "PlaylistQueue.hxx"
+#include "TimePrint.hxx"
+#include "ClientInternal.hxx"
+#include "protocol/ArgParser.hxx"
+#include "protocol/Result.hxx"
+#include "ls.hxx"
+#include "Playlist.hxx"
+
+extern "C" {
+#include "uri.h"
+}
+
+#include <assert.h>
+#include <stdlib.h>
+
+static void
+print_spl_list(Client *client, const PlaylistVector &list)
+{
+ for (const auto &i : list) {
+ client_printf(client, "playlist: %s\n", i.name.c_str());
+
+ if (i.mtime > 0)
+ time_print(client, "Last-Modified", i.mtime);
+ }
+}
+
+enum command_return
+handle_save(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ enum playlist_result result;
+
+ result = spl_save_playlist(argv[1], &client->playlist);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_load(Client *client, int argc, char *argv[])
+{
+ unsigned start_index, end_index;
+
+ if (argc < 3) {
+ start_index = 0;
+ end_index = G_MAXUINT;
+ } else if (!check_range(client, &start_index, &end_index, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result;
+
+ result = playlist_open_into_queue(argv[1],
+ start_index, end_index,
+ &client->playlist,
+ client->player_control, true);
+ if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
+ return print_playlist_result(client, result);
+
+ GError *error = NULL;
+ if (playlist_load_spl(&client->playlist, client->player_control,
+ argv[1], start_index, end_index,
+ &error))
+ return COMMAND_RETURN_OK;
+
+ if (error->domain == playlist_quark() &&
+ error->code == PLAYLIST_RESULT_BAD_NAME)
+ /* the message for BAD_NAME is confusing when the
+ client wants to load a playlist file from the music
+ directory; patch the GError object to show "no such
+ playlist" instead */
+ error->code = PLAYLIST_RESULT_NO_SUCH_LIST;
+
+ return print_error(client, error);
+}
+
+enum command_return
+handle_listplaylist(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ if (playlist_file_print(client, argv[1], false))
+ return COMMAND_RETURN_OK;
+
+ GError *error = NULL;
+ return spl_print(client, argv[1], false, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_listplaylistinfo(Client *client,
+ G_GNUC_UNUSED int argc, char *argv[])
+{
+ if (playlist_file_print(client, argv[1], true))
+ return COMMAND_RETURN_OK;
+
+ GError *error = NULL;
+ return spl_print(client, argv[1], true, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_rm(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ GError *error = NULL;
+ return spl_delete(argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_rename(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ GError *error = NULL;
+ return spl_rename(argv[1], argv[2], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_playlistdelete(Client *client,
+ G_GNUC_UNUSED int argc, char *argv[]) {
+ char *playlist = argv[1];
+ unsigned from;
+
+ if (!check_unsigned(client, &from, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ GError *error = NULL;
+ return spl_remove_index(playlist, from, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_playlistmove(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ char *playlist = argv[1];
+ unsigned from, to;
+
+ if (!check_unsigned(client, &from, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &to, argv[3]))
+ return COMMAND_RETURN_ERROR;
+
+ GError *error = NULL;
+ return spl_move_index(playlist, from, to, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_playlistclear(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ GError *error = NULL;
+ return spl_clear(argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_playlistadd(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ char *playlist = argv[1];
+ char *uri = argv[2];
+
+ bool success;
+ GError *error = NULL;
+ if (uri_has_scheme(uri)) {
+ if (!uri_supported_scheme(uri)) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "unsupported URI scheme");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ success = spl_append_uri(argv[1], playlist, &error);
+ } else
+ success = search_add_to_playlist(uri, playlist, nullptr,
+ &error);
+
+ if (!success && error == NULL) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return success ? COMMAND_RETURN_OK : print_error(client, error);
+}
+
+enum command_return
+handle_listplaylists(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ GError *error = NULL;
+ const auto list = ListPlaylistFiles(&error);
+ if (list.empty() && error != NULL)
+ return print_error(client, error);
+
+ print_spl_list(client, list);
+ return COMMAND_RETURN_OK;
+}
diff --git a/src/PlaylistCommands.hxx b/src/PlaylistCommands.hxx
new file mode 100644
index 000000000..067f428b6
--- /dev/null
+++ b/src/PlaylistCommands.hxx
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2003-2012 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_PLAYLIST_COMMANDS_HXX
+#define MPD_PLAYLIST_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_save(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_load(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listplaylist(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listplaylistinfo(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_rm(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_rename(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistdelete(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistmove(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistclear(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistadd(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listplaylists(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/playlist_control.c b/src/PlaylistControl.cxx
index 0dea7676a..e4c3298fe 100644
--- a/src/playlist_control.c
+++ b/src/PlaylistControl.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,9 +23,9 @@
*/
#include "config.h"
-#include "playlist_internal.h"
-#include "player_control.h"
-#include "idle.h"
+#include "PlaylistInternal.hxx"
+#include "PlayerControl.hxx"
+#include "song.h"
#include <glib.h>
@@ -50,16 +50,14 @@ playlist_stop(struct playlist *playlist, struct player_control *pc)
result in a new random order */
unsigned current_position =
- queue_order_to_position(&playlist->queue,
- playlist->current);
+ playlist->queue.OrderToPosition(playlist->current);
- queue_shuffle_order(&playlist->queue);
+ playlist->queue.ShuffleOrder();
/* make sure that "current" stays valid, and the next
"play" command plays the same song again */
playlist->current =
- queue_position_to_order(&playlist->queue,
- current_position);
+ playlist->queue.PositionToOrder(current_position);
}
}
@@ -74,7 +72,7 @@ playlist_play(struct playlist *playlist, struct player_control *pc,
if (song == -1) {
/* play any song ("current" song, or the first song */
- if (queue_is_empty(&playlist->queue))
+ if (playlist->queue.IsEmpty())
return PLAYLIST_RESULT_SUCCESS;
if (playlist->playing) {
@@ -88,7 +86,7 @@ playlist_play(struct playlist *playlist, struct player_control *pc,
i = playlist->current >= 0
? playlist->current
: 0;
- } else if (!queue_valid_position(&playlist->queue, song))
+ } else if (!playlist->queue.IsValidPosition(song))
return PLAYLIST_RESULT_BAD_RANGE;
if (playlist->queue.random) {
@@ -97,15 +95,14 @@ playlist_play(struct playlist *playlist, struct player_control *pc,
would be equal to the order number in
no-random mode); convert it to a order
number, because random mode is enabled */
- i = queue_position_to_order(&playlist->queue, song);
+ i = playlist->queue.PositionToOrder(song);
if (!playlist->playing)
playlist->current = 0;
/* swap the new song with the previous "current" one,
so playback continues as planned */
- queue_swap_order(&playlist->queue,
- i, playlist->current);
+ playlist->queue.SwapOrders(i, playlist->current);
i = playlist->current;
}
@@ -126,7 +123,7 @@ playlist_play_id(struct playlist *playlist, struct player_control *pc,
return playlist_play(playlist, pc, id);
}
- song = queue_id_to_position(&playlist->queue, id);
+ song = playlist->queue.IdToPosition(id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
@@ -142,15 +139,15 @@ playlist_next(struct playlist *playlist, struct player_control *pc)
if (!playlist->playing)
return;
- assert(!queue_is_empty(&playlist->queue));
- assert(queue_valid_order(&playlist->queue, playlist->current));
+ assert(!playlist->queue.IsEmpty());
+ assert(playlist->queue.IsValidOrder(playlist->current));
current = playlist->current;
playlist->stop_on_error = false;
/* determine the next song from the queue's order list */
- next_order = queue_next_order(&playlist->queue, playlist->current);
+ next_order = playlist->queue.GetNextOrder(playlist->current);
if (next_order < 0) {
/* no song after this one: stop playback */
playlist_stop(playlist, pc);
@@ -167,7 +164,7 @@ playlist_next(struct playlist *playlist, struct player_control *pc)
songs in a different than before */
assert(playlist->queue.repeat);
- queue_shuffle_order(&playlist->queue);
+ playlist->queue.ShuffleOrder();
/* note that playlist->current and playlist->queued are
now invalid, but playlist_play_order() will
@@ -180,8 +177,7 @@ playlist_next(struct playlist *playlist, struct player_control *pc)
/* Consume mode removes each played songs. */
if(playlist->queue.consume)
playlist_delete(playlist, pc,
- queue_order_to_position(&playlist->queue,
- current));
+ playlist->queue.OrderToPosition(current));
}
void
@@ -190,7 +186,7 @@ playlist_previous(struct playlist *playlist, struct player_control *pc)
if (!playlist->playing)
return;
- assert(queue_length(&playlist->queue) > 0);
+ assert(playlist->queue.GetLength() > 0);
if (playlist->current > 0) {
/* play the preceding song */
@@ -199,7 +195,7 @@ playlist_previous(struct playlist *playlist, struct player_control *pc)
} else if (playlist->queue.repeat) {
/* play the last song in "repeat" mode */
playlist_play_order(playlist, pc,
- queue_length(&playlist->queue) - 1);
+ playlist->queue.GetLength() - 1);
} else {
/* re-start playing the current song if it's
the first one */
@@ -215,13 +211,13 @@ playlist_seek_song(struct playlist *playlist, struct player_control *pc,
unsigned i;
bool success;
- if (!queue_valid_position(&playlist->queue, song))
+ if (!playlist->queue.IsValidPosition(song))
return PLAYLIST_RESULT_BAD_RANGE;
queued = playlist_get_queued_song(playlist);
if (playlist->queue.random)
- i = queue_position_to_order(&playlist->queue, song);
+ i = playlist->queue.PositionToOrder(song);
else
i = song;
@@ -239,7 +235,9 @@ playlist_seek_song(struct playlist *playlist, struct player_control *pc,
queued = NULL;
}
- success = pc_seek(pc, queue_get_order(&playlist->queue, i), seek_time);
+ struct song *the_song =
+ song_dup_detached(playlist->queue.GetOrder(i));
+ success = pc_seek(pc, the_song, seek_time);
if (!success) {
playlist_update_queued_song(playlist, pc, queued);
@@ -256,7 +254,7 @@ enum playlist_result
playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
unsigned id, float seek_time)
{
- int song = queue_id_to_position(&playlist->queue, id);
+ int song = playlist->queue.IdToPosition(id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
diff --git a/src/playlist_database.c b/src/PlaylistDatabase.cxx
index 6b9d87155..edc6a2815 100644
--- a/src/playlist_database.c
+++ b/src/PlaylistDatabase.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,13 @@
*/
#include "config.h"
-#include "playlist_database.h"
-#include "playlist_vector.h"
-#include "text_file.h"
+#include "PlaylistDatabase.hxx"
+#include "PlaylistVector.hxx"
+#include "TextFile.hxx"
+
+extern "C" {
#include "string_util.h"
+}
#include <string.h>
#include <stdlib.h>
@@ -33,27 +36,25 @@ playlist_database_quark(void)
}
void
-playlist_vector_save(FILE *fp, const struct list_head *pv)
+playlist_vector_save(FILE *fp, const PlaylistVector &pv)
{
- struct playlist_metadata *pm;
- playlist_vector_for_each(pm, pv)
+ for (const PlaylistInfo &pi : pv)
fprintf(fp, PLAYLIST_META_BEGIN "%s\n"
"mtime: %li\n"
"playlist_end\n",
- pm->name, (long)pm->mtime);
+ pi.name.c_str(), (long)pi.mtime);
}
bool
-playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name,
- GString *buffer, GError **error_r)
+playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name,
+ GError **error_r)
{
- struct playlist_metadata pm = {
- .mtime = 0,
- };
+ PlaylistInfo pm(name, 0);
+
char *line, *colon;
const char *value;
- while ((line = read_text_line(fp, buffer)) != NULL &&
+ while ((line = file.ReadLine()) != NULL &&
strcmp(line, "playlist_end") != 0) {
colon = strchr(line, ':');
if (colon == NULL || colon == line) {
@@ -74,6 +75,6 @@ playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name,
}
}
- playlist_vector_update_or_add(pv, name, pm.mtime);
+ pv.UpdateOrInsert(std::move(pm));
return true;
}
diff --git a/src/playlist_database.h b/src/PlaylistDatabase.hxx
index 3238fa06b..f5f039267 100644
--- a/src/playlist_database.h
+++ b/src/PlaylistDatabase.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,24 +17,24 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_DATABASE_H
-#define MPD_PLAYLIST_DATABASE_H
+#ifndef MPD_PLAYLIST_DATABASE_HXX
+#define MPD_PLAYLIST_DATABASE_HXX
#include "check.h"
-#include <stdbool.h>
#include <stdio.h>
#include <glib.h>
#define PLAYLIST_META_BEGIN "playlist_begin: "
-struct list_head;
+class PlaylistVector;
+class TextFile;
void
-playlist_vector_save(FILE *fp, const struct list_head *pv);
+playlist_vector_save(FILE *fp, const PlaylistVector &pv);
bool
-playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name,
- GString *buffer, GError **error_r);
+playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name,
+ GError **error_r);
#endif
diff --git a/src/playlist_edit.c b/src/PlaylistEdit.cxx
index d10f49451..e9e085897 100644
--- a/src/playlist_edit.c
+++ b/src/PlaylistEdit.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,18 +24,23 @@
*/
#include "config.h"
-#include "playlist_internal.h"
-#include "player_control.h"
-#include "database.h"
+#include "PlaylistInternal.hxx"
+#include "PlayerControl.hxx"
+
+extern "C" {
#include "uri.h"
#include "song.h"
#include "idle.h"
+}
+
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
#include <stdlib.h>
static void playlist_increment_version(struct playlist *playlist)
{
- queue_increment_version(&playlist->queue);
+ playlist->queue.IncrementVersion();
idle_add(IDLE_PLAYLIST);
}
@@ -45,15 +50,7 @@ playlist_clear(struct playlist *playlist, struct player_control *pc)
{
playlist_stop(playlist, pc);
- /* make sure there are no references to allocated songs
- anymore */
- for (unsigned i = 0; i < queue_length(&playlist->queue); i++) {
- const struct song *song = queue_get(&playlist->queue, i);
- if (!song_in_database(song))
- pc_song_deleted(pc, song);
- }
-
- queue_clear(&playlist->queue);
+ playlist->queue.Clear();
playlist->current = -1;
@@ -78,12 +75,12 @@ playlist_append_song(struct playlist *playlist, struct player_control *pc,
const struct song *queued;
unsigned id;
- if (queue_is_full(&playlist->queue))
+ if (playlist->queue.IsFull())
return PLAYLIST_RESULT_TOO_LARGE;
queued = playlist_get_queued_song(playlist);
- id = queue_append(&playlist->queue, song, 0);
+ id = playlist->queue.Append(song, 0);
if (playlist->queue.random) {
/* shuffle the new song into the list of remaining
@@ -94,9 +91,9 @@ playlist_append_song(struct playlist *playlist, struct player_control *pc,
start = playlist->queued + 1;
else
start = playlist->current + 1;
- if (start < queue_length(&playlist->queue))
- queue_shuffle_order_last(&playlist->queue, start,
- queue_length(&playlist->queue));
+ if (start < playlist->queue.GetLength())
+ playlist->queue.ShuffleOrderLast(start,
+ playlist->queue.GetLength());
}
playlist_increment_version(playlist);
@@ -109,34 +106,32 @@ playlist_append_song(struct playlist *playlist, struct player_control *pc,
return PLAYLIST_RESULT_SUCCESS;
}
-static struct song *
-song_by_uri(const char *uri)
-{
- struct song *song;
-
- song = db_get_song(uri);
- if (song != NULL)
- return song;
-
- if (uri_has_scheme(uri))
- return song_remote_new(uri);
-
- return NULL;
-}
-
enum playlist_result
playlist_append_uri(struct playlist *playlist, struct player_control *pc,
const char *uri, unsigned *added_id)
{
+ g_debug("add to playlist: %s", uri);
+
+ const Database *db = nullptr;
struct song *song;
+ if (uri_has_scheme(uri)) {
+ song = song_remote_new(uri);
+ } else {
+ db = GetDatabase(nullptr);
+ if (db == nullptr)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
- g_debug("add to playlist: %s", uri);
+ song = db->GetSong(uri, nullptr);
+ if (song == nullptr)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+ }
- song = song_by_uri(uri);
- if (song == NULL)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
+ enum playlist_result result =
+ playlist_append_song(playlist, pc, song, added_id);
+ if (db != nullptr)
+ db->ReturnSong(song);
- return playlist_append_song(playlist, pc, song, added_id);
+ return result;
}
enum playlist_result
@@ -145,23 +140,20 @@ playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
{
const struct song *queued;
- if (!queue_valid_position(&playlist->queue, song1) ||
- !queue_valid_position(&playlist->queue, song2))
+ if (!playlist->queue.IsValidPosition(song1) ||
+ !playlist->queue.IsValidPosition(song2))
return PLAYLIST_RESULT_BAD_RANGE;
queued = playlist_get_queued_song(playlist);
- queue_swap(&playlist->queue, song1, song2);
+ playlist->queue.SwapPositions(song1, song2);
if (playlist->queue.random) {
/* update the queue order, so that playlist->current
still points to the current song order */
- queue_swap_order(&playlist->queue,
- queue_position_to_order(&playlist->queue,
- song1),
- queue_position_to_order(&playlist->queue,
- song2));
+ playlist->queue.SwapOrders(playlist->queue.PositionToOrder(song1),
+ playlist->queue.PositionToOrder(song2));
} else {
/* correct the "current" song order */
@@ -182,8 +174,8 @@ enum playlist_result
playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
unsigned id1, unsigned id2)
{
- int song1 = queue_id_to_position(&playlist->queue, id1);
- int song2 = queue_id_to_position(&playlist->queue, id2);
+ int song1 = playlist->queue.IdToPosition(id1);
+ int song2 = playlist->queue.IdToPosition(id2);
if (song1 < 0 || song2 < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
@@ -196,11 +188,11 @@ playlist_set_priority(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end,
uint8_t priority)
{
- if (start >= queue_length(&playlist->queue))
+ if (start >= playlist->queue.GetLength())
return PLAYLIST_RESULT_BAD_RANGE;
- if (end > queue_length(&playlist->queue))
- end = queue_length(&playlist->queue);
+ if (end > playlist->queue.GetLength())
+ end = playlist->queue.GetLength();
if (start >= end)
return PLAYLIST_RESULT_SUCCESS;
@@ -208,24 +200,22 @@ playlist_set_priority(struct playlist *playlist, struct player_control *pc,
/* remember "current" and "queued" */
int current_position = playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- playlist->current)
+ ? (int)playlist->queue.OrderToPosition(playlist->current)
: -1;
const struct song *queued = playlist_get_queued_song(playlist);
/* apply the priority changes */
- queue_set_priority_range(&playlist->queue, start, end, priority,
- playlist->current);
+ playlist->queue.SetPriorityRange(start, end, priority,
+ playlist->current);
playlist_increment_version(playlist);
/* restore "current" and choose a new "queued" */
if (current_position >= 0)
- playlist->current = queue_position_to_order(&playlist->queue,
- current_position);
+ playlist->current = playlist->queue.PositionToOrder(current_position);
playlist_update_queued_song(playlist, pc, queued);
@@ -236,7 +226,7 @@ enum playlist_result
playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
unsigned song_id, uint8_t priority)
{
- int song_position = queue_id_to_position(&playlist->queue, song_id);
+ int song_position = playlist->queue.IdToPosition(song_id);
if (song_position < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
@@ -252,9 +242,9 @@ playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
{
unsigned songOrder;
- assert(song < queue_length(&playlist->queue));
+ assert(song < playlist->queue.GetLength());
- songOrder = queue_position_to_order(&playlist->queue, song);
+ songOrder = playlist->queue.PositionToOrder(song);
if (playlist->playing && playlist->current == (int)songOrder) {
bool paused = pc_get_state(pc) == PLAYER_STATE_PAUSE;
@@ -266,8 +256,7 @@ playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
/* see which song is going to be played instead */
- playlist->current = queue_next_order(&playlist->queue,
- playlist->current);
+ playlist->current = playlist->queue.GetNextOrder(playlist->current);
if (playlist->current == (int)songOrder)
playlist->current = -1;
@@ -287,10 +276,7 @@ playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
/* now do it: remove the song */
- if (!song_in_database(queue_get(&playlist->queue, song)))
- pc_song_deleted(pc, queue_get(&playlist->queue, song));
-
- queue_delete(&playlist->queue, song);
+ playlist->queue.DeletePosition(song);
/* update the "current" and "queued" variables */
@@ -305,7 +291,7 @@ playlist_delete(struct playlist *playlist, struct player_control *pc,
{
const struct song *queued;
- if (song >= queue_length(&playlist->queue))
+ if (song >= playlist->queue.GetLength())
return PLAYLIST_RESULT_BAD_RANGE;
queued = playlist_get_queued_song(playlist);
@@ -324,11 +310,11 @@ playlist_delete_range(struct playlist *playlist, struct player_control *pc,
{
const struct song *queued;
- if (start >= queue_length(&playlist->queue))
+ if (start >= playlist->queue.GetLength())
return PLAYLIST_RESULT_BAD_RANGE;
- if (end > queue_length(&playlist->queue))
- end = queue_length(&playlist->queue);
+ if (end > playlist->queue.GetLength())
+ end = playlist->queue.GetLength();
if (start >= end)
return PLAYLIST_RESULT_SUCCESS;
@@ -349,7 +335,7 @@ enum playlist_result
playlist_delete_id(struct playlist *playlist, struct player_control *pc,
unsigned id)
{
- int song = queue_id_to_position(&playlist->queue, id);
+ int song = playlist->queue.IdToPosition(id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
@@ -360,11 +346,9 @@ void
playlist_delete_song(struct playlist *playlist, struct player_control *pc,
const struct song *song)
{
- for (int i = queue_length(&playlist->queue) - 1; i >= 0; --i)
- if (song == queue_get(&playlist->queue, i))
+ for (int i = playlist->queue.GetLength() - 1; i >= 0; --i)
+ if (song == playlist->queue.Get(i))
playlist_delete(playlist, pc, i);
-
- pc_song_deleted(pc, song);
}
enum playlist_result
@@ -374,12 +358,12 @@ playlist_move_range(struct playlist *playlist, struct player_control *pc,
const struct song *queued;
int currentSong;
- if (!queue_valid_position(&playlist->queue, start) ||
- !queue_valid_position(&playlist->queue, end - 1))
+ if (!playlist->queue.IsValidPosition(start) ||
+ !playlist->queue.IsValidPosition(end - 1))
return PLAYLIST_RESULT_BAD_RANGE;
- if ((to >= 0 && to + end - start - 1 >= queue_length(&playlist->queue)) ||
- (to < 0 && abs(to) > (int)queue_length(&playlist->queue)))
+ if ((to >= 0 && to + end - start - 1 >= playlist->queue.GetLength()) ||
+ (to < 0 && abs(to) > (int)playlist->queue.GetLength()))
return PLAYLIST_RESULT_BAD_RANGE;
if ((int)start == to)
@@ -393,19 +377,18 @@ playlist_move_range(struct playlist *playlist, struct player_control *pc,
* (-playlist.length == to) => move to position BEFORE current song
*/
currentSong = playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- playlist->current)
+ ? (int)playlist->queue.OrderToPosition(playlist->current)
: -1;
if (to < 0 && playlist->current >= 0) {
if (start <= (unsigned)currentSong && (unsigned)currentSong < end)
/* no-op, can't be moved to offset of itself */
return PLAYLIST_RESULT_SUCCESS;
- to = (currentSong + abs(to)) % queue_length(&playlist->queue);
+ to = (currentSong + abs(to)) % playlist->queue.GetLength();
if (start < (unsigned)to)
to--;
}
- queue_move_range(&playlist->queue, start, end, to);
+ playlist->queue.MoveRange(start, end, to);
if (!playlist->queue.random) {
/* update current/queued */
@@ -432,7 +415,7 @@ enum playlist_result
playlist_move_id(struct playlist *playlist, struct player_control *pc,
unsigned id1, int to)
{
- int song = queue_id_to_position(&playlist->queue, id1);
+ int song = playlist->queue.IdToPosition(id1);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
@@ -445,9 +428,9 @@ playlist_shuffle(struct playlist *playlist, struct player_control *pc,
{
const struct song *queued;
- if (end > queue_length(&playlist->queue))
+ if (end > playlist->queue.GetLength())
/* correct the "end" offset */
- end = queue_length(&playlist->queue);
+ end = playlist->queue.GetLength();
if ((start+1) >= end)
/* needs at least two entries. */
@@ -456,17 +439,16 @@ playlist_shuffle(struct playlist *playlist, struct player_control *pc,
queued = playlist_get_queued_song(playlist);
if (playlist->playing && playlist->current >= 0) {
unsigned current_position;
- current_position = queue_order_to_position(&playlist->queue,
- playlist->current);
+ current_position = playlist->queue.OrderToPosition(playlist->current);
if (current_position >= start && current_position < end)
{
/* put current playing song first */
- queue_swap(&playlist->queue, start, current_position);
+ playlist->queue.SwapPositions(start, current_position);
if (playlist->queue.random) {
playlist->current =
- queue_position_to_order(&playlist->queue, start);
+ playlist->queue.PositionToOrder(start);
} else
playlist->current = start;
@@ -479,7 +461,7 @@ playlist_shuffle(struct playlist *playlist, struct player_control *pc,
playlist->current = -1;
}
- queue_shuffle_range(&playlist->queue, start, end);
+ playlist->queue.ShuffleRange(start, end);
playlist_increment_version(playlist);
diff --git a/src/stored_playlist.c b/src/PlaylistFile.cxx
index 39ba2bac1..de12518d9 100644
--- a/src/stored_playlist.c
+++ b/src/PlaylistFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,24 @@
*/
#include "config.h"
-#include "stored_playlist.h"
-#include "playlist_save.h"
-#include "text_file.h"
+#include "PlaylistFile.hxx"
+#include "PlaylistSave.hxx"
+#include "PlaylistInfo.hxx"
+#include "PlaylistVector.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseGlue.hxx"
#include "song.h"
-#include "mapper.h"
+#include "io_error.h"
+#include "Mapper.hxx"
+#include "TextFile.hxx"
+
+extern "C" {
#include "path.h"
#include "uri.h"
-#include "database.h"
#include "idle.h"
#include "conf.h"
+}
+
#include "glib_compat.h"
#include <assert.h>
@@ -128,96 +136,74 @@ playlist_errno(GError **error_r)
break;
default:
- g_set_error_literal(error_r, g_file_error_quark(), errno,
- g_strerror(errno));
+ set_error_errno(error_r);
break;
}
}
-static struct stored_playlist_info *
-load_playlist_info(const char *parent_path_fs, const char *name_fs)
+static bool
+LoadPlaylistFileInfo(PlaylistInfo &info,
+ const char *parent_path_fs, const char *name_fs)
{
size_t name_length = strlen(name_fs);
- char *path_fs, *name, *name_utf8;
- int ret;
- struct stat st;
- struct stored_playlist_info *playlist;
if (name_length < sizeof(PLAYLIST_FILE_SUFFIX) ||
memchr(name_fs, '\n', name_length) != NULL)
- return NULL;
+ return false;
if (!g_str_has_suffix(name_fs, PLAYLIST_FILE_SUFFIX))
- return NULL;
+ return false;
- path_fs = g_build_filename(parent_path_fs, name_fs, NULL);
- ret = stat(path_fs, &st);
+ char *path_fs = g_build_filename(parent_path_fs, name_fs, NULL);
+ struct stat st;
+ int ret = stat(path_fs, &st);
g_free(path_fs);
if (ret < 0 || !S_ISREG(st.st_mode))
- return NULL;
+ return false;
- name = g_strndup(name_fs,
- name_length + 1 - sizeof(PLAYLIST_FILE_SUFFIX));
- name_utf8 = fs_charset_to_utf8(name);
+ char *name = g_strndup(name_fs,
+ name_length + 1 - sizeof(PLAYLIST_FILE_SUFFIX));
+ char *name_utf8 = fs_charset_to_utf8(name);
g_free(name);
if (name_utf8 == NULL)
- return NULL;
+ return false;
- playlist = g_new(struct stored_playlist_info, 1);
- playlist->name = name_utf8;
- playlist->mtime = st.st_mtime;
- return playlist;
+ info.name = name_utf8;
+ g_free(name_utf8);
+ info.mtime = st.st_mtime;
+ return true;
}
-GPtrArray *
-spl_list(GError **error_r)
+PlaylistVector
+ListPlaylistFiles(GError **error_r)
{
- const char *parent_path_fs = spl_map(error_r);
- DIR *dir;
- struct dirent *ent;
- GPtrArray *list;
- struct stored_playlist_info *playlist;
+ PlaylistVector list;
+ const char *parent_path_fs = spl_map(error_r);
if (parent_path_fs == NULL)
- return NULL;
+ return list;
- dir = opendir(parent_path_fs);
+ DIR *dir = opendir(parent_path_fs);
if (dir == NULL) {
- g_set_error_literal(error_r, g_file_error_quark(), errno,
- g_strerror(errno));
- return NULL;
+ set_error_errno(error_r);
+ return list;
}
- list = g_ptr_array_new();
-
+ PlaylistInfo info;
+ struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
- playlist = load_playlist_info(parent_path_fs, ent->d_name);
- if (playlist != NULL)
- g_ptr_array_add(list, playlist);
+ if (LoadPlaylistFileInfo(info, parent_path_fs, ent->d_name))
+ list.push_back(std::move(info));
}
closedir(dir);
return list;
}
-void
-spl_list_free(GPtrArray *list)
-{
- for (unsigned i = 0; i < list->len; ++i) {
- struct stored_playlist_info *playlist =
- g_ptr_array_index(list, i);
- g_free(playlist->name);
- g_free(playlist);
- }
-
- g_ptr_array_free(list, true);
-}
-
static bool
-spl_save(GPtrArray *list, const char *utf8path, GError **error_r)
+SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path,
+ GError **error_r)
{
- FILE *file;
-
assert(utf8path != NULL);
if (spl_map(error_r) == NULL)
@@ -227,48 +213,40 @@ spl_save(GPtrArray *list, const char *utf8path, GError **error_r)
if (path_fs == NULL)
return false;
- file = fopen(path_fs, "w");
+ FILE *file = fopen(path_fs, "w");
g_free(path_fs);
if (file == NULL) {
playlist_errno(error_r);
return false;
}
- for (unsigned i = 0; i < list->len; ++i) {
- const char *uri = g_ptr_array_index(list, i);
- playlist_print_uri(file, uri);
- }
+ for (const auto &uri_utf8 : contents)
+ playlist_print_uri(file, uri_utf8.c_str());
fclose(file);
return true;
}
-GPtrArray *
-spl_load(const char *utf8path, GError **error_r)
+PlaylistFileContents
+LoadPlaylistFile(const char *utf8path, GError **error_r)
{
- FILE *file;
- GPtrArray *list;
- char *path_fs;
+ PlaylistFileContents contents;
if (spl_map(error_r) == NULL)
- return NULL;
+ return contents;
- path_fs = spl_map_to_fs(utf8path, error_r);
+ char *path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL)
- return NULL;
+ return contents;
- file = fopen(path_fs, "r");
- g_free(path_fs);
- if (file == NULL) {
+ TextFile file(path_fs);
+ if (file.HasFailed()) {
playlist_errno(error_r);
- return NULL;
+ return contents;
}
- list = g_ptr_array_new();
-
- GString *buffer = g_string_sized_new(1024);
char *s;
- while ((s = read_text_line(file, buffer)) != NULL) {
+ while ((s = file.ReadLine()) != NULL) {
if (*s == 0 || *s == PLAYLIST_COMMENT)
continue;
@@ -283,80 +261,45 @@ spl_load(const char *utf8path, GError **error_r)
} else
s = g_strdup(s);
- g_ptr_array_add(list, s);
-
- if (list->len >= playlist_max_length)
+ contents.emplace_back(s);
+ if (contents.size() >= playlist_max_length)
break;
}
- fclose(file);
- return list;
-}
-
-void
-spl_free(GPtrArray *list)
-{
- for (unsigned i = 0; i < list->len; ++i) {
- char *uri = g_ptr_array_index(list, i);
- g_free(uri);
- }
-
- g_ptr_array_free(list, true);
-}
-
-static char *
-spl_remove_index_internal(GPtrArray *list, unsigned idx)
-{
- char *uri;
-
- assert(idx < list->len);
-
- uri = g_ptr_array_remove_index(list, idx);
- assert(uri != NULL);
- return uri;
-}
-
-static void
-spl_insert_index_internal(GPtrArray *list, unsigned idx, char *uri)
-{
- assert(idx <= list->len);
-
- g_ptr_array_add(list, uri);
-
- memmove(list->pdata + idx + 1, list->pdata + idx,
- (list->len - idx - 1) * sizeof(list->pdata[0]));
- g_ptr_array_index(list, idx) = uri;
+ return contents;
}
bool
spl_move_index(const char *utf8path, unsigned src, unsigned dest,
GError **error_r)
{
- char *uri;
-
if (src == dest)
/* this doesn't check whether the playlist exists, but
what the hell.. */
return true;
- GPtrArray *list = spl_load(utf8path, error_r);
- if (list == NULL)
+ GError *error = nullptr;
+ auto contents = LoadPlaylistFile(utf8path, &error);
+ if (contents.empty() && error != nullptr) {
+ g_propagate_error(error_r, error);
return false;
+ }
- if (src >= list->len || dest >= list->len) {
- spl_free(list);
+ if (src >= contents.size() || dest >= contents.size()) {
g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_BAD_RANGE,
"Bad range");
return false;
}
- uri = spl_remove_index_internal(list, src);
- spl_insert_index_internal(list, dest, uri);
+ const auto src_i = std::next(contents.begin(), src);
+ auto value = std::move(*src_i);
+ contents.erase(src_i);
- bool result = spl_save(list, utf8path, error_r);
+ const auto dest_i = std::next(contents.begin(), dest);
+ contents.insert(dest_i, std::move(value));
- spl_free(list);
+ bool result = SavePlaylistFile(contents, utf8path, error_r);
idle_add(IDLE_STORED_PLAYLIST);
return result;
@@ -390,14 +333,11 @@ spl_clear(const char *utf8path, GError **error_r)
bool
spl_delete(const char *name_utf8, GError **error_r)
{
- char *path_fs;
- int ret;
-
- path_fs = spl_map_to_fs(name_utf8, error_r);
+ char *path_fs = spl_map_to_fs(name_utf8, error_r);
if (path_fs == NULL)
return false;
- ret = unlink(path_fs);
+ int ret = unlink(path_fs);
g_free(path_fs);
if (ret < 0) {
playlist_errno(error_r);
@@ -411,25 +351,23 @@ spl_delete(const char *name_utf8, GError **error_r)
bool
spl_remove_index(const char *utf8path, unsigned pos, GError **error_r)
{
- char *uri;
-
- GPtrArray *list = spl_load(utf8path, error_r);
- if (list == NULL)
+ GError *error = nullptr;
+ auto contents = LoadPlaylistFile(utf8path, &error);
+ if (contents.empty() && error != nullptr) {
+ g_propagate_error(error_r, error);
return false;
+ }
- if (pos >= list->len) {
- spl_free(list);
+ if (pos >= contents.size()) {
g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_BAD_RANGE,
"Bad range");
return false;
}
- uri = spl_remove_index_internal(list, pos);
- g_free(uri);
- bool result = spl_save(list, utf8path, error_r);
+ contents.erase(std::next(contents.begin(), pos));
- spl_free(list);
+ bool result = SavePlaylistFile(contents, utf8path, error_r);
idle_add(IDLE_STORED_PLAYLIST);
return result;
@@ -439,7 +377,6 @@ bool
spl_append_song(const char *utf8path, struct song *song, GError **error_r)
{
FILE *file;
- struct stat st;
if (spl_map(error_r) == NULL)
return false;
@@ -455,6 +392,7 @@ spl_append_song(const char *utf8path, struct song *song, GError **error_r)
return false;
}
+ struct stat st;
if (fstat(fileno(file), &st) < 0) {
playlist_errno(error_r);
fclose(file);
@@ -480,23 +418,23 @@ spl_append_song(const char *utf8path, struct song *song, GError **error_r)
bool
spl_append_uri(const char *url, const char *utf8file, GError **error_r)
{
- struct song *song;
-
if (uri_has_scheme(url)) {
- song = song_remote_new(url);
+ struct song *song = song_remote_new(url);
bool success = spl_append_song(utf8file, song, error_r);
song_free(song);
return success;
} else {
- song = db_get_song(url);
- if (song == NULL) {
- g_set_error_literal(error_r, playlist_quark(),
- PLAYLIST_RESULT_NO_SUCH_SONG,
- "No such song");
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
return false;
- }
- return spl_append_song(utf8file, song, error_r);
+ song *song = db->GetSong(url, error_r);
+ if (song == nullptr)
+ return false;
+
+ bool success = spl_append_song(utf8file, song, error_r);
+ db->ReturnSong(song);
+ return success;
}
}
diff --git a/src/stored_playlist.h b/src/PlaylistFile.hxx
index cfe49633c..3f63253ad 100644
--- a/src/stored_playlist.h
+++ b/src/PlaylistFile.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 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_STORED_PLAYLIST_H
-#define MPD_STORED_PLAYLIST_H
+#ifndef MPD_PLAYLIST_FILE_HXX
+#define MPD_PLAYLIST_FILE_HXX
+
+#include <vector>
+#include <string>
#include <glib.h>
-#include <stdbool.h>
#include <time.h>
struct song;
+struct PlaylistInfo;
+class PlaylistVector;
-struct stored_playlist_info {
- char *name;
-
- time_t mtime;
-};
+typedef std::vector<std::string> PlaylistFileContents;
extern bool playlist_saveAbsolutePaths;
@@ -40,6 +40,8 @@ extern bool playlist_saveAbsolutePaths;
void
spl_global_init(void);
+#ifdef __cplusplus
+
/**
* Determines whether the specified string is a valid name for a
* stored playlist.
@@ -51,17 +53,11 @@ spl_valid_name(const char *name_utf8);
* Returns a list of stored_playlist_info struct pointers. Returns
* NULL if an error occurred.
*/
-GPtrArray *
-spl_list(GError **error_r);
-
-void
-spl_list_free(GPtrArray *list);
+PlaylistVector
+ListPlaylistFiles(GError **error_r);
-GPtrArray *
-spl_load(const char *utf8path, GError **error_r);
-
-void
-spl_free(GPtrArray *list);
+PlaylistFileContents
+LoadPlaylistFile(const char *utf8path, GError **error_r);
bool
spl_move_index(const char *utf8path, unsigned src, unsigned dest,
@@ -86,3 +82,5 @@ bool
spl_rename(const char *utf8from, const char *utf8to, GError **error_r);
#endif
+
+#endif
diff --git a/src/playlist_global.c b/src/PlaylistGlobal.cxx
index 650b88bb8..92572cde9 100644
--- a/src/playlist_global.c
+++ b/src/PlaylistGlobal.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,36 +23,30 @@
*/
#include "config.h"
-#include "playlist.h"
-#include "playlist_state.h"
-#include "event_pipe.h"
-#include "main.h"
+#include "Playlist.hxx"
+#include "Main.hxx"
+#include "Partition.hxx"
-struct playlist g_playlist;
+extern "C" {
+#include "event_pipe.h"
+}
static void
playlist_tag_event(void)
{
- playlist_tag_changed(&g_playlist);
+ playlist_tag_changed(&global_partition->playlist);
}
static void
playlist_event(void)
{
- playlist_sync(&g_playlist, global_player_control);
+ playlist_sync(&global_partition->playlist,
+ &global_partition->pc);
}
void
-playlist_global_init(void)
+playlist_global_init()
{
- playlist_init(&g_playlist);
-
event_pipe_register(PIPE_EVENT_TAG, playlist_tag_event);
event_pipe_register(PIPE_EVENT_PLAYLIST, playlist_event);
}
-
-void
-playlist_global_finish(void)
-{
- playlist_finish(&g_playlist);
-}
diff --git a/src/PlaylistInfo.hxx b/src/PlaylistInfo.hxx
new file mode 100644
index 000000000..96e4f6db9
--- /dev/null
+++ b/src/PlaylistInfo.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_INFO_HXX
+#define MPD_PLAYLIST_INFO_HXX
+
+#include "check.h"
+#include "gcc.h"
+
+#include <string>
+
+#include <sys/time.h>
+
+/**
+ * A directory entry pointing to a playlist file.
+ */
+struct PlaylistInfo {
+ /**
+ * The UTF-8 encoded name of the playlist file.
+ */
+ std::string name;
+
+ time_t mtime;
+
+ class CompareName {
+ const char *const name;
+
+ public:
+ constexpr CompareName(const char *_name):name(_name) {}
+
+ gcc_pure
+ bool operator()(const PlaylistInfo &pi) const {
+ return pi.name.compare(name) == 0;
+ }
+ };
+
+ PlaylistInfo() = default;
+
+ template<typename N>
+ PlaylistInfo(N &&_name, time_t _mtime)
+ :name(std::forward<N>(_name)), mtime(_mtime) {}
+
+ PlaylistInfo(const PlaylistInfo &other) = delete;
+ PlaylistInfo(PlaylistInfo &&) = default;
+};
+
+#endif
diff --git a/src/playlist_internal.h b/src/PlaylistInternal.hxx
index 81b175176..f0cad8e68 100644
--- a/src/playlist_internal.h
+++ b/src/PlaylistInternal.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,10 +22,10 @@
*
*/
-#ifndef PLAYLIST_INTERNAL_H
-#define PLAYLIST_INTERNAL_H
+#ifndef MPD_PLAYLIST_INTERNAL_HXX
+#define MPD_PLAYLIST_INTERNAL_HXX
-#include "playlist.h"
+#include "Playlist.hxx"
struct player_control;
diff --git a/src/playlist_mapper.c b/src/PlaylistMapper.cxx
index 13adb80d0..01b8f7dd8 100644
--- a/src/playlist_mapper.c
+++ b/src/PlaylistMapper.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,14 @@
*/
#include "config.h"
-#include "playlist_mapper.h"
+#include "PlaylistMapper.hxx"
+#include "PlaylistFile.hxx"
+#include "Mapper.hxx"
+
+extern "C" {
#include "playlist_list.h"
-#include "stored_playlist.h"
-#include "mapper.h"
#include "uri.h"
+}
#include <assert.h>
diff --git a/src/playlist_mapper.h b/src/PlaylistMapper.hxx
index 9a7187d93..dc4e5cce8 100644
--- a/src/playlist_mapper.h
+++ b/src/PlaylistMapper.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_MAPPER_H
-#define MPD_PLAYLIST_MAPPER_H
+#ifndef MPD_PLAYLIST_MAPPER_HXX
+#define MPD_PLAYLIST_MAPPER_HXX
#include <glib.h>
diff --git a/src/playlist_print.c b/src/PlaylistPrint.cxx
index 59c42f969..7df7a9c39 100644
--- a/src/playlist_print.c
+++ b/src/PlaylistPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,37 +18,41 @@
*/
#include "config.h"
-#include "playlist_print.h"
+#include "PlaylistPrint.hxx"
+#include "PlaylistFile.hxx"
+#include "PlaylistAny.hxx"
+#include "PlaylistSong.hxx"
+#include "Playlist.hxx"
+#include "QueuePrint.hxx"
+#include "SongPrint.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+#include "Client.hxx"
+
+extern "C" {
#include "playlist_list.h"
#include "playlist_plugin.h"
-#include "playlist_any.h"
-#include "playlist_song.h"
-#include "playlist.h"
-#include "queue_print.h"
-#include "stored_playlist.h"
-#include "song_print.h"
#include "song.h"
-#include "database.h"
-#include "client.h"
#include "input_stream.h"
+}
void
-playlist_print_uris(struct client *client, const struct playlist *playlist)
+playlist_print_uris(Client *client, const struct playlist *playlist)
{
const struct queue *queue = &playlist->queue;
- queue_print_uris(client, queue, 0, queue_length(queue));
+ queue_print_uris(client, queue, 0, queue->GetLength());
}
bool
-playlist_print_info(struct client *client, const struct playlist *playlist,
+playlist_print_info(Client *client, const struct playlist *playlist,
unsigned start, unsigned end)
{
const struct queue *queue = &playlist->queue;
- if (end > queue_length(queue))
+ if (end > queue->GetLength())
/* correct the "end" offset */
- end = queue_length(queue);
+ end = queue->GetLength();
if (start > end)
/* an invalid "start" offset is fatal */
@@ -59,13 +63,13 @@ playlist_print_info(struct client *client, const struct playlist *playlist,
}
bool
-playlist_print_id(struct client *client, const struct playlist *playlist,
+playlist_print_id(Client *client, const struct playlist *playlist,
unsigned id)
{
const struct queue *queue = &playlist->queue;
int position;
- position = queue_id_to_position(queue, id);
+ position = queue->IdToPosition(id);
if (position < 0)
/* no such song */
return false;
@@ -74,7 +78,7 @@ playlist_print_id(struct client *client, const struct playlist *playlist,
}
bool
-playlist_print_current(struct client *client, const struct playlist *playlist)
+playlist_print_current(Client *client, const struct playlist *playlist)
{
int current_position = playlist_get_current_song(playlist);
@@ -87,21 +91,14 @@ playlist_print_current(struct client *client, const struct playlist *playlist)
}
void
-playlist_print_find(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list)
-{
- queue_find(client, &playlist->queue, list);
-}
-
-void
-playlist_print_search(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list)
+playlist_print_find(Client *client, const struct playlist *playlist,
+ const SongFilter &filter)
{
- queue_search(client, &playlist->queue, list);
+ queue_find(client, &playlist->queue, filter);
}
void
-playlist_print_changes_info(struct client *client,
+playlist_print_changes_info(Client *client,
const struct playlist *playlist,
uint32_t version)
{
@@ -109,46 +106,51 @@ playlist_print_changes_info(struct client *client,
}
void
-playlist_print_changes_position(struct client *client,
+playlist_print_changes_position(Client *client,
const struct playlist *playlist,
uint32_t version)
{
queue_print_changes_position(client, &playlist->queue, version);
}
+static bool
+PrintSongDetails(Client *client, const char *uri_utf8)
+{
+ const Database *db = GetDatabase(nullptr);
+ if (db == nullptr)
+ return false;
+
+ song *song = db->GetSong(uri_utf8, nullptr);
+ if (song == nullptr)
+ return false;
+
+ song_print_info(client, song);
+ db->ReturnSong(song);
+ return true;
+}
+
bool
-spl_print(struct client *client, const char *name_utf8, bool detail,
+spl_print(Client *client, const char *name_utf8, bool detail,
GError **error_r)
{
- GPtrArray *list;
-
- list = spl_load(name_utf8, error_r);
- if (list == NULL)
+ GError *error = NULL;
+ PlaylistFileContents contents = LoadPlaylistFile(name_utf8, &error);
+ if (contents.empty() && error != nullptr) {
+ g_propagate_error(error_r, error);
return false;
+ }
- for (unsigned i = 0; i < list->len; ++i) {
- const char *temp = g_ptr_array_index(list, i);
- bool wrote = false;
-
- if (detail) {
- struct song *song = db_get_song(temp);
- if (song) {
- song_print_info(client, song);
- wrote = true;
- }
- }
-
- if (!wrote) {
- client_printf(client, SONG_FILE "%s\n", temp);
- }
+ for (const auto &uri_utf8 : contents) {
+ if (!detail || !PrintSongDetails(client, uri_utf8.c_str()))
+ client_printf(client, SONG_FILE "%s\n",
+ uri_utf8.c_str());
}
- spl_free(list);
return true;
}
static void
-playlist_provider_print(struct client *client, const char *uri,
+playlist_provider_print(Client *client, const char *uri,
struct playlist_provider *playlist, bool detail)
{
struct song *song;
@@ -164,15 +166,14 @@ playlist_provider_print(struct client *client, const char *uri,
else
song_print_uri(client, song);
- if (!song_in_database(song))
- song_free(song);
+ song_free(song);
}
g_free(base_uri);
}
bool
-playlist_file_print(struct client *client, const char *uri, bool detail)
+playlist_file_print(Client *client, const char *uri, bool detail)
{
GMutex *mutex = g_mutex_new();
GCond *cond = g_cond_new();
diff --git a/src/playlist_print.h b/src/PlaylistPrint.hxx
index d4f1911d2..588aeca85 100644
--- a/src/playlist_print.h
+++ b/src/PlaylistPrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,22 +17,21 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef PLAYLIST_PRINT_H
-#define PLAYLIST_PRINT_H
+#ifndef MPD_PLAYLIST_PRINT_HXX
+#define MPD_PLAYLIST_PRINT_HXX
#include <glib.h>
-#include <stdbool.h>
#include <stdint.h>
-struct client;
struct playlist;
-struct locate_item_list;
+class SongFilter;
+class Client;
/**
* Sends the whole playlist to the client, song URIs only.
*/
void
-playlist_print_uris(struct client *client, const struct playlist *playlist);
+playlist_print_uris(Client *client, const struct playlist *playlist);
/**
* Sends a range of the playlist to the client, including all known
@@ -41,7 +40,7 @@ playlist_print_uris(struct client *client, const struct playlist *playlist);
* This function however fails when the start offset is invalid.
*/
bool
-playlist_print_info(struct client *client, const struct playlist *playlist,
+playlist_print_info(Client *client, const struct playlist *playlist,
unsigned start, unsigned end);
/**
@@ -50,7 +49,7 @@ playlist_print_info(struct client *client, const struct playlist *playlist,
* @return true on suite, false if there is no such song
*/
bool
-playlist_print_id(struct client *client, const struct playlist *playlist,
+playlist_print_id(Client *client, const struct playlist *playlist,
unsigned id);
/**
@@ -59,27 +58,20 @@ playlist_print_id(struct client *client, const struct playlist *playlist,
* @return true on success, false if there is no current song
*/
bool
-playlist_print_current(struct client *client, const struct playlist *playlist);
+playlist_print_current(Client *client, const struct playlist *playlist);
/**
* Find songs in the playlist.
*/
void
-playlist_print_find(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list);
-
-/**
- * Search for songs in the playlist.
- */
-void
-playlist_print_search(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list);
+playlist_print_find(Client *client, const struct playlist *playlist,
+ const SongFilter &filter);
/**
* Print detailed changes since the specified playlist version.
*/
void
-playlist_print_changes_info(struct client *client,
+playlist_print_changes_info(Client *client,
const struct playlist *playlist,
uint32_t version);
@@ -87,7 +79,7 @@ playlist_print_changes_info(struct client *client,
* Print changes since the specified playlist version, position only.
*/
void
-playlist_print_changes_position(struct client *client,
+playlist_print_changes_position(Client *client,
const struct playlist *playlist,
uint32_t version);
@@ -100,7 +92,7 @@ playlist_print_changes_position(struct client *client,
* @return true on success, false if the playlist does not exist
*/
bool
-spl_print(struct client *client, const char *name_utf8, bool detail,
+spl_print(Client *client, const char *name_utf8, bool detail,
GError **error_r);
/**
@@ -112,6 +104,6 @@ spl_print(struct client *client, const char *name_utf8, bool detail,
* @return true on success, false if the playlist does not exist
*/
bool
-playlist_file_print(struct client *client, const char *uri, bool detail);
+playlist_file_print(Client *client, const char *uri, bool detail);
#endif
diff --git a/src/playlist_queue.c b/src/PlaylistQueue.cxx
index aada94984..e75720315 100644
--- a/src/playlist_queue.c
+++ b/src/PlaylistQueue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,16 @@
*/
#include "config.h"
-#include "playlist_queue.h"
+#include "PlaylistQueue.hxx"
#include "playlist_plugin.h"
-#include "playlist_any.h"
-#include "playlist_song.h"
-#include "playlist.h"
+#include "PlaylistAny.hxx"
+#include "PlaylistSong.hxx"
+#include "Playlist.hxx"
+
+extern "C" {
#include "song.h"
#include "input_stream.h"
+}
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
@@ -41,8 +44,7 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
++i) {
if (i < start_index) {
/* skip songs before the start index */
- if (!song_in_database(song))
- song_free(song);
+ song_free(song);
continue;
}
@@ -51,9 +53,8 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
continue;
result = playlist_append_song(dest, pc, song, NULL);
+ song_free(song);
if (result != PLAYLIST_RESULT_SUCCESS) {
- if (!song_in_database(song))
- song_free(song);
g_free(base_uri);
return result;
}
diff --git a/src/playlist_queue.h b/src/PlaylistQueue.hxx
index 24a851aab..cda77c818 100644
--- a/src/playlist_queue.h
+++ b/src/PlaylistQueue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,13 +21,11 @@
* \brief Glue between playlist plugin and the play queue
*/
-#ifndef MPD_PLAYLIST_QUEUE_H
-#define MPD_PLAYLIST_QUEUE_H
+#ifndef MPD_PLAYLIST_QUEUE_HXX
+#define MPD_PLAYLIST_QUEUE_HXX
#include "playlist_error.h"
-#include <stdbool.h>
-
struct playlist_provider;
struct playlist;
struct player_control;
diff --git a/src/playlist_save.c b/src/PlaylistSave.cxx
index 334159e0d..4d537accf 100644
--- a/src/playlist_save.c
+++ b/src/PlaylistSave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,18 @@
*/
#include "config.h"
-#include "playlist_save.h"
-#include "playlist.h"
-#include "stored_playlist.h"
-#include "queue.h"
+#include "PlaylistSave.hxx"
+#include "PlaylistFile.hxx"
+#include "Playlist.hxx"
#include "song.h"
-#include "mapper.h"
+#include "Mapper.hxx"
+
+extern "C" {
#include "path.h"
#include "uri.h"
-#include "database.h"
#include "idle.h"
+}
+
#include "glib_compat.h"
#include <glib.h>
@@ -96,8 +98,8 @@ spl_save_queue(const char *name_utf8, const struct queue *queue)
if (file == NULL)
return PLAYLIST_RESULT_ERRNO;
- for (unsigned i = 0; i < queue_length(queue); i++)
- playlist_print_song(file, queue_get(queue, i));
+ for (unsigned i = 0; i < queue->GetLength(); i++)
+ playlist_print_song(file, queue->Get(i));
fclose(file);
@@ -117,34 +119,35 @@ playlist_load_spl(struct playlist *playlist, struct player_control *pc,
unsigned start_index, unsigned end_index,
GError **error_r)
{
- GPtrArray *list;
-
- list = spl_load(name_utf8, error_r);
- if (list == NULL)
+ GError *error = NULL;
+ PlaylistFileContents contents = LoadPlaylistFile(name_utf8, &error);
+ if (contents.empty() && error != nullptr) {
+ g_propagate_error(error_r, error);
return false;
+ }
- if (list->len < end_index)
- end_index = list->len;
+ if (end_index > contents.size())
+ end_index = contents.size();
for (unsigned i = start_index; i < end_index; ++i) {
- const char *temp = g_ptr_array_index(list, i);
- if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
+ const auto &uri_utf8 = contents[i];
+
+ if ((playlist_append_uri(playlist, pc, uri_utf8.c_str(),
+ nullptr)) != PLAYLIST_RESULT_SUCCESS) {
/* for windows compatibility, convert slashes */
- char *temp2 = g_strdup(temp);
+ char *temp2 = g_strdup(uri_utf8.c_str());
char *p = temp2;
while (*p) {
if (*p == '\\')
*p = '/';
p++;
}
- if ((playlist_append_uri(playlist, pc, temp2,
- NULL)) != PLAYLIST_RESULT_SUCCESS) {
+ if ((playlist_append_uri(playlist, pc, temp2, NULL)) != PLAYLIST_RESULT_SUCCESS) {
g_warning("can't add file \"%s\"", temp2);
}
g_free(temp2);
}
}
- spl_free(list);
return true;
}
diff --git a/src/playlist_save.h b/src/PlaylistSave.hxx
index a6c31a9a6..ff5f0c494 100644
--- a/src/playlist_save.h
+++ b/src/PlaylistSave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,7 +22,6 @@
#include "playlist_error.h"
-#include <stdbool.h>
#include <stdio.h>
struct song;
diff --git a/src/playlist_song.c b/src/PlaylistSong.cxx
index a3d9ab4d9..4bfcc5969 100644
--- a/src/playlist_song.c
+++ b/src/PlaylistSong.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,18 @@
*/
#include "config.h"
-#include "playlist_song.h"
-#include "database.h"
-#include "mapper.h"
+#include "PlaylistSong.hxx"
+#include "Mapper.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseGlue.hxx"
+#include "ls.hxx"
+
+extern "C" {
#include "song.h"
#include "uri.h"
#include "path.h"
-#include "ls.h"
#include "tag.h"
+}
#include <assert.h>
#include <string.h>
@@ -86,9 +90,7 @@ apply_song_metadata(struct song *dest, const struct song *src)
(e.g. last track on a CUE file); fix it up here */
tmp->tag->time = dest->tag->time - src->start_ms / 1000;
- if (!song_in_database(dest))
- song_free(dest);
-
+ song_free(dest);
return tmp;
}
@@ -104,10 +106,17 @@ playlist_check_load_song(const struct song *song, const char *uri, bool secure)
if (dest == NULL)
return NULL;
} else {
- dest = db_get_song(uri);
- if (dest == NULL)
+ const Database *db = GetDatabase(nullptr);
+ if (db == nullptr)
+ return nullptr;
+
+ struct song *tmp = db->GetSong(uri, nullptr);
+ if (tmp == NULL)
/* not found in database */
return NULL;
+
+ dest = song_dup_detached(tmp);
+ db->ReturnSong(tmp);
}
return apply_song_metadata(dest, song);
diff --git a/src/playlist_song.h b/src/PlaylistSong.hxx
index ea8786912..117ee1338 100644
--- a/src/playlist_song.h
+++ b/src/PlaylistSong.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_SONG_H
-#define MPD_PLAYLIST_SONG_H
-
-#include <stdbool.h>
+#ifndef MPD_PLAYLIST_SONG_HXX
+#define MPD_PLAYLIST_SONG_HXX
/**
* Verifies the song, returns NULL if it is unsafe. Translate the
diff --git a/src/playlist_state.c b/src/PlaylistState.cxx
index 4aa2c2c92..7e93e7803 100644
--- a/src/playlist_state.c
+++ b/src/PlaylistState.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,13 +23,15 @@
*/
#include "config.h"
-#include "playlist_state.h"
-#include "playlist.h"
-#include "player_control.h"
-#include "queue_save.h"
-#include "path.h"
-#include "text_file.h"
+#include "PlaylistState.hxx"
+#include "Playlist.hxx"
+#include "QueueSave.hxx"
+#include "TextFile.hxx"
+#include "PlayerControl.hxx"
+
+extern "C" {
#include "conf.h"
+}
#include <string.h>
#include <stdlib.h>
@@ -72,8 +74,7 @@ playlist_state_save(FILE *fp, const struct playlist *playlist,
fputs(PLAYLIST_STATE_FILE_STATE_PLAY "\n", fp);
}
fprintf(fp, PLAYLIST_STATE_FILE_CURRENT "%i\n",
- queue_order_to_position(&playlist->queue,
- playlist->current));
+ playlist->queue.OrderToPosition(playlist->current));
fprintf(fp, PLAYLIST_STATE_FILE_TIME "%i\n",
(int)player_status.elapsed_time);
} else {
@@ -81,8 +82,7 @@ playlist_state_save(FILE *fp, const struct playlist *playlist,
if (playlist->current >= 0)
fprintf(fp, PLAYLIST_STATE_FILE_CURRENT "%i\n",
- queue_order_to_position(&playlist->queue,
- playlist->current));
+ playlist->queue.OrderToPosition(playlist->current));
}
fprintf(fp, PLAYLIST_STATE_FILE_RANDOM "%i\n", playlist->queue.random);
@@ -102,18 +102,18 @@ playlist_state_save(FILE *fp, const struct playlist *playlist,
}
static void
-playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist)
+playlist_state_load(TextFile &file, struct playlist *playlist)
{
- const char *line = read_text_line(fp, buffer);
+ const char *line = file.ReadLine();
if (line == NULL) {
g_warning("No playlist in state file");
return;
}
while (!g_str_has_prefix(line, PLAYLIST_STATE_FILE_PLAYLIST_END)) {
- queue_load_song(fp, buffer, line, &playlist->queue);
+ queue_load_song(file, line, &playlist->queue);
- line = read_text_line(fp, buffer);
+ line = file.ReadLine();
if (line == NULL) {
g_warning("'" PLAYLIST_STATE_FILE_PLAYLIST_END
"' not found in state file");
@@ -121,11 +121,11 @@ playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist)
}
}
- queue_increment_version(&playlist->queue);
+ playlist->queue.IncrementVersion();
}
bool
-playlist_state_restore(const char *line, FILE *fp, GString *buffer,
+playlist_state_restore(const char *line, TextFile &file,
struct playlist *playlist, struct player_control *pc)
{
int current = -1;
@@ -143,7 +143,7 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
else if (strcmp(line, PLAYLIST_STATE_FILE_STATE_PAUSE) == 0)
state = PLAYER_STATE_PAUSE;
- while ((line = read_text_line(fp, buffer)) != NULL) {
+ while ((line = file.ReadLine()) != NULL) {
if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_TIME)) {
seek_time =
atoi(&(line[strlen(PLAYLIST_STATE_FILE_TIME)]));
@@ -187,14 +187,14 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
(PLAYLIST_STATE_FILE_CURRENT)]));
} else if (g_str_has_prefix(line,
PLAYLIST_STATE_FILE_PLAYLIST_BEGIN)) {
- playlist_state_load(fp, buffer, playlist);
+ playlist_state_load(file, playlist);
}
}
playlist_set_random(playlist, pc, random_mode);
- if (!queue_is_empty(&playlist->queue)) {
- if (!queue_valid_position(&playlist->queue, current))
+ if (!playlist->queue.IsEmpty()) {
+ if (!playlist->queue.IsValidPosition(current))
current = 0;
if (state == PLAYER_STATE_PLAY &&
@@ -237,8 +237,7 @@ playlist_state_get_hash(const struct playlist *playlist,
? ((int)player_status.elapsed_time << 8)
: 0) ^
(playlist->current >= 0
- ? (queue_order_to_position(&playlist->queue,
- playlist->current) << 16)
+ ? (playlist->queue.OrderToPosition(playlist->current) << 16)
: 0) ^
((int)pc_get_cross_fade(pc) << 20) ^
(player_status.state << 24) ^
diff --git a/src/playlist_state.h b/src/PlaylistState.hxx
index f67d01d2c..572f6fb4a 100644
--- a/src/playlist_state.h
+++ b/src/PlaylistState.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,22 +22,22 @@
*
*/
-#ifndef PLAYLIST_STATE_H
-#define PLAYLIST_STATE_H
+#ifndef MPD_PLAYLIST_STATE_HXX
+#define MPD_PLAYLIST_STATE_HXX
#include <glib.h>
-#include <stdbool.h>
#include <stdio.h>
struct playlist;
struct player_control;
+class TextFile;
void
playlist_state_save(FILE *fp, const struct playlist *playlist,
struct player_control *pc);
bool
-playlist_state_restore(const char *line, FILE *fp, GString *buffer,
+playlist_state_restore(const char *line, TextFile &file,
struct playlist *playlist, struct player_control *pc);
/**
diff --git a/src/PlaylistVector.cxx b/src/PlaylistVector.cxx
new file mode 100644
index 000000000..06c7b9ff0
--- /dev/null
+++ b/src/PlaylistVector.cxx
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PlaylistVector.hxx"
+#include "DatabaseLock.hxx"
+
+#include <algorithm>
+
+#include <assert.h>
+#include <string.h>
+#include <glib.h>
+
+PlaylistVector::iterator
+PlaylistVector::find(const char *name)
+{
+ assert(holding_db_lock());
+ assert(name != NULL);
+
+ return std::find_if(begin(), end(),
+ PlaylistInfo::CompareName(name));
+}
+
+bool
+PlaylistVector::UpdateOrInsert(PlaylistInfo &&pi)
+{
+ assert(holding_db_lock());
+
+ auto i = find(pi.name.c_str());
+ if (i != end()) {
+ if (pi.mtime == i->mtime)
+ return false;
+
+ i->mtime = pi.mtime;
+ } else
+ push_back(std::move(pi));
+
+ return true;
+}
+
+bool
+PlaylistVector::erase(const char *name)
+{
+ assert(holding_db_lock());
+
+ auto i = find(name);
+ if (i == end())
+ return false;
+
+ erase(i);
+ return true;
+}
diff --git a/src/PlaylistVector.hxx b/src/PlaylistVector.hxx
new file mode 100644
index 000000000..d10c90fda
--- /dev/null
+++ b/src/PlaylistVector.hxx
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_VECTOR_HXX
+#define MPD_PLAYLIST_VECTOR_HXX
+
+#include "PlaylistInfo.hxx"
+#include "gcc.h"
+
+#include <list>
+
+class PlaylistVector : protected std::list<PlaylistInfo> {
+protected:
+ /**
+ * Caller must lock the #db_mutex.
+ */
+ gcc_pure
+ iterator find(const char *name);
+
+public:
+ using std::list<PlaylistInfo>::empty;
+ using std::list<PlaylistInfo>::begin;
+ using std::list<PlaylistInfo>::end;
+ using std::list<PlaylistInfo>::push_back;
+ using std::list<PlaylistInfo>::erase;
+
+ /**
+ * Caller must lock the #db_mutex.
+ *
+ * @return true if the vector or one of its items was modified
+ */
+ bool UpdateOrInsert(PlaylistInfo &&pi);
+
+ /**
+ * Caller must lock the #db_mutex.
+ */
+ bool erase(const char *name);
+};
+
+#endif /* SONGVEC_H */
diff --git a/src/queue.c b/src/Queue.cxx
index 4fe564a35..1b329e485 100644
--- a/src/queue.c
+++ b/src/Queue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,39 @@
*/
#include "config.h"
-#include "queue.h"
+#include "Queue.hxx"
#include "song.h"
#include <stdlib.h>
+queue::queue(unsigned _max_length)
+ :max_length(_max_length), length(0),
+ version(1),
+ items(g_new(struct queue_item, max_length)),
+ order((unsigned *)g_malloc(sizeof(order[0]) * max_length)),
+ id_to_position((int *)g_malloc(sizeof(id_to_position[0]) *
+ max_length * QUEUE_HASH_MULT)),
+ repeat(false),
+ single(false),
+ consume(false),
+ random(false),
+ rand(g_rand_new())
+{
+ for (unsigned i = 0; i < max_length * QUEUE_HASH_MULT; ++i)
+ id_to_position[i] = -1;
+}
+
+queue::~queue()
+{
+ Clear();
+
+ g_free(items);
+ g_free(order);
+ g_free(id_to_position);
+
+ g_rand_free(rand);
+}
+
/**
* Generate a non-existing id number.
*/
@@ -42,15 +70,15 @@ queue_generate_id(const struct queue *queue)
}
int
-queue_next_order(const struct queue *queue, unsigned order)
+queue::GetNextOrder(unsigned _order) const
{
- assert(order < queue->length);
+ assert(_order < length);
- if (queue->single && queue->repeat && !queue->consume)
- return order;
- else if (order + 1 < queue->length)
- return order + 1;
- else if (queue->repeat && (order > 0 || !queue->consume))
+ if (single && repeat && !consume)
+ return _order;
+ else if (_order + 1 < length)
+ return _order + 1;
+ else if (repeat && (_order > 0 || !consume))
/* restart at first song */
return 0;
else
@@ -59,80 +87,74 @@ queue_next_order(const struct queue *queue, unsigned order)
}
void
-queue_increment_version(struct queue *queue)
+queue::IncrementVersion()
{
static unsigned long max = ((uint32_t) 1 << 31) - 1;
- queue->version++;
+ version++;
- if (queue->version >= max) {
- for (unsigned i = 0; i < queue->length; i++)
- queue->items[i].version = 0;
+ if (version >= max) {
+ for (unsigned i = 0; i < length; i++)
+ items[i].version = 0;
- queue->version = 1;
+ version = 1;
}
}
void
-queue_modify(struct queue *queue, unsigned order)
+queue::ModifyAtOrder(unsigned _order)
{
- unsigned position;
-
- assert(order < queue->length);
+ assert(_order < length);
- position = queue->order[order];
- queue->items[position].version = queue->version;
+ unsigned position = order[_order];
+ items[position].version = version;
- queue_increment_version(queue);
+ IncrementVersion();
}
void
-queue_modify_all(struct queue *queue)
+queue::ModifyAll()
{
- for (unsigned i = 0; i < queue->length; i++)
- queue->items[i].version = queue->version;
+ for (unsigned i = 0; i < length; i++)
+ items[i].version = version;
- queue_increment_version(queue);
+ IncrementVersion();
}
unsigned
-queue_append(struct queue *queue, struct song *song, uint8_t priority)
+queue::Append(struct song *song, uint8_t priority)
{
- unsigned id = queue_generate_id(queue);
+ unsigned id = queue_generate_id(this);
- assert(!queue_is_full(queue));
+ assert(!IsFull());
- queue->items[queue->length] = (struct queue_item){
- .song = song,
- .id = id,
- .version = queue->version,
- .priority = priority,
- };
+ auto &item = items[length];
+ item.song = song_dup_detached(song);
+ item.id = id;
+ item.version = version;
+ item.priority = priority;
- queue->order[queue->length] = queue->length;
- queue->id_to_position[id] = queue->length;
+ order[length] = length;
+ id_to_position[id] = length;
- ++queue->length;
+ ++length;
return id;
}
void
-queue_swap(struct queue *queue, unsigned position1, unsigned position2)
+queue::SwapPositions(unsigned position1, unsigned position2)
{
- struct queue_item tmp;
- unsigned id1 = queue->items[position1].id;
- unsigned id2 = queue->items[position2].id;
+ unsigned id1 = items[position1].id;
+ unsigned id2 = items[position2].id;
- tmp = queue->items[position1];
- queue->items[position1] = queue->items[position2];
- queue->items[position2] = tmp;
+ std::swap(items[position1], items[position2]);
- queue->items[position1].version = queue->version;
- queue->items[position2].version = queue->version;
+ items[position1].version = version;
+ items[position2].version = version;
- queue->id_to_position[id1] = position2;
- queue->id_to_position[id2] = position1;
+ id_to_position[id1] = position2;
+ id_to_position[id2] = position1;
}
static void
@@ -146,79 +168,79 @@ queue_move_song_to(struct queue *queue, unsigned from, unsigned to)
}
void
-queue_move(struct queue *queue, unsigned from, unsigned to)
+queue::MovePostion(unsigned from, unsigned to)
{
- struct queue_item item = queue->items[from];
+ struct queue_item item = items[from];
/* move songs to one less in from->to */
for (unsigned i = from; i < to; i++)
- queue_move_song_to(queue, i + 1, i);
+ queue_move_song_to(this, i + 1, i);
/* move songs to one more in to->from */
for (unsigned i = from; i > to; i--)
- queue_move_song_to(queue, i - 1, i);
+ queue_move_song_to(this, i - 1, i);
/* put song at _to_ */
- queue->id_to_position[item.id] = to;
- queue->items[to] = item;
- queue->items[to].version = queue->version;
+ id_to_position[item.id] = to;
+ items[to] = item;
+ items[to].version = version;
/* now deal with order */
- if (queue->random) {
- for (unsigned i = 0; i < queue->length; i++) {
- if (queue->order[i] > from && queue->order[i] <= to)
- queue->order[i]--;
- else if (queue->order[i] < from &&
- queue->order[i] >= to)
- queue->order[i]++;
- else if (from == queue->order[i])
- queue->order[i] = to;
+ if (random) {
+ for (unsigned i = 0; i < length; i++) {
+ if (order[i] > from && order[i] <= to)
+ order[i]--;
+ else if (order[i] < from &&
+ order[i] >= to)
+ order[i]++;
+ else if (from == order[i])
+ order[i] = to;
}
}
}
void
-queue_move_range(struct queue *queue, unsigned start, unsigned end, unsigned to)
+queue::MoveRange(unsigned start, unsigned end, unsigned to)
{
- struct queue_item items[end - start];
+ struct queue_item tmp[end - start];
// Copy the original block [start,end-1]
for (unsigned i = start; i < end; i++)
- items[i - start] = queue->items[i];
+ tmp[i - start] = items[i];
// If to > start, we need to move to-start items to start, starting from end
for (unsigned i = end; i < end + to - start; i++)
- queue_move_song_to(queue, i, start + i - end);
+ queue_move_song_to(this, i, start + i - end);
// If to < start, we need to move start-to items to newend (= end + to - start), starting from to
// This is the same as moving items from start-1 to to (decreasing), with start-1 going to end-1
// We have to iterate in this order to avoid writing over something we haven't yet moved
for (unsigned i = start - 1; i >= to && i != G_MAXUINT; i--)
- queue_move_song_to(queue, i, i + end - start);
+ queue_move_song_to(this, i, i + end - start);
// Copy the original block back in, starting at to.
for (unsigned i = start; i< end; i++)
{
- queue->id_to_position[items[i-start].id] = to + i - start;
- queue->items[to + i - start] = items[i-start];
- queue->items[to + i - start].version = queue->version;
+ id_to_position[tmp[i-start].id] = to + i - start;
+ items[to + i - start] = tmp[i-start];
+ items[to + i - start].version = version;
}
- if (queue->random) {
+ if (random) {
// Update the positions in the queue.
// Note that the ranges for these cases are the same as the ranges of
// the loops above.
- for (unsigned i = 0; i < queue->length; i++) {
- if (queue->order[i] >= end && queue->order[i] < to + end - start)
- queue->order[i] -= end - start;
- else if (queue->order[i] < start &&
- queue->order[i] >= to)
- queue->order[i] += end - start;
- else if (start <= queue->order[i] && queue->order[i] < end)
- queue->order[i] += to - start;
+ for (unsigned i = 0; i < length; i++) {
+ if (order[i] >= end && order[i] < to + end - start)
+ order[i] -= end - start;
+ else if (order[i] < start &&
+ order[i] >= to)
+ order[i] += end - start;
+ else if (start <= order[i] && order[i] < end)
+ order[i] += to - start;
}
}
}
@@ -233,8 +255,7 @@ queue_move_order(struct queue *queue, unsigned from_order, unsigned to_order)
assert(from_order < queue->length);
assert(to_order <= queue->length);
- const unsigned from_position =
- queue_order_to_position(queue, from_order);
+ const unsigned from_position = queue->OrderToPosition(from_order);
if (from_order < to_order) {
for (unsigned i = from_order; i < to_order; ++i)
@@ -248,91 +269,54 @@ queue_move_order(struct queue *queue, unsigned from_order, unsigned to_order)
}
void
-queue_delete(struct queue *queue, unsigned position)
+queue::DeletePosition(unsigned position)
{
- struct song *song;
- unsigned id, order;
+ assert(position < length);
- assert(position < queue->length);
+ struct song *song = Get(position);
+ assert(!song_in_database(song) || song_is_detached(song));
+ song_free(song);
- song = queue_get(queue, position);
- if (!song_in_database(song))
- song_free(song);
+ const unsigned id = PositionToId(position);
+ const unsigned _order = PositionToOrder(position);
- id = queue_position_to_id(queue, position);
- order = queue_position_to_order(queue, position);
-
- --queue->length;
+ --length;
/* release the song id */
- queue->id_to_position[id] = -1;
+ id_to_position[id] = -1;
/* delete song from songs array */
- for (unsigned i = position; i < queue->length; i++)
- queue_move_song_to(queue, i + 1, i);
+ for (unsigned i = position; i < length; i++)
+ queue_move_song_to(this, i + 1, i);
/* delete the entry from the order array */
- for (unsigned i = order; i < queue->length; i++)
- queue->order[i] = queue->order[i + 1];
+ for (unsigned i = _order; i < length; i++)
+ order[i] = order[i + 1];
/* readjust values in the order array */
- for (unsigned i = 0; i < queue->length; i++)
- if (queue->order[i] > position)
- --queue->order[i];
+ for (unsigned i = 0; i < length; i++)
+ if (order[i] > position)
+ --order[i];
}
void
-queue_clear(struct queue *queue)
+queue::Clear()
{
- for (unsigned i = 0; i < queue->length; i++) {
- struct queue_item *item = &queue->items[i];
+ for (unsigned i = 0; i < length; i++) {
+ struct queue_item *item = &items[i];
- if (!song_in_database(item->song))
- song_free(item->song);
+ assert(!song_in_database(item->song) ||
+ song_is_detached(item->song));
+ song_free(item->song);
- queue->id_to_position[item->id] = -1;
+ id_to_position[item->id] = -1;
}
- queue->length = 0;
-}
-
-void
-queue_init(struct queue *queue, unsigned max_length)
-{
- queue->max_length = max_length;
- queue->length = 0;
- queue->version = 1;
- queue->repeat = false;
- queue->random = false;
- queue->single = false;
- queue->consume = false;
-
- queue->items = g_new(struct queue_item, max_length);
- queue->order = g_malloc(sizeof(queue->order[0]) *
- max_length);
- queue->id_to_position = g_malloc(sizeof(queue->id_to_position[0]) *
- max_length * QUEUE_HASH_MULT);
-
- for (unsigned i = 0; i < max_length * QUEUE_HASH_MULT; ++i)
- queue->id_to_position[i] = -1;
-
- queue->rand = g_rand_new();
-}
-
-void
-queue_finish(struct queue *queue)
-{
- queue_clear(queue);
-
- g_free(queue->items);
- g_free(queue->order);
- g_free(queue->id_to_position);
-
- g_rand_free(queue->rand);
+ length = 0;
}
static const struct queue_item *
@@ -354,9 +338,9 @@ static gint
queue_item_compare_order_priority(gconstpointer av, gconstpointer bv,
gpointer user_data)
{
- const struct queue *queue = user_data;
- const unsigned *const ap = av;
- const unsigned *const bp = bv;
+ const struct queue *queue = (const struct queue *)user_data;
+ const unsigned *const ap = (const unsigned *)av;
+ const unsigned *const bp = (const unsigned *)bv;
assert(ap >= queue->order && ap < queue->order + queue->length);
assert(bp >= queue->order && bp < queue->order + queue->length);
uint8_t a = queue->items[*ap].priority;
@@ -397,8 +381,7 @@ queue_shuffle_order_range(struct queue *queue, unsigned start, unsigned end)
assert(end <= queue->length);
for (unsigned i = start; i < end; ++i)
- queue_swap_order(queue, i,
- g_rand_int_range(queue->rand, i, end));
+ queue->SwapOrders(i, g_rand_int_range(queue->rand, i, end));
}
/**
@@ -406,70 +389,66 @@ queue_shuffle_order_range(struct queue *queue, unsigned start, unsigned end)
* priority group.
*/
void
-queue_shuffle_order_range_with_priority(struct queue *queue,
- unsigned start, unsigned end)
+queue::ShuffleOrderRangeWithPriority(unsigned start, unsigned end)
{
- assert(queue != NULL);
- assert(queue->random);
+ assert(random);
assert(start <= end);
- assert(end <= queue->length);
+ assert(end <= length);
if (start == end)
return;
/* first group the range by priority */
- queue_sort_order_by_priority(queue, start, end);
+ queue_sort_order_by_priority(this, start, end);
/* now shuffle each priority group */
unsigned group_start = start;
- uint8_t group_priority = queue_get_order_priority(queue, start);
+ uint8_t group_priority = queue_get_order_priority(this, start);
for (unsigned i = start + 1; i < end; ++i) {
- uint8_t priority = queue_get_order_priority(queue, i);
+ uint8_t priority = queue_get_order_priority(this, i);
assert(priority <= group_priority);
if (priority != group_priority) {
/* start of a new group - shuffle the one that
has just ended */
- queue_shuffle_order_range(queue, group_start, i);
+ queue_shuffle_order_range(this, group_start, i);
group_start = i;
group_priority = priority;
}
}
/* shuffle the last group */
- queue_shuffle_order_range(queue, group_start, end);
+ queue_shuffle_order_range(this, group_start, end);
}
void
-queue_shuffle_order(struct queue *queue)
+queue::ShuffleOrder()
{
- queue_shuffle_order_range_with_priority(queue, 0, queue->length);
+ ShuffleOrderRangeWithPriority(0, length);
}
static void
queue_shuffle_order_first(struct queue *queue, unsigned start, unsigned end)
{
- queue_swap_order(queue, start,
- g_rand_int_range(queue->rand, start, end));
+ queue->SwapOrders(start, g_rand_int_range(queue->rand, start, end));
}
void
-queue_shuffle_order_last(struct queue *queue, unsigned start, unsigned end)
+queue::ShuffleOrderLast(unsigned start, unsigned end)
{
- queue_swap_order(queue, end - 1,
- g_rand_int_range(queue->rand, start, end));
+ SwapOrders(end - 1, g_rand_int_range(rand, start, end));
}
void
-queue_shuffle_range(struct queue *queue, unsigned start, unsigned end)
+queue::ShuffleRange(unsigned start, unsigned end)
{
assert(start <= end);
- assert(end <= queue->length);
+ assert(end <= length);
for (unsigned i = start; i < end; i++) {
- unsigned ri = g_rand_int_range(queue->rand, i, end);
- queue_swap(queue, i, ri);
+ unsigned ri = g_rand_int_range(rand, i, end);
+ SwapPositions(i, ri);
}
}
@@ -486,7 +465,7 @@ queue_find_priority_order(const struct queue *queue, unsigned start_order,
assert(start_order <= queue->length);
for (unsigned order = start_order; order < queue->length; ++order) {
- const unsigned position = queue_order_to_position(queue, order);
+ const unsigned position = queue->OrderToPosition(order);
const struct queue_item *item = &queue->items[position];
if (item->priority <= priority && order != exclude_order)
return order;
@@ -505,7 +484,7 @@ queue_count_same_priority(const struct queue *queue, unsigned start_order,
assert(start_order <= queue->length);
for (unsigned order = start_order; order < queue->length; ++order) {
- const unsigned position = queue_order_to_position(queue, order);
+ const unsigned position = queue->OrderToPosition(order);
const struct queue_item *item = &queue->items[position];
if (item->priority != priority)
return order - start_order;
@@ -515,39 +494,37 @@ queue_count_same_priority(const struct queue *queue, unsigned start_order,
}
bool
-queue_set_priority(struct queue *queue, unsigned position, uint8_t priority,
- int after_order)
+queue::SetPriority(unsigned position, uint8_t priority, int after_order)
{
- assert(queue != NULL);
- assert(position < queue->length);
+ assert(position < length);
- struct queue_item *item = &queue->items[position];
+ struct queue_item *item = &items[position];
uint8_t old_priority = item->priority;
if (old_priority == priority)
return false;
- item->version = queue->version;
+ item->version = version;
item->priority = priority;
- if (!queue->random)
+ if (!random)
/* don't reorder if not in random mode */
return true;
- unsigned order = queue_position_to_order(queue, position);
+ unsigned _order = PositionToOrder(position);
if (after_order >= 0) {
- if (order == (unsigned)after_order)
+ if (_order == (unsigned)after_order)
/* don't reorder the current song */
return true;
- if (order < (unsigned)after_order) {
+ if (_order < (unsigned)after_order) {
/* the specified song has been played already
- enqueue it only if its priority has just
become bigger than the current one's */
const unsigned after_position =
- queue_order_to_position(queue, after_order);
+ OrderToPosition(after_order);
const struct queue_item *after_item =
- &queue->items[after_position];
+ &items[after_position];
if (old_priority > after_item->priority ||
priority <= after_item->priority)
/* priority hasn't become bigger */
@@ -559,44 +536,41 @@ queue_set_priority(struct queue *queue, unsigned position, uint8_t priority,
create a new priority group) */
const unsigned before_order =
- queue_find_priority_order(queue, after_order + 1, priority,
- order);
- const unsigned new_order = before_order > order
+ queue_find_priority_order(this, after_order + 1, priority,
+ _order);
+ const unsigned new_order = before_order > _order
? before_order - 1
: before_order;
- queue_move_order(queue, order, new_order);
+ queue_move_order(this, _order, new_order);
/* shuffle the song within that priority group */
const unsigned priority_count =
- queue_count_same_priority(queue, new_order, priority);
+ queue_count_same_priority(this, new_order, priority);
assert(priority_count >= 1);
- queue_shuffle_order_first(queue, new_order,
+ queue_shuffle_order_first(this, new_order,
new_order + priority_count);
return true;
}
bool
-queue_set_priority_range(struct queue *queue,
- unsigned start_position, unsigned end_position,
- uint8_t priority, int after_order)
+queue::SetPriorityRange(unsigned start_position, unsigned end_position,
+ uint8_t priority, int after_order)
{
- assert(queue != NULL);
assert(start_position <= end_position);
- assert(end_position <= queue->length);
+ assert(end_position <= length);
bool modified = false;
int after_position = after_order >= 0
- ? (int)queue_order_to_position(queue, after_order)
+ ? (int)OrderToPosition(after_order)
: -1;
for (unsigned i = start_position; i < end_position; ++i) {
after_order = after_position >= 0
- ? (int)queue_position_to_order(queue, after_position)
+ ? (int)PositionToOrder(after_position)
: -1;
- modified |= queue_set_priority(queue, i, priority,
- after_order);
+ modified |= SetPriority(i, priority, after_order);
}
return modified;
diff --git a/src/Queue.hxx b/src/Queue.hxx
new file mode 100644
index 000000000..f52dd3335
--- /dev/null
+++ b/src/Queue.hxx
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_QUEUE_HXX
+#define MPD_QUEUE_HXX
+
+#include "gcc.h"
+
+#include <glib.h>
+
+#include <algorithm>
+
+#include <assert.h>
+#include <stdint.h>
+
+enum {
+ /**
+ * reserve max_length * QUEUE_HASH_MULT elements in the id
+ * number space
+ */
+ QUEUE_HASH_MULT = 4,
+};
+
+/**
+ * One element of the queue: basically a song plus some queue specific
+ * information attached.
+ */
+struct queue_item {
+ struct song *song;
+
+ /** the unique id of this item in the queue */
+ unsigned id;
+
+ /** when was this item last changed? */
+ uint32_t version;
+
+ /**
+ * The priority of this item, between 0 and 255. High
+ * priority value means that this song gets played first in
+ * "random" mode.
+ */
+ uint8_t priority;
+};
+
+/**
+ * A queue of songs. This is the backend of the playlist: it contains
+ * an ordered list of songs.
+ *
+ * Songs can be addressed in three possible ways:
+ *
+ * - the position in the queue
+ * - the unique id (which stays the same, regardless of moves)
+ * - the order number (which only differs from "position" in random mode)
+ */
+struct queue {
+ /** configured maximum length of the queue */
+ unsigned max_length;
+
+ /** number of songs in the queue */
+ unsigned length;
+
+ /** the current version number */
+ uint32_t version;
+
+ /** all songs in "position" order */
+ struct queue_item *items;
+
+ /** map order numbers to positions */
+ unsigned *order;
+
+ /** map song ids to positions */
+ int *id_to_position;
+
+ /** repeat playback when the end of the queue has been
+ reached? */
+ bool repeat;
+
+ /** play only current song. */
+ bool single;
+
+ /** remove each played files. */
+ bool consume;
+
+ /** play back songs in random order? */
+ bool random;
+
+ /** random number generator for shuffle and random mode */
+ GRand *rand;
+
+ queue(unsigned max_length);
+
+ /**
+ * Deinitializes a queue object. It does not free the queue
+ * pointer itself.
+ */
+ ~queue();
+
+ queue(const queue &other) = delete;
+ queue &operator=(const queue &other) = delete;
+
+ unsigned GetLength() const {
+ assert(length <= max_length);
+
+ return length;
+ }
+
+ /**
+ * Determine if the queue is empty, i.e. there are no songs.
+ */
+ bool IsEmpty() const {
+ return length == 0;
+ }
+
+ /**
+ * Determine if the maximum number of songs has been reached.
+ */
+ bool IsFull() const {
+ assert(length <= max_length);
+
+ return length >= max_length;
+ }
+
+ /**
+ * Is that a valid position number?
+ */
+ bool IsValidPosition(unsigned position) const {
+ return position < length;
+ }
+
+ /**
+ * Is that a valid order number?
+ */
+ bool IsValidOrder(unsigned _order) const {
+ return _order < length;
+ }
+
+ int IdToPosition(unsigned id) const {
+ if (id >= max_length * QUEUE_HASH_MULT)
+ return -1;
+
+ assert(id_to_position[id] >= -1);
+ assert(id_to_position[id] < (int)length);
+
+ return id_to_position[id];
+ }
+
+ int PositionToId(unsigned position) const
+ {
+ assert(position < length);
+
+ return items[position].id;
+ }
+
+ gcc_pure
+ unsigned OrderToPosition(unsigned _order) const {
+ assert(_order < length);
+
+ return order[_order];
+ }
+
+ gcc_pure
+ unsigned PositionToOrder(unsigned position) const {
+ assert(position < length);
+
+ for (unsigned i = 0;; ++i) {
+ assert(i < length);
+
+ if (order[i] == position)
+ return i;
+ }
+ }
+
+ G_GNUC_PURE
+ uint8_t GetPriorityAtPosition(unsigned position) const {
+ assert(position < length);
+
+ return items[position].priority;
+ }
+
+ /**
+ * Returns the song at the specified position.
+ */
+ struct song *Get(unsigned position) const {
+ assert(position < length);
+
+ return items[position].song;
+ }
+
+ /**
+ * Returns the song at the specified order number.
+ */
+ struct song *GetOrder(unsigned _order) const {
+ return Get(OrderToPosition(_order));
+ }
+
+ /**
+ * Is the song at the specified position newer than the specified
+ * version?
+ */
+ bool IsNewerAtPosition(unsigned position, uint32_t _version) const {
+ assert(position < length);
+
+ return _version > version ||
+ items[position].version >= _version ||
+ items[position].version == 0;
+ }
+
+ /**
+ * Returns the order number following the specified one. This takes
+ * end of queue and "repeat" mode into account.
+ *
+ * @return the next order number, or -1 to stop playback
+ */
+ gcc_pure
+ int GetNextOrder(unsigned order) const;
+
+ /**
+ * Increments the queue's version number. This handles integer
+ * overflow well.
+ */
+ void IncrementVersion();
+
+ /**
+ * Marks the specified song as "modified" and increments the version
+ * number.
+ */
+ void ModifyAtOrder(unsigned order);
+
+ /**
+ * Marks all songs as "modified" and increments the version number.
+ */
+ void ModifyAll();
+
+ /**
+ * Appends a song to the queue and returns its position. Prior to
+ * that, the caller must check if the queue is already full.
+ *
+ * If a song is not in the database (determined by
+ * song_in_database()), it is freed when removed from the queue.
+ *
+ * @param priority the priority of this new queue item
+ */
+ unsigned Append(struct song *song, uint8_t priority);
+
+ /**
+ * Swaps two songs, addressed by their position.
+ */
+ void SwapPositions(unsigned position1, unsigned position2);
+
+ /**
+ * Swaps two songs, addressed by their order number.
+ */
+ void SwapOrders(unsigned order1, unsigned order2) {
+ std::swap(order[order1], order[order2]);
+ }
+
+ /**
+ * Moves a song to a new position.
+ */
+ void MovePostion(unsigned from, unsigned to);
+
+ /**
+ * Moves a range of songs to a new position.
+ */
+ void MoveRange(unsigned start, unsigned end, unsigned to);
+
+ /**
+ * Removes a song from the playlist.
+ */
+ void DeletePosition(unsigned position);
+
+ /**
+ * Removes all songs from the playlist.
+ */
+ void Clear();
+
+ /**
+ * Initializes the "order" array, and restores "normal" order.
+ */
+ void RestoreOrder() {
+ for (unsigned i = 0; i < length; ++i)
+ order[i] = i;
+ }
+
+ /**
+ * Shuffle the order of items in the specified range, taking their
+ * priorities into account.
+ */
+ void ShuffleOrderRangeWithPriority(unsigned start, unsigned end);
+
+ /**
+ * Shuffles the virtual order of songs, but does not move them
+ * physically. This is used in random mode.
+ */
+ void ShuffleOrder();
+
+ /**
+ * Shuffles the virtual order of the last song in the specified
+ * (order) range. This is used in random mode after a song has been
+ * appended by queue_append().
+ */
+ void ShuffleOrderLast(unsigned start, unsigned end);
+
+ /**
+ * Shuffles a (position) range in the queue. The songs are physically
+ * shuffled, not by using the "order" mapping.
+ */
+ void ShuffleRange(unsigned start, unsigned end);
+
+ bool SetPriority(unsigned position, uint8_t priority, int after_order);
+
+ bool SetPriorityRange(unsigned start_position, unsigned end_position,
+ uint8_t priority, int after_order);
+};
+
+#endif
diff --git a/src/QueueCommands.cxx b/src/QueueCommands.cxx
new file mode 100644
index 000000000..3e24f0c92
--- /dev/null
+++ b/src/QueueCommands.cxx
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "QueueCommands.hxx"
+#include "CommandError.hxx"
+#include "DatabaseQueue.hxx"
+#include "SongFilter.hxx"
+#include "Playlist.hxx"
+#include "PlaylistPrint.hxx"
+#include "ClientFile.hxx"
+#include "ClientInternal.hxx"
+#include "protocol/ArgParser.hxx"
+#include "protocol/Result.hxx"
+#include "ls.hxx"
+
+extern "C" {
+#include "uri.h"
+}
+
+#include <string.h>
+
+enum command_return
+handle_add(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ char *uri = argv[1];
+ enum playlist_result result;
+
+ if (strncmp(uri, "file:///", 8) == 0) {
+ const char *path = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path, &error))
+ return print_error(client, error);
+
+ result = playlist_append_file(&client->playlist,
+ client->player_control,
+ path,
+ NULL);
+ return print_playlist_result(client, result);
+ }
+
+ if (uri_has_scheme(uri)) {
+ if (!uri_supported_scheme(uri)) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "unsupported URI scheme");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ result = playlist_append_uri(&client->playlist,
+ client->player_control,
+ uri, NULL);
+ return print_playlist_result(client, result);
+ }
+
+ GError *error = NULL;
+ return findAddIn(client->playlist, client->player_control,
+ uri, nullptr, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_addid(Client *client, int argc, char *argv[])
+{
+ char *uri = argv[1];
+ unsigned added_id;
+ enum playlist_result result;
+
+ if (strncmp(uri, "file:///", 8) == 0) {
+ const char *path = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path, &error))
+ return print_error(client, error);
+
+ result = playlist_append_file(&client->playlist,
+ client->player_control,
+ path,
+ &added_id);
+ } else {
+ if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "unsupported URI scheme");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ result = playlist_append_uri(&client->playlist,
+ client->player_control,
+ uri, &added_id);
+ }
+
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+
+ if (argc == 3) {
+ unsigned to;
+ if (!check_unsigned(client, &to, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ result = playlist_move_id(&client->playlist,
+ client->player_control,
+ added_id, to);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ enum command_return ret =
+ print_playlist_result(client, result);
+ playlist_delete_id(&client->playlist,
+ client->player_control,
+ added_id);
+ return ret;
+ }
+ }
+
+ client_printf(client, "Id: %u\n", added_id);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_delete(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned start, end;
+ enum playlist_result result;
+
+ if (!check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ result = playlist_delete_range(&client->playlist,
+ client->player_control,
+ start, end);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_deleteid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id;
+ enum playlist_result result;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ result = playlist_delete_id(&client->playlist,
+ client->player_control, id);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_playlist(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist_print_uris(client, &client->playlist);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_shuffle(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ unsigned start = 0, end = client->playlist.queue.GetLength();
+ if (argc == 2 && !check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_shuffle(&client->playlist, client->player_control,
+ start, end);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_clear(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist_clear(&client->playlist, client->player_control);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_plchanges(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ uint32_t version;
+
+ if (!check_uint32(client, &version, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_print_changes_info(client, &client->playlist, version);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_plchangesposid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ uint32_t version;
+
+ if (!check_uint32(client, &version, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_print_changes_position(client, &client->playlist, version);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_playlistinfo(Client *client, int argc, char *argv[])
+{
+ unsigned start = 0, end = G_MAXUINT;
+ bool ret;
+
+ if (argc == 2 && !check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ ret = playlist_print_info(client, &client->playlist, start, end);
+ if (!ret)
+ return print_playlist_result(client,
+ PLAYLIST_RESULT_BAD_RANGE);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_playlistid(Client *client, int argc, char *argv[])
+{
+ if (argc >= 2) {
+ unsigned id;
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ bool ret = playlist_print_id(client, &client->playlist, id);
+ if (!ret)
+ return print_playlist_result(client,
+ PLAYLIST_RESULT_NO_SUCH_SONG);
+ } else {
+ playlist_print_info(client, &client->playlist, 0, G_MAXUINT);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_playlist_match(Client *client, int argc, char *argv[],
+ bool fold_case)
+{
+ SongFilter filter;
+ if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ playlist_print_find(client, &client->playlist, filter);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_playlistfind(Client *client, int argc, char *argv[])
+{
+ return handle_playlist_match(client, argc, argv, false);
+}
+
+enum command_return
+handle_playlistsearch(Client *client, int argc, char *argv[])
+{
+ return handle_playlist_match(client, argc, argv, true);
+}
+
+enum command_return
+handle_prio(Client *client, int argc, char *argv[])
+{
+ unsigned priority;
+
+ if (!check_unsigned(client, &priority, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (priority > 0xff) {
+ command_error(client, ACK_ERROR_ARG,
+ "Priority out of range: %s", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ unsigned start_position, end_position;
+ if (!check_range(client, &start_position, &end_position,
+ argv[i]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ playlist_set_priority(&client->playlist,
+ client->player_control,
+ start_position, end_position,
+ priority);
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_prioid(Client *client, int argc, char *argv[])
+{
+ unsigned priority;
+
+ if (!check_unsigned(client, &priority, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (priority > 0xff) {
+ command_error(client, ACK_ERROR_ARG,
+ "Priority out of range: %s", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ unsigned song_id;
+ if (!check_unsigned(client, &song_id, argv[i]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ playlist_set_priority_id(&client->playlist,
+ client->player_control,
+ song_id, priority);
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_move(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned start, end;
+ int to;
+ enum playlist_result result;
+
+ if (!check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_int(client, &to, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ result = playlist_move_range(&client->playlist, client->player_control,
+ start, end, to);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_moveid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id;
+ int to;
+ enum playlist_result result;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_int(client, &to, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ result = playlist_move_id(&client->playlist, client->player_control,
+ id, to);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_swap(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned song1, song2;
+ enum playlist_result result;
+
+ if (!check_unsigned(client, &song1, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &song2, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ result = playlist_swap_songs(&client->playlist, client->player_control,
+ song1, song2);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_swapid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id1, id2;
+ enum playlist_result result;
+
+ if (!check_unsigned(client, &id1, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &id2, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ result = playlist_swap_songs_id(&client->playlist,
+ client->player_control,
+ id1, id2);
+ return print_playlist_result(client, result);
+}
diff --git a/src/QueueCommands.hxx b/src/QueueCommands.hxx
new file mode 100644
index 000000000..97b61e212
--- /dev/null
+++ b/src/QueueCommands.hxx
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003-2012 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_QUEUE_COMMANDS_HXX
+#define MPD_QUEUE_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_add(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_addid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_delete(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_deleteid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlist(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_shuffle(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_clear(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_plchanges(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_plchangesposid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistinfo(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistfind(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistsearch(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_prio(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_prioid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_move(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_moveid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_swap(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_swapid(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/QueuePrint.cxx b/src/QueuePrint.cxx
new file mode 100644
index 000000000..28abc5a8c
--- /dev/null
+++ b/src/QueuePrint.cxx
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "QueuePrint.hxx"
+#include "Queue.hxx"
+#include "SongFilter.hxx"
+#include "SongPrint.hxx"
+#include "Mapper.hxx"
+#include "Client.hxx"
+
+extern "C" {
+#include "song.h"
+}
+
+/**
+ * Send detailed information about a range of songs in the queue to a
+ * client.
+ *
+ * @param client the client which has requested information
+ * @param start the index of the first song (including)
+ * @param end the index of the last song (excluding)
+ */
+static void
+queue_print_song_info(Client *client, const struct queue *queue,
+ unsigned position)
+{
+ song_print_info(client, queue->Get(position));
+ client_printf(client, "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);
+}
+
+void
+queue_print_info(Client *client, const struct 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);
+}
+
+void
+queue_print_uris(Client *client, const struct 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));
+ }
+}
+
+void
+queue_print_changes_info(Client *client, const struct 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);
+ }
+}
+
+void
+queue_print_changes_position(Client *client, const struct 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));
+}
+
+void
+queue_find(Client *client, const struct queue *queue,
+ const SongFilter &filter)
+{
+ for (unsigned i = 0; i < queue->GetLength(); i++) {
+ const struct song *song = queue->Get(i);
+
+ if (filter.Match(*song))
+ queue_print_song_info(client, queue, i);
+ }
+}
diff --git a/src/queue_print.h b/src/QueuePrint.hxx
index 371e20416..6b3a29fb6 100644
--- a/src/queue_print.h
+++ b/src/QueuePrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,37 +22,33 @@
* client.
*/
-#ifndef QUEUE_PRINT_H
-#define QUEUE_PRINT_H
+#ifndef MPD_QUEUE_PRINT_HXX
+#define MPD_QUEUE_PRINT_HXX
#include <stdint.h>
-struct client;
struct queue;
-struct locate_item_list;
+class SongFilter;
+class Client;
void
-queue_print_info(struct client *client, const struct queue *queue,
+queue_print_info(Client *client, const struct queue *queue,
unsigned start, unsigned end);
void
-queue_print_uris(struct client *client, const struct queue *queue,
+queue_print_uris(Client *client, const struct queue *queue,
unsigned start, unsigned end);
void
-queue_print_changes_info(struct client *client, const struct queue *queue,
+queue_print_changes_info(Client *client, const struct queue *queue,
uint32_t version);
void
-queue_print_changes_position(struct client *client, const struct queue *queue,
+queue_print_changes_position(Client *client, const struct queue *queue,
uint32_t version);
void
-queue_search(struct client *client, const struct queue *queue,
- const struct locate_item_list *criteria);
-
-void
-queue_find(struct client *client, const struct queue *queue,
- const struct locate_item_list *criteria);
+queue_find(Client *client, const struct queue *queue,
+ const SongFilter &filter);
#endif
diff --git a/src/queue_save.c b/src/QueueSave.cxx
index 16852d3c1..09b0645f8 100644
--- a/src/queue_save.c
+++ b/src/QueueSave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,17 @@
*/
#include "config.h"
-#include "queue_save.h"
-#include "queue.h"
+#include "QueueSave.hxx"
+#include "Playlist.hxx"
#include "song.h"
+#include "SongSave.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseGlue.hxx"
+#include "TextFile.hxx"
+
+extern "C" {
#include "uri.h"
-#include "database.h"
-#include "song_save.h"
-#include "text_file.h"
+}
#include <stdlib.h>
@@ -57,48 +61,40 @@ queue_save_song(FILE *fp, int idx, const struct song *song)
void
queue_save(FILE *fp, const struct queue *queue)
{
- for (unsigned i = 0; i < queue_length(queue); i++) {
- uint8_t prio = queue_get_priority_at_position(queue, i);
+ for (unsigned i = 0; i < queue->GetLength(); i++) {
+ uint8_t prio = queue->GetPriorityAtPosition(i);
if (prio != 0)
fprintf(fp, PRIO_LABEL "%u\n", prio);
- queue_save_song(fp, i, queue_get(queue, i));
+ queue_save_song(fp, i, queue->Get(i));
}
}
-static struct song *
-get_song(const char *uri)
-{
- return uri_has_scheme(uri)
- ? song_remote_new(uri)
- : db_get_song(uri);
-}
-
void
-queue_load_song(FILE *fp, GString *buffer, const char *line,
- struct queue *queue)
+queue_load_song(TextFile &file, const char *line, queue *queue)
{
- struct song *song;
-
- if (queue_is_full(queue))
+ if (queue->IsFull())
return;
uint8_t priority = 0;
if (g_str_has_prefix(line, PRIO_LABEL)) {
priority = strtoul(line + sizeof(PRIO_LABEL) - 1, NULL, 10);
- line = read_text_line(fp, buffer);
+ line = file.ReadLine();
if (line == NULL)
return;
}
+ const Database *db = nullptr;
+ struct song *song;
+
if (g_str_has_prefix(line, SONG_BEGIN)) {
const char *uri = line + sizeof(SONG_BEGIN) - 1;
if (!uri_has_scheme(uri) && !g_path_is_absolute(uri))
return;
GError *error = NULL;
- song = song_load(fp, NULL, uri, buffer, &error);
+ song = song_load(file, NULL, uri, &error);
if (song == NULL) {
g_warning("%s", error->message);
g_error_free(error);
@@ -112,12 +108,23 @@ queue_load_song(FILE *fp, GString *buffer, const char *line,
return;
}
- line = endptr + 1;
+ const char *uri = endptr + 1;
- song = get_song(line);
- if (song == NULL)
- return;
+ if (uri_has_scheme(uri)) {
+ song = song_remote_new(uri);
+ } else {
+ db = GetDatabase(nullptr);
+ if (db == nullptr)
+ return;
+
+ song = db->GetSong(uri, nullptr);
+ if (song == nullptr)
+ return;
+ }
}
- queue_append(queue, song, priority);
+ queue->Append(song, priority);
+
+ if (db != nullptr)
+ db->ReturnSong(song);
}
diff --git a/src/queue_save.h b/src/QueueSave.hxx
index 5526d615d..dc4a764a9 100644
--- a/src/queue_save.h
+++ b/src/QueueSave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,13 +22,14 @@
* back into memory.
*/
-#ifndef QUEUE_SAVE_H
-#define QUEUE_SAVE_H
+#ifndef MPD_QUEUE_SAVE_HXX
+#define MPD_QUEUE_SAVE_HXX
#include <glib.h>
#include <stdio.h>
struct queue;
+class TextFile;
void
queue_save(FILE *fp, const struct queue *queue);
@@ -37,7 +38,6 @@ queue_save(FILE *fp, const struct queue *queue);
* Loads one song from the state file and appends it to the queue.
*/
void
-queue_load_song(FILE *fp, GString *buffer, const char *line,
- struct queue *queue);
+queue_load_song(TextFile &file, const char *line, queue *queue);
#endif
diff --git a/src/replay_gain_config.c b/src/ReplayGainConfig.cxx
index 2181387b7..d3af332ce 100644
--- a/src/replay_gain_config.c
+++ b/src/ReplayGainConfig.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,14 @@
*/
#include "config.h"
+
+extern "C" {
#include "replay_gain_config.h"
-#include "playlist.h"
#include "conf.h"
#include "idle.h"
+}
+
+#include "Playlist.hxx"
#include "mpd_error.h"
#include <glib.h>
@@ -31,11 +35,6 @@
#include <string.h>
#include <math.h>
-static const char *const replay_gain_mode_names[] = {
- [REPLAY_GAIN_ALBUM] = "album",
- [REPLAY_GAIN_TRACK] = "track",
-};
-
enum replay_gain_mode replay_gain_mode = REPLAY_GAIN_OFF;
const bool DEFAULT_REPLAYGAIN_LIMIT = true;
@@ -137,14 +136,15 @@ void replay_gain_global_init(void)
replay_gain_limit = config_get_bool(CONF_REPLAYGAIN_LIMIT, DEFAULT_REPLAYGAIN_LIMIT);
}
-enum replay_gain_mode replay_gain_get_real_mode(void)
+enum replay_gain_mode
+replay_gain_get_real_mode(bool random_mode)
{
enum replay_gain_mode rgm;
rgm = replay_gain_mode;
if (rgm == REPLAY_GAIN_AUTO)
- rgm = g_playlist.queue.random ? REPLAY_GAIN_TRACK : REPLAY_GAIN_ALBUM;
+ rgm = random_mode ? REPLAY_GAIN_TRACK : REPLAY_GAIN_ALBUM;
return rgm;
}
diff --git a/src/replay_gain_info.c b/src/ReplayGainInfo.cxx
index 1f09e7a1a..5e5e4ab4b 100644
--- a/src/replay_gain_info.c
+++ b/src/ReplayGainInfo.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,10 @@
*/
#include "config.h"
+
+extern "C" {
#include "replay_gain_info.h"
+}
float
replay_gain_tuple_scale(const struct replay_gain_tuple *tuple, float preamp, float missing_preamp, bool peak_limit)
diff --git a/src/song.c b/src/Song.cxx
index f5cc7c35a..c845384ae 100644
--- a/src/song.c
+++ b/src/Song.cxx
@@ -19,26 +19,31 @@
#include "config.h"
#include "song.h"
-#include "uri.h"
-#include "directory.h"
+#include "Directory.hxx"
+
+extern "C" {
#include "tag.h"
+}
#include <glib.h>
#include <assert.h>
+Directory detached_root;
+
static struct song *
-song_alloc(const char *uri, struct directory *parent)
+song_alloc(const char *uri, Directory *parent)
{
size_t uri_length;
- struct song *song;
assert(uri);
uri_length = strlen(uri);
assert(uri_length);
- song = g_malloc(sizeof(*song) - sizeof(song->uri) + uri_length + 1);
- song->tag = NULL;
+ struct song *song = (struct song *)
+ g_malloc(sizeof(*song) - sizeof(song->uri) + uri_length + 1);
+
+ song->tag = nullptr;
memcpy(song->uri, uri, uri_length + 1);
song->parent = parent;
song->mtime = 0;
@@ -50,13 +55,13 @@ song_alloc(const char *uri, struct directory *parent)
struct song *
song_remote_new(const char *uri)
{
- return song_alloc(uri, NULL);
+ return song_alloc(uri, nullptr);
}
struct song *
-song_file_new(const char *path, struct directory *parent)
+song_file_new(const char *path, Directory *parent)
{
- assert((parent == NULL) == (*path == '/'));
+ assert((parent == nullptr) == (*path == '/'));
return song_alloc(path, parent);
}
@@ -73,6 +78,35 @@ song_replace_uri(struct song *old_song, const char *uri)
return new_song;
}
+struct song *
+song_detached_new(const char *uri)
+{
+ assert(uri != nullptr);
+
+ return song_alloc(uri, &detached_root);
+}
+
+struct song *
+song_dup_detached(const struct song *src)
+{
+ assert(src != nullptr);
+
+ struct song *song;
+ if (song_in_database(src)) {
+ char *uri = song_get_uri(src);
+ song = song_detached_new(uri);
+ g_free(uri);
+ } else
+ song = song_alloc(src->uri, nullptr);
+
+ song->tag = tag_dup(src->tag);
+ song->mtime = src->mtime;
+ song->start_ms = src->start_ms;
+ song->end_ms = src->end_ms;
+
+ return song;
+}
+
void
song_free(struct song *song)
{
@@ -81,17 +115,57 @@ song_free(struct song *song)
g_free(song);
}
+gcc_pure
+static inline bool
+directory_equals(const Directory &a, const Directory &b)
+{
+ return strcmp(a.path, b.path) == 0;
+}
+
+gcc_pure
+static inline bool
+directory_is_same(const Directory *a, const Directory *b)
+{
+ return a == b ||
+ (a != nullptr && b != nullptr &&
+ directory_equals(*a, *b));
+
+}
+
+bool
+song_equals(const struct song *a, const struct song *b)
+{
+ assert(a != nullptr);
+ assert(b != nullptr);
+
+ if (a->parent != nullptr && b->parent != nullptr &&
+ !directory_equals(*a->parent, *b->parent) &&
+ (a->parent == &detached_root || b->parent == &detached_root)) {
+ /* must compare the full URI if one of the objects is
+ "detached" */
+ char *au = song_get_uri(a);
+ char *bu = song_get_uri(b);
+ const bool result = strcmp(au, bu) == 0;
+ g_free(bu);
+ g_free(au);
+ return result;
+ }
+
+ return directory_is_same(a->parent, b->parent) &&
+ strcmp(a->uri, b->uri) == 0;
+}
+
char *
song_get_uri(const struct song *song)
{
- assert(song != NULL);
+ assert(song != nullptr);
assert(*song->uri);
- if (!song_in_database(song) || directory_is_root(song->parent))
+ if (!song_in_database(song) || song->parent->IsRoot())
return g_strdup(song->uri);
else
- return g_strconcat(directory_get_path(song->parent),
- "/", song->uri, NULL);
+ return g_strconcat(song->parent->GetPath(),
+ "/", song->uri, nullptr);
}
double
@@ -100,7 +174,7 @@ song_get_duration(const struct song *song)
if (song->end_ms > 0)
return (song->end_ms - song->start_ms) / 1000.0;
- if (song->tag == NULL)
+ if (song->tag == nullptr)
return 0;
return song->tag->time - song->start_ms / 1000.0;
diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx
new file mode 100644
index 000000000..6803b453e
--- /dev/null
+++ b/src/SongFilter.cxx
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2003-2012 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 "SongFilter.hxx"
+#include "path.h"
+#include "song.h"
+
+extern "C" {
+#include "tag.h"
+}
+
+#include <glib.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+#define LOCATE_TAG_FILE_KEY "file"
+#define LOCATE_TAG_FILE_KEY_OLD "filename"
+#define LOCATE_TAG_ANY_KEY "any"
+
+unsigned
+locate_parse_type(const char *str)
+{
+ if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_FILE_KEY) ||
+ 0 == g_ascii_strcasecmp(str, LOCATE_TAG_FILE_KEY_OLD))
+ return LOCATE_TAG_FILE_TYPE;
+
+ if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_ANY_KEY))
+ return LOCATE_TAG_ANY_TYPE;
+
+ return tag_name_parse_i(str);
+}
+
+SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
+ :tag(_tag), fold_case(_fold_case),
+ value(fold_case
+ ? g_utf8_casefold(_value, -1)
+ : g_strdup(_value))
+{
+}
+
+SongFilter::Item::~Item()
+{
+ g_free(value);
+}
+
+bool
+SongFilter::Item::StringMatch(const char *s) const
+{
+ assert(value != nullptr);
+ assert(s != nullptr);
+
+ if (fold_case) {
+ char *p = g_utf8_casefold(s, -1);
+ const bool result = strstr(p, value) != NULL;
+ g_free(p);
+ return result;
+ } else {
+ return strcmp(s, value) == 0;
+ }
+}
+
+bool
+SongFilter::Item::Match(const tag_item &item) const
+{
+ return (tag == LOCATE_TAG_ANY_TYPE || (unsigned)item.type == tag) &&
+ StringMatch(item.value);
+}
+
+bool
+SongFilter::Item::Match(const struct tag &_tag) const
+{
+ bool visited_types[TAG_NUM_OF_ITEM_TYPES];
+ std::fill(visited_types, visited_types + TAG_NUM_OF_ITEM_TYPES, false);
+
+ for (unsigned i = 0; i < _tag.num_items; i++) {
+ visited_types[_tag.items[i]->type] = true;
+
+ if (Match(*_tag.items[i]))
+ return true;
+ }
+
+ /** If the search critieron was not visited during the sweep
+ * through the song's tag, it means this field is absent from
+ * the tag or empty. Thus, if the searched string is also
+ * empty (first char is a \0), then it's a match as well and
+ * we should return true.
+ */
+ if (*value == 0 && tag < TAG_NUM_OF_ITEM_TYPES &&
+ !visited_types[tag])
+ return true;
+
+ return false;
+}
+
+bool
+SongFilter::Item::Match(const song &song) const
+{
+ if (tag == LOCATE_TAG_FILE_TYPE || tag == LOCATE_TAG_ANY_TYPE) {
+ char *uri = song_get_uri(&song);
+ const bool result = StringMatch(uri);
+ g_free(uri);
+
+ if (result || tag == LOCATE_TAG_FILE_TYPE)
+ return result;
+ }
+
+ return song.tag != NULL && Match(*song.tag);
+}
+
+SongFilter::SongFilter(unsigned tag, const char *value, bool fold_case)
+{
+ items.push_back(Item(tag, value, fold_case));
+}
+
+SongFilter::~SongFilter()
+{
+ /* this destructor exists here just so it won't get inlined */
+}
+
+bool
+SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
+{
+ unsigned tag = locate_parse_type(tag_string);
+ if (tag == TAG_NUM_OF_ITEM_TYPES)
+ return false;
+
+ items.push_back(Item(tag, value, fold_case));
+ return true;
+}
+
+bool
+SongFilter::Parse(unsigned argc, char *argv[], bool fold_case)
+{
+ if (argc == 0 || argc % 2 != 0)
+ return false;
+
+ for (unsigned i = 0; i < argc; i += 2)
+ if (!Parse(argv[i], argv[i + 1], fold_case))
+ return false;
+
+ return true;
+}
+
+bool
+SongFilter::Match(const song &song) const
+{
+ for (const auto &i : items)
+ if (!i.Match(song))
+ return false;
+
+ return true;
+}
diff --git a/src/SongFilter.hxx b/src/SongFilter.hxx
new file mode 100644
index 000000000..afec81300
--- /dev/null
+++ b/src/SongFilter.hxx
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2003-2012 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_SONG_FILTER_HXX
+#define MPD_SONG_FILTER_HXX
+
+#include "gcc.h"
+
+#include <list>
+
+#include <stdint.h>
+
+#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10
+#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
+
+struct tag;
+struct tag_item;
+struct song;
+
+class SongFilter {
+ class Item {
+ uint8_t tag;
+
+ bool fold_case;
+
+ char *value;
+
+ public:
+ gcc_nonnull(3)
+ Item(unsigned tag, const char *value, bool fold_case=false);
+
+ Item(const Item &other) = delete;
+
+ Item(Item &&other)
+ :tag(other.tag), fold_case(other.fold_case),
+ value(other.value) {
+ other.value = nullptr;
+ }
+
+ ~Item();
+
+ Item &operator=(const Item &other) = delete;
+
+ unsigned GetTag() const {
+ return tag;
+ }
+
+ gcc_pure gcc_nonnull(2)
+ bool StringMatch(const char *s) const;
+
+ gcc_pure
+ bool Match(const tag_item &tag_item) const;
+
+ gcc_pure
+ bool Match(const struct tag &tag) const;
+
+ gcc_pure
+ bool Match(const song &song) const;
+ };
+
+ std::list<Item> items;
+
+public:
+ SongFilter() = default;
+
+ gcc_nonnull(3)
+ SongFilter(unsigned tag, const char *value, bool fold_case=false);
+
+ ~SongFilter();
+
+ gcc_nonnull(2,3)
+ bool Parse(const char *tag, const char *value, bool fold_case=false);
+
+ gcc_nonnull(3)
+ bool Parse(unsigned argc, char *argv[], bool fold_case=false);
+
+ gcc_pure
+ bool Match(const tag &tag) const;
+
+ gcc_pure
+ bool Match(const song &song) const;
+};
+
+/**
+ * @return #TAG_NUM_OF_ITEM_TYPES on error
+ */
+gcc_pure
+unsigned
+locate_parse_type(const char *str);
+
+#endif
diff --git a/src/song_print.c b/src/SongPrint.cxx
index fb608a8b2..c56e54d8b 100644
--- a/src/song_print.c
+++ b/src/SongPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,20 +18,24 @@
*/
#include "config.h"
-#include "song_print.h"
+#include "SongPrint.hxx"
#include "song.h"
-#include "directory.h"
-#include "tag_print.h"
-#include "client.h"
+#include "Directory.hxx"
+#include "TimePrint.hxx"
+#include "TagPrint.hxx"
+#include "Mapper.hxx"
+#include "Client.hxx"
+
+extern "C" {
#include "uri.h"
-#include "mapper.h"
+}
void
-song_print_uri(struct client *client, struct song *song)
+song_print_uri(Client *client, struct song *song)
{
- if (song_in_database(song) && !directory_is_root(song->parent)) {
+ if (song_in_database(song) && !song->parent->IsRoot()) {
client_printf(client, "%s%s/%s\n", SONG_FILE,
- directory_get_path(song->parent), song->uri);
+ song->parent->GetPath(), song->uri);
} else {
char *allocated;
const char *uri;
@@ -48,7 +52,7 @@ song_print_uri(struct client *client, struct song *song)
}
void
-song_print_info(struct client *client, struct song *song)
+song_print_info(Client *client, struct song *song)
{
song_print_uri(client, song);
@@ -63,32 +67,8 @@ song_print_info(struct client *client, struct song *song)
song->start_ms / 1000,
song->start_ms % 1000);
- if (song->mtime > 0) {
-#ifndef G_OS_WIN32
- struct tm tm;
-#endif
- const struct tm *tm2;
-
-#ifdef G_OS_WIN32
- tm2 = gmtime(&song->mtime);
-#else
- tm2 = gmtime_r(&song->mtime, &tm);
-#endif
-
- if (tm2 != NULL) {
- char timestamp[32];
-
- strftime(timestamp, sizeof(timestamp),
-#ifdef G_OS_WIN32
- "%Y-%m-%dT%H:%M:%SZ",
-#else
- "%FT%TZ",
-#endif
- tm2);
- client_printf(client, "Last-Modified: %s\n",
- timestamp);
- }
- }
+ if (song->mtime > 0)
+ time_print(client, "Last-Modified", song->mtime);
if (song->tag)
tag_print(client, song->tag);
diff --git a/src/SongPrint.hxx b/src/SongPrint.hxx
new file mode 100644
index 000000000..49f9478be
--- /dev/null
+++ b/src/SongPrint.hxx
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SONG_PRINT_HXX
+#define MPD_SONG_PRINT_HXX
+
+struct song;
+class Client;
+
+void
+song_print_info(Client *client, struct song *song);
+
+void
+song_print_uri(Client *client, struct song *song);
+
+#endif
diff --git a/src/song_save.c b/src/SongSave.cxx
index 4fcb46e22..a32b7fecc 100644
--- a/src/song_save.c
+++ b/src/SongSave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,16 @@
*/
#include "config.h"
-#include "song_save.h"
+#include "SongSave.hxx"
#include "song.h"
-#include "tag_save.h"
-#include "directory.h"
+#include "TagSave.hxx"
+#include "Directory.hxx"
+#include "TextFile.hxx"
+
+extern "C" {
#include "tag.h"
-#include "text_file.h"
#include "string_util.h"
+}
#include <glib.h>
@@ -60,8 +63,8 @@ song_save(FILE *fp, const struct song *song)
}
struct song *
-song_load(FILE *fp, struct directory *parent, const char *uri,
- GString *buffer, GError **error_r)
+song_load(TextFile &file, Directory *parent, const char *uri,
+ GError **error_r)
{
struct song *song = parent != NULL
? song_file_new(uri, parent)
@@ -70,7 +73,7 @@ song_load(FILE *fp, struct directory *parent, const char *uri,
enum tag_type type;
const char *value;
- while ((line = read_text_line(fp, buffer)) != NULL &&
+ while ((line = file.ReadLine()) != NULL &&
strcmp(line, SONG_END) != 0) {
colon = strchr(line, ':');
if (colon == NULL || colon == line) {
diff --git a/src/song_save.h b/src/SongSave.hxx
index f6ecbbfeb..3214c545f 100644
--- a/src/song_save.h
+++ b/src/SongSave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SONG_SAVE_H
-#define MPD_SONG_SAVE_H
+#ifndef MPD_SONG_SAVE_HXX
+#define MPD_SONG_SAVE_HXX
#include <glib.h>
@@ -27,7 +27,8 @@
#define SONG_BEGIN "song_begin: "
struct song;
-struct directory;
+struct Directory;
+class TextFile;
void
song_save(FILE *fp, const struct song *song);
@@ -41,7 +42,7 @@ song_save(FILE *fp, const struct song *song);
* @return true on success, false on error
*/
struct song *
-song_load(FILE *fp, struct directory *parent, const char *uri,
- GString *buffer, GError **error_r);
+song_load(TextFile &file, Directory *parent, const char *uri,
+ GError **error_r);
#endif
diff --git a/src/song_sticker.c b/src/SongSticker.cxx
index 78025906e..40af50b31 100644
--- a/src/song_sticker.c
+++ b/src/SongSticker.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,10 @@
*/
#include "config.h"
-#include "song_sticker.h"
+#include "SongSticker.hxx"
+#include "StickerDatabase.hxx"
#include "song.h"
-#include "directory.h"
-#include "sticker.h"
+#include "Directory.hxx"
#include <glib.h>
@@ -109,7 +109,7 @@ sticker_song_get(const struct song *song)
}
struct sticker_song_find_data {
- struct directory *directory;
+ Directory *directory;
const char *base_uri;
size_t base_uri_length;
@@ -121,34 +121,31 @@ struct sticker_song_find_data {
static void
sticker_song_find_cb(const char *uri, const char *value, gpointer user_data)
{
- struct sticker_song_find_data *data = user_data;
- struct song *song;
+ struct sticker_song_find_data *data =
+ (struct sticker_song_find_data *)user_data;
if (memcmp(uri, data->base_uri, data->base_uri_length) != 0)
/* should not happen, ignore silently */
return;
- song = directory_lookup_song(data->directory,
- uri + data->base_uri_length);
+ song *song = data->directory->LookupSong(uri + data->base_uri_length);
if (song != NULL)
data->func(song, value, data->user_data);
}
bool
-sticker_song_find(struct directory *directory, const char *name,
+sticker_song_find(Directory *directory, const char *name,
void (*func)(struct song *song, const char *value,
gpointer user_data),
gpointer user_data)
{
- struct sticker_song_find_data data = {
- .directory = directory,
- .func = func,
- .user_data = user_data,
- };
- char *allocated;
- bool success;
+ struct sticker_song_find_data data;
+ data.directory = directory;
+ data.func = func;
+ data.user_data = user_data;
- data.base_uri = directory_get_path(directory);
+ char *allocated;
+ data.base_uri = directory->GetPath();
if (*data.base_uri != 0)
/* append slash to base_uri */
data.base_uri = allocated =
@@ -159,8 +156,8 @@ sticker_song_find(struct directory *directory, const char *name,
data.base_uri_length = strlen(data.base_uri);
- success = sticker_find("song", data.base_uri, name,
- sticker_song_find_cb, &data);
+ bool success = sticker_find("song", data.base_uri, name,
+ sticker_song_find_cb, &data);
g_free(allocated);
return success;
diff --git a/src/song_sticker.h b/src/SongSticker.hxx
index 20ae68ce9..fd25af4c8 100644
--- a/src/song_sticker.h
+++ b/src/SongSticker.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef SONG_STICKER_H
-#define SONG_STICKER_H
+#ifndef MPD_SONG_STICKER_HXX
+#define MPD_SONG_STICKER_HXX
-#include <stdbool.h>
#include <glib.h>
struct song;
-struct directory;
+struct Directory;
struct sticker;
/**
@@ -76,7 +75,7 @@ sticker_song_get(const struct song *song);
* failure
*/
bool
-sticker_song_find(struct directory *directory, const char *name,
+sticker_song_find(Directory *directory, const char *name,
void (*func)(struct song *song, const char *value,
gpointer user_data),
gpointer user_data);
diff --git a/src/song_update.c b/src/SongUpdate.cxx
index 37f502a20..2d22f27c4 100644
--- a/src/song_update.c
+++ b/src/SongUpdate.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,16 @@
*/
#include "config.h" /* must be first for large file support */
+
+extern "C" {
#include "song.h"
#include "uri.h"
-#include "directory.h"
-#include "mapper.h"
+}
+
+#include "Directory.hxx"
+#include "Mapper.hxx"
+
+extern "C" {
#include "decoder_list.h"
#include "decoder_plugin.h"
#include "tag_ape.h"
@@ -29,6 +35,7 @@
#include "tag.h"
#include "tag_handler.h"
#include "input_stream.h"
+}
#include <glib.h>
@@ -38,7 +45,7 @@
#include <stdio.h>
struct song *
-song_file_load(const char *path, struct directory *parent)
+song_file_load(const char *path, Directory *parent)
{
struct song *song;
bool ret;
@@ -187,7 +194,7 @@ song_file_update_inarchive(struct song *song)
if (suffix == NULL)
return false;
- plugin = decoder_plugin_from_suffix(suffix, false);
+ plugin = decoder_plugin_from_suffix(suffix, nullptr);
if (plugin == NULL)
return false;
diff --git a/src/state_file.c b/src/StateFile.cxx
index de0e70538..1d77b77de 100644
--- a/src/state_file.c
+++ b/src/StateFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,12 +18,15 @@
*/
#include "config.h"
-#include "state_file.h"
-#include "output_state.h"
-#include "playlist.h"
-#include "playlist_state.h"
+#include "StateFile.hxx"
+#include "OutputState.hxx"
+#include "PlaylistState.hxx"
+#include "TextFile.hxx"
+#include "Partition.hxx"
+
+extern "C" {
#include "volume.h"
-#include "text_file.h"
+}
#include <glib.h>
#include <assert.h>
@@ -46,7 +49,7 @@ static unsigned prev_volume_version, prev_output_version,
prev_playlist_version;
static void
-state_file_write(struct player_control *pc)
+state_file_write(Partition &partition)
{
FILE *fp;
@@ -63,51 +66,46 @@ state_file_write(struct player_control *pc)
save_sw_volume_state(fp);
audio_output_state_save(fp);
- playlist_state_save(fp, &g_playlist, pc);
+ playlist_state_save(fp, &partition.playlist, &partition.pc);
fclose(fp);
prev_volume_version = sw_volume_state_get_hash();
prev_output_version = audio_output_state_get_version();
- prev_playlist_version = playlist_state_get_hash(&g_playlist, pc);
+ prev_playlist_version = playlist_state_get_hash(&partition.playlist,
+ &partition.pc);
}
static void
-state_file_read(struct player_control *pc)
+state_file_read(Partition &partition)
{
- FILE *fp;
bool success;
assert(state_file_path != NULL);
g_debug("Loading state file %s", state_file_path);
- fp = fopen(state_file_path, "r");
- if (G_UNLIKELY(!fp)) {
+ TextFile file(state_file_path);
+ if (file.HasFailed()) {
g_warning("failed to open %s: %s",
state_file_path, g_strerror(errno));
return;
}
- GString *buffer = g_string_sized_new(1024);
const char *line;
- while ((line = read_text_line(fp, buffer)) != NULL) {
+ while ((line = file.ReadLine()) != NULL) {
success = read_sw_volume_state(line) ||
audio_output_state_read(line) ||
- playlist_state_restore(line, fp, buffer,
- &g_playlist, pc);
+ playlist_state_restore(line, file, &partition.playlist,
+ &partition.pc);
if (!success)
g_warning("Unrecognized line in state file: %s", line);
}
- fclose(fp);
-
prev_volume_version = sw_volume_state_get_hash();
prev_output_version = audio_output_state_get_version();
- prev_playlist_version = playlist_state_get_hash(&g_playlist, pc);
-
-
- g_string_free(buffer, true);
+ prev_playlist_version = playlist_state_get_hash(&partition.playlist,
+ &partition.pc);
}
/**
@@ -117,21 +115,22 @@ state_file_read(struct player_control *pc)
static gboolean
timer_save_state_file(gpointer data)
{
- struct player_control *pc = data;
+ Partition &partition = *(Partition *)data;
if (prev_volume_version == sw_volume_state_get_hash() &&
prev_output_version == audio_output_state_get_version() &&
- prev_playlist_version == playlist_state_get_hash(&g_playlist, pc))
+ prev_playlist_version == playlist_state_get_hash(&partition.playlist,
+ &partition.pc))
/* nothing has changed - don't save the state file,
don't spin up the hard disk */
return true;
- state_file_write(pc);
+ state_file_write(partition);
return true;
}
void
-state_file_init(const char *path, struct player_control *pc)
+state_file_init(const char *path, Partition &partition)
{
assert(state_file_path == NULL);
@@ -139,15 +138,15 @@ state_file_init(const char *path, struct player_control *pc)
return;
state_file_path = g_strdup(path);
- state_file_read(pc);
+ state_file_read(partition);
save_state_source_id = g_timeout_add_seconds(5 * 60,
timer_save_state_file,
- pc);
+ &partition);
}
void
-state_file_finish(struct player_control *pc)
+state_file_finish(Partition &partition)
{
if (state_file_path == NULL)
/* no state file configured, no cleanup required */
@@ -156,7 +155,7 @@ state_file_finish(struct player_control *pc)
if (save_state_source_id != 0)
g_source_remove(save_state_source_id);
- state_file_write(pc);
+ state_file_write(partition);
g_free(state_file_path);
}
diff --git a/src/state_file.h b/src/StateFile.hxx
index 4c4f881cc..35269fe40 100644
--- a/src/state_file.h
+++ b/src/StateFile.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,16 +17,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_STATE_FILE_H
-#define MPD_STATE_FILE_H
+#ifndef MPD_STATE_FILE_HXX
+#define MPD_STATE_FILE_HXX
-struct player_control;
+struct Partition;
void
-state_file_init(const char *path, struct player_control *pc);
+state_file_init(const char *path, Partition &partition);
void
-state_file_finish(struct player_control *pc);
+state_file_finish(Partition &partition);
void write_state_file(void);
diff --git a/src/Stats.cxx b/src/Stats.cxx
new file mode 100644
index 000000000..f848a9b03
--- /dev/null
+++ b/src/Stats.cxx
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+extern "C" {
+#include "stats.h"
+}
+
+#include "PlayerControl.hxx"
+#include "ClientInternal.hxx"
+#include "DatabaseSelection.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseSimple.hxx"
+
+struct stats stats;
+
+void stats_global_init(void)
+{
+ stats.timer = g_timer_new();
+}
+
+void stats_global_finish(void)
+{
+ g_timer_destroy(stats.timer);
+}
+
+void stats_update(void)
+{
+ GError *error = nullptr;
+
+ DatabaseStats stats2;
+
+ const DatabaseSelection selection("", true);
+ if (GetDatabase()->GetStats(selection, stats2, &error)) {
+ stats.song_count = stats2.song_count;
+ stats.song_duration = stats2.total_duration;
+ stats.artist_count = stats2.artist_count;
+ stats.album_count = stats2.album_count;
+ } else {
+ g_warning("%s", error->message);
+ g_error_free(error);
+
+ stats.song_count = 0;
+ stats.song_duration = 0;
+ stats.artist_count = 0;
+ stats.album_count = 0;
+ }
+}
+
+void
+stats_print(Client *client)
+{
+ client_printf(client,
+ "artists: %u\n"
+ "albums: %u\n"
+ "songs: %i\n"
+ "uptime: %li\n"
+ "playtime: %li\n"
+ "db_playtime: %li\n",
+ stats.artist_count,
+ stats.album_count,
+ stats.song_count,
+ (long)g_timer_elapsed(stats.timer, NULL),
+ (long)(pc_get_total_play_time(client->player_control) + 0.5),
+ stats.song_duration);
+
+ if (db_is_simple())
+ client_printf(client,
+ "db_update: %li\n",
+ (long)db_get_mtime());
+}
diff --git a/src/StickerCommands.cxx b/src/StickerCommands.cxx
new file mode 100644
index 000000000..d13647c33
--- /dev/null
+++ b/src/StickerCommands.cxx
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2003-2012 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 "StickerCommands.hxx"
+#include "SongPrint.hxx"
+#include "DatabaseLock.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabaseSimple.hxx"
+#include "SongSticker.hxx"
+#include "StickerPrint.hxx"
+#include "StickerDatabase.hxx"
+#include "CommandError.hxx"
+#include "protocol/Result.hxx"
+
+#include <string.h>
+
+struct sticker_song_find_data {
+ Client *client;
+ const char *name;
+};
+
+static void
+sticker_song_find_print_cb(struct song *song, const char *value,
+ gpointer user_data)
+{
+ 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);
+}
+
+static enum command_return
+handle_sticker_song(Client *client, int argc, char *argv[])
+{
+ GError *error = nullptr;
+ const Database *db = GetDatabase(&error);
+ if (db == nullptr)
+ return print_error(client, error);
+
+ /* get song song_id key */
+ if (argc == 5 && strcmp(argv[1], "get") == 0) {
+ song *song = db->GetSong(argv[3], &error);
+ if (song == nullptr)
+ return print_error(client, error);
+
+ char *value = sticker_song_get_value(song, argv[4]);
+ db->ReturnSong(song);
+ if (value == NULL) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "no such sticker");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ sticker_print_value(client, argv[4], value);
+ g_free(value);
+
+ return COMMAND_RETURN_OK;
+ /* list song song_id */
+ } else if (argc == 4 && strcmp(argv[1], "list") == 0) {
+ song *song = db->GetSong(argv[3], &error);
+ if (song == nullptr)
+ return print_error(client, error);
+
+ sticker *sticker = sticker_song_get(song);
+ db->ReturnSong(song);
+ if (sticker) {
+ sticker_print(client, sticker);
+ sticker_free(sticker);
+ }
+
+ return COMMAND_RETURN_OK;
+ /* set song song_id id key */
+ } else if (argc == 6 && strcmp(argv[1], "set") == 0) {
+ song *song = db->GetSong(argv[3], &error);
+ if (song == nullptr)
+ return print_error(client, error);
+
+ bool ret = sticker_song_set_value(song, argv[4], argv[5]);
+ db->ReturnSong(song);
+ if (!ret) {
+ command_error(client, ACK_ERROR_SYSTEM,
+ "failed to set sticker value");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+ /* delete song song_id [key] */
+ } else if ((argc == 4 || argc == 5) &&
+ strcmp(argv[1], "delete") == 0) {
+ song *song = db->GetSong(argv[3], &error);
+ if (song == nullptr)
+ return print_error(client, error);
+
+ bool ret = argc == 4
+ ? sticker_song_delete(song)
+ : sticker_song_delete_value(song, argv[4]);
+ db->ReturnSong(song);
+ if (!ret) {
+ command_error(client, ACK_ERROR_SYSTEM,
+ "no such sticker");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+ /* find song dir key */
+ } else if (argc == 5 && strcmp(argv[1], "find") == 0) {
+ /* "sticker find song a/directory name" */
+ bool success;
+ struct sticker_song_find_data data = {
+ client,
+ argv[4],
+ };
+
+ db_lock();
+ Directory *directory = db_get_directory(argv[3]);
+ if (directory == NULL) {
+ db_unlock();
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "no such directory");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ success = sticker_song_find(directory, data.name,
+ sticker_song_find_print_cb, &data);
+ db_unlock();
+ if (!success) {
+ command_error(client, ACK_ERROR_SYSTEM,
+ "failed to set search sticker database");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+ } else {
+ command_error(client, ACK_ERROR_ARG, "bad request");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+enum command_return
+handle_sticker(Client *client, int argc, char *argv[])
+{
+ assert(argc >= 4);
+
+ if (!sticker_enabled()) {
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "sticker database is disabled");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ if (strcmp(argv[2], "song") == 0)
+ return handle_sticker_song(client, argc, argv);
+ else {
+ command_error(client, ACK_ERROR_ARG,
+ "unknown sticker domain");
+ return COMMAND_RETURN_ERROR;
+ }
+}
diff --git a/src/StickerCommands.hxx b/src/StickerCommands.hxx
new file mode 100644
index 000000000..840bd33d5
--- /dev/null
+++ b/src/StickerCommands.hxx
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2003-2012 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_COMMANDS_HXX
+#define MPD_STICKER_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_sticker(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/sticker.c b/src/StickerDatabase.cxx
index 346a827a5..3d6e212d1 100644
--- a/src/sticker.c
+++ b/src/StickerDatabase.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,14 @@
*/
#include "config.h"
-#include "sticker.h"
+#include "StickerDatabase.hxx"
+
+extern "C" {
#include "idle.h"
+}
+
+#include <string>
+#include <map>
#include <glib.h>
#include <sqlite3.h>
@@ -33,7 +39,7 @@
#endif
struct sticker {
- GHashTable *table;
+ std::map<std::string, std::string> table;
};
enum sticker_sql {
@@ -47,19 +53,19 @@ enum sticker_sql {
};
static const char *const sticker_sql[] = {
- [STICKER_SQL_GET] =
+ //[STICKER_SQL_GET] =
"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?",
- [STICKER_SQL_LIST] =
+ //[STICKER_SQL_LIST] =
"SELECT name,value FROM sticker WHERE type=? AND uri=?",
- [STICKER_SQL_UPDATE] =
+ //[STICKER_SQL_UPDATE] =
"UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?",
- [STICKER_SQL_INSERT] =
+ //[STICKER_SQL_INSERT] =
"INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)",
- [STICKER_SQL_DELETE] =
+ //[STICKER_SQL_DELETE] =
"DELETE FROM sticker WHERE type=? AND uri=?",
- [STICKER_SQL_DELETE_VALUE] =
+ //[STICKER_SQL_DELETE_VALUE] =
"DELETE FROM sticker WHERE type=? AND uri=? AND name=?",
- [STICKER_SQL_FIND] =
+ //[STICKER_SQL_FIND] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",
};
@@ -226,13 +232,12 @@ sticker_load_value(const char *type, const char *uri, const char *name)
}
static bool
-sticker_list_values(GHashTable *hash, const char *type, const char *uri)
+sticker_list_values(std::map<std::string, std::string> &table,
+ const char *type, const char *uri)
{
sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST];
int ret;
- char *name, *value;
- assert(hash != NULL);
assert(type != NULL);
assert(uri != NULL);
assert(sticker_enabled());
@@ -256,10 +261,13 @@ sticker_list_values(GHashTable *hash, const char *type, const char *uri)
do {
ret = sqlite3_step(stmt);
switch (ret) {
+ const char *name, *value;
+
case SQLITE_ROW:
- name = g_strdup((const char*)sqlite3_column_text(stmt, 0));
- value = g_strdup((const char*)sqlite3_column_text(stmt, 1));
- g_hash_table_insert(hash, name, value);
+ name = (const char*)sqlite3_column_text(stmt, 0);
+ value = (const char*)sqlite3_column_text(stmt, 1);
+
+ table.insert(std::make_pair(name, value));
break;
case SQLITE_DONE:
break;
@@ -518,44 +526,20 @@ sticker_delete_value(const char *type, const char *uri, const char *name)
return ret > 0;
}
-static struct sticker *
-sticker_new(void)
-{
- struct sticker *sticker = g_new(struct sticker, 1);
-
- sticker->table = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_free);
- return sticker;
-}
-
void
sticker_free(struct sticker *sticker)
{
- assert(sticker != NULL);
- assert(sticker->table != NULL);
-
- g_hash_table_destroy(sticker->table);
- g_free(sticker);
+ delete sticker;
}
const char *
sticker_get_value(const struct sticker *sticker, const char *name)
{
- return g_hash_table_lookup(sticker->table, name);
-}
+ auto i = sticker->table.find(name);
+ if (i == sticker->table.end())
+ return nullptr;
-struct sticker_foreach_data {
- void (*func)(const char *name, const char *value,
- gpointer user_data);
- gpointer user_data;
-};
-
-static void
-sticker_foreach_func(gpointer key, gpointer value, gpointer user_data)
-{
- struct sticker_foreach_data *data = user_data;
-
- data->func(key, value, data->user_data);
+ return i->second.c_str();
}
void
@@ -564,33 +548,23 @@ sticker_foreach(const struct sticker *sticker,
gpointer user_data),
gpointer user_data)
{
- struct sticker_foreach_data data = {
- .func = func,
- .user_data = user_data,
- };
-
- g_hash_table_foreach(sticker->table, sticker_foreach_func, &data);
+ for (const auto &i : sticker->table)
+ func(i.first.c_str(), i.second.c_str(), user_data);
}
struct sticker *
sticker_load(const char *type, const char *uri)
{
- struct sticker *sticker = sticker_new();
- bool success;
+ sticker s;
- success = sticker_list_values(sticker->table, type, uri);
- if (!success) {
- sticker_free(sticker);
+ if (!sticker_list_values(s.table, type, uri))
return NULL;
- }
- if (g_hash_table_size(sticker->table) == 0) {
+ if (s.table.empty())
/* don't return empty sticker objects */
- sticker_free(sticker);
return NULL;
- }
- return sticker;
+ return new sticker(std::move(s));
}
bool
diff --git a/src/sticker.h b/src/StickerDatabase.hxx
index 5545206a5..90ff9b066 100644
--- a/src/sticker.h
+++ b/src/StickerDatabase.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -39,12 +39,10 @@
*
*/
-#ifndef STICKER_H
-#define STICKER_H
+#ifndef MPD_STICKER_DATABASE_HXX
+#define MPD_STICKER_DATABASE_HXX
-#include <glib.h>
-
-#include <stdbool.h>
+#include "gerror.h"
struct sticker;
@@ -127,8 +125,8 @@ sticker_get_value(const struct sticker *sticker, const char *name);
void
sticker_foreach(const struct sticker *sticker,
void (*func)(const char *name, const char *value,
- gpointer user_data),
- gpointer user_data);
+ void *user_data),
+ void *user_data);
/**
* Loads the sticker for the specified resource.
@@ -153,7 +151,7 @@ sticker_load(const char *type, const char *uri);
bool
sticker_find(const char *type, const char *base_uri, const char *name,
void (*func)(const char *uri, const char *value,
- gpointer user_data),
- gpointer user_data);
+ void *user_data),
+ void *user_data);
#endif
diff --git a/src/sticker_print.c b/src/StickerPrint.cxx
index 65e79513c..1708ee4f0 100644
--- a/src/sticker_print.c
+++ b/src/StickerPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,27 +18,27 @@
*/
#include "config.h"
-#include "sticker_print.h"
-#include "sticker.h"
-#include "client.h"
+#include "StickerPrint.hxx"
+#include "StickerDatabase.hxx"
+#include "Client.hxx"
void
-sticker_print_value(struct client *client,
+sticker_print_value(Client *client,
const char *name, const char *value)
{
client_printf(client, "sticker: %s=%s\n", name, value);
}
static void
-print_sticker_cb(const char *name, const char *value, gpointer data)
+print_sticker_cb(const char *name, const char *value, void *data)
{
- struct client *client = data;
+ Client *client = (Client *)data;
sticker_print_value(client, name, value);
}
void
-sticker_print(struct client *client, const struct sticker *sticker)
+sticker_print(Client *client, const struct sticker *sticker)
{
sticker_foreach(sticker, print_sticker_cb, client);
}
diff --git a/src/sticker_print.h b/src/StickerPrint.hxx
index 7398c8083..27225a494 100644
--- a/src/sticker_print.h
+++ b/src/StickerPrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,23 +17,22 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_STICKER_PRINT_H
-#define MPD_STICKER_PRINT_H
+#ifndef MPD_STICKER_PRINT_HXX
+#define MPD_STICKER_PRINT_HXX
struct sticker;
-struct client;
+class Client;
/**
* Sends one sticker value to the client.
*/
void
-sticker_print_value(struct client *client,
- const char *name, const char *value);
+sticker_print_value(Client *client, const char *name, const char *value);
/**
* Sends all sticker values to the client.
*/
void
-sticker_print(struct client *client, const struct sticker *sticker);
+sticker_print(Client *client, const struct sticker *sticker);
#endif
diff --git a/src/tag_print.c b/src/TagPrint.cxx
index 9a46b247a..5a5dbbeff 100644
--- a/src/tag_print.c
+++ b/src/TagPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,13 @@
*/
#include "config.h"
-#include "tag_print.h"
+#include "TagPrint.hxx"
#include "tag.h"
#include "tag_internal.h"
-#include "client.h"
#include "song.h"
+#include "Client.hxx"
-void tag_print_types(struct client *client)
+void tag_print_types(Client *client)
{
int i;
@@ -35,7 +35,7 @@ void tag_print_types(struct client *client)
}
}
-void tag_print(struct client *client, const struct tag *tag)
+void tag_print(Client *client, const struct tag *tag)
{
if (tag->time >= 0)
client_printf(client, SONG_TIME "%i\n", tag->time);
diff --git a/src/tag_print.h b/src/TagPrint.hxx
index b9eeeaecf..99e1d0850 100644
--- a/src/tag_print.h
+++ b/src/TagPrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TAG_PRINT_H
-#define MPD_TAG_PRINT_H
+#ifndef MPD_TAG_PRINT_HXX
+#define MPD_TAG_PRINT_HXX
struct tag;
-struct client;
+class Client;
-void tag_print_types(struct client *client);
+void tag_print_types(Client *client);
-void tag_print(struct client *client, const struct tag *tag);
+void tag_print(Client *client, const struct tag *tag);
#endif
diff --git a/src/tag_save.c b/src/TagSave.cxx
index 2fdaef56c..639dc2d97 100644
--- a/src/tag_save.c
+++ b/src/TagSave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "tag_save.h"
+#include "TagSave.hxx"
#include "tag.h"
#include "tag_internal.h"
#include "song.h"
diff --git a/src/tag_save.h b/src/TagSave.hxx
index 9f6a580c8..a7ccfa613 100644
--- a/src/tag_save.h
+++ b/src/TagSave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TAG_SAVE_H
-#define MPD_TAG_SAVE_H
+#ifndef MPD_TAG_SAVE_HXX
+#define MPD_TAG_SAVE_HXX
#include <stdio.h>
diff --git a/src/text_file.c b/src/TextFile.cxx
index 3674e5ce2..4ad59ee4a 100644
--- a/src/text_file.c
+++ b/src/TextFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,27 +18,20 @@
*/
#include "config.h"
-#include "text_file.h"
+#include "TextFile.hxx"
#include <assert.h>
#include <string.h>
char *
-read_text_line(FILE *file, GString *buffer)
+TextFile::ReadLine()
{
- enum {
- max_length = 512 * 1024,
- step = 1024,
- };
-
gsize length = 0, i;
char *p;
assert(file != NULL);
assert(buffer != NULL);
-
- if (buffer->allocated_len < step)
- g_string_set_size(buffer, step);
+ assert(buffer->allocated_len >= step);
while (buffer->len < max_length) {
p = fgets(buffer->str + length,
diff --git a/src/TextFile.hxx b/src/TextFile.hxx
new file mode 100644
index 000000000..25f6ea7a8
--- /dev/null
+++ b/src/TextFile.hxx
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_TEXT_FILE_HXX
+#define MPD_TEXT_FILE_HXX
+
+#include "gcc.h"
+
+#include <glib.h>
+
+#include <stdio.h>
+
+class TextFile {
+ static constexpr size_t max_length = 512 * 1024;
+ static constexpr size_t step = 1024;
+
+ FILE *const file;
+
+ GString *const buffer;
+
+public:
+ TextFile(const char *path_fs)
+ :file(fopen(path_fs, "r")), buffer(g_string_sized_new(step)) {}
+
+ TextFile(const TextFile &other) = delete;
+
+ ~TextFile() {
+ if (file != nullptr)
+ fclose(file);
+
+ g_string_free(buffer, true);
+ }
+
+ bool HasFailed() const {
+ return gcc_unlikely(file == nullptr);
+ }
+
+ /**
+ * Reads a line from the input file, and strips trailing
+ * space. There is a reasonable maximum line length, only to
+ * prevent denial of service.
+ *
+ * @param file the source file, opened in text mode
+ * @param buffer an allocator for the buffer
+ * @return a pointer to the line, or NULL on end-of-file or error
+ */
+ char *ReadLine();
+};
+
+#endif
diff --git a/src/TimePrint.cxx b/src/TimePrint.cxx
new file mode 100644
index 000000000..c5247dd9d
--- /dev/null
+++ b/src/TimePrint.cxx
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "TimePrint.hxx"
+#include "Client.hxx"
+
+#include <glib.h>
+
+void
+time_print(Client *client, const char *name, time_t t)
+{
+#ifdef G_OS_WIN32
+ const struct tm *tm2 = gmtime(&t);
+#else
+ struct tm tm;
+ const struct tm *tm2 = gmtime_r(&t, &tm);
+#endif
+ if (tm2 == NULL)
+ return;
+
+ char buffer[32];
+ strftime(buffer, sizeof(buffer),
+#ifdef G_OS_WIN32
+ "%Y-%m-%dT%H:%M:%SZ",
+#else
+ "%FT%TZ",
+#endif
+ tm2);
+ client_printf(client, "%s: %s\n", name, buffer);
+}
diff --git a/src/TimePrint.hxx b/src/TimePrint.hxx
new file mode 100644
index 000000000..f45101256
--- /dev/null
+++ b/src/TimePrint.hxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_TIME_PRINT_HXX
+#define MPD_TIME_PRINT_HXX
+
+#include <time.h>
+
+class Client;
+
+/**
+ * Write a line with a time stamp to the client.
+ */
+void
+time_print(Client *client, const char *name, time_t t);
+
+#endif
diff --git a/src/update_archive.c b/src/UpdateArchive.cxx
index 3fb2bc18c..72f7aaf19 100644
--- a/src/update_archive.c
+++ b/src/UpdateArchive.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,29 +18,32 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_archive.h"
-#include "update_internal.h"
-#include "db_lock.h"
-#include "directory.h"
+#include "UpdateArchive.hxx"
+#include "UpdateInternal.hxx"
+#include "DatabaseLock.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "mapper.h"
+#include "Mapper.hxx"
+
+extern "C" {
#include "archive_list.h"
#include "archive_plugin.h"
+}
#include <glib.h>
#include <string.h>
static void
-update_archive_tree(struct directory *directory, char *name)
+update_archive_tree(Directory *directory, char *name)
{
char *tmp = strchr(name, '/');
if (tmp) {
*tmp = 0;
//add dir is not there already
db_lock();
- struct directory *subdir =
- directory_make_child(directory, name);
+ Directory *subdir =
+ directory->MakeChild(name);
subdir->device = DEVICE_INARCHIVE;
db_unlock();
//create directories first
@@ -53,18 +56,18 @@ update_archive_tree(struct directory *directory, char *name)
//add file
db_lock();
- struct song *song = directory_get_song(directory, name);
+ struct song *song = directory->FindSong(name);
db_unlock();
if (song == NULL) {
song = song_file_load(name, directory);
if (song != NULL) {
db_lock();
- directory_add_song(directory, song);
+ directory->AddSong(song);
db_unlock();
modified = true;
g_message("added %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
}
}
}
@@ -79,12 +82,12 @@ update_archive_tree(struct directory *directory, char *name)
* @param plugin the archive plugin which fits this archive type
*/
static void
-update_archive_file2(struct directory *parent, const char *name,
+update_archive_file2(Directory *parent, const char *name,
const struct stat *st,
const struct archive_plugin *plugin)
{
db_lock();
- struct directory *directory = directory_get_child(parent, name);
+ Directory *directory = parent->FindChild(name);
db_unlock();
if (directory != NULL && directory->mtime == st->st_mtime &&
@@ -111,7 +114,7 @@ update_archive_file2(struct directory *parent, const char *name,
if (directory == NULL) {
g_debug("creating archive directory: %s", name);
db_lock();
- directory = directory_new_child(parent, name);
+ directory = parent->CreateChild(name);
/* mark this directory as archive (we use device for
this) */
directory->device = DEVICE_INARCHIVE;
@@ -133,7 +136,7 @@ update_archive_file2(struct directory *parent, const char *name,
}
bool
-update_archive_file(struct directory *directory,
+update_archive_file(Directory *directory,
const char *name, const char *suffix,
const struct stat *st)
{
diff --git a/src/update_archive.h b/src/UpdateArchive.hxx
index 838697dd9..aa9882ae0 100644
--- a/src/update_archive.h
+++ b/src/UpdateArchive.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,21 +17,21 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_ARCHIVE_H
-#define MPD_UPDATE_ARCHIVE_H
+#ifndef MPD_UPDATE_ARCHIVE_HXX
+#define MPD_UPDATE_ARCHIVE_HXX
#include "check.h"
+#include "gcc.h"
-#include <stdbool.h>
#include <sys/stat.h>
-struct directory;
+struct Directory;
struct archive_plugin;
#ifdef ENABLE_ARCHIVE
bool
-update_archive_file(struct directory *directory,
+update_archive_file(Directory *directory,
const char *name, const char *suffix,
const struct stat *st);
@@ -40,10 +40,10 @@ update_archive_file(struct directory *directory,
#include <glib.h>
static inline bool
-update_archive_file(G_GNUC_UNUSED struct directory *directory,
- G_GNUC_UNUSED const char *name,
- G_GNUC_UNUSED const char *suffix,
- G_GNUC_UNUSED const struct stat *st)
+update_archive_file(gcc_unused Directory *directory,
+ gcc_unused const char *name,
+ gcc_unused const char *suffix,
+ gcc_unused const struct stat *st)
{
return false;
}
diff --git a/src/update_container.c b/src/UpdateContainer.cxx
index bda95dabe..07bf0cee1 100644
--- a/src/update_container.c
+++ b/src/UpdateContainer.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,19 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_container.h"
-#include "update_internal.h"
-#include "update_db.h"
-#include "db_lock.h"
-#include "directory.h"
+#include "UpdateContainer.hxx"
+#include "UpdateInternal.hxx"
+#include "UpdateDatabase.hxx"
+#include "DatabaseLock.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "mapper.h"
#include "decoder_plugin.h"
+#include "Mapper.hxx"
+
+extern "C" {
#include "tag.h"
#include "tag_handler.h"
+}
#include <glib.h>
@@ -39,11 +42,11 @@
*
* The caller must lock the database.
*/
-static struct directory *
-make_directory_if_modified(struct directory *parent, const char *name,
+static Directory *
+make_directory_if_modified(Directory *parent, const char *name,
const struct stat *st)
{
- struct directory *directory = directory_get_child(parent, name);
+ Directory *directory = parent->FindChild(name);
// directory exists already
if (directory != NULL) {
@@ -57,13 +60,13 @@ make_directory_if_modified(struct directory *parent, const char *name,
modified = true;
}
- directory = directory_make_child(parent, name);
+ directory = parent->MakeChild(name);
directory->mtime = st->st_mtime;
return directory;
}
bool
-update_container_file(struct directory *directory,
+update_container_file(Directory *directory,
const char *name,
const struct stat *st,
const struct decoder_plugin *plugin)
@@ -72,8 +75,7 @@ update_container_file(struct directory *directory,
return false;
db_lock();
- struct directory *contdir =
- make_directory_if_modified(directory, name, st);
+ Directory *contdir = make_directory_if_modified(directory, name, st);
if (contdir == NULL) {
/* not modified */
db_unlock();
@@ -101,13 +103,12 @@ update_container_file(struct directory *directory,
g_free(child_path_fs);
db_lock();
- directory_add_song(contdir, song);
+ contdir->AddSong(song);
db_unlock();
modified = true;
- g_message("added %s/%s",
- directory_get_path(directory), vtrack);
+ g_message("added %s/%s", directory->GetPath(), vtrack);
g_free(vtrack);
}
diff --git a/src/update_container.h b/src/UpdateContainer.hxx
index 7f42c80ca..0078730d6 100644
--- a/src/update_container.h
+++ b/src/UpdateContainer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,19 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_CONTAINER_H
-#define MPD_UPDATE_CONTAINER_H
+#ifndef MPD_UPDATE_CONTAINER_HXX
+#define MPD_UPDATE_CONTAINER_HXX
#include "check.h"
-#include <stdbool.h>
#include <sys/stat.h>
-struct directory;
+struct Directory;
struct decoder_plugin;
bool
-update_container_file(struct directory *directory,
+update_container_file(Directory *directory,
const char *name,
const struct stat *st,
const struct decoder_plugin *plugin);
diff --git a/src/update_db.c b/src/UpdateDatabase.cxx
index 8982a53e2..984fb1be8 100644
--- a/src/update_db.c
+++ b/src/UpdateDatabase.cxx
@@ -18,23 +18,23 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_db.h"
-#include "update_remove.h"
-#include "directory.h"
+#include "UpdateDatabase.hxx"
+#include "UpdateRemove.hxx"
+#include "PlaylistVector.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "playlist_vector.h"
-#include "db_lock.h"
+#include "DatabaseLock.hxx"
#include <glib.h>
#include <assert.h>
void
-delete_song(struct directory *dir, struct song *del)
+delete_song(Directory *dir, struct song *del)
{
assert(del->parent == dir);
/* first, prevent traversers in main task from getting this */
- directory_remove_song(dir, del);
+ dir->RemoveSong(del);
db_unlock(); /* temporary unlock, because update_remove_song() blocks */
@@ -54,9 +54,9 @@ delete_song(struct directory *dir, struct song *del)
* Caller must lock the #db_mutex.
*/
static void
-clear_directory(struct directory *directory)
+clear_directory(Directory *directory)
{
- struct directory *child, *n;
+ Directory *child, *n;
directory_for_each_child_safe(child, n, directory)
delete_directory(child);
@@ -68,35 +68,35 @@ clear_directory(struct directory *directory)
}
void
-delete_directory(struct directory *directory)
+delete_directory(Directory *directory)
{
assert(directory->parent != NULL);
clear_directory(directory);
- directory_delete(directory);
+ directory->Delete();
}
bool
-delete_name_in(struct directory *parent, const char *name)
+delete_name_in(Directory *parent, const char *name)
{
bool modified = false;
db_lock();
- struct directory *directory = directory_get_child(parent, name);
+ Directory *directory = parent->FindChild(name);
if (directory != NULL) {
delete_directory(directory);
modified = true;
}
- struct song *song = directory_get_song(parent, name);
+ struct song *song = parent->FindSong(name);
if (song != NULL) {
delete_song(parent, song);
modified = true;
}
- playlist_vector_remove(&parent->playlists, name);
+ parent->playlists.erase(name);
db_unlock();
diff --git a/src/update_db.h b/src/UpdateDatabase.hxx
index 0a9e46b05..7b55ce95d 100644
--- a/src/update_db.h
+++ b/src/UpdateDatabase.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,21 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_DB_H
-#define MPD_UPDATE_DB_H
+#ifndef MPD_UPDATE_DATABASE_HXX
+#define MPD_UPDATE_DATABASE_HXX
#include "check.h"
-#include <stdbool.h>
-
-struct directory;
+struct Directory;
struct song;
/**
* Caller must lock the #db_mutex.
*/
void
-delete_song(struct directory *parent, struct song *song);
+delete_song(Directory *parent, struct song *song);
/**
* Recursively free a directory and all its contents.
@@ -39,7 +37,7 @@ delete_song(struct directory *parent, struct song *song);
* Caller must lock the #db_mutex.
*/
void
-delete_directory(struct directory *directory);
+delete_directory(Directory *directory);
/**
* Caller must NOT lock the #db_mutex.
@@ -47,6 +45,6 @@ delete_directory(struct directory *directory);
* @return true if the database was modified
*/
bool
-delete_name_in(struct directory *parent, const char *name);
+delete_name_in(Directory *parent, const char *name);
#endif
diff --git a/src/update.c b/src/UpdateGlue.cxx
index 12eec40c4..2edd19cc2 100644
--- a/src/update.c
+++ b/src/UpdateGlue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,19 +18,21 @@
*/
#include "config.h"
-#include "update.h"
-#include "update_queue.h"
-#include "update_walk.h"
-#include "update_remove.h"
-#include "update.h"
-#include "database.h"
-#include "mapper.h"
-#include "playlist.h"
+#include "UpdateGlue.hxx"
+#include "UpdateQueue.hxx"
+#include "UpdateWalk.hxx"
+#include "UpdateRemove.hxx"
+#include "Mapper.hxx"
+#include "DatabaseSimple.hxx"
+
+extern "C" {
#include "event_pipe.h"
-#include "update.h"
#include "idle.h"
#include "stats.h"
-#include "main.h"
+}
+
+#include "Main.hxx"
+#include "Partition.hxx"
#include "mpd_error.h"
#include <glib.h>
@@ -65,7 +67,7 @@ isUpdatingDB(void)
static void * update_task(void *_path)
{
- const char *path = _path;
+ const char *path = (const char *)_path;
if (path != NULL && *path != 0)
g_debug("starting: %s", path);
@@ -118,7 +120,7 @@ update_enqueue(const char *path, bool _discard)
{
assert(g_thread_self() == main_task);
- if (!mapper_has_music_directory())
+ if (!db_is_simple() || !mapper_has_music_directory())
return 0;
if (progress != UPDATE_PROGRESS_IDLE) {
@@ -153,7 +155,7 @@ static void update_finished_event(void)
if (modified) {
/* send "idle" events */
- playlist_increment_version_all(&g_playlist);
+ playlist_increment_version_all(&global_partition->playlist);
idle_add(IDLE_DATABASE);
}
diff --git a/src/update.h b/src/UpdateGlue.hxx
index 3d586b694..9d546a2a3 100644
--- a/src/update.h
+++ b/src/UpdateGlue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_H
-#define MPD_UPDATE_H
-
-#include <stdbool.h>
+#ifndef MPD_UPDATE_GLUE_HXX
+#define MPD_UPDATE_GLUE_HXX
void update_global_init(void);
diff --git a/src/update_io.c b/src/UpdateIO.cxx
index c6a540a0f..2aee56514 100644
--- a/src/update_io.c
+++ b/src/UpdateIO.cxx
@@ -18,9 +18,9 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_io.h"
-#include "mapper.h"
-#include "directory.h"
+#include "UpdateIO.hxx"
+#include "Directory.hxx"
+#include "Mapper.hxx"
#include "glib_compat.h"
#include <glib.h>
@@ -29,7 +29,7 @@
#include <unistd.h>
int
-stat_directory(const struct directory *directory, struct stat *st)
+stat_directory(const Directory *directory, struct stat *st)
{
char *path_fs = map_directory_fs(directory);
if (path_fs == NULL)
@@ -44,7 +44,7 @@ stat_directory(const struct directory *directory, struct stat *st)
}
int
-stat_directory_child(const struct directory *parent, const char *name,
+stat_directory_child(const Directory *parent, const char *name,
struct stat *st)
{
char *path_fs = map_directory_child_fs(parent, name);
@@ -60,7 +60,7 @@ stat_directory_child(const struct directory *parent, const char *name,
}
bool
-directory_exists(const struct directory *directory)
+directory_exists(const Directory *directory)
{
char *path_fs = map_directory_fs(directory);
if (path_fs == NULL)
@@ -79,7 +79,7 @@ directory_exists(const struct directory *directory)
}
bool
-directory_child_is_regular(const struct directory *directory,
+directory_child_is_regular(const Directory *directory,
const char *name_utf8)
{
char *path_fs = map_directory_child_fs(directory, name_utf8);
@@ -94,7 +94,7 @@ directory_child_is_regular(const struct directory *directory,
}
bool
-directory_child_access(const struct directory *directory,
+directory_child_access(const Directory *directory,
const char *name, int mode)
{
#ifdef WIN32
diff --git a/src/update_io.h b/src/UpdateIO.hxx
index 6ff1ccebd..ee47b2682 100644
--- a/src/update_io.h
+++ b/src/UpdateIO.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,35 +17,34 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_IO_H
-#define MPD_UPDATE_IO_H
+#ifndef MPD_UPDATE_IO_HXX
+#define MPD_UPDATE_IO_HXX
#include "check.h"
-#include <stdbool.h>
#include <sys/stat.h>
-struct directory;
+struct Directory;
int
-stat_directory(const struct directory *directory, struct stat *st);
+stat_directory(const Directory *directory, struct stat *st);
int
-stat_directory_child(const struct directory *parent, const char *name,
+stat_directory_child(const Directory *parent, const char *name,
struct stat *st);
bool
-directory_exists(const struct directory *directory);
+directory_exists(const Directory *directory);
bool
-directory_child_is_regular(const struct directory *directory,
+directory_child_is_regular(const Directory *directory,
const char *name_utf8);
/**
* Checks if the given permissions on the mapped file are given.
*/
bool
-directory_child_access(const struct directory *directory,
+directory_child_access(const Directory *directory,
const char *name, int mode);
#endif
diff --git a/src/update_internal.h b/src/UpdateInternal.hxx
index 76ab7bed3..50443c086 100644
--- a/src/update_internal.h
+++ b/src/UpdateInternal.hxx
@@ -22,8 +22,6 @@
#include "check.h"
-#include <stdbool.h>
-
extern bool walk_discard;
extern bool modified;
diff --git a/src/update_queue.c b/src/UpdateQueue.cxx
index 2150fa4e4..85eb22358 100644
--- a/src/update_queue.c
+++ b/src/UpdateQueue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "update_queue.h"
+#include "UpdateQueue.hxx"
#include <glib.h>
diff --git a/src/update_queue.h b/src/UpdateQueue.hxx
index 84ba474b0..7de06964f 100644
--- a/src/update_queue.h
+++ b/src/UpdateQueue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_QUEUE_H
-#define MPD_UPDATE_QUEUE_H
+#ifndef MPD_UPDATE_QUEUE_HXX
+#define MPD_UPDATE_QUEUE_HXX
#include "check.h"
-#include <stdbool.h>
-
unsigned
update_queue_push(const char *path, bool discard, unsigned base);
diff --git a/src/update_remove.c b/src/UpdateRemove.cxx
index f443f5eb2..c1a0cd32b 100644
--- a/src/update_remove.c
+++ b/src/UpdateRemove.cxx
@@ -18,15 +18,20 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_remove.h"
+#include "UpdateRemove.hxx"
+#include "Playlist.hxx"
+#include "Partition.hxx"
+
+extern "C" {
#include "event_pipe.h"
+}
+
#include "song.h"
-#include "playlist.h"
-#include "main.h"
+#include "Main.hxx"
#ifdef ENABLE_SQLITE
-#include "sticker.h"
-#include "song_sticker.h"
+#include "StickerDatabase.hxx"
+#include "SongSticker.hxx"
#endif
#include <glib.h>
@@ -59,7 +64,9 @@ song_remove_event(void)
sticker_song_delete(removed_song);
#endif
- playlist_delete_song(&g_playlist, global_player_control, removed_song);
+ playlist_delete_song(&global_partition->playlist,
+ &global_partition->pc,
+ removed_song);
/* clear "removed_song" and send signal to update thread */
g_mutex_lock(remove_mutex);
diff --git a/src/update_remove.h b/src/UpdateRemove.hxx
index 479ef83ff..dc15622fc 100644
--- a/src/update_remove.h
+++ b/src/UpdateRemove.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_REMOVE_H
-#define MPD_UPDATE_REMOVE_H
+#ifndef MPD_UPDATE_REMOVE_HXX
+#define MPD_UPDATE_REMOVE_HXX
#include "check.h"
diff --git a/src/update_song.c b/src/UpdateSong.cxx
index 1126ad115..7994ea754 100644
--- a/src/update_song.c
+++ b/src/UpdateSong.cxx
@@ -18,33 +18,36 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_song.h"
-#include "update_internal.h"
-#include "update_io.h"
-#include "update_db.h"
-#include "update_container.h"
-#include "db_lock.h"
-#include "directory.h"
+#include "UpdateSong.hxx"
+#include "UpdateInternal.hxx"
+#include "UpdateIO.hxx"
+#include "UpdateDatabase.hxx"
+#include "UpdateContainer.hxx"
+#include "DatabaseLock.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "decoder_list.h"
#include "decoder_plugin.h"
+extern "C" {
+#include "decoder_list.h"
+}
+
#include <glib.h>
#include <unistd.h>
static void
-update_song_file2(struct directory *directory,
+update_song_file2(Directory *directory,
const char *name, const struct stat *st,
const struct decoder_plugin *plugin)
{
db_lock();
- struct song *song = directory_get_song(directory, name);
+ struct song *song = directory->FindSong(name);
db_unlock();
if (!directory_child_access(directory, name, R_OK)) {
g_warning("no read permissions on %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
if (song != NULL) {
db_lock();
delete_song(directory, song);
@@ -67,28 +70,27 @@ update_song_file2(struct directory *directory,
}
if (song == NULL) {
- g_debug("reading %s/%s",
- directory_get_path(directory), name);
+ g_debug("reading %s/%s", directory->GetPath(), name);
song = song_file_load(name, directory);
if (song == NULL) {
g_debug("ignoring unrecognized file %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
return;
}
db_lock();
- directory_add_song(directory, song);
+ directory->AddSong(song);
db_unlock();
modified = true;
g_message("added %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
} else if (st->st_mtime != song->mtime || walk_discard) {
g_message("updating %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
if (!song_file_update(song)) {
g_debug("deleting unrecognized file %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
db_lock();
delete_song(directory, song);
db_unlock();
@@ -99,12 +101,12 @@ update_song_file2(struct directory *directory,
}
bool
-update_song_file(struct directory *directory,
+update_song_file(Directory *directory,
const char *name, const char *suffix,
const struct stat *st)
{
const struct decoder_plugin *plugin =
- decoder_plugin_from_suffix(suffix, false);
+ decoder_plugin_from_suffix(suffix, nullptr);
if (plugin == NULL)
return false;
diff --git a/src/update_song.h b/src/UpdateSong.hxx
index cff63f576..60a532e3a 100644
--- a/src/update_song.h
+++ b/src/UpdateSong.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,18 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_SONG_H
-#define MPD_UPDATE_SONG_H
+#ifndef MPD_UPDATE_SONG_HXX
+#define MPD_UPDATE_SONG_HXX
#include "check.h"
-#include <stdbool.h>
#include <sys/stat.h>
-struct directory;
+struct Directory;
bool
-update_song_file(struct directory *directory,
+update_song_file(Directory *directory,
const char *name, const char *suffix,
const struct stat *st);
diff --git a/src/update_walk.c b/src/UpdateWalk.cxx
index 8554e8f3c..30e2ca989 100644
--- a/src/update_walk.c
+++ b/src/UpdateWalk.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,22 +18,25 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_walk.h"
-#include "update_io.h"
-#include "update_db.h"
-#include "update_song.h"
-#include "update_archive.h"
-#include "database.h"
-#include "db_lock.h"
-#include "exclude.h"
-#include "directory.h"
+#include "UpdateWalk.hxx"
+#include "UpdateIO.hxx"
+#include "UpdateDatabase.hxx"
+#include "UpdateSong.hxx"
+#include "UpdateArchive.hxx"
+#include "DatabaseLock.hxx"
+#include "DatabaseSimple.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "playlist_vector.h"
+#include "PlaylistVector.hxx"
+#include "Mapper.hxx"
+#include "ExcludeList.hxx"
+
+extern "C" {
#include "uri.h"
-#include "mapper.h"
#include "path.h"
#include "playlist_list.h"
#include "conf.h"
+}
#include <glib.h>
@@ -84,7 +87,7 @@ update_walk_global_finish(void)
}
static void
-directory_set_stat(struct directory *dir, const struct stat *st)
+directory_set_stat(Directory *dir, const struct stat *st)
{
dir->inode = st->st_ino;
dir->device = st->st_dev;
@@ -92,16 +95,16 @@ directory_set_stat(struct directory *dir, const struct stat *st)
}
static void
-remove_excluded_from_directory(struct directory *directory,
- GSList *exclude_list)
+remove_excluded_from_directory(Directory *directory,
+ const ExcludeList &exclude_list)
{
db_lock();
- struct directory *child, *n;
+ Directory *child, *n;
directory_for_each_child_safe(child, n, directory) {
- char *name_fs = utf8_to_fs_charset(directory_get_name(child));
+ char *name_fs = utf8_to_fs_charset(child->GetName());
- if (exclude_list_check(exclude_list, name_fs)) {
+ if (exclude_list.Check(name_fs)) {
delete_directory(child);
modified = true;
}
@@ -114,7 +117,7 @@ remove_excluded_from_directory(struct directory *directory,
assert(song->parent == directory);
char *name_fs = utf8_to_fs_charset(song->uri);
- if (exclude_list_check(exclude_list, name_fs)) {
+ if (exclude_list.Check(name_fs)) {
delete_song(directory, song);
modified = true;
}
@@ -126,9 +129,9 @@ remove_excluded_from_directory(struct directory *directory,
}
static void
-purge_deleted_from_directory(struct directory *directory)
+purge_deleted_from_directory(Directory *directory)
{
- struct directory *child, *n;
+ Directory *child, *n;
directory_for_each_child_safe(child, n, directory) {
if (directory_exists(child))
continue;
@@ -156,19 +159,21 @@ purge_deleted_from_directory(struct directory *directory)
g_free(path);
}
- struct playlist_metadata *pm, *np;
- directory_for_each_playlist_safe(pm, np, directory) {
- if (!directory_child_is_regular(directory, pm->name)) {
+ for (auto i = directory->playlists.begin(),
+ end = directory->playlists.end();
+ i != end;) {
+ if (!directory_child_is_regular(directory, i->name.c_str())) {
db_lock();
- playlist_vector_remove(&directory->playlists, pm->name);
+ i = directory->playlists.erase(i);
db_unlock();
- }
+ } else
+ ++i;
}
}
#ifndef G_OS_WIN32
static int
-update_directory_stat(struct directory *directory)
+update_directory_stat(Directory *directory)
{
struct stat st;
if (stat_directory(directory, &st) < 0)
@@ -180,7 +185,7 @@ update_directory_stat(struct directory *directory)
#endif
static int
-find_inode_ancestor(struct directory *parent, ino_t inode, dev_t device)
+find_inode_ancestor(Directory *parent, ino_t inode, dev_t device)
{
#ifndef G_OS_WIN32
while (parent) {
@@ -204,23 +209,24 @@ find_inode_ancestor(struct directory *parent, ino_t inode, dev_t device)
}
static bool
-update_playlist_file2(struct directory *directory,
+update_playlist_file2(Directory *directory,
const char *name, const char *suffix,
const struct stat *st)
{
if (!playlist_suffix_supported(suffix))
return false;
+ PlaylistInfo pi(name, st->st_mtime);
+
db_lock();
- if (playlist_vector_update_or_add(&directory->playlists, name,
- st->st_mtime))
+ if (directory->playlists.UpdateOrInsert(std::move(pi)))
modified = true;
db_unlock();
return true;
}
static bool
-update_regular_file(struct directory *directory,
+update_regular_file(Directory *directory,
const char *name, const struct stat *st)
{
const char *suffix = uri_get_suffix(name);
@@ -233,10 +239,10 @@ update_regular_file(struct directory *directory,
}
static bool
-update_directory(struct directory *directory, const struct stat *st);
+update_directory(Directory *directory, const struct stat *st);
static void
-update_directory_child(struct directory *directory,
+update_directory_child(Directory *directory,
const char *name, const struct stat *st)
{
assert(strchr(name, '/') == NULL);
@@ -248,8 +254,7 @@ update_directory_child(struct directory *directory,
return;
db_lock();
- struct directory *subdir =
- directory_make_child(directory, name);
+ Directory *subdir = directory->MakeChild(name);
db_unlock();
assert(directory == subdir->parent);
@@ -275,7 +280,7 @@ static bool skip_path(const char *path)
G_GNUC_PURE
static bool
-skip_symlink(const struct directory *directory, const char *utf8_name)
+skip_symlink(const Directory *directory, const char *utf8_name)
{
#ifndef WIN32
char *path_fs = map_directory_child_fs(directory, utf8_name);
@@ -348,7 +353,7 @@ skip_symlink(const struct directory *directory, const char *utf8_name)
}
static bool
-update_directory(struct directory *directory, const struct stat *st)
+update_directory(Directory *directory, const struct stat *st)
{
assert(S_ISDIR(st->st_mode));
@@ -367,12 +372,13 @@ update_directory(struct directory *directory, const struct stat *st)
}
char *exclude_path_fs = g_build_filename(path_fs, ".mpdignore", NULL);
- GSList *exclude_list = exclude_list_load(exclude_path_fs);
+ ExcludeList exclude_list;
+ exclude_list.LoadFile(exclude_path_fs);
g_free(exclude_path_fs);
g_free(path_fs);
- if (exclude_list != NULL)
+ if (!exclude_list.IsEmpty())
remove_excluded_from_directory(directory, exclude_list);
purge_deleted_from_directory(directory);
@@ -382,8 +388,7 @@ update_directory(struct directory *directory, const struct stat *st)
char *utf8;
struct stat st2;
- if (skip_path(ent->d_name) ||
- exclude_list_check(exclude_list, ent->d_name))
+ if (skip_path(ent->d_name) || exclude_list.Check(ent->d_name))
continue;
utf8 = fs_charset_to_utf8(ent->d_name);
@@ -404,8 +409,6 @@ update_directory(struct directory *directory, const struct stat *st)
g_free(utf8);
}
- exclude_list_free(exclude_list);
-
closedir(dir);
directory->mtime = st->st_mtime;
@@ -413,11 +416,11 @@ update_directory(struct directory *directory, const struct stat *st)
return true;
}
-static struct directory *
-directory_make_child_checked(struct directory *parent, const char *name_utf8)
+static Directory *
+directory_make_child_checked(Directory *parent, const char *name_utf8)
{
db_lock();
- struct directory *directory = directory_get_child(parent, name_utf8);
+ Directory *directory = parent->FindChild(name_utf8);
db_unlock();
if (directory != NULL)
@@ -434,21 +437,21 @@ directory_make_child_checked(struct directory *parent, const char *name_utf8)
/* if we're adding directory paths, make sure to delete filenames
with potentially the same name */
db_lock();
- struct song *conflicting = directory_get_song(parent, name_utf8);
+ struct song *conflicting = parent->FindSong(name_utf8);
if (conflicting)
delete_song(parent, conflicting);
- directory = directory_new_child(parent, name_utf8);
+ directory = parent->CreateChild(name_utf8);
db_unlock();
directory_set_stat(directory, &st);
return directory;
}
-static struct directory *
+static Directory *
directory_make_uri_parent_checked(const char *uri)
{
- struct directory *directory = db_get_root();
+ Directory *directory = db_get_root();
char *duplicated = g_strdup(uri);
char *name_utf8 = duplicated, *slash;
@@ -472,7 +475,7 @@ directory_make_uri_parent_checked(const char *uri)
static void
update_uri(const char *uri)
{
- struct directory *parent = directory_make_uri_parent_checked(uri);
+ Directory *parent = directory_make_uri_parent_checked(uri);
if (parent == NULL)
return;
@@ -497,7 +500,7 @@ update_walk(const char *path, bool discard)
if (path != NULL && !isRootDirectory(path)) {
update_uri(path);
} else {
- struct directory *directory = db_get_root();
+ Directory *directory = db_get_root();
struct stat st;
if (stat_directory(directory, &st) == 0)
diff --git a/src/update_walk.h b/src/UpdateWalk.hxx
index ab1e41fb9..62c0d0a8e 100644
--- a/src/update_walk.h
+++ b/src/UpdateWalk.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_WALK_H
-#define MPD_UPDATE_WALK_H
+#ifndef MPD_UPDATE_WALK_HXX
+#define MPD_UPDATE_WALK_HXX
#include "check.h"
-#include <stdbool.h>
-
void
update_walk_global_init(void);
diff --git a/src/main_win32.c b/src/Win32Main.cxx
index aac7ad886..8543ea108 100644
--- a/src/main_win32.c
+++ b/src/Win32Main.cxx
@@ -18,12 +18,15 @@
*/
#include "config.h"
-#include "main.h"
+#include "Main.hxx"
#ifdef WIN32
#include "mpd_error.h"
+
+extern "C" {
#include "event_pipe.h"
+}
#include <glib.h>
diff --git a/src/audio_check.h b/src/audio_check.h
index 9f71cf9c0..e2302126f 100644
--- a/src/audio_check.h
+++ b/src/audio_check.h
@@ -28,7 +28,7 @@
/**
* The GLib quark used for errors reported by this library.
*/
-G_GNUC_CONST
+gcc_const
static inline GQuark
audio_format_quark(void)
{
diff --git a/src/audio_format.h b/src/audio_format.h
index bf77add3b..d86ade5ee 100644
--- a/src/audio_format.h
+++ b/src/audio_format.h
@@ -20,7 +20,8 @@
#ifndef MPD_AUDIO_FORMAT_H
#define MPD_AUDIO_FORMAT_H
-#include <glib.h>
+#include "gcc.h"
+
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
@@ -189,7 +190,7 @@ audio_valid_channel_count(unsigned channels)
* Returns false if the format is not valid for playback with MPD.
* This function performs some basic validity checks.
*/
-G_GNUC_PURE
+gcc_pure
static inline bool audio_format_valid(const struct audio_format *af)
{
return audio_valid_sample_rate(af->sample_rate) &&
@@ -201,7 +202,7 @@ static inline bool audio_format_valid(const struct audio_format *af)
* Returns false if the format mask is not valid for playback with
* MPD. This function performs some basic validity checks.
*/
-G_GNUC_PURE
+gcc_pure
static inline bool audio_format_mask_valid(const struct audio_format *af)
{
return (af->sample_rate == 0 ||
@@ -223,7 +224,7 @@ void
audio_format_mask_apply(struct audio_format *af,
const struct audio_format *mask);
-G_GNUC_CONST
+gcc_const
static inline unsigned
sample_format_size(enum sample_format format)
{
@@ -254,7 +255,7 @@ sample_format_size(enum sample_format format)
/**
* Returns the size of each (mono) sample in bytes.
*/
-G_GNUC_PURE
+gcc_pure
static inline unsigned audio_format_sample_size(const struct audio_format *af)
{
return sample_format_size((enum sample_format)af->format);
@@ -263,7 +264,7 @@ static inline unsigned audio_format_sample_size(const struct audio_format *af)
/**
* Returns the size of each full frame in bytes.
*/
-G_GNUC_PURE
+gcc_pure
static inline unsigned
audio_format_frame_size(const struct audio_format *af)
{
@@ -274,7 +275,7 @@ audio_format_frame_size(const struct audio_format *af)
* Returns the floating point factor which converts a time span to a
* storage size in bytes.
*/
-G_GNUC_PURE
+gcc_pure
static inline double audio_format_time_to_size(const struct audio_format *af)
{
return af->sample_rate * audio_format_frame_size(af);
@@ -287,7 +288,7 @@ static inline double audio_format_time_to_size(const struct audio_format *af)
* @param format a #sample_format enum value
* @return the string
*/
-G_GNUC_PURE G_GNUC_MALLOC
+gcc_pure gcc_malloc
const char *
sample_format_to_string(enum sample_format format);
@@ -299,7 +300,7 @@ sample_format_to_string(enum sample_format format);
* @param s a buffer to print into
* @return the string, or NULL if the #audio_format object is invalid
*/
-G_GNUC_PURE G_GNUC_MALLOC
+gcc_pure gcc_malloc
const char *
audio_format_to_string(const struct audio_format *af,
struct audio_format_string *s);
diff --git a/src/audio_parser.h b/src/audio_parser.h
index a963eb467..49926999e 100644
--- a/src/audio_parser.h
+++ b/src/audio_parser.h
@@ -25,7 +25,7 @@
#ifndef AUDIO_PARSER_H
#define AUDIO_PARSER_H
-#include <glib.h>
+#include "gerror.h"
#include <stdbool.h>
diff --git a/src/buffer.c b/src/buffer.c
deleted file mode 100644
index 559f39a9a..000000000
--- a/src/buffer.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "buffer.h"
-#include "chunk.h"
-#include "poison.h"
-
-#include <glib.h>
-
-#include <assert.h>
-
-struct music_buffer {
- struct music_chunk *chunks;
- unsigned num_chunks;
-
- struct music_chunk *available;
-
- /** a mutex which protects #available */
- GMutex *mutex;
-
-#ifndef NDEBUG
- unsigned num_allocated;
-#endif
-};
-
-struct music_buffer *
-music_buffer_new(unsigned num_chunks)
-{
- struct music_buffer *buffer;
- struct music_chunk *chunk;
-
- assert(num_chunks > 0);
-
- buffer = g_new(struct music_buffer, 1);
-
- buffer->chunks = g_new(struct music_chunk, num_chunks);
- buffer->num_chunks = num_chunks;
-
- chunk = buffer->available = buffer->chunks;
- poison_undefined(chunk, sizeof(*chunk));
-
- for (unsigned i = 1; i < num_chunks; ++i) {
- chunk->next = &buffer->chunks[i];
- chunk = chunk->next;
- poison_undefined(chunk, sizeof(*chunk));
- }
-
- chunk->next = NULL;
-
- buffer->mutex = g_mutex_new();
-
-#ifndef NDEBUG
- buffer->num_allocated = 0;
-#endif
-
- return buffer;
-}
-
-void
-music_buffer_free(struct music_buffer *buffer)
-{
- assert(buffer->chunks != NULL);
- assert(buffer->num_chunks > 0);
- assert(buffer->num_allocated == 0);
-
- g_mutex_free(buffer->mutex);
- g_free(buffer->chunks);
- g_free(buffer);
-}
-
-unsigned
-music_buffer_size(const struct music_buffer *buffer)
-{
- return buffer->num_chunks;
-}
-
-struct music_chunk *
-music_buffer_allocate(struct music_buffer *buffer)
-{
- struct music_chunk *chunk;
-
- g_mutex_lock(buffer->mutex);
-
- chunk = buffer->available;
- if (chunk != NULL) {
- buffer->available = chunk->next;
- music_chunk_init(chunk);
-
-#ifndef NDEBUG
- ++buffer->num_allocated;
-#endif
- }
-
- g_mutex_unlock(buffer->mutex);
- return chunk;
-}
-
-void
-music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk)
-{
- assert(buffer != NULL);
- assert(chunk != NULL);
-
- if (chunk->other != NULL)
- music_buffer_return(buffer, chunk->other);
-
- g_mutex_lock(buffer->mutex);
-
- music_chunk_free(chunk);
- poison_undefined(chunk, sizeof(*chunk));
-
- chunk->next = buffer->available;
- buffer->available = chunk;
-
-#ifndef NDEBUG
- --buffer->num_allocated;
-#endif
-
- g_mutex_unlock(buffer->mutex);
-}
diff --git a/src/chunk.c b/src/chunk.c
deleted file mode 100644
index 1eb96f4b9..000000000
--- a/src/chunk.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "chunk.h"
-#include "audio_format.h"
-#include "tag.h"
-
-#include <assert.h>
-
-void
-music_chunk_init(struct music_chunk *chunk)
-{
- chunk->other = NULL;
- chunk->length = 0;
- chunk->tag = NULL;
- chunk->replay_gain_serial = 0;
-}
-
-void
-music_chunk_free(struct music_chunk *chunk)
-{
- if (chunk->tag != NULL)
- tag_free(chunk->tag);
-}
-
-#ifndef NDEBUG
-bool
-music_chunk_check_format(const struct music_chunk *chunk,
- const struct audio_format *audio_format)
-{
- assert(chunk != NULL);
- assert(audio_format != NULL);
- assert(audio_format_valid(audio_format));
-
- return chunk->length == 0 ||
- audio_format_equals(&chunk->audio_format, audio_format);
-}
-#endif
-
-void *
-music_chunk_write(struct music_chunk *chunk,
- const struct audio_format *audio_format,
- float data_time, uint16_t bit_rate,
- size_t *max_length_r)
-{
- const size_t frame_size = audio_format_frame_size(audio_format);
- size_t num_frames;
-
- assert(music_chunk_check_format(chunk, audio_format));
- assert(chunk->length == 0 || audio_format_valid(&chunk->audio_format));
-
- if (chunk->length == 0) {
- /* if the chunk is empty, nobody has set bitRate and
- times yet */
-
- chunk->bit_rate = bit_rate;
- chunk->times = data_time;
- }
-
- num_frames = (sizeof(chunk->data) - chunk->length) / frame_size;
- if (num_frames == 0)
- return NULL;
-
-#ifndef NDEBUG
- chunk->audio_format = *audio_format;
-#endif
-
- *max_length_r = num_frames * frame_size;
- return chunk->data + chunk->length;
-}
-
-bool
-music_chunk_expand(struct music_chunk *chunk,
- const struct audio_format *audio_format, size_t length)
-{
- const size_t frame_size = audio_format_frame_size(audio_format);
-
- assert(chunk != NULL);
- assert(chunk->length + length <= sizeof(chunk->data));
- assert(audio_format_equals(&chunk->audio_format, audio_format));
-
- chunk->length += length;
-
- return chunk->length + frame_size > sizeof(chunk->data);
-}
diff --git a/src/client_message.c b/src/client_message.c
deleted file mode 100644
index b681b4e7f..000000000
--- a/src/client_message.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "client_message.h"
-
-#include <assert.h>
-#include <glib.h>
-
-G_GNUC_PURE
-static bool
-valid_channel_char(const char ch)
-{
- return g_ascii_isalnum(ch) ||
- ch == '_' || ch == '-' || ch == '.' || ch == ':';
-}
-
-bool
-client_message_valid_channel_name(const char *name)
-{
- do {
- if (!valid_channel_char(*name))
- return false;
- } while (*++name != 0);
-
- return true;
-}
-
-void
-client_message_init_null(struct client_message *msg)
-{
- assert(msg != NULL);
-
- msg->channel = NULL;
- msg->message = NULL;
-}
-
-void
-client_message_init(struct client_message *msg,
- const char *channel, const char *message)
-{
- assert(msg != NULL);
-
- msg->channel = g_strdup(channel);
- msg->message = g_strdup(message);
-}
-
-void
-client_message_copy(struct client_message *dest,
- const struct client_message *src)
-{
- assert(dest != NULL);
- assert(src != NULL);
- assert(client_message_defined(src));
-
- client_message_init(dest, src->channel, src->message);
-}
-
-struct client_message *
-client_message_dup(const struct client_message *src)
-{
- struct client_message *dest = g_slice_new(struct client_message);
- client_message_copy(dest, src);
- return dest;
-}
-
-void
-client_message_deinit(struct client_message *msg)
-{
- assert(msg != NULL);
-
- g_free(msg->channel);
- g_free(msg->message);
-}
-
-void
-client_message_free(struct client_message *msg)
-{
- client_message_deinit(msg);
- g_slice_free(struct client_message, msg);
-}
diff --git a/src/client_message.h b/src/client_message.h
deleted file mode 100644
index 38c2e7615..000000000
--- a/src/client_message.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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_CLIENT_MESSAGE_H
-#define MPD_CLIENT_MESSAGE_H
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <glib.h>
-
-/**
- * A client-to-client message.
- */
-struct client_message {
- char *channel;
-
- char *message;
-};
-
-G_GNUC_PURE
-bool
-client_message_valid_channel_name(const char *name);
-
-G_GNUC_PURE
-static inline bool
-client_message_defined(const struct client_message *msg)
-{
- assert(msg != NULL);
- assert((msg->channel == NULL) == (msg->message == NULL));
-
- return msg->channel != NULL;
-}
-
-void
-client_message_init_null(struct client_message *msg);
-
-void
-client_message_init(struct client_message *msg,
- const char *channel, const char *message);
-
-void
-client_message_copy(struct client_message *dest,
- const struct client_message *src);
-
-G_GNUC_MALLOC
-struct client_message *
-client_message_dup(const struct client_message *src);
-
-void
-client_message_deinit(struct client_message *msg);
-
-void
-client_message_free(struct client_message *msg);
-
-#endif
diff --git a/src/client_subscribe.c b/src/client_subscribe.c
deleted file mode 100644
index c65a7ed31..000000000
--- a/src/client_subscribe.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "client_subscribe.h"
-#include "client_internal.h"
-#include "client_idle.h"
-#include "idle.h"
-
-#include <string.h>
-
-G_GNUC_PURE
-static GSList *
-client_find_subscription(const struct client *client, const char *channel)
-{
- for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i))
- if (strcmp((const char *)i->data, channel) == 0)
- return i;
-
- return NULL;
-}
-
-enum client_subscribe_result
-client_subscribe(struct client *client, const char *channel)
-{
- assert(client != NULL);
- assert(channel != NULL);
-
- if (!client_message_valid_channel_name(channel))
- return CLIENT_SUBSCRIBE_INVALID;
-
- if (client_find_subscription(client, channel) != NULL)
- return CLIENT_SUBSCRIBE_ALREADY;
-
- if (client->num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS)
- return CLIENT_SUBSCRIBE_FULL;
-
- client->subscriptions = g_slist_prepend(client->subscriptions,
- g_strdup(channel));
- ++client->num_subscriptions;
-
- idle_add(IDLE_SUBSCRIPTION);
-
- return CLIENT_SUBSCRIBE_OK;
-}
-
-bool
-client_unsubscribe(struct client *client, const char *channel)
-{
- GSList *i = client_find_subscription(client, channel);
- if (i == NULL)
- return false;
-
- assert(client->num_subscriptions > 0);
-
- client->subscriptions = g_slist_remove(client->subscriptions, i->data);
- --client->num_subscriptions;
-
- idle_add(IDLE_SUBSCRIPTION);
-
- assert((client->num_subscriptions == 0) ==
- (client->subscriptions == NULL));
-
- return true;
-}
-
-void
-client_unsubscribe_all(struct client *client)
-{
- for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i))
- g_free(i->data);
-
- g_slist_free(client->subscriptions);
- client->subscriptions = NULL;
- client->num_subscriptions = 0;
-}
-
-bool
-client_push_message(struct client *client, const struct client_message *msg)
-{
- assert(client != NULL);
- assert(msg != NULL);
- assert(client_message_defined(msg));
-
- if (client->num_messages >= CLIENT_MAX_MESSAGES ||
- client_find_subscription(client, msg->channel) == NULL)
- return false;
-
- if (client->messages == NULL)
- client_idle_add(client, IDLE_MESSAGE);
-
- client->messages = g_slist_prepend(client->messages,
- client_message_dup(msg));
- ++client->num_messages;
-
- return true;
-}
-
-GSList *
-client_read_messages(struct client *client)
-{
- GSList *messages = g_slist_reverse(client->messages);
-
- client->messages = NULL;
- client->num_messages = 0;
-
- return messages;
-}
diff --git a/src/clock.h b/src/clock.h
index 4ece35ab1..f1338938f 100644
--- a/src/clock.h
+++ b/src/clock.h
@@ -20,21 +20,21 @@
#ifndef MPD_CLOCK_H
#define MPD_CLOCK_H
-#include <glib.h>
+#include "gcc.h"
#include <stdint.h>
/**
* Returns the value of a monotonic clock in milliseconds.
*/
-G_GNUC_PURE
+gcc_pure
unsigned
monotonic_clock_ms(void);
/**
* Returns the value of a monotonic clock in microseconds.
*/
-G_GNUC_PURE
+gcc_pure
uint64_t
monotonic_clock_us(void);
diff --git a/src/command.c b/src/command.c
deleted file mode 100644
index c405925f2..000000000
--- a/src/command.c
+++ /dev/null
@@ -1,2298 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "command.h"
-#include "protocol/argparser.h"
-#include "protocol/result.h"
-#include "player_control.h"
-#include "playlist.h"
-#include "playlist_print.h"
-#include "playlist_save.h"
-#include "playlist_queue.h"
-#include "playlist_error.h"
-#include "queue_print.h"
-#include "ls.h"
-#include "uri.h"
-#include "decoder_print.h"
-#include "directory.h"
-#include "database.h"
-#include "update.h"
-#include "volume.h"
-#include "stats.h"
-#include "permission.h"
-#include "tokenizer.h"
-#include "stored_playlist.h"
-#include "ack.h"
-#include "output_command.h"
-#include "output_print.h"
-#include "locate.h"
-#include "dbUtils.h"
-#include "db_error.h"
-#include "db_print.h"
-#include "db_selection.h"
-#include "db_lock.h"
-#include "tag.h"
-#include "client.h"
-#include "client_idle.h"
-#include "client_internal.h"
-#include "client_subscribe.h"
-#include "client_file.h"
-#include "tag_print.h"
-#include "path.h"
-#include "replay_gain_config.h"
-#include "idle.h"
-#include "mapper.h"
-#include "song.h"
-#include "song_print.h"
-
-#ifdef ENABLE_SQLITE
-#include "sticker.h"
-#include "sticker_print.h"
-#include "song_sticker.h"
-#endif
-
-#include <assert.h>
-#include <time.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#define COMMAND_STATUS_STATE "state"
-#define COMMAND_STATUS_REPEAT "repeat"
-#define COMMAND_STATUS_SINGLE "single"
-#define COMMAND_STATUS_CONSUME "consume"
-#define COMMAND_STATUS_RANDOM "random"
-#define COMMAND_STATUS_PLAYLIST "playlist"
-#define COMMAND_STATUS_PLAYLIST_LENGTH "playlistlength"
-#define COMMAND_STATUS_SONG "song"
-#define COMMAND_STATUS_SONGID "songid"
-#define COMMAND_STATUS_NEXTSONG "nextsong"
-#define COMMAND_STATUS_NEXTSONGID "nextsongid"
-#define COMMAND_STATUS_TIME "time"
-#define COMMAND_STATUS_BITRATE "bitrate"
-#define COMMAND_STATUS_ERROR "error"
-#define COMMAND_STATUS_CROSSFADE "xfade"
-#define COMMAND_STATUS_MIXRAMPDB "mixrampdb"
-#define COMMAND_STATUS_MIXRAMPDELAY "mixrampdelay"
-#define COMMAND_STATUS_AUDIO "audio"
-#define COMMAND_STATUS_UPDATING_DB "updating_db"
-
-/*
- * The most we ever use is for search/find, and that limits it to the
- * number of tags we can have. Add one for the command, and one extra
- * to catch errors clients may send us
- */
-#define COMMAND_ARGV_MAX (2+(TAG_NUM_OF_ITEM_TYPES*2))
-
-/* if min: -1 don't check args *
- * if max: -1 no max args */
-struct command {
- const char *cmd;
- unsigned permission;
- int min;
- int max;
- enum command_return (*handler)(struct client *client, int argc, char **argv);
-};
-
-static enum command_return
-print_playlist_result(struct client *client,
- enum playlist_result result)
-{
- switch (result) {
- case PLAYLIST_RESULT_SUCCESS:
- return COMMAND_RETURN_OK;
-
- case PLAYLIST_RESULT_ERRNO:
- command_error(client, ACK_ERROR_SYSTEM, "%s",
- g_strerror(errno));
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_DENIED:
- command_error(client, ACK_ERROR_PERMISSION, "Access denied");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_NO_SUCH_SONG:
- command_error(client, ACK_ERROR_NO_EXIST, "No such song");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_NO_SUCH_LIST:
- command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_LIST_EXISTS:
- command_error(client, ACK_ERROR_EXIST,
- "Playlist already exists");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_BAD_NAME:
- command_error(client, ACK_ERROR_ARG,
- "playlist name is invalid: "
- "playlist names may not contain slashes,"
- " newlines or carriage returns");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_BAD_RANGE:
- command_error(client, ACK_ERROR_ARG, "Bad song index");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_NOT_PLAYING:
- command_error(client, ACK_ERROR_PLAYER_SYNC, "Not playing");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_TOO_LARGE:
- command_error(client, ACK_ERROR_PLAYLIST_MAX,
- "playlist is at the max size");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_DISABLED:
- command_error(client, ACK_ERROR_UNKNOWN,
- "stored playlist support is disabled");
- return COMMAND_RETURN_ERROR;
- }
-
- assert(0);
- return COMMAND_RETURN_ERROR;
-}
-
-/**
- * Send the GError to the client and free the GError.
- */
-static enum command_return
-print_error(struct client *client, GError *error)
-{
- assert(client != NULL);
- assert(error != NULL);
-
- g_warning("%s", error->message);
-
- if (error->domain == playlist_quark()) {
- enum playlist_result result = error->code;
- g_error_free(error);
- return print_playlist_result(client, result);
- } else if (error->domain == ack_quark()) {
- command_error(client, error->code, "%s", error->message);
- g_error_free(error);
- return COMMAND_RETURN_ERROR;
- } else if (error->domain == db_quark()) {
- switch ((enum db_error)error->code) {
- case DB_DISABLED:
- command_error(client, ACK_ERROR_NO_EXIST, "%s",
- error->message);
- g_error_free(error);
- return COMMAND_RETURN_ERROR;
-
- case DB_NOT_FOUND:
- g_error_free(error);
- command_error(client, ACK_ERROR_NO_EXIST, "Not found");
- return COMMAND_RETURN_ERROR;
- }
- } else if (error->domain == g_file_error_quark()) {
- command_error(client, ACK_ERROR_SYSTEM, "%s",
- g_strerror(error->code));
- g_error_free(error);
- return COMMAND_RETURN_ERROR;
- }
-
- g_error_free(error);
- command_error(client, ACK_ERROR_UNKNOWN, "error");
- return COMMAND_RETURN_ERROR;
-}
-
-static void
-print_spl_list(struct client *client, GPtrArray *list)
-{
- for (unsigned i = 0; i < list->len; ++i) {
- struct stored_playlist_info *playlist =
- g_ptr_array_index(list, i);
- time_t t;
-#ifndef WIN32
- struct tm tm;
-#endif
- char timestamp[32];
-
- client_printf(client, "playlist: %s\n", playlist->name);
-
- t = playlist->mtime;
- strftime(timestamp, sizeof(timestamp),
-#ifdef G_OS_WIN32
- "%Y-%m-%dT%H:%M:%SZ",
- gmtime(&t)
-#else
- "%FT%TZ",
- gmtime_r(&t, &tm)
-#endif
- );
- client_printf(client, "Last-Modified: %s\n", timestamp);
- }
-}
-
-static enum command_return
-handle_urlhandlers(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- if (client_is_local(client))
- client_puts(client, "handler: file://\n");
- print_supported_uri_schemes(client);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_decoders(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- decoder_list_print(client);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_tagtypes(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- tag_print_types(client);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_play(struct client *client, int argc, char *argv[])
-{
- int song = -1;
- enum playlist_result result;
-
- if (argc == 2 && !check_int(client, &song, argv[1]))
- return COMMAND_RETURN_ERROR;
- result = playlist_play(&g_playlist, client->player_control, song);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_playid(struct client *client, int argc, char *argv[])
-{
- int id = -1;
- enum playlist_result result;
-
- if (argc == 2 && !check_int(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_play_id(&g_playlist, client->player_control, id);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_stop(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_stop(&g_playlist, client->player_control);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_currentsong(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_print_current(client, &g_playlist);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_pause(struct client *client,
- int argc, char *argv[])
-{
- if (argc == 2) {
- bool pause_flag;
- if (!check_bool(client, &pause_flag, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- pc_set_pause(client->player_control, pause_flag);
- } else
- pc_pause(client->player_control);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_status(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- const char *state = NULL;
- struct player_status player_status;
- int updateJobId;
- char *error;
- int song;
-
- pc_get_status(client->player_control, &player_status);
-
- switch (player_status.state) {
- case PLAYER_STATE_STOP:
- state = "stop";
- break;
- case PLAYER_STATE_PAUSE:
- state = "pause";
- break;
- case PLAYER_STATE_PLAY:
- state = "play";
- break;
- }
-
- 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_CROSSFADE ": %i\n"
- COMMAND_STATUS_MIXRAMPDB ": %f\n"
- COMMAND_STATUS_MIXRAMPDELAY ": %f\n"
- COMMAND_STATUS_STATE ": %s\n",
- volume_level_get(),
- playlist_get_repeat(&g_playlist),
- playlist_get_random(&g_playlist),
- playlist_get_single(&g_playlist),
- playlist_get_consume(&g_playlist),
- playlist_get_version(&g_playlist),
- playlist_get_length(&g_playlist),
- (int)(pc_get_cross_fade(client->player_control) + 0.5),
- pc_get_mixramp_db(client->player_control),
- pc_get_mixramp_delay(client->player_control),
- state);
-
- song = playlist_get_current_song(&g_playlist);
- if (song >= 0) {
- client_printf(client,
- COMMAND_STATUS_SONG ": %i\n"
- COMMAND_STATUS_SONGID ": %u\n",
- song, playlist_get_song_id(&g_playlist, song));
- }
-
- if (player_status.state != PLAYER_STATE_STOP) {
- struct audio_format_string af_string;
-
- client_printf(client,
- COMMAND_STATUS_TIME ": %i:%i\n"
- "elapsed: %1.3f\n"
- COMMAND_STATUS_BITRATE ": %u\n"
- COMMAND_STATUS_AUDIO ": %s\n",
- (int)(player_status.elapsed_time + 0.5),
- (int)(player_status.total_time + 0.5),
- player_status.elapsed_time,
- player_status.bit_rate,
- audio_format_to_string(&player_status.audio_format,
- &af_string));
- }
-
- if ((updateJobId = isUpdatingDB())) {
- client_printf(client,
- COMMAND_STATUS_UPDATING_DB ": %i\n",
- updateJobId);
- }
-
- error = pc_get_error_message(client->player_control);
- if (error != NULL) {
- client_printf(client,
- COMMAND_STATUS_ERROR ": %s\n",
- error);
- g_free(error);
- }
-
- song = playlist_get_next_song(&g_playlist);
- if (song >= 0) {
- client_printf(client,
- COMMAND_STATUS_NEXTSONG ": %i\n"
- COMMAND_STATUS_NEXTSONGID ": %u\n",
- song, playlist_get_song_id(&g_playlist, song));
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_kill(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- return COMMAND_RETURN_KILL;
-}
-
-static enum command_return
-handle_close(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- return COMMAND_RETURN_CLOSE;
-}
-
-static enum command_return
-handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- char *uri = argv[1];
- enum playlist_result result;
-
- if (strncmp(uri, "file:///", 8) == 0) {
- const char *path = uri + 7;
-
- GError *error = NULL;
- if (!client_allow_file(client, path, &error))
- return print_error(client, error);
-
- result = playlist_append_file(&g_playlist,
- client->player_control,
- path,
- NULL);
- return print_playlist_result(client, result);
- }
-
- if (uri_has_scheme(uri)) {
- if (!uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return COMMAND_RETURN_ERROR;
- }
-
- result = playlist_append_uri(&g_playlist,
- client->player_control,
- uri, NULL);
- return print_playlist_result(client, result);
- }
-
- GError *error = NULL;
- return addAllIn(client->player_control, uri, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_addid(struct client *client, int argc, char *argv[])
-{
- char *uri = argv[1];
- unsigned added_id;
- enum playlist_result result;
-
- if (strncmp(uri, "file:///", 8) == 0) {
- const char *path = uri + 7;
-
- GError *error = NULL;
- if (!client_allow_file(client, path, &error))
- return print_error(client, error);
-
- result = playlist_append_file(&g_playlist,
- client->player_control,
- path,
- &added_id);
- } else {
- if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return COMMAND_RETURN_ERROR;
- }
-
- result = playlist_append_uri(&g_playlist,
- client->player_control,
- uri, &added_id);
- }
-
- if (result != PLAYLIST_RESULT_SUCCESS)
- return print_playlist_result(client, result);
-
- if (argc == 3) {
- unsigned to;
- if (!check_unsigned(client, &to, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_move_id(&g_playlist, client->player_control,
- added_id, to);
- if (result != PLAYLIST_RESULT_SUCCESS) {
- enum command_return ret =
- print_playlist_result(client, result);
- playlist_delete_id(&g_playlist, client->player_control,
- added_id);
- return ret;
- }
- }
-
- client_printf(client, "Id: %u\n", added_id);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_delete(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned start, end;
- enum playlist_result result;
-
- if (!check_range(client, &start, &end, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_delete_range(&g_playlist, client->player_control,
- start, end);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_deleteid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned id;
- enum playlist_result result;
-
- if (!check_unsigned(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_delete_id(&g_playlist, client->player_control, id);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_playlist(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_print_uris(client, &g_playlist);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_shuffle(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- unsigned start = 0, end = queue_length(&g_playlist.queue);
- if (argc == 2 && !check_range(client, &start, &end, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_shuffle(&g_playlist, client->player_control, start, end);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_clear(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_clear(&g_playlist, client->player_control);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_save(struct client *client,
- G_GNUC_UNUSED int argc, char *argv[])
-{
- enum playlist_result result;
-
- result = spl_save_playlist(argv[1], &g_playlist);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_load(struct client *client, int argc, char *argv[])
-{
- unsigned start_index, end_index;
-
- if (argc < 3) {
- start_index = 0;
- end_index = G_MAXUINT;
- } else if (!check_range(client, &start_index, &end_index, argv[2]))
- return COMMAND_RETURN_ERROR;
-
- enum playlist_result result;
-
- result = playlist_open_into_queue(argv[1],
- start_index, end_index,
- &g_playlist,
- client->player_control, true);
- if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
- return print_playlist_result(client, result);
-
- GError *error = NULL;
- if (playlist_load_spl(&g_playlist, client->player_control,
- argv[1], start_index, end_index,
- &error))
- return COMMAND_RETURN_OK;
-
- if (error->domain == playlist_quark() &&
- error->code == PLAYLIST_RESULT_BAD_NAME)
- /* the message for BAD_NAME is confusing when the
- client wants to load a playlist file from the music
- directory; patch the GError object to show "no such
- playlist" instead */
- error->code = PLAYLIST_RESULT_NO_SUCH_LIST;
-
- return print_error(client, error);
-}
-
-static enum command_return
-handle_listplaylist(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- if (playlist_file_print(client, argv[1], false))
- return COMMAND_RETURN_OK;
-
- GError *error = NULL;
- return spl_print(client, argv[1], false, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_listplaylistinfo(struct client *client,
- G_GNUC_UNUSED int argc, char *argv[])
-{
- if (playlist_file_print(client, argv[1], true))
- return COMMAND_RETURN_OK;
-
- GError *error = NULL;
- return spl_print(client, argv[1], true, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_lsinfo(struct client *client, int argc, char *argv[])
-{
- const char *uri;
-
- if (argc == 2)
- uri = argv[1];
- else
- /* default is root directory */
- uri = "";
-
- if (strncmp(uri, "file:///", 8) == 0) {
- /* print information about an arbitrary local file */
- const char *path = uri + 7;
-
- GError *error = NULL;
- if (!client_allow_file(client, path, &error))
- return print_error(client, error);
-
- struct song *song = song_file_load(path, NULL);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "No such file");
- return COMMAND_RETURN_ERROR;
- }
-
- song_print_info(client, song);
- song_free(song);
- return COMMAND_RETURN_OK;
- }
-
- struct db_selection selection;
- db_selection_init(&selection, uri, false);
-
- GError *error = NULL;
- if (!db_selection_print(client, &selection, true, &error))
- return print_error(client, error);
-
- if (isRootDirectory(uri)) {
- GPtrArray *list = spl_list(NULL);
- if (list != NULL) {
- print_spl_list(client, list);
- spl_list_free(list);
- }
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_rm(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- GError *error = NULL;
- return spl_delete(argv[1], &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_rename(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- GError *error = NULL;
- return spl_rename(argv[1], argv[2], &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_plchanges(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- uint32_t version;
-
- if (!check_uint32(client, &version, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_print_changes_info(client, &g_playlist, version);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_plchangesposid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- uint32_t version;
-
- if (!check_uint32(client, &version, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_print_changes_position(client, &g_playlist, version);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistinfo(struct client *client, int argc, char *argv[])
-{
- unsigned start = 0, end = G_MAXUINT;
- bool ret;
-
- if (argc == 2 && !check_range(client, &start, &end, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- ret = playlist_print_info(client, &g_playlist, start, end);
- if (!ret)
- return print_playlist_result(client,
- PLAYLIST_RESULT_BAD_RANGE);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistid(struct client *client, int argc, char *argv[])
-{
- if (argc >= 2) {
- unsigned id;
- if (!check_unsigned(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- bool ret = playlist_print_id(client, &g_playlist, id);
- if (!ret)
- return print_playlist_result(client,
- PLAYLIST_RESULT_NO_SUCH_SONG);
- } else {
- playlist_print_info(client, &g_playlist, 0, G_MAXUINT);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_find(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret = findSongsIn(client, "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_findadd(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret =
- findAddIn(client->player_control, "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_search(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret = searchForSongsIn(client, "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_searchadd(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret = search_add_songs(client->player_control,
- "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_searchaddpl(struct client *client, int argc, char *argv[])
-{
- const char *playlist = argv[1];
-
- struct locate_item_list *list =
- locate_item_list_parse(argv + 2, argc - 2);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret =
- search_add_to_playlist("", playlist, list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_count(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret =
- searchStatsForSongsIn(client, "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_playlistfind(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- playlist_print_find(client, &g_playlist, list);
-
- locate_item_list_free(list);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistsearch(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- playlist_print_search(client, &g_playlist, list);
-
- locate_item_list_free(list);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistdelete(struct client *client,
- G_GNUC_UNUSED int argc, char *argv[]) {
- char *playlist = argv[1];
- unsigned from;
-
- if (!check_unsigned(client, &from, argv[2]))
- return COMMAND_RETURN_ERROR;
-
- GError *error = NULL;
- return spl_remove_index(playlist, from, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_playlistmove(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- char *playlist = argv[1];
- unsigned from, to;
-
- if (!check_unsigned(client, &from, argv[2]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &to, argv[3]))
- return COMMAND_RETURN_ERROR;
-
- GError *error = NULL;
- return spl_move_index(playlist, from, to, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_update(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *path = NULL;
- unsigned ret;
-
- assert(argc <= 2);
- if (argc == 2) {
- path = argv[1];
-
- if (*path == 0 || strcmp(path, "/") == 0)
- /* backwards compatibility with MPD 0.15 */
- path = NULL;
- else if (!uri_safe_local(path)) {
- command_error(client, ACK_ERROR_ARG,
- "Malformed path");
- return COMMAND_RETURN_ERROR;
- }
- }
-
- ret = update_enqueue(path, false);
- if (ret > 0) {
- client_printf(client, "updating_db: %i\n", ret);
- return COMMAND_RETURN_OK;
- } else {
- command_error(client, ACK_ERROR_UPDATE_ALREADY,
- "already updating");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-static enum command_return
-handle_rescan(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *path = NULL;
- unsigned ret;
-
- assert(argc <= 2);
- if (argc == 2) {
- path = argv[1];
-
- if (!uri_safe_local(path)) {
- command_error(client, ACK_ERROR_ARG,
- "Malformed path");
- return COMMAND_RETURN_ERROR;
- }
- }
-
- ret = update_enqueue(path, true);
- if (ret > 0) {
- client_printf(client, "updating_db: %i\n", ret);
- return COMMAND_RETURN_OK;
- } else {
- command_error(client, ACK_ERROR_UPDATE_ALREADY,
- "already updating");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-static enum command_return
-handle_next(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- /* single mode is not considered when this is user who
- * wants to change song. */
- const bool single = g_playlist.queue.single;
- g_playlist.queue.single = false;
-
- playlist_next(&g_playlist, client->player_control);
-
- g_playlist.queue.single = single;
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_previous(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_previous(&g_playlist, client->player_control);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_prio(struct client *client, int argc, char *argv[])
-{
- unsigned priority;
-
- if (!check_unsigned(client, &priority, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- if (priority > 0xff) {
- command_error(client, ACK_ERROR_ARG,
- "Priority out of range: %s", argv[1]);
- return COMMAND_RETURN_ERROR;
- }
-
- for (int i = 2; i < argc; ++i) {
- unsigned start_position, end_position;
- if (!check_range(client, &start_position, &end_position,
- argv[i]))
- return COMMAND_RETURN_ERROR;
-
- enum playlist_result result =
- playlist_set_priority(&g_playlist,
- client->player_control,
- start_position, end_position,
- priority);
- if (result != PLAYLIST_RESULT_SUCCESS)
- return print_playlist_result(client, result);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_prioid(struct client *client, int argc, char *argv[])
-{
- unsigned priority;
-
- if (!check_unsigned(client, &priority, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- if (priority > 0xff) {
- command_error(client, ACK_ERROR_ARG,
- "Priority out of range: %s", argv[1]);
- return COMMAND_RETURN_ERROR;
- }
-
- for (int i = 2; i < argc; ++i) {
- unsigned song_id;
- if (!check_unsigned(client, &song_id, argv[i]))
- return COMMAND_RETURN_ERROR;
-
- enum playlist_result result =
- playlist_set_priority_id(&g_playlist,
- client->player_control,
- song_id, priority);
- if (result != PLAYLIST_RESULT_SUCCESS)
- return print_playlist_result(client, result);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_listall(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *directory = "";
-
- if (argc == 2)
- directory = argv[1];
-
- GError *error = NULL;
- return printAllIn(client, directory, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned level;
- bool success;
-
- if (!check_unsigned(client, &level, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- if (level > 100) {
- command_error(client, ACK_ERROR_ARG, "Invalid volume value");
- return COMMAND_RETURN_ERROR;
- }
-
- success = volume_level_change(level);
- if (!success) {
- command_error(client, ACK_ERROR_SYSTEM,
- "problems setting volume");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_repeat(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- bool status;
- if (!check_bool(client, &status, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_set_repeat(&g_playlist, client->player_control, status);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_single(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- bool status;
- if (!check_bool(client, &status, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_set_single(&g_playlist, client->player_control, status);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_consume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- bool status;
- if (!check_bool(client, &status, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_set_consume(&g_playlist, status);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_random(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- bool status;
- if (!check_bool(client, &status, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_set_random(&g_playlist, client->player_control, status);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_stats(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- return stats_print(client);
-}
-
-static enum command_return
-handle_clearerror(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- pc_clear_error(client->player_control);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_list(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *conditionals;
- int tagType = locate_parse_type(argv[1]);
-
- if (tagType < 0) {
- command_error(client, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]);
- return COMMAND_RETURN_ERROR;
- }
-
- if (tagType == LOCATE_TAG_ANY_TYPE) {
- command_error(client, ACK_ERROR_ARG,
- "\"any\" is not a valid return tag type");
- return COMMAND_RETURN_ERROR;
- }
-
- /* for compatibility with < 0.12.0 */
- if (argc == 3) {
- if (tagType != TAG_ALBUM) {
- command_error(client, ACK_ERROR_ARG,
- "should be \"%s\" for 3 arguments",
- tag_item_names[TAG_ALBUM]);
- return COMMAND_RETURN_ERROR;
- }
-
- locate_item_list_parse(argv + 1, argc - 1);
-
- conditionals = locate_item_list_new(1);
- conditionals->items[0].tag = TAG_ARTIST;
- conditionals->items[0].needle = g_strdup(argv[2]);
- } else {
- conditionals =
- locate_item_list_parse(argv + 2, argc - 2);
- if (conditionals == NULL) {
- command_error(client, ACK_ERROR_ARG,
- "not able to parse args");
- return COMMAND_RETURN_ERROR;
- }
- }
-
- GError *error = NULL;
- enum command_return ret =
- listAllUniqueTags(client, tagType, conditionals, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(conditionals);
-
- return ret;
-}
-
-static enum command_return
-handle_move(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned start, end;
- int to;
- enum playlist_result result;
-
- if (!check_range(client, &start, &end, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_int(client, &to, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_move_range(&g_playlist, client->player_control,
- start, end, to);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_moveid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned id;
- int to;
- enum playlist_result result;
-
- if (!check_unsigned(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_int(client, &to, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_move_id(&g_playlist, client->player_control,
- id, to);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_swap(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned song1, song2;
- enum playlist_result result;
-
- if (!check_unsigned(client, &song1, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &song2, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_swap_songs(&g_playlist, client->player_control,
- song1, song2);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_swapid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned id1, id2;
- enum playlist_result result;
-
- if (!check_unsigned(client, &id1, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &id2, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_swap_songs_id(&g_playlist, client->player_control,
- id1, id2);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_seek(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned song, seek_time;
- enum playlist_result result;
-
- if (!check_unsigned(client, &song, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &seek_time, argv[2]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_seek_song(&g_playlist, client->player_control,
- song, seek_time);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned id, seek_time;
- enum playlist_result result;
-
- if (!check_unsigned(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &seek_time, argv[2]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_seek_song_id(&g_playlist, client->player_control,
- id, seek_time);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_seekcur(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *p = argv[1];
- bool relative = *p == '+' || *p == '-';
- int seek_time;
- if (!check_int(client, &seek_time, p))
- return COMMAND_RETURN_ERROR;
-
- enum playlist_result result =
- playlist_seek_current(&g_playlist, client->player_control,
- seek_time, relative);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_listallinfo(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *directory = "";
-
- if (argc == 2)
- directory = argv[1];
-
- GError *error = NULL;
- return printInfoForAllIn(client, directory, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_ping(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_password(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned permission = 0;
-
- if (getPermissionFromPassword(argv[1], &permission) < 0) {
- command_error(client, ACK_ERROR_PASSWORD, "incorrect password");
- return COMMAND_RETURN_ERROR;
- }
-
- client_set_permission(client, permission);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_crossfade(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned xfade_time;
-
- if (!check_unsigned(client, &xfade_time, argv[1]))
- return COMMAND_RETURN_ERROR;
- pc_set_cross_fade(client->player_control, xfade_time);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_mixrampdb(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- float db;
-
- if (!check_float(client, &db, argv[1]))
- return COMMAND_RETURN_ERROR;
- pc_set_mixramp_db(client->player_control, db);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_mixrampdelay(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- float delay_secs;
-
- if (!check_float(client, &delay_secs, argv[1]))
- return COMMAND_RETURN_ERROR;
- pc_set_mixramp_delay(client->player_control, delay_secs);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_enableoutput(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned device;
- bool ret;
-
- if (!check_unsigned(client, &device, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- ret = audio_output_enable_index(device);
- if (!ret) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "No such audio output");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_disableoutput(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned device;
- bool ret;
-
- if (!check_unsigned(client, &device, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- ret = audio_output_disable_index(device);
- if (!ret) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "No such audio output");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_devices(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- printAudioDevices(client);
-
- return COMMAND_RETURN_OK;
-}
-
-/* don't be fooled, this is the command handler for "commands" command */
-static enum command_return
-handle_commands(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
-
-static enum command_return
-handle_not_commands(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
-
-static enum command_return
-handle_config(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- if (!client_is_local(client)) {
- command_error(client, ACK_ERROR_PERMISSION,
- "Command only permitted to local clients");
- return COMMAND_RETURN_ERROR;
- }
-
- const char *path = mapper_get_music_directory_utf8();
- if (path != NULL)
- client_printf(client, "music_directory: %s\n", path);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistclear(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- GError *error = NULL;
- return spl_clear(argv[1], &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_playlistadd(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- char *playlist = argv[1];
- char *uri = argv[2];
-
- bool success;
- GError *error = NULL;
- if (uri_has_scheme(uri)) {
- if (!uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return COMMAND_RETURN_ERROR;
- }
-
- success = spl_append_uri(argv[1], playlist, &error);
- } else
- success = addAllInToStoredPlaylist(uri, playlist, &error);
-
- if (!success && error == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
- return COMMAND_RETURN_ERROR;
- }
-
- return success ? COMMAND_RETURN_OK : print_error(client, error);
-}
-
-static enum command_return
-handle_listplaylists(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- GError *error = NULL;
- GPtrArray *list = spl_list(&error);
- if (list == NULL)
- return print_error(client, error);
-
- print_spl_list(client, list);
- spl_list_free(list);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_replay_gain_mode(struct client *client,
- G_GNUC_UNUSED int argc, char *argv[])
-{
- if (!replay_gain_set_mode_string(argv[1])) {
- command_error(client, ACK_ERROR_ARG,
- "Unrecognized replay gain mode");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_replay_gain_status(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- client_printf(client, "replay_gain_mode: %s\n",
- replay_gain_get_mode_string());
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_idle(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- unsigned flags = 0, j;
- int i;
- const char *const* idle_names;
-
- idle_names = idle_get_names();
- for (i = 1; i < argc; ++i) {
- if (!argv[i])
- continue;
-
- for (j = 0; idle_names[j]; ++j) {
- if (!g_ascii_strcasecmp(argv[i], idle_names[j])) {
- flags |= (1 << j);
- }
- }
- }
-
- /* No argument means that the client wants to receive everything */
- if (flags == 0)
- flags = ~0;
-
- /* enable "idle" mode on this client */
- client_idle_wait(client, flags);
-
- /* return value is "1" so the caller won't print "OK" */
- return 1;
-}
-
-#ifdef ENABLE_SQLITE
-struct sticker_song_find_data {
- struct client *client;
- const char *name;
-};
-
-static void
-sticker_song_find_print_cb(struct song *song, const char *value,
- gpointer user_data)
-{
- struct sticker_song_find_data *data = user_data;
-
- song_print_uri(data->client, song);
- sticker_print_value(data->client, data->name, value);
-}
-
-static enum command_return
-handle_sticker_song(struct client *client, int argc, char *argv[])
-{
- /* get song song_id key */
- if (argc == 5 && strcmp(argv[1], "get") == 0) {
- struct song *song;
- char *value;
-
- song = db_get_song(argv[3]);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such song");
- return COMMAND_RETURN_ERROR;
- }
-
- value = sticker_song_get_value(song, argv[4]);
- if (value == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such sticker");
- return COMMAND_RETURN_ERROR;
- }
-
- sticker_print_value(client, argv[4], value);
- g_free(value);
-
- return COMMAND_RETURN_OK;
- /* list song song_id */
- } else if (argc == 4 && strcmp(argv[1], "list") == 0) {
- struct song *song;
- struct sticker *sticker;
-
- song = db_get_song(argv[3]);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such song");
- return COMMAND_RETURN_ERROR;
- }
-
- sticker = sticker_song_get(song);
- if (sticker) {
- sticker_print(client, sticker);
- sticker_free(sticker);
- }
-
- return COMMAND_RETURN_OK;
- /* set song song_id id key */
- } else if (argc == 6 && strcmp(argv[1], "set") == 0) {
- struct song *song;
- bool ret;
-
- song = db_get_song(argv[3]);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such song");
- return COMMAND_RETURN_ERROR;
- }
-
- ret = sticker_song_set_value(song, argv[4], argv[5]);
- if (!ret) {
- command_error(client, ACK_ERROR_SYSTEM,
- "failed to set sticker value");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
- /* delete song song_id [key] */
- } else if ((argc == 4 || argc == 5) &&
- strcmp(argv[1], "delete") == 0) {
- struct song *song;
- bool ret;
-
- song = db_get_song(argv[3]);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such song");
- return COMMAND_RETURN_ERROR;
- }
-
- ret = argc == 4
- ? sticker_song_delete(song)
- : sticker_song_delete_value(song, argv[4]);
- if (!ret) {
- command_error(client, ACK_ERROR_SYSTEM,
- "no such sticker");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
- /* find song dir key */
- } else if (argc == 5 && strcmp(argv[1], "find") == 0) {
- /* "sticker find song a/directory name" */
- struct directory *directory;
- bool success;
- struct sticker_song_find_data data = {
- .client = client,
- .name = argv[4],
- };
-
- db_lock();
- directory = db_get_directory(argv[3]);
- if (directory == NULL) {
- db_unlock();
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such directory");
- return COMMAND_RETURN_ERROR;
- }
-
- success = sticker_song_find(directory, data.name,
- sticker_song_find_print_cb, &data);
- db_unlock();
- if (!success) {
- command_error(client, ACK_ERROR_SYSTEM,
- "failed to set search sticker database");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
- } else {
- command_error(client, ACK_ERROR_ARG, "bad request");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-static enum command_return
-handle_sticker(struct client *client, int argc, char *argv[])
-{
- assert(argc >= 4);
-
- if (!sticker_enabled()) {
- command_error(client, ACK_ERROR_UNKNOWN,
- "sticker database is disabled");
- return COMMAND_RETURN_ERROR;
- }
-
- if (strcmp(argv[2], "song") == 0)
- return handle_sticker_song(client, argc, argv);
- else {
- command_error(client, ACK_ERROR_ARG,
- "unknown sticker domain");
- return COMMAND_RETURN_ERROR;
- }
-}
-#endif
-
-static enum command_return
-handle_subscribe(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- assert(argc == 2);
-
- switch (client_subscribe(client, argv[1])) {
- case CLIENT_SUBSCRIBE_OK:
- return COMMAND_RETURN_OK;
-
- case CLIENT_SUBSCRIBE_INVALID:
- command_error(client, ACK_ERROR_ARG,
- "invalid channel name");
- return COMMAND_RETURN_ERROR;
-
- case CLIENT_SUBSCRIBE_ALREADY:
- command_error(client, ACK_ERROR_EXIST,
- "already subscribed to this channel");
- return COMMAND_RETURN_ERROR;
-
- case CLIENT_SUBSCRIBE_FULL:
- command_error(client, ACK_ERROR_EXIST,
- "subscription list is full");
- return COMMAND_RETURN_ERROR;
- }
-
- /* unreachable */
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_unsubscribe(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- assert(argc == 2);
-
- if (client_unsubscribe(client, argv[1]))
- return COMMAND_RETURN_OK;
- else {
- command_error(client, ACK_ERROR_NO_EXIST,
- "not subscribed to this channel");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-struct channels_context {
- GStringChunk *chunk;
-
- GHashTable *channels;
-};
-
-static void
-collect_channels(gpointer data, gpointer user_data)
-{
- struct channels_context *context = user_data;
- const struct client *client = data;
-
- for (GSList *i = client->subscriptions; i != NULL;
- i = g_slist_next(i)) {
- const char *channel = i->data;
-
- if (g_hash_table_lookup(context->channels, channel) == NULL) {
- char *channel2 = g_string_chunk_insert(context->chunk,
- channel);
- g_hash_table_insert(context->channels, channel2,
- context);
- }
- }
-}
-
-static void
-print_channel(gpointer key, G_GNUC_UNUSED gpointer value, gpointer user_data)
-{
- struct client *client = user_data;
- const char *channel = key;
-
- client_printf(client, "channel: %s\n", channel);
-}
-
-static enum command_return
-handle_channels(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- assert(argc == 1);
-
- struct channels_context context = {
- .chunk = g_string_chunk_new(1024),
- .channels = g_hash_table_new(g_str_hash, g_str_equal),
- };
-
- client_list_foreach(collect_channels, &context);
-
- g_hash_table_foreach(context.channels, print_channel, client);
-
- g_hash_table_destroy(context.channels);
- g_string_chunk_free(context.chunk);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_read_messages(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- assert(argc == 1);
-
- GSList *messages = client_read_messages(client);
-
- for (GSList *i = messages; i != NULL; i = g_slist_next(i)) {
- struct client_message *msg = i->data;
-
- client_printf(client, "channel: %s\nmessage: %s\n",
- msg->channel, msg->message);
- client_message_free(msg);
- }
-
- g_slist_free(messages);
-
- return COMMAND_RETURN_OK;
-}
-
-struct send_message_context {
- struct client_message msg;
-
- bool sent;
-};
-
-static void
-send_message(gpointer data, gpointer user_data)
-{
- struct send_message_context *context = user_data;
- struct client *client = data;
-
- if (client_push_message(client, &context->msg))
- context->sent = true;
-}
-
-static enum command_return
-handle_send_message(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- assert(argc == 3);
-
- if (!client_message_valid_channel_name(argv[1])) {
- command_error(client, ACK_ERROR_ARG,
- "invalid channel name");
- return COMMAND_RETURN_ERROR;
- }
-
- struct send_message_context context = {
- .sent = false,
- };
-
- client_message_init(&context.msg, argv[1], argv[2]);
-
- client_list_foreach(send_message, &context);
-
- client_message_deinit(&context.msg);
-
- if (context.sent)
- return COMMAND_RETURN_OK;
- else {
- command_error(client, ACK_ERROR_NO_EXIST,
- "nobody is subscribed to this channel");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-/**
- * The command registry.
- *
- * This array must be sorted!
- */
-static const struct command commands[] = {
- { "add", PERMISSION_ADD, 1, 1, handle_add },
- { "addid", PERMISSION_ADD, 1, 2, handle_addid },
- { "channels", PERMISSION_READ, 0, 0, handle_channels },
- { "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
- { "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
- { "close", PERMISSION_NONE, -1, -1, handle_close },
- { "commands", PERMISSION_NONE, 0, 0, handle_commands },
- { "config", PERMISSION_ADMIN, 0, 0, handle_config },
- { "consume", PERMISSION_CONTROL, 1, 1, handle_consume },
- { "count", PERMISSION_READ, 2, -1, handle_count },
- { "crossfade", PERMISSION_CONTROL, 1, 1, handle_crossfade },
- { "currentsong", PERMISSION_READ, 0, 0, handle_currentsong },
- { "decoders", PERMISSION_READ, 0, 0, handle_decoders },
- { "delete", PERMISSION_CONTROL, 1, 1, handle_delete },
- { "deleteid", PERMISSION_CONTROL, 1, 1, handle_deleteid },
- { "disableoutput", PERMISSION_ADMIN, 1, 1, handle_disableoutput },
- { "enableoutput", PERMISSION_ADMIN, 1, 1, handle_enableoutput },
- { "find", PERMISSION_READ, 2, -1, handle_find },
- { "findadd", PERMISSION_READ, 2, -1, handle_findadd},
- { "idle", PERMISSION_READ, 0, -1, handle_idle },
- { "kill", PERMISSION_ADMIN, -1, -1, handle_kill },
- { "list", PERMISSION_READ, 1, -1, handle_list },
- { "listall", PERMISSION_READ, 0, 1, handle_listall },
- { "listallinfo", PERMISSION_READ, 0, 1, handle_listallinfo },
- { "listplaylist", PERMISSION_READ, 1, 1, handle_listplaylist },
- { "listplaylistinfo", PERMISSION_READ, 1, 1, handle_listplaylistinfo },
- { "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },
- { "load", PERMISSION_ADD, 1, 2, handle_load },
- { "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },
- { "mixrampdb", PERMISSION_CONTROL, 1, 1, handle_mixrampdb },
- { "mixrampdelay", PERMISSION_CONTROL, 1, 1, handle_mixrampdelay },
- { "move", PERMISSION_CONTROL, 2, 2, handle_move },
- { "moveid", PERMISSION_CONTROL, 2, 2, handle_moveid },
- { "next", PERMISSION_CONTROL, 0, 0, handle_next },
- { "notcommands", PERMISSION_NONE, 0, 0, handle_not_commands },
- { "outputs", PERMISSION_READ, 0, 0, handle_devices },
- { "password", PERMISSION_NONE, 1, 1, handle_password },
- { "pause", PERMISSION_CONTROL, 0, 1, handle_pause },
- { "ping", PERMISSION_NONE, 0, 0, handle_ping },
- { "play", PERMISSION_CONTROL, 0, 1, handle_play },
- { "playid", PERMISSION_CONTROL, 0, 1, handle_playid },
- { "playlist", PERMISSION_READ, 0, 0, handle_playlist },
- { "playlistadd", PERMISSION_CONTROL, 2, 2, handle_playlistadd },
- { "playlistclear", PERMISSION_CONTROL, 1, 1, handle_playlistclear },
- { "playlistdelete", PERMISSION_CONTROL, 2, 2, handle_playlistdelete },
- { "playlistfind", PERMISSION_READ, 2, -1, handle_playlistfind },
- { "playlistid", PERMISSION_READ, 0, 1, handle_playlistid },
- { "playlistinfo", PERMISSION_READ, 0, 1, handle_playlistinfo },
- { "playlistmove", PERMISSION_CONTROL, 3, 3, handle_playlistmove },
- { "playlistsearch", PERMISSION_READ, 2, -1, handle_playlistsearch },
- { "plchanges", PERMISSION_READ, 1, 1, handle_plchanges },
- { "plchangesposid", PERMISSION_READ, 1, 1, handle_plchangesposid },
- { "previous", PERMISSION_CONTROL, 0, 0, handle_previous },
- { "prio", PERMISSION_CONTROL, 2, -1, handle_prio },
- { "prioid", PERMISSION_CONTROL, 2, -1, handle_prioid },
- { "random", PERMISSION_CONTROL, 1, 1, handle_random },
- { "readmessages", PERMISSION_READ, 0, 0, handle_read_messages },
- { "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
- { "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
- { "replay_gain_mode", PERMISSION_CONTROL, 1, 1,
- handle_replay_gain_mode },
- { "replay_gain_status", PERMISSION_READ, 0, 0,
- handle_replay_gain_status },
- { "rescan", PERMISSION_CONTROL, 0, 1, handle_rescan },
- { "rm", PERMISSION_CONTROL, 1, 1, handle_rm },
- { "save", PERMISSION_CONTROL, 1, 1, handle_save },
- { "search", PERMISSION_READ, 2, -1, handle_search },
- { "searchadd", PERMISSION_ADD, 2, -1, handle_searchadd },
- { "searchaddpl", PERMISSION_CONTROL, 3, -1, handle_searchaddpl },
- { "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
- { "seekcur", PERMISSION_CONTROL, 1, 1, handle_seekcur },
- { "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
- { "sendmessage", PERMISSION_CONTROL, 2, 2, handle_send_message },
- { "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
- { "shuffle", PERMISSION_CONTROL, 0, 1, handle_shuffle },
- { "single", PERMISSION_CONTROL, 1, 1, handle_single },
- { "stats", PERMISSION_READ, 0, 0, handle_stats },
- { "status", PERMISSION_READ, 0, 0, handle_status },
-#ifdef ENABLE_SQLITE
- { "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
-#endif
- { "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
- { "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
- { "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
- { "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
- { "tagtypes", PERMISSION_READ, 0, 0, handle_tagtypes },
- { "unsubscribe", PERMISSION_READ, 1, 1, handle_unsubscribe },
- { "update", PERMISSION_CONTROL, 0, 1, handle_update },
- { "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
-};
-
-static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]);
-
-static bool
-command_available(G_GNUC_UNUSED const struct command *cmd)
-{
-#ifdef ENABLE_SQLITE
- if (strcmp(cmd->cmd, "sticker") == 0)
- return sticker_enabled();
-#endif
-
- return true;
-}
-
-/* don't be fooled, this is the command handler for "commands" command */
-static enum command_return
-handle_commands(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- const unsigned permission = client_get_permission(client);
- const struct command *cmd;
-
- for (unsigned i = 0; i < num_commands; ++i) {
- cmd = &commands[i];
-
- if (cmd->permission == (permission & cmd->permission) &&
- command_available(cmd))
- client_printf(client, "command: %s\n", cmd->cmd);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_not_commands(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- const unsigned permission = client_get_permission(client);
- const struct command *cmd;
-
- for (unsigned i = 0; i < num_commands; ++i) {
- cmd = &commands[i];
-
- if (cmd->permission != (permission & cmd->permission))
- client_printf(client, "command: %s\n", cmd->cmd);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-void command_init(void)
-{
-#ifndef NDEBUG
- /* ensure that the command list is sorted */
- for (unsigned i = 0; i < num_commands - 1; ++i)
- assert(strcmp(commands[i].cmd, commands[i + 1].cmd) < 0);
-#endif
-}
-
-void command_finish(void)
-{
-}
-
-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);
- if (cmp == 0)
- return &commands[i];
- else if (cmp < 0)
- b = i;
- else if (cmp > 0)
- a = i + 1;
- } while (a < b);
-
- return NULL;
-}
-
-static bool
-command_check_request(const struct command *cmd, struct client *client,
- unsigned permission, int argc, char *argv[])
-{
- int min = cmd->min + 1;
- int max = cmd->max + 1;
-
- if (cmd->permission != (permission & cmd->permission)) {
- if (client != NULL)
- command_error(client, ACK_ERROR_PERMISSION,
- "you don't have permission for \"%s\"",
- cmd->cmd);
- return false;
- }
-
- if (min == 0)
- return true;
-
- if (min == max && max != argc) {
- if (client != NULL)
- command_error(client, ACK_ERROR_ARG,
- "wrong number of arguments for \"%s\"",
- argv[0]);
- return false;
- } else if (argc < min) {
- if (client != NULL)
- command_error(client, ACK_ERROR_ARG,
- "too few arguments for \"%s\"", argv[0]);
- return false;
- } else if (argc > max && max /* != 0 */ ) {
- if (client != NULL)
- command_error(client, ACK_ERROR_ARG,
- "too many arguments for \"%s\"", argv[0]);
- return false;
- } else
- return true;
-}
-
-static const struct command *
-command_checked_lookup(struct client *client, unsigned permission,
- int argc, char *argv[])
-{
- const struct command *cmd;
-
- current_command = "";
-
- if (argc == 0)
- return NULL;
-
- cmd = command_lookup(argv[0]);
- if (cmd == NULL) {
- if (client != NULL)
- command_error(client, ACK_ERROR_UNKNOWN,
- "unknown command \"%s\"", argv[0]);
- return NULL;
- }
-
- current_command = cmd->cmd;
-
- if (!command_check_request(cmd, client, permission, argc, argv))
- return NULL;
-
- return cmd;
-}
-
-enum command_return
-command_process(struct client *client, unsigned num, char *line)
-{
- GError *error = NULL;
- int argc;
- char *argv[COMMAND_ARGV_MAX] = { NULL };
- const struct command *cmd;
- enum command_return ret = COMMAND_RETURN_ERROR;
-
- command_list_num = num;
-
- /* get the command name (first word on the line) */
-
- argv[0] = tokenizer_next_word(&line, &error);
- if (argv[0] == NULL) {
- current_command = "";
- if (*line == 0)
- command_error(client, ACK_ERROR_UNKNOWN,
- "No command given");
- else {
- command_error(client, ACK_ERROR_UNKNOWN,
- "%s", error->message);
- g_error_free(error);
- }
- current_command = NULL;
-
- return COMMAND_RETURN_ERROR;
- }
-
- argc = 1;
-
- /* now parse the arguments (quoted or unquoted) */
-
- while (argc < (int)G_N_ELEMENTS(argv) &&
- (argv[argc] =
- tokenizer_next_param(&line, &error)) != NULL)
- ++argc;
-
- /* some error checks; we have to set current_command because
- command_error() expects it to be set */
-
- current_command = argv[0];
-
- if (argc >= (int)G_N_ELEMENTS(argv)) {
- command_error(client, ACK_ERROR_ARG, "Too many arguments");
- current_command = NULL;
- return COMMAND_RETURN_ERROR;
- }
-
- if (*line != 0) {
- command_error(client, ACK_ERROR_ARG,
- "%s", error->message);
- current_command = NULL;
- g_error_free(error);
- return COMMAND_RETURN_ERROR;
- }
-
- /* look up and invoke the command handler */
-
- cmd = command_checked_lookup(client, client_get_permission(client),
- argc, argv);
- if (cmd)
- ret = cmd->handler(client, argc, argv);
-
- current_command = NULL;
- command_list_num = 0;
-
- return ret;
-}
diff --git a/src/command.h b/src/command.h
index 68d1f95e4..9ea5bb52f 100644
--- a/src/command.h
+++ b/src/command.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,27 +20,34 @@
#ifndef MPD_COMMAND_H
#define MPD_COMMAND_H
-#include "ack.h"
-
-#include <glib.h>
-#include <stdbool.h>
-
enum command_return {
- COMMAND_RETURN_ERROR = -1,
- COMMAND_RETURN_OK = 0,
- COMMAND_RETURN_KILL = 10,
- COMMAND_RETURN_CLOSE = 20,
+ /**
+ * The command has succeeded, but the "OK" response was not
+ * yet sent to the client.
+ */
+ COMMAND_RETURN_OK,
+
+ /**
+ * The connection is now in "idle" mode, and no response shall
+ * be generated.
+ */
+ COMMAND_RETURN_IDLE,
+
+ /**
+ * There was an error. The "ACK" response was sent to the
+ * client.
+ */
+ COMMAND_RETURN_ERROR,
+
+ /**
+ * The connection to this client shall be closed.
+ */
+ COMMAND_RETURN_CLOSE,
+
+ /**
+ * The MPD process shall be shut down.
+ */
+ COMMAND_RETURN_KILL,
};
-struct client;
-
-void command_init(void);
-
-void command_finish(void);
-
-enum command_return
-command_process(struct client *client, unsigned num, char *line);
-
-void command_success(struct client *client);
-
#endif
diff --git a/src/conf.c b/src/conf.c
index 167f2da92..5f12d84d9 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -102,6 +102,7 @@ static struct config_entry config_entries[] = {
{ .name = CONF_DESPOTIFY_PASSWORD, false, false},
{ .name = CONF_DESPOTIFY_HIGH_BITRATE, false, false },
{ .name = "filter", true, true },
+ { .name = "database", false, true },
};
static bool
diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx
new file mode 100644
index 000000000..e41ee8819
--- /dev/null
+++ b/src/db/ProxyDatabasePlugin.cxx
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2003-2012 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 "ProxyDatabasePlugin.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseSelection.hxx"
+#include "PlaylistVector.hxx"
+#include "Directory.hxx"
+#include "gcc.h"
+
+extern "C" {
+#include "db_error.h"
+#include "conf.h"
+#include "song.h"
+#include "tag.h"
+}
+
+#undef MPD_DIRECTORY_H
+#undef MPD_SONG_H
+#include <mpd/client.h>
+
+#include <cassert>
+#include <string>
+#include <list>
+
+class ProxyDatabase : public Database {
+ std::string host;
+ unsigned port;
+
+ struct mpd_connection *connection;
+ Directory *root;
+
+public:
+ static Database *Create(const struct config_param *param,
+ GError **error_r);
+
+ virtual bool Open(GError **error_r) override;
+ virtual void Close() override;
+ virtual struct song *GetSong(const char *uri_utf8,
+ GError **error_r) const override;
+ virtual void ReturnSong(struct song *song) const;
+
+ virtual bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const override;
+
+ virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const override;
+
+ virtual bool GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats,
+ GError **error_r) const override;
+
+protected:
+ bool Configure(const struct config_param *param, GError **error_r);
+};
+
+G_GNUC_CONST
+static inline GQuark
+libmpdclient_quark(void)
+{
+ return g_quark_from_static_string("libmpdclient");
+}
+
+static constexpr struct {
+ enum tag_type d;
+ enum mpd_tag_type s;
+} tag_table[] = {
+ { TAG_ARTIST, MPD_TAG_ARTIST },
+ { TAG_ALBUM, MPD_TAG_ALBUM },
+ { TAG_ALBUM_ARTIST, MPD_TAG_ALBUM_ARTIST },
+ { TAG_TITLE, MPD_TAG_TITLE },
+ { TAG_TRACK, MPD_TAG_TRACK },
+ { TAG_NAME, MPD_TAG_NAME },
+ { TAG_GENRE, MPD_TAG_GENRE },
+ { TAG_DATE, MPD_TAG_DATE },
+ { TAG_COMPOSER, MPD_TAG_COMPOSER },
+ { TAG_PERFORMER, MPD_TAG_PERFORMER },
+ { TAG_COMMENT, MPD_TAG_COMMENT },
+ { TAG_DISC, MPD_TAG_DISC },
+ { TAG_MUSICBRAINZ_ARTISTID, MPD_TAG_MUSICBRAINZ_ARTISTID },
+ { TAG_MUSICBRAINZ_ALBUMID, MPD_TAG_MUSICBRAINZ_ALBUMID },
+ { TAG_MUSICBRAINZ_ALBUMARTISTID,
+ MPD_TAG_MUSICBRAINZ_ALBUMARTISTID },
+ { TAG_MUSICBRAINZ_TRACKID, MPD_TAG_MUSICBRAINZ_TRACKID },
+ { TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT }
+};
+
+G_GNUC_CONST
+static enum mpd_tag_type
+Convert(enum tag_type tag_type)
+{
+ for (auto i = tag_table; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
+ if (i->d == tag_type)
+ return i->s;
+
+ return MPD_TAG_COUNT;
+}
+
+static bool
+CheckError(struct mpd_connection *connection, GError **error_r)
+{
+ const auto error = mpd_connection_get_error(connection);
+ if (error == MPD_ERROR_SUCCESS)
+ return true;
+
+ g_set_error_literal(error_r, libmpdclient_quark(), (int)error,
+ mpd_connection_get_error_message(connection));
+ mpd_connection_clear_error(connection);
+ return false;
+}
+
+Database *
+ProxyDatabase::Create(const struct config_param *param, GError **error_r)
+{
+ ProxyDatabase *db = new ProxyDatabase();
+ if (!db->Configure(param, error_r)) {
+ delete db;
+ db = NULL;
+ }
+
+ return db;
+}
+
+bool
+ProxyDatabase::Configure(const struct config_param *param, GError **)
+{
+ host = config_get_block_string(param, "host", "");
+ port = config_get_block_unsigned(param, "port", 0);
+
+ return true;
+}
+
+bool
+ProxyDatabase::Open(GError **error_r)
+{
+ connection = mpd_connection_new(host.empty() ? NULL : host.c_str(),
+ port, 0);
+ if (connection == NULL) {
+ g_set_error_literal(error_r, libmpdclient_quark(),
+ (int)MPD_ERROR_OOM, "Out of memory");
+ return false;
+ }
+
+ if (!CheckError(connection, error_r)) {
+ mpd_connection_free(connection);
+ return false;
+ }
+
+ root = Directory::NewRoot();
+
+ return true;
+}
+
+void
+ProxyDatabase::Close()
+{
+ assert(connection != nullptr);
+
+ root->Free();
+ mpd_connection_free(connection);
+}
+
+static song *
+Convert(const struct mpd_song *song);
+
+struct song *
+ProxyDatabase::GetSong(const char *uri, GError **error_r) const
+{
+ // TODO: implement
+ // TODO: auto-reconnect
+
+ if (!mpd_send_list_meta(connection, uri)) {
+ CheckError(connection, error_r);
+ return nullptr;
+ }
+
+ struct mpd_song *song = mpd_recv_song(connection);
+ struct song *song2 = song != nullptr
+ ? Convert(song)
+ : nullptr;
+ mpd_song_free(song);
+ if (!mpd_response_finish(connection)) {
+ if (song2 != nullptr)
+ song_free(song2);
+
+ CheckError(connection, error_r);
+ return nullptr;
+ }
+
+ if (song2 == nullptr)
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such song: %s", uri);
+
+ return song2;
+}
+
+void
+ProxyDatabase::ReturnSong(struct song *song) const
+{
+ assert(song != nullptr);
+ assert(song_in_database(song));
+ assert(song_is_detached(song));
+
+ song_free(song);
+}
+
+static bool
+Visit(struct mpd_connection *connection, const char *uri,
+ bool recursive, VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r);
+
+static bool
+Visit(struct mpd_connection *connection,
+ bool recursive, const struct mpd_directory *directory,
+ VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ const char *path = mpd_directory_get_path(directory);
+
+ if (visit_directory) {
+ Directory *d = Directory::NewGeneric(path, &detached_root);
+ bool success = visit_directory(*d, error_r);
+ d->Free();
+ if (!success)
+ return false;
+ }
+
+ if (recursive &&
+ !Visit(connection, path, recursive,
+ visit_directory, visit_song, visit_playlist, error_r))
+ return false;
+
+ return true;
+}
+
+static void
+Copy(struct tag *tag, enum tag_type d_tag,
+ const struct mpd_song *song, enum mpd_tag_type s_tag)
+{
+
+ for (unsigned i = 0;; ++i) {
+ const char *value = mpd_song_get_tag(song, s_tag, i);
+ if (value == NULL)
+ break;
+
+ tag_add_item(tag, d_tag, value);
+ }
+}
+
+static song *
+Convert(const struct mpd_song *song)
+{
+ struct song *s = song_detached_new(mpd_song_get_uri(song));
+
+ s->mtime = mpd_song_get_last_modified(song);
+ s->start_ms = mpd_song_get_start(song) * 1000;
+ s->end_ms = mpd_song_get_end(song) * 1000;
+
+ struct tag *tag = tag_new();
+ tag->time = mpd_song_get_duration(song);
+
+ tag_begin_add(tag);
+ for (auto i = tag_table; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
+ Copy(tag, i->d, song, i->s);
+ tag_end_add(tag);
+
+ s->tag = tag;
+
+ return s;
+}
+
+static bool
+Visit(const struct mpd_song *song,
+ VisitSong visit_song, GError **error_r)
+{
+ if (!visit_song)
+ return true;
+
+ struct song *s = Convert(song);
+ bool success = visit_song(*s, error_r);
+ song_free(s);
+
+ return success;
+}
+
+static bool
+Visit(const struct mpd_playlist *playlist,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ if (!visit_playlist)
+ return true;
+
+ PlaylistInfo p(mpd_playlist_get_path(playlist),
+ mpd_playlist_get_last_modified(playlist));
+
+ return visit_playlist(p, detached_root, error_r);
+}
+
+class ProxyEntity {
+ struct mpd_entity *entity;
+
+public:
+ explicit ProxyEntity(struct mpd_entity *_entity)
+ :entity(_entity) {}
+
+ ProxyEntity(const ProxyEntity &other) = delete;
+
+ ProxyEntity(ProxyEntity &&other)
+ :entity(other.entity) {
+ other.entity = nullptr;
+ }
+
+ ~ProxyEntity() {
+ if (entity != nullptr)
+ mpd_entity_free(entity);
+ }
+
+ ProxyEntity &operator=(const ProxyEntity &other) = delete;
+
+ operator const struct mpd_entity *() const {
+ return entity;
+ }
+};
+
+static std::list<ProxyEntity>
+ReceiveEntities(struct mpd_connection *connection)
+{
+ std::list<ProxyEntity> entities;
+ struct mpd_entity *entity;
+ while ((entity = mpd_recv_entity(connection)) != NULL)
+ entities.push_back(ProxyEntity(entity));
+
+ mpd_response_finish(connection);
+ return entities;
+}
+
+static bool
+Visit(struct mpd_connection *connection, const char *uri,
+ bool recursive, VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ if (!mpd_send_list_meta(connection, uri))
+ return CheckError(connection, error_r);
+
+ std::list<ProxyEntity> entities(ReceiveEntities(connection));
+ if (!CheckError(connection, error_r))
+ return false;
+
+ for (const auto &entity : entities) {
+ switch (mpd_entity_get_type(entity)) {
+ case MPD_ENTITY_TYPE_UNKNOWN:
+ break;
+
+ case MPD_ENTITY_TYPE_DIRECTORY:
+ if (!Visit(connection, recursive,
+ mpd_entity_get_directory(entity),
+ visit_directory, visit_song, visit_playlist,
+ error_r))
+ return false;
+ break;
+
+ case MPD_ENTITY_TYPE_SONG:
+ if (!Visit(mpd_entity_get_song(entity), visit_song,
+ error_r))
+ return false;
+ break;
+
+ case MPD_ENTITY_TYPE_PLAYLIST:
+ if (!Visit(mpd_entity_get_playlist(entity),
+ visit_playlist, error_r))
+ return false;
+ break;
+ }
+ }
+
+ return CheckError(connection, error_r);
+}
+
+bool
+ProxyDatabase::Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const
+{
+ // TODO: match
+ // TODO: auto-reconnect
+
+ return ::Visit(connection, selection.uri, selection.recursive,
+ visit_directory, visit_song, visit_playlist,
+ error_r);
+}
+
+bool
+ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const
+{
+ enum mpd_tag_type tag_type2 = Convert(tag_type);
+ if (tag_type2 == MPD_TAG_COUNT) {
+ g_set_error_literal(error_r, libmpdclient_quark(), 0,
+ "Unsupported tag");
+ return false;
+ }
+
+ if (!mpd_search_db_tags(connection, tag_type2))
+ return CheckError(connection, error_r);
+
+ // TODO: match
+ (void)selection;
+
+ if (!mpd_search_commit(connection))
+ return CheckError(connection, error_r);
+
+ bool result = true;
+
+ struct mpd_pair *pair;
+ while (result &&
+ (pair = mpd_recv_pair_tag(connection, tag_type2)) != nullptr) {
+ result = visit_string(pair->value, error_r);
+ mpd_return_pair(connection, pair);
+ }
+
+ return mpd_response_finish(connection) &&
+ CheckError(connection, error_r) &&
+ result;
+}
+
+bool
+ProxyDatabase::GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r) const
+{
+ // TODO: match
+ (void)selection;
+
+ struct mpd_stats *stats2 =
+ mpd_run_stats(connection);
+ if (stats2 == nullptr)
+ return CheckError(connection, error_r);
+
+ stats.song_count = mpd_stats_get_number_of_songs(stats2);
+ stats.total_duration = mpd_stats_get_db_play_time(stats2);
+ stats.artist_count = mpd_stats_get_number_of_artists(stats2);
+ stats.album_count = mpd_stats_get_number_of_albums(stats2);
+ mpd_stats_free(stats2);
+
+ return true;
+}
+
+const DatabasePlugin proxy_db_plugin = {
+ "proxy",
+ ProxyDatabase::Create,
+};
diff --git a/src/db/ProxyDatabasePlugin.hxx b/src/db/ProxyDatabasePlugin.hxx
new file mode 100644
index 000000000..8e878baca
--- /dev/null
+++ b/src/db/ProxyDatabasePlugin.hxx
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2003-2012 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_PROXY_DATABASE_PLUGIN_HXX
+#define MPD_PROXY_DATABASE_PLUGIN_HXX
+
+struct DatabasePlugin;
+
+extern const DatabasePlugin proxy_db_plugin;
+
+#endif
diff --git a/src/db/SimpleDatabasePlugin.cxx b/src/db/SimpleDatabasePlugin.cxx
new file mode 100644
index 000000000..84e4e7cee
--- /dev/null
+++ b/src/db/SimpleDatabasePlugin.cxx
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2003-2011 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 "SimpleDatabasePlugin.hxx"
+#include "DatabaseSelection.hxx"
+#include "DatabaseHelpers.hxx"
+#include "Directory.hxx"
+#include "SongFilter.hxx"
+#include "DatabaseSave.hxx"
+#include "DatabaseLock.hxx"
+#include "db_error.h"
+#include "TextFile.hxx"
+
+extern "C" {
+#include "conf.h"
+}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+G_GNUC_CONST
+static inline GQuark
+simple_db_quark(void)
+{
+ return g_quark_from_static_string("simple_db");
+}
+
+Database *
+SimpleDatabase::Create(const struct config_param *param, GError **error_r)
+{
+ SimpleDatabase *db = new SimpleDatabase();
+ if (!db->Configure(param, error_r)) {
+ delete db;
+ db = NULL;
+ }
+
+ return db;
+}
+
+bool
+SimpleDatabase::Configure(const struct config_param *param, GError **error_r)
+{
+ GError *error = NULL;
+
+ char *_path = config_dup_block_path(param, "path", &error);
+ if (_path == NULL) {
+ if (error != NULL)
+ g_propagate_error(error_r, error);
+ else
+ g_set_error(error_r, simple_db_quark(), 0,
+ "No \"path\" parameter specified");
+ return false;
+ }
+
+ path = _path;
+ free(_path);
+
+ return true;
+}
+
+bool
+SimpleDatabase::Check(GError **error_r) const
+{
+ assert(!path.empty());
+
+ /* Check if the file exists */
+ if (access(path.c_str(), F_OK)) {
+ /* 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 */
+ char *dirPath = g_path_get_dirname(path.c_str());
+
+ /* Check that the parent part of the path is a directory */
+ struct stat st;
+ if (stat(dirPath, &st) < 0) {
+ g_free(dirPath);
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Couldn't stat parent directory of db file "
+ "\"%s\": %s",
+ path.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ g_free(dirPath);
+ g_set_error(error_r, simple_db_quark(), 0,
+ "Couldn't create db file \"%s\" because the "
+ "parent path is not a directory",
+ path.c_str());
+ return false;
+ }
+
+ /* Check if we can write to the directory */
+ if (access(dirPath, X_OK | W_OK)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Can't create db file in \"%s\": %s",
+ dirPath, g_strerror(errno));
+ g_free(dirPath);
+ return false;
+ }
+
+ g_free(dirPath);
+
+ return true;
+ }
+
+ /* Path exists, now check if it's a regular file */
+ struct stat st;
+ if (stat(path.c_str(), &st) < 0) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Couldn't stat db file \"%s\": %s",
+ path.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ g_set_error(error_r, simple_db_quark(), 0,
+ "db file \"%s\" is not a regular file",
+ path.c_str());
+ return false;
+ }
+
+ /* And check that we can write to it */
+ if (access(path.c_str(), R_OK | W_OK)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Can't open db file \"%s\" for reading/writing: %s",
+ path.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool
+SimpleDatabase::Load(GError **error_r)
+{
+ assert(!path.empty());
+ assert(root != NULL);
+
+ TextFile file(path.c_str());
+ if (file.HasFailed()) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Failed to open database file \"%s\": %s",
+ path.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ if (!db_load_internal(file, root, error_r))
+ return false;
+
+ struct stat st;
+ if (stat(path.c_str(), &st) == 0)
+ mtime = st.st_mtime;
+
+ return true;
+}
+
+bool
+SimpleDatabase::Open(GError **error_r)
+{
+ root = Directory::NewRoot();
+ mtime = 0;
+
+#ifndef NDEBUG
+ borrowed_song_count = 0;
+#endif
+
+ GError *error = NULL;
+ if (!Load(&error)) {
+ root->Free();
+
+ g_warning("Failed to load database: %s", error->message);
+ g_error_free(error);
+
+ if (!Check(error_r))
+ return false;
+
+ root = Directory::NewRoot();
+ }
+
+ return true;
+}
+
+void
+SimpleDatabase::Close()
+{
+ assert(root != NULL);
+ assert(borrowed_song_count == 0);
+
+ root->Free();
+}
+
+struct song *
+SimpleDatabase::GetSong(const char *uri, GError **error_r) const
+{
+ assert(root != NULL);
+
+ db_lock();
+ song *song = root->LookupSong(uri);
+ db_unlock();
+ if (song == NULL)
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such song: %s", uri);
+#ifndef NDEBUG
+ else
+ ++const_cast<unsigned &>(borrowed_song_count);
+#endif
+
+ return song;
+}
+
+void
+SimpleDatabase::ReturnSong(gcc_unused struct song *song) const
+{
+ assert(song != nullptr);
+
+#ifndef NDEBUG
+ assert(borrowed_song_count > 0);
+ --const_cast<unsigned &>(borrowed_song_count);
+#endif
+}
+
+G_GNUC_PURE
+const Directory *
+SimpleDatabase::LookupDirectory(const char *uri) const
+{
+ assert(root != NULL);
+ assert(uri != NULL);
+
+ ScopeDatabaseLock protect;
+ return root->LookupDirectory(uri);
+}
+
+bool
+SimpleDatabase::Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const
+{
+ ScopeDatabaseLock protect;
+
+ const Directory *directory = root->LookupDirectory(selection.uri);
+ if (directory == NULL) {
+ if (visit_song) {
+ song *song = root->LookupSong(selection.uri);
+ if (song != nullptr)
+ return !selection.Match(*song) ||
+ visit_song(*song, error_r);
+ }
+
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such directory");
+ return false;
+ }
+
+ if (selection.recursive && visit_directory &&
+ !visit_directory(*directory, error_r))
+ return false;
+
+ return directory->Walk(selection.recursive, selection.filter,
+ visit_directory, visit_song, visit_playlist,
+ error_r);
+}
+
+bool
+SimpleDatabase::VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const
+{
+ return ::VisitUniqueTags(*this, selection, tag_type, visit_string,
+ error_r);
+}
+
+bool
+SimpleDatabase::GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r) const
+{
+ return ::GetStats(*this, selection, stats, error_r);
+}
+
+bool
+SimpleDatabase::Save(GError **error_r)
+{
+ db_lock();
+
+ g_debug("removing empty directories from DB");
+ root->PruneEmpty();
+
+ g_debug("sorting DB");
+ root->Sort();
+
+ db_unlock();
+
+ g_debug("writing DB");
+
+ FILE *fp = fopen(path.c_str(), "w");
+ if (!fp) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "unable to write to db file \"%s\": %s",
+ path.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ db_save_internal(fp, root);
+
+ if (ferror(fp)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Failed to write to database file: %s",
+ g_strerror(errno));
+ fclose(fp);
+ return false;
+ }
+
+ fclose(fp);
+
+ struct stat st;
+ if (stat(path.c_str(), &st) == 0)
+ mtime = st.st_mtime;
+
+ return true;
+}
+
+const DatabasePlugin simple_db_plugin = {
+ "simple",
+ SimpleDatabase::Create,
+};
diff --git a/src/db/SimpleDatabasePlugin.hxx b/src/db/SimpleDatabasePlugin.hxx
new file mode 100644
index 000000000..789dcdae9
--- /dev/null
+++ b/src/db/SimpleDatabasePlugin.hxx
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2003-2011 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_SIMPLE_DATABASE_PLUGIN_HXX
+#define MPD_SIMPLE_DATABASE_PLUGIN_HXX
+
+#include "DatabasePlugin.hxx"
+#include "gcc.h"
+
+#include <cassert>
+#include <string>
+
+#include <time.h>
+
+struct Directory;
+
+class SimpleDatabase : public Database {
+ std::string path;
+
+ Directory *root;
+
+ time_t mtime;
+
+#ifndef NDEBUG
+ unsigned borrowed_song_count;
+#endif
+
+public:
+ gcc_pure
+ Directory *GetRoot() {
+ assert(root != NULL);
+
+ return root;
+ }
+
+ bool Save(GError **error_r);
+
+ gcc_pure
+ time_t GetLastModified() const {
+ return mtime;
+ }
+
+ static Database *Create(const struct config_param *param,
+ GError **error_r);
+
+ virtual bool Open(GError **error_r) override;
+ virtual void Close() override;
+
+ virtual struct song *GetSong(const char *uri_utf8,
+ GError **error_r) const override;
+ virtual void ReturnSong(struct song *song) const;
+
+ virtual bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const override;
+
+ virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const override;
+
+ virtual bool GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats,
+ GError **error_r) const override;
+
+protected:
+ bool Configure(const struct config_param *param, GError **error_r);
+
+ gcc_pure
+ bool Check(GError **error_r) const;
+
+ bool Load(GError **error_r);
+
+ gcc_pure
+ const Directory *LookupDirectory(const char *uri) const;
+};
+
+extern const DatabasePlugin simple_db_plugin;
+
+#endif
diff --git a/src/db/simple_db_plugin.c b/src/db/simple_db_plugin.c
deleted file mode 100644
index 697e8da5f..000000000
--- a/src/db/simple_db_plugin.c
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "simple_db_plugin.h"
-#include "db_internal.h"
-#include "db_error.h"
-#include "db_selection.h"
-#include "db_visitor.h"
-#include "db_save.h"
-#include "db_lock.h"
-#include "conf.h"
-#include "directory.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-
-struct simple_db {
- struct db base;
-
- char *path;
-
- struct directory *root;
-
- time_t mtime;
-};
-
-G_GNUC_CONST
-static inline GQuark
-simple_db_quark(void)
-{
- return g_quark_from_static_string("simple_db");
-}
-
-G_GNUC_PURE
-static const struct directory *
-simple_db_lookup_directory(const struct simple_db *db, const char *uri)
-{
- assert(db != NULL);
- assert(db->root != NULL);
- assert(uri != NULL);
-
- db_lock();
- struct directory *directory =
- directory_lookup_directory(db->root, uri);
- db_unlock();
- return directory;
-}
-
-static struct db *
-simple_db_init(const struct config_param *param, GError **error_r)
-{
- struct simple_db *db = g_malloc(sizeof(*db));
- db_base_init(&db->base, &simple_db_plugin);
-
- GError *error = NULL;
- db->path = config_dup_block_path(param, "path", &error);
- if (db->path == NULL) {
- g_free(db);
- if (error != NULL)
- g_propagate_error(error_r, error);
- else
- g_set_error(error_r, simple_db_quark(), 0,
- "No \"path\" parameter specified");
- return NULL;
- }
-
- return &db->base;
-}
-
-static void
-simple_db_finish(struct db *_db)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- g_free(db->path);
- g_free(db);
-}
-
-static bool
-simple_db_check(struct simple_db *db, GError **error_r)
-{
- assert(db != NULL);
- assert(db->path != NULL);
-
- /* Check if the file exists */
- if (access(db->path, F_OK)) {
- /* 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 */
- char *dirPath = g_path_get_dirname(db->path);
-
- /* Check that the parent part of the path is a directory */
- struct stat st;
- if (stat(dirPath, &st) < 0) {
- g_free(dirPath);
- g_set_error(error_r, simple_db_quark(), errno,
- "Couldn't stat parent directory of db file "
- "\"%s\": %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- g_free(dirPath);
- g_set_error(error_r, simple_db_quark(), 0,
- "Couldn't create db file \"%s\" because the "
- "parent path is not a directory",
- db->path);
- return false;
- }
-
- /* Check if we can write to the directory */
- if (access(dirPath, X_OK | W_OK)) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Can't create db file in \"%s\": %s",
- dirPath, g_strerror(errno));
- g_free(dirPath);
- return false;
- }
-
- g_free(dirPath);
-
- return true;
- }
-
- /* Path exists, now check if it's a regular file */
- struct stat st;
- if (stat(db->path, &st) < 0) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Couldn't stat db file \"%s\": %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- if (!S_ISREG(st.st_mode)) {
- g_set_error(error_r, simple_db_quark(), 0,
- "db file \"%s\" is not a regular file",
- db->path);
- return false;
- }
-
- /* And check that we can write to it */
- if (access(db->path, R_OK | W_OK)) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Can't open db file \"%s\" for reading/writing: %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- return true;
-}
-
-static bool
-simple_db_load(struct simple_db *db, GError **error_r)
-{
- assert(db != NULL);
- assert(db->path != NULL);
- assert(db->root != NULL);
-
- FILE *fp = fopen(db->path, "r");
- if (fp == NULL) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Failed to open database file \"%s\": %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- if (!db_load_internal(fp, db->root, error_r)) {
- fclose(fp);
- return false;
- }
-
- fclose(fp);
-
- struct stat st;
- if (stat(db->path, &st) == 0)
- db->mtime = st.st_mtime;
-
- return true;
-}
-
-static bool
-simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- db->root = directory_new_root();
- db->mtime = 0;
-
- GError *error = NULL;
- if (!simple_db_load(db, &error)) {
- directory_free(db->root);
-
- g_warning("Failed to load database: %s", error->message);
- g_error_free(error);
-
- if (!simple_db_check(db, error_r))
- return false;
-
- db->root = directory_new_root();
- }
-
- return true;
-}
-
-static void
-simple_db_close(struct db *_db)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- assert(db->root != NULL);
-
- directory_free(db->root);
-}
-
-static struct song *
-simple_db_get_song(struct db *_db, const char *uri, GError **error_r)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- assert(db->root != NULL);
-
- db_lock();
- struct song *song = directory_lookup_song(db->root, uri);
- db_unlock();
- if (song == NULL)
- g_set_error(error_r, db_quark(), DB_NOT_FOUND,
- "No such song: %s", uri);
-
- return song;
-}
-
-static bool
-simple_db_visit(struct db *_db, const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
-{
- const struct simple_db *db = (const struct simple_db *)_db;
- const struct directory *directory =
- simple_db_lookup_directory(db, selection->uri);
- if (directory == NULL) {
- struct song *song;
- if (visitor->song != NULL &&
- (song = simple_db_get_song(_db, selection->uri, NULL)) != NULL)
- return visitor->song(song, ctx, error_r);
-
- g_set_error(error_r, db_quark(), DB_NOT_FOUND,
- "No such directory");
- return false;
- }
-
- if (selection->recursive && visitor->directory != NULL &&
- !visitor->directory(directory, ctx, error_r))
- return false;
-
- db_lock();
- bool ret = directory_walk(directory, selection->recursive,
- visitor, ctx, error_r);
- db_unlock();
- return ret;
-}
-
-const struct db_plugin simple_db_plugin = {
- .name = "simple",
- .init = simple_db_init,
- .finish = simple_db_finish,
- .open = simple_db_open,
- .close = simple_db_close,
- .get_song = simple_db_get_song,
- .visit = simple_db_visit,
-};
-
-struct directory *
-simple_db_get_root(struct db *_db)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- assert(db != NULL);
- assert(db->root != NULL);
-
- return db->root;
-}
-
-bool
-simple_db_save(struct db *_db, GError **error_r)
-{
- struct simple_db *db = (struct simple_db *)_db;
- struct directory *music_root = db->root;
-
- db_lock();
-
- g_debug("removing empty directories from DB");
- directory_prune_empty(music_root);
-
- g_debug("sorting DB");
- directory_sort(music_root);
-
- db_unlock();
-
- g_debug("writing DB");
-
- FILE *fp = fopen(db->path, "w");
- if (!fp) {
- g_set_error(error_r, simple_db_quark(), errno,
- "unable to write to db file \"%s\": %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- db_save_internal(fp, music_root);
-
- if (ferror(fp)) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Failed to write to database file: %s",
- g_strerror(errno));
- fclose(fp);
- return false;
- }
-
- fclose(fp);
-
- struct stat st;
- if (stat(db->path, &st) == 0)
- db->mtime = st.st_mtime;
-
- return true;
-}
-
-time_t
-simple_db_get_mtime(const struct db *_db)
-{
- const struct simple_db *db = (const struct simple_db *)_db;
-
- assert(db != NULL);
- assert(db->root != NULL);
-
- return db->mtime;
-}
diff --git a/src/dbUtils.c b/src/dbUtils.c
deleted file mode 100644
index c212d9f9c..000000000
--- a/src/dbUtils.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "dbUtils.h"
-#include "locate.h"
-#include "database.h"
-#include "db_visitor.h"
-#include "playlist.h"
-#include "stored_playlist.h"
-
-#include <glib.h>
-
-static bool
-add_to_queue_song(struct song *song, void *ctx, GError **error_r)
-{
- struct player_control *pc = ctx;
-
- enum playlist_result result =
- playlist_append_song(&g_playlist, pc, song, NULL);
- if (result != PLAYLIST_RESULT_SUCCESS) {
- g_set_error(error_r, playlist_quark(), result,
- "Playlist error");
- return false;
- }
-
- return true;
-}
-
-static const struct db_visitor add_to_queue_visitor = {
- .song = add_to_queue_song,
-};
-
-bool
-addAllIn(struct player_control *pc, const char *uri, GError **error_r)
-{
- return db_walk(uri, &add_to_queue_visitor, pc, error_r);
-}
-
-struct add_data {
- const char *path;
-};
-
-static bool
-add_to_spl_song(struct song *song, void *ctx, GError **error_r)
-{
- struct add_data *data = ctx;
-
- if (!spl_append_song(data->path, song, error_r))
- return false;
-
- return true;
-}
-
-static const struct db_visitor add_to_spl_visitor = {
- .song = add_to_spl_song,
-};
-
-bool
-addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8,
- GError **error_r)
-{
- struct add_data data = {
- .path = path_utf8,
- };
-
- return db_walk(uri_utf8, &add_to_spl_visitor, &data, error_r);
-}
-
-struct find_add_data {
- struct player_control *pc;
- const struct locate_item_list *criteria;
-};
-
-static bool
-find_add_song(struct song *song, void *ctx, GError **error_r)
-{
- struct find_add_data *data = ctx;
-
- if (!locate_song_match(song, data->criteria))
- return true;
-
- enum playlist_result result =
- playlist_append_song(&g_playlist, data->pc,
- song, NULL);
- if (result != PLAYLIST_RESULT_SUCCESS) {
- g_set_error(error_r, playlist_quark(), result,
- "Playlist error");
- return false;
- }
-
- return true;
-}
-
-static const struct db_visitor find_add_visitor = {
- .song = find_add_song,
-};
-
-bool
-findAddIn(struct player_control *pc, const char *name,
- const struct locate_item_list *criteria, GError **error_r)
-{
- struct find_add_data data;
- data.pc = pc;
- data.criteria = criteria;
-
- return db_walk(name, &find_add_visitor, &data, error_r);
-}
-
-static bool
-searchadd_visitor_song(struct song *song, void *_data, GError **error_r)
-{
- struct find_add_data *data = _data;
-
- if (!locate_song_search(song, data->criteria))
- return true;
-
- enum playlist_result result =
- playlist_append_song(&g_playlist, data->pc, song, NULL);
- if (result != PLAYLIST_RESULT_SUCCESS) {
- g_set_error(error_r, playlist_quark(), result,
- "Playlist error");
- return false;
- }
-
- return true;
-}
-
-static const struct db_visitor searchadd_visitor = {
- .song = searchadd_visitor_song,
-};
-
-bool
-search_add_songs(struct player_control *pc, const char *uri,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- struct locate_item_list *new_list =
- locate_item_list_casefold(criteria);
- struct find_add_data data = {
- .pc = pc,
- .criteria = new_list,
- };
-
- bool success = db_walk(uri, &searchadd_visitor, &data, error_r);
-
- locate_item_list_free(new_list);
-
- return success;
-}
-
-struct search_add_playlist_data {
- const char *playlist;
- const struct locate_item_list *criteria;
-};
-
-static bool
-searchaddpl_visitor_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct search_add_playlist_data *data = _data;
-
- if (!locate_song_search(song, data->criteria))
- return true;
-
- if (!spl_append_song(data->playlist, song, error_r))
- return false;
-
- return true;
-}
-
-static const struct db_visitor searchaddpl_visitor = {
- .song = searchaddpl_visitor_song,
-};
-
-bool
-search_add_to_playlist(const char *uri, const char *path_utf8,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- struct locate_item_list *new_list
- = locate_item_list_casefold(criteria);
- struct search_add_playlist_data data = {
- .playlist = path_utf8,
- .criteria = new_list,
- };
-
- bool success = db_walk(uri, &searchaddpl_visitor, &data, error_r);
-
- locate_item_list_free(new_list);
-
- return success;
-}
diff --git a/src/dbUtils.h b/src/dbUtils.h
deleted file mode 100644
index 706c807fd..000000000
--- a/src/dbUtils.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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_DB_UTILS_H
-#define MPD_DB_UTILS_H
-
-#include "gcc.h"
-
-#include <glib.h>
-#include <stdbool.h>
-
-struct locate_item_list;
-struct player_control;
-
-gcc_nonnull(1,2)
-bool
-addAllIn(struct player_control *pc, const char *uri, GError **error_r);
-
-gcc_nonnull(1,2)
-bool
-addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8,
- GError **error_r);
-
-gcc_nonnull(1,2,3)
-bool
-findAddIn(struct player_control *pc, const char *name,
- const struct locate_item_list *criteria, GError **error_r);
-
-gcc_nonnull(1,2,3)
-bool
-search_add_songs(struct player_control *pc, const char *uri,
- const struct locate_item_list *criteria, GError **error_r);
-
-gcc_nonnull(1,2,3)
-bool
-search_add_to_playlist(const char *uri, const char *path_utf8,
- const struct locate_item_list *criteria,
- GError **error_r);
-
-#endif
diff --git a/src/db_plugin.h b/src/db_plugin.h
deleted file mode 100644
index 1c7e14ede..000000000
--- a/src/db_plugin.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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
- *
- * This header declares the db_plugin class. It describes a
- * plugin API for databases of song metadata.
- */
-
-#ifndef MPD_DB_PLUGIN_H
-#define MPD_DB_PLUGIN_H
-
-#include <glib.h>
-#include <assert.h>
-#include <stdbool.h>
-
-struct config_param;
-struct db_selection;
-struct db_visitor;
-
-struct db {
- const struct db_plugin *plugin;
-};
-
-struct db_plugin {
- const char *name;
-
- /**
- * Allocates and configures a database.
- */
- struct db *(*init)(const struct config_param *param, GError **error_r);
-
- /**
- * Free instance data.
- */
- void (*finish)(struct db *db);
-
- /**
- * Open the database. Read it into memory if applicable.
- */
- bool (*open)(struct db *db, GError **error_r);
-
- /**
- * Close the database, free allocated memory.
- */
- void (*close)(struct db *db);
-
- /**
- * Look up a song (including tag data) in the database.
- *
- * @param the URI of the song within the music directory
- * (UTF-8)
- */
- struct song *(*get_song)(struct db *db, const char *uri,
- GError **error_r);
-
- /**
- * Visit the selected entities.
- */
- bool (*visit)(struct db *db, const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r);
-};
-
-G_GNUC_MALLOC
-static inline struct db *
-db_plugin_new(const struct db_plugin *plugin, const struct config_param *param,
- GError **error_r)
-{
- assert(plugin != NULL);
- assert(plugin->init != NULL);
- assert(plugin->finish != NULL);
- assert(plugin->get_song != NULL);
- assert(plugin->visit != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- struct db *db = plugin->init(param, error_r);
- assert(db == NULL || db->plugin == plugin);
- assert(db != NULL || error_r == NULL || *error_r != NULL);
-
- return db;
-}
-
-static inline void
-db_plugin_free(struct db *db)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
- assert(db->plugin->finish != NULL);
-
- db->plugin->finish(db);
-}
-
-static inline bool
-db_plugin_open(struct db *db, GError **error_r)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
-
- return db->plugin->open != NULL
- ? db->plugin->open(db, error_r)
- : true;
-}
-
-static inline void
-db_plugin_close(struct db *db)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
-
- if (db->plugin->close != NULL)
- db->plugin->close(db);
-}
-
-static inline struct song *
-db_plugin_get_song(struct db *db, const char *uri, GError **error_r)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
- assert(db->plugin->get_song != NULL);
- assert(uri != NULL);
-
- return db->plugin->get_song(db, uri, error_r);
-}
-
-static inline bool
-db_plugin_visit(struct db *db, const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
- assert(selection != NULL);
- assert(visitor != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- return db->plugin->visit(db, selection, visitor, ctx, error_r);
-}
-
-#endif
diff --git a/src/db_print.c b/src/db_print.c
deleted file mode 100644
index 4d7e3f5ef..000000000
--- a/src/db_print.c
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "db_print.h"
-#include "db_selection.h"
-#include "db_visitor.h"
-#include "locate.h"
-#include "directory.h"
-#include "database.h"
-#include "client.h"
-#include "song.h"
-#include "song_print.h"
-#include "playlist_vector.h"
-#include "tag.h"
-#include "strset.h"
-
-#include <glib.h>
-
-typedef struct _ListCommandItem {
- int8_t tagType;
- const struct locate_item_list *criteria;
-} ListCommandItem;
-
-typedef struct _SearchStats {
- const struct locate_item_list *criteria;
- int numberOfSongs;
- unsigned long playTime;
-} SearchStats;
-
-static bool
-print_visitor_directory(const struct directory *directory, void *data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct client *client = data;
-
- if (!directory_is_root(directory))
- client_printf(client, "directory: %s\n", directory_get_path(directory));
-
- return true;
-}
-
-static void
-print_playlist_in_directory(struct client *client,
- const struct directory *directory,
- const char *name_utf8)
-{
- if (directory_is_root(directory))
- client_printf(client, "playlist: %s\n", name_utf8);
- else
- client_printf(client, "playlist: %s/%s\n",
- directory_get_path(directory), name_utf8);
-}
-
-static bool
-print_visitor_song(struct song *song, void *data,
- G_GNUC_UNUSED GError **error_r)
-{
- assert(song != NULL);
- assert(song->parent != NULL);
-
- struct client *client = data;
- song_print_uri(client, song);
-
- if (song->tag != NULL && song->tag->has_playlist)
- /* this song file has an embedded CUE sheet */
- print_playlist_in_directory(client, song->parent,
- song->uri);
-
- return true;
-}
-
-static bool
-print_visitor_song_info(struct song *song, void *data,
- G_GNUC_UNUSED GError **error_r)
-{
- assert(song != NULL);
- assert(song->parent != NULL);
-
- struct client *client = data;
- song_print_info(client, song);
-
- if (song->tag != NULL && song->tag->has_playlist)
- /* this song file has an embedded CUE sheet */
- print_playlist_in_directory(client, song->parent,
- song->uri);
-
- return true;
-}
-
-static bool
-print_visitor_playlist(const struct playlist_metadata *playlist,
- const struct directory *directory, void *ctx,
- G_GNUC_UNUSED GError **error_r)
-{
- struct client *client = ctx;
- print_playlist_in_directory(client, directory, playlist->name);
- return true;
-}
-
-static bool
-print_visitor_playlist_info(const struct playlist_metadata *playlist,
- const struct directory *directory,
- void *ctx, G_GNUC_UNUSED GError **error_r)
-{
- struct client *client = ctx;
- print_playlist_in_directory(client, directory, playlist->name);
-
-#ifndef G_OS_WIN32
- struct tm tm;
-#endif
- char timestamp[32];
- time_t t = playlist->mtime;
- strftime(timestamp, sizeof(timestamp),
-#ifdef G_OS_WIN32
- "%Y-%m-%dT%H:%M:%SZ",
- gmtime(&t)
-#else
- "%FT%TZ",
- gmtime_r(&t, &tm)
-#endif
- );
- client_printf(client, "Last-Modified: %s\n", timestamp);
-
- return true;
-}
-
-static const struct db_visitor print_visitor = {
- .directory = print_visitor_directory,
- .song = print_visitor_song,
- .playlist = print_visitor_playlist,
-};
-
-static const struct db_visitor print_info_visitor = {
- .directory = print_visitor_directory,
- .song = print_visitor_song_info,
- .playlist = print_visitor_playlist_info,
-};
-
-bool
-db_selection_print(struct client *client, const struct db_selection *selection,
- bool full, GError **error_r)
-{
- return db_visit(selection, full ? &print_info_visitor : &print_visitor,
- client, error_r);
-}
-
-struct search_data {
- struct client *client;
- const struct locate_item_list *criteria;
-};
-
-static bool
-search_visitor_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct search_data *data = _data;
-
- if (locate_song_search(song, data->criteria))
- song_print_info(data->client, song);
-
- return true;
-}
-
-static const struct db_visitor search_visitor = {
- .song = search_visitor_song,
-};
-
-bool
-searchForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- struct locate_item_list *new_list
- = locate_item_list_casefold(criteria);
- struct search_data data;
-
- data.client = client;
- data.criteria = new_list;
-
- bool success = db_walk(name, &search_visitor, &data, error_r);
-
- locate_item_list_free(new_list);
-
- return success;
-}
-
-static bool
-find_visitor_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct search_data *data = _data;
-
- if (locate_song_match(song, data->criteria))
- song_print_info(data->client, song);
-
- return true;
-}
-
-static const struct db_visitor find_visitor = {
- .song = find_visitor_song,
-};
-
-bool
-findSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- struct search_data data;
-
- data.client = client;
- data.criteria = criteria;
-
- return db_walk(name, &find_visitor, &data, error_r);
-}
-
-static void printSearchStats(struct client *client, SearchStats *stats)
-{
- client_printf(client, "songs: %i\n", stats->numberOfSongs);
- client_printf(client, "playtime: %li\n", stats->playTime);
-}
-
-static bool
-stats_visitor_song(struct song *song, void *data,
- G_GNUC_UNUSED GError **error_r)
-{
- SearchStats *stats = data;
-
- if (locate_song_match(song, stats->criteria)) {
- stats->numberOfSongs++;
- stats->playTime += song_get_duration(song);
- }
-
- return true;
-}
-
-static const struct db_visitor stats_visitor = {
- .song = stats_visitor_song,
-};
-
-bool
-searchStatsForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- SearchStats stats;
-
- stats.criteria = criteria;
- stats.numberOfSongs = 0;
- stats.playTime = 0;
-
- if (!db_walk(name, &stats_visitor, &stats, error_r))
- return false;
-
- printSearchStats(client, &stats);
- return true;
-}
-
-bool
-printAllIn(struct client *client, const char *uri_utf8, GError **error_r)
-{
- struct db_selection selection;
- db_selection_init(&selection, uri_utf8, true);
- return db_selection_print(client, &selection, false, error_r);
-}
-
-bool
-printInfoForAllIn(struct client *client, const char *uri_utf8,
- GError **error_r)
-{
- struct db_selection selection;
- db_selection_init(&selection, uri_utf8, true);
- return db_selection_print(client, &selection, true, error_r);
-}
-
-static ListCommandItem *
-newListCommandItem(int tagType, const struct locate_item_list *criteria)
-{
- ListCommandItem *item = g_new(ListCommandItem, 1);
-
- item->tagType = tagType;
- item->criteria = criteria;
-
- return item;
-}
-
-static void freeListCommandItem(ListCommandItem * item)
-{
- g_free(item);
-}
-
-static void
-visitTag(struct client *client, struct strset *set,
- struct song *song, enum tag_type tagType)
-{
- struct tag *tag = song->tag;
- bool found = false;
-
- if (tagType == LOCATE_TAG_FILE_TYPE) {
- song_print_uri(client, song);
- return;
- }
-
- if (!tag)
- return;
-
- for (unsigned i = 0; i < tag->num_items; i++) {
- if (tag->items[i]->type == tagType) {
- strset_add(set, tag->items[i]->value);
- found = true;
- }
- }
-
- if (!found)
- strset_add(set, "");
-}
-
-struct list_tags_data {
- struct client *client;
- ListCommandItem *item;
- struct strset *set;
-};
-
-static bool
-unique_tags_visitor_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct list_tags_data *data = _data;
- ListCommandItem *item = data->item;
-
- if (locate_song_match(song, item->criteria))
- visitTag(data->client, data->set, song, item->tagType);
-
- return true;
-}
-
-static const struct db_visitor unique_tags_visitor = {
- .song = unique_tags_visitor_song,
-};
-
-bool
-listAllUniqueTags(struct client *client, int type,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- ListCommandItem *item = newListCommandItem(type, criteria);
- struct list_tags_data data = {
- .client = client,
- .item = item,
- };
-
- if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
- data.set = strset_new();
- }
-
- if (!db_walk("", &unique_tags_visitor, &data, error_r)) {
- freeListCommandItem(item);
- return false;
- }
-
- if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
- const char *value;
-
- strset_rewind(data.set);
-
- while ((value = strset_next(data.set)) != NULL)
- client_printf(client, "%s: %s\n",
- tag_item_names[type],
- value);
-
- strset_free(data.set);
- }
-
- freeListCommandItem(item);
-
- return true;
-}
diff --git a/src/db_visitor.h b/src/db_visitor.h
deleted file mode 100644
index 6b90c1868..000000000
--- a/src/db_visitor.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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_DB_VISITOR_H
-#define MPD_DB_VISITOR_H
-
-struct directory;
-struct song;
-struct playlist_metadata;
-
-struct db_visitor {
- /**
- * Visit a directory. Optional method.
- *
- * @return true to continue the operation, false on error (set error_r)
- */
- bool (*directory)(const struct directory *directory, void *ctx,
- GError **error_r);
-
- /**
- * Visit a song. Optional method.
- *
- * @return true to continue the operation, false on error (set error_r)
- */
- bool (*song)(struct song *song, void *ctx, GError **error_r);
-
- /**
- * Visit a playlist. Optional method.
- *
- * @param directory the directory the playlist resides in
- * @return true to continue the operation, false on error (set error_r)
- */
- bool (*playlist)(const struct playlist_metadata *playlist,
- const struct directory *directory, void *ctx,
- GError **error_r);
-};
-
-#endif
diff --git a/src/decoder/AdPlugDecoderPlugin.cxx b/src/decoder/AdPlugDecoderPlugin.cxx
new file mode 100644
index 000000000..b3b7f1d73
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.cxx
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2012 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 "AdPlugDecoderPlugin.h"
+#include "tag_handler.h"
+
+extern "C" {
+#include "decoder_api.h"
+#include "audio_check.h"
+}
+
+#include <adplug/adplug.h>
+#include <adplug/emuopl.h>
+
+#include <glib.h>
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "adplug"
+
+static unsigned sample_rate;
+
+static bool
+adplug_init(const struct config_param *param)
+{
+ GError *error = NULL;
+
+ sample_rate = config_get_block_unsigned(param, "sample_rate", 48000);
+ if (!audio_check_sample_rate(sample_rate, &error)) {
+ g_warning("%s\n", error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ return true;
+}
+
+static void
+adplug_file_decode(struct decoder *decoder, const char *path_fs)
+{
+ CEmuopl opl(sample_rate, true, true);
+ opl.init();
+
+ CPlayer *player = CAdPlug::factory(path_fs, &opl);
+ if (player == nullptr)
+ return;
+
+ struct audio_format audio_format;
+ audio_format_init(&audio_format, sample_rate, SAMPLE_FORMAT_S16, 2);
+ assert(audio_format_valid(&audio_format));
+
+ decoder_initialized(decoder, &audio_format, false,
+ player->songlength() / 1000.);
+
+ int16_t buffer[2048];
+ const unsigned frames_per_buffer = G_N_ELEMENTS(buffer) / 2;
+ enum decoder_command cmd;
+
+ do {
+ if (!player->update())
+ break;
+
+ opl.update(buffer, frames_per_buffer);
+ cmd = decoder_data(decoder, NULL,
+ buffer, sizeof(buffer),
+ 0);
+ } while (cmd == DECODE_COMMAND_NONE);
+
+ delete player;
+}
+
+static void
+adplug_scan_tag(enum tag_type type, const std::string &value,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ if (!value.empty())
+ tag_handler_invoke_tag(handler, handler_ctx,
+ type, value.c_str());
+}
+
+static bool
+adplug_scan_file(const char *path_fs,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ CEmuopl opl(sample_rate, true, true);
+ opl.init();
+
+ CPlayer *player = CAdPlug::factory(path_fs, &opl);
+ if (player == nullptr)
+ return false;
+
+ tag_handler_invoke_duration(handler, handler_ctx,
+ player->songlength() / 1000);
+
+ if (handler->tag != nullptr) {
+ adplug_scan_tag(TAG_TITLE, player->gettitle(),
+ handler, handler_ctx);
+ adplug_scan_tag(TAG_ARTIST, player->getauthor(),
+ handler, handler_ctx);
+ adplug_scan_tag(TAG_COMMENT, player->getdesc(),
+ handler, handler_ctx);
+ }
+
+ delete player;
+ return true;
+}
+
+static const char *const adplug_suffixes[] = {
+ "amd",
+ "d00",
+ "hsc",
+ "laa",
+ "rad",
+ "raw",
+ "sa2",
+ nullptr
+};
+
+const struct decoder_plugin adplug_decoder_plugin = {
+ "adplug",
+ adplug_init,
+ nullptr,
+ nullptr,
+ adplug_file_decode,
+ adplug_scan_file,
+ nullptr,
+ nullptr,
+ adplug_suffixes,
+ nullptr,
+};
diff --git a/src/decoder/AdPlugDecoderPlugin.h b/src/decoder/AdPlugDecoderPlugin.h
new file mode 100644
index 000000000..9fdf438aa
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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_DECODER_ADPLUG_H
+#define MPD_DECODER_ADPLUG_H
+
+extern const struct decoder_plugin adplug_decoder_plugin;
+
+#endif
diff --git a/src/decoder/_flac_common.c b/src/decoder/FLACCommon.cxx
index d7f0c4a8a..25fd1f964 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/FLACCommon.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,40 +22,35 @@
*/
#include "config.h"
-#include "_flac_common.h"
-#include "flac_metadata.h"
-#include "flac_pcm.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
+#include "FLAC_PCM.hxx"
+
+extern "C" {
#include "audio_check.h"
+}
#include <glib.h>
#include <assert.h>
-void
-flac_data_init(struct flac_data *data, struct decoder * decoder,
- struct input_stream *input_stream)
+flac_data::flac_data(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :FLACInput(_input_stream, _decoder),
+ initialized(false), unsupported(false),
+ total_frames(0), first_frame(0), next_frame(0), position(0),
+ decoder(_decoder), input_stream(_input_stream),
+ tag(nullptr)
{
- pcm_buffer_init(&data->buffer);
-
- data->unsupported = false;
- data->initialized = false;
- data->total_frames = 0;
- data->first_frame = 0;
- data->next_frame = 0;
-
- data->position = 0;
- data->decoder = decoder;
- data->input_stream = input_stream;
- data->tag = NULL;
+ pcm_buffer_init(&buffer);
}
-void
-flac_data_deinit(struct flac_data *data)
+flac_data::~flac_data()
{
- pcm_buffer_deinit(&data->buffer);
+ pcm_buffer_deinit(&buffer);
- if (data->tag != NULL)
- tag_free(data->tag);
+ if (tag != nullptr)
+ tag_free(tag);
}
static enum sample_format
@@ -86,7 +81,7 @@ flac_got_stream_info(struct flac_data *data,
if (data->initialized || data->unsupported)
return;
- GError *error = NULL;
+ GError *error = nullptr;
if (!audio_format_init_checked(&data->audio_format,
stream_info->sample_rate,
flac_sample_format(stream_info->bits_per_sample),
@@ -114,7 +109,6 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
- float replay_gain_db = 0;
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
@@ -123,14 +117,14 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
if (flac_parse_replay_gain(&rgi, block))
- replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
+ decoder_replay_gain(data->decoder, &rgi);
if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block))
- decoder_mixramp(data->decoder, replay_gain_db,
+ decoder_mixramp(data->decoder,
mixramp_start, mixramp_end);
- if (data->tag != NULL)
- flac_vorbis_comments_to_tag(data->tag, NULL,
+ if (data->tag != nullptr)
+ flac_vorbis_comments_to_tag(data->tag,
&block->data.vorbis_comment);
default:
@@ -138,15 +132,6 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
}
}
-void flac_error_common_cb(const FLAC__StreamDecoderErrorStatus status,
- struct flac_data *data)
-{
- if (decoder_get_command(data->decoder) == DECODE_COMMAND_STOP)
- return;
-
- g_warning("%s", FLAC__StreamDecoderErrorStatusString[status]);
-}
-
/**
* This function attempts to call decoder_initialized() in case there
* was no STREAMINFO block. This is allowed for nonseekable streams,
@@ -160,7 +145,7 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
if (data->unsupported)
return false;
- GError *error = NULL;
+ GError *error = nullptr;
if (!audio_format_init_checked(&data->audio_format,
header->sample_rate,
flac_sample_format(header->bits_per_sample),
@@ -199,7 +184,7 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
buffer = pcm_buffer_get(&data->buffer, buffer_size);
flac_convert(buffer, frame->header.channels,
- data->audio_format.format, buf,
+ (enum sample_format)data->audio_format.format, buf,
0, frame->header.blocksize);
if (nbytes > 0)
diff --git a/src/decoder/_flac_common.h b/src/decoder/FLACCommon.hxx
index 0d90ba656..e9b45976d 100644
--- a/src/decoder/_flac_common.h
+++ b/src/decoder/FLACCommon.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,13 +21,15 @@
* Common data structures and functions used by FLAC and OggFLAC
*/
-#ifndef MPD_FLAC_COMMON_H
-#define MPD_FLAC_COMMON_H
+#ifndef MPD_FLAC_COMMON_HXX
+#define MPD_FLAC_COMMON_HXX
+#include "FLACInput.hxx"
+
+extern "C" {
#include "decoder_api.h"
#include "pcm_buffer.h"
-
-#include <glib.h>
+}
#include <FLAC/stream_decoder.h>
#include <FLAC/metadata.h>
@@ -35,7 +37,7 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "flac"
-struct flac_data {
+struct flac_data : public FLACInput {
struct pcm_buffer buffer;
/**
@@ -78,25 +80,19 @@ struct flac_data {
FLAC__uint64 next_frame;
FLAC__uint64 position;
+
struct decoder *decoder;
struct input_stream *input_stream;
- struct tag *tag;
-};
-/* initializes a given FlacData struct */
-void
-flac_data_init(struct flac_data *data, struct decoder * decoder,
- struct input_stream *input_stream);
+ struct tag *tag;
-void
-flac_data_deinit(struct flac_data *data);
+ flac_data(struct decoder *decoder, struct input_stream *input_stream);
+ ~flac_data();
+};
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct flac_data *data);
-void flac_error_common_cb(FLAC__StreamDecoderErrorStatus status,
- struct flac_data *data);
-
FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[],
diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/FLACDecoderPlugin.cxx
index fb0b3502d..dbe7f207f 100644
--- a/src/decoder/flac_decoder_plugin.c
+++ b/src/decoder/FLACDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,13 @@
*/
#include "config.h" /* must be first for large file support */
-#include "_flac_common.h"
-#include "flac_compat.h"
-#include "flac_metadata.h"
+#include "FLACDecoderPlugin.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-#include "_ogg_common.h"
-#endif
+extern "C" {
+#include "ogg_codec.h"
+}
#include <glib.h>
@@ -34,114 +34,10 @@
#include <sys/stat.h>
#include <sys/types.h>
-/* this code was based on flac123, from flac-tools */
-
-static FLAC__StreamDecoderReadStatus
-flac_read_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__byte buf[], flac_read_status_size_t *bytes,
- void *fdata)
-{
- struct flac_data *data = fdata;
- size_t r;
-
- r = decoder_read(data->decoder, data->input_stream,
- (void *)buf, *bytes);
- *bytes = r;
-
- if (r == 0) {
- if (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE ||
- input_stream_lock_eof(data->input_stream))
- return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
- else
- return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
- }
-
- return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
-}
-
-static FLAC__StreamDecoderSeekStatus
-flac_seek_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!data->input_stream->seekable)
- return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
-
- if (!input_stream_lock_seek(data->input_stream, offset, SEEK_SET,
- NULL))
- return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
-
- return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
-}
-
-static FLAC__StreamDecoderTellStatus
-flac_tell_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 * offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!data->input_stream->seekable)
- return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
-
- *offset = (long)(data->input_stream->offset);
-
- return FLAC__STREAM_DECODER_TELL_STATUS_OK;
-}
-
-static FLAC__StreamDecoderLengthStatus
-flac_length_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 * length, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (data->input_stream->size < 0)
- return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
-
- *length = (size_t) (data->input_stream->size);
-
- return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
-}
-
-static FLAC__bool
-flac_eof_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- return (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE &&
- decoder_get_command(data->decoder) != DECODE_COMMAND_SEEK) ||
- input_stream_lock_eof(data->input_stream);
-}
-
-static void
-flac_error_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__StreamDecoderErrorStatus status, void *fdata)
-{
- flac_error_common_cb(status, (struct flac_data *) fdata);
-}
-
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
-{
- switch (state) {
- case FLAC__SEEKABLE_STREAM_DECODER_OK:
- case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
- case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
- return;
-
- case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
- case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
- case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
- break;
- }
+#error libFLAC is too old
+#endif
- g_warning("%s\n", FLAC__SeekableStreamDecoderStateString[state]);
-}
-#else /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacPrintErroredState(FLAC__StreamDecoderState state)
{
switch (state) {
@@ -162,7 +58,6 @@ static void flacPrintErroredState(FLAC__StreamDecoderState state)
g_warning("%s\n", FLAC__StreamDecoderStateString[state]);
}
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec,
const FLAC__StreamMetadata * block, void *vdata)
@@ -195,7 +90,30 @@ static bool
flac_scan_file(const char *file,
const struct tag_handler *handler, void *handler_ctx)
{
- return flac_scan_file2(file, NULL, handler, handler_ctx);
+ FLACMetadataChain chain;
+ if (!chain.Read(file)) {
+ g_debug("Failed to read FLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
+
+ chain.Scan(handler, handler_ctx);
+ return true;
+}
+
+static bool
+flac_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ FLACMetadataChain chain;
+ if (!chain.Read(is)) {
+ g_debug("Failed to read FLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
+
+ chain.Scan(handler, handler_ctx);
+ return true;
}
/**
@@ -205,15 +123,13 @@ static FLAC__StreamDecoder *
flac_decoder_new(void)
{
FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
- if (sd == NULL) {
+ if (sd == nullptr) {
g_warning("FLAC__stream_decoder_new() failed");
- return NULL;
+ return nullptr;
}
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(sd, FLAC__METADATA_TYPE_VORBIS_COMMENT))
g_debug("FLAC__stream_decoder_set_metadata_respond() has failed");
-#endif
return sd;
}
@@ -259,7 +175,7 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
data->first_frame = t_start;
while (true) {
- if (data->tag != NULL && !tag_is_empty(data->tag)) {
+ if (data->tag != nullptr && !tag_is_empty(data->tag)) {
cmd = decoder_tag(data->decoder, data->input_stream,
data->tag);
tag_free(data->tag);
@@ -300,34 +216,30 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
static FLAC__StreamDecoderInitStatus
stream_init_oggflac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
{
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return FLAC__stream_decoder_init_ogg_stream(flac_dec,
- flac_read_cb,
- flac_seek_cb,
- flac_tell_cb,
- flac_length_cb,
- flac_eof_cb,
+ FLACInput::Read,
+ FLACInput::Seek,
+ FLACInput::Tell,
+ FLACInput::Length,
+ FLACInput::Eof,
flac_write_cb,
flacMetadata,
- flac_error_cb,
+ FLACInput::Error,
data);
-#else
- (void)flac_dec;
- (void)data;
-
- return FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-#endif
}
static FLAC__StreamDecoderInitStatus
stream_init_flac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
{
return FLAC__stream_decoder_init_stream(flac_dec,
- flac_read_cb, flac_seek_cb,
- flac_tell_cb, flac_length_cb,
- flac_eof_cb, flac_write_cb,
+ FLACInput::Read,
+ FLACInput::Seek,
+ FLACInput::Tell,
+ FLACInput::Length,
+ FLACInput::Eof,
+ flac_write_cb,
flacMetadata,
- flac_error_cb,
+ FLACInput::Error,
data);
}
@@ -345,28 +257,23 @@ flac_decode_internal(struct decoder * decoder,
bool is_ogg)
{
FLAC__StreamDecoder *flac_dec;
- struct flac_data data;
flac_dec = flac_decoder_new();
- if (flac_dec == NULL)
+ if (flac_dec == nullptr)
return;
- flac_data_init(&data, decoder, input_stream);
+ struct flac_data data(decoder, input_stream);
data.tag = tag_new();
FLAC__StreamDecoderInitStatus status =
stream_init(flac_dec, &data, is_ogg);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
- flac_data_deinit(&data);
FLAC__stream_decoder_delete(flac_dec);
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
g_warning("%s", FLAC__StreamDecoderInitStatusString[status]);
-#endif
return;
}
if (!flac_decoder_initialize(&data, flac_dec, 0)) {
- flac_data_deinit(&data);
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
return;
@@ -374,8 +281,6 @@ flac_decode_internal(struct decoder * decoder,
flac_decoder_loop(&data, flac_dec, 0, 0);
- flac_data_deinit(&data);
-
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
}
@@ -386,101 +291,96 @@ flac_decode(struct decoder * decoder, struct input_stream *input_stream)
flac_decode_internal(decoder, input_stream, false);
}
-#ifndef HAVE_OGGFLAC
-
static bool
oggflac_init(G_GNUC_UNUSED const struct config_param *param)
{
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return !!FLAC_API_SUPPORTS_OGG_FLAC;
-#else
- /* disable oggflac when libflac is too old */
- return false;
-#endif
}
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-
static bool
oggflac_scan_file(const char *file,
const struct tag_handler *handler, void *handler_ctx)
{
- FLAC__Metadata_Iterator *it;
- FLAC__StreamMetadata *block;
- FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
-
- if (!(FLAC__metadata_chain_read_ogg(chain, file))) {
- FLAC__metadata_chain_delete(chain);
+ FLACMetadataChain chain;
+ if (!chain.ReadOgg(file)) {
+ g_debug("Failed to read OggFLAC tags: %s",
+ chain.GetStatusString());
return false;
}
- it = FLAC__metadata_iterator_new();
- FLAC__metadata_iterator_init(it, chain);
-
- do {
- if (!(block = FLAC__metadata_iterator_get_block(it)))
- break;
+ chain.Scan(handler, handler_ctx);
+ return true;
+}
- flac_scan_metadata(NULL, block,
- handler, handler_ctx);
- } while (FLAC__metadata_iterator_next(it));
- FLAC__metadata_iterator_delete(it);
+static bool
+oggflac_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ FLACMetadataChain chain;
+ if (!chain.ReadOgg(is)) {
+ g_debug("Failed to read OggFLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
- FLAC__metadata_chain_delete(chain);
+ chain.Scan(handler, handler_ctx);
return true;
}
static void
oggflac_decode(struct decoder *decoder, struct input_stream *input_stream)
{
- if (ogg_stream_type_detect(input_stream) != FLAC)
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_FLAC)
return;
- /* rewind the stream, because ogg_stream_type_detect() has
+ /* rewind the stream, because ogg_codec_detect() has
moved it */
- input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL);
+ input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
flac_decode_internal(decoder, input_stream, true);
}
-static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
+static const char *const oggflac_suffixes[] = { "ogg", "oga", nullptr };
static const char *const oggflac_mime_types[] = {
"application/ogg",
"application/x-ogg",
"audio/ogg",
"audio/x-flac+ogg",
"audio/x-ogg",
- NULL
+ nullptr
};
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-
const struct decoder_plugin oggflac_decoder_plugin = {
- .name = "oggflac",
- .init = oggflac_init,
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
- .stream_decode = oggflac_decode,
- .scan_file = oggflac_scan_file,
- .suffixes = oggflac_suffixes,
- .mime_types = oggflac_mime_types
-#endif
+ "oggflac",
+ oggflac_init,
+ nullptr,
+ oggflac_decode,
+ nullptr,
+ oggflac_scan_file,
+ oggflac_scan_stream,
+ nullptr,
+ oggflac_suffixes,
+ oggflac_mime_types,
};
-#endif /* HAVE_OGGFLAC */
-
-static const char *const flac_suffixes[] = { "flac", NULL };
+static const char *const flac_suffixes[] = { "flac", nullptr };
static const char *const flac_mime_types[] = {
"application/flac",
"application/x-flac",
"audio/flac",
"audio/x-flac",
- NULL
+ nullptr
};
const struct decoder_plugin flac_decoder_plugin = {
- .name = "flac",
- .stream_decode = flac_decode,
- .scan_file = flac_scan_file,
- .suffixes = flac_suffixes,
- .mime_types = flac_mime_types,
+ "flac",
+ nullptr,
+ nullptr,
+ flac_decode,
+ nullptr,
+ flac_scan_file,
+ flac_scan_stream,
+ nullptr,
+ flac_suffixes,
+ flac_mime_types,
};
diff --git a/src/decoder/FLACDecoderPlugin.h b/src/decoder/FLACDecoderPlugin.h
new file mode 100644
index 000000000..c99deeef7
--- /dev/null
+++ b/src/decoder/FLACDecoderPlugin.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2012 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_DECODER_FLAC_H
+#define MPD_DECODER_FLAC_H
+
+extern const struct decoder_plugin flac_decoder_plugin;
+extern const struct decoder_plugin oggflac_decoder_plugin;
+
+#endif
diff --git a/src/decoder/FLACIOHandle.cxx b/src/decoder/FLACIOHandle.cxx
new file mode 100644
index 000000000..08ec36e48
--- /dev/null
+++ b/src/decoder/FLACIOHandle.cxx
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2003-2012 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 "FLACIOHandle.hxx"
+#include "io_error.h"
+#include "gcc.h"
+
+#include <errno.h>
+
+static size_t
+FLACIORead(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ uint8_t *const p0 = (uint8_t *)ptr, *p = p0,
+ *const end = p0 + size * nmemb;
+
+ /* libFLAC is very picky about short reads, and expects the IO
+ callback to fill the whole buffer (undocumented!) */
+
+ GError *error = nullptr;
+ while (p < end) {
+ size_t nbytes = input_stream_lock_read(is, p, end - p, &error);
+ if (nbytes == 0) {
+ if (error == nullptr)
+ /* end of file */
+ break;
+
+ if (error->domain == errno_quark())
+ errno = error->code;
+ else
+ /* just some random non-zero
+ errno value */
+ errno = EINVAL;
+ g_error_free(error);
+ return 0;
+ }
+
+ p += nbytes;
+ }
+
+ /* libFLAC expects a clean errno after returning from the IO
+ callbacks (undocumented!) */
+ errno = 0;
+ return (p - p0) / size;
+}
+
+static int
+FLACIOSeek(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return input_stream_lock_seek(is, offset, whence, nullptr) ? 0 : -1;
+}
+
+static FLAC__int64
+FLACIOTell(FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return is->offset;
+}
+
+static int
+FLACIOEof(FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return input_stream_lock_eof(is);
+}
+
+static int
+FLACIOClose(gcc_unused FLAC__IOHandle handle)
+{
+ /* no-op because the libFLAC caller is repsonsible for closing
+ the #input_stream */
+
+ return 0;
+}
+
+const FLAC__IOCallbacks flac_io_callbacks = {
+ FLACIORead,
+ nullptr,
+ nullptr,
+ nullptr,
+ FLACIOEof,
+ FLACIOClose,
+};
+
+const FLAC__IOCallbacks flac_io_callbacks_seekable = {
+ FLACIORead,
+ nullptr,
+ FLACIOSeek,
+ FLACIOTell,
+ FLACIOEof,
+ FLACIOClose,
+};
diff --git a/src/decoder/FLACIOHandle.hxx b/src/decoder/FLACIOHandle.hxx
new file mode 100644
index 000000000..193a15ef5
--- /dev/null
+++ b/src/decoder/FLACIOHandle.hxx
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2003-2012 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_IO_HANDLE_HXX
+#define MPD_FLAC_IO_HANDLE_HXX
+
+#include "gcc.h"
+
+extern "C" {
+#include "input_stream.h"
+}
+
+#include <FLAC/callback.h>
+
+extern const FLAC__IOCallbacks flac_io_callbacks;
+extern const FLAC__IOCallbacks flac_io_callbacks_seekable;
+
+static inline FLAC__IOHandle
+ToFLACIOHandle(input_stream *is)
+{
+ return (FLAC__IOHandle)is;
+}
+
+static inline const FLAC__IOCallbacks &
+GetFLACIOCallbacks(const input_stream *is)
+{
+ return is->seekable
+ ? flac_io_callbacks_seekable
+ : flac_io_callbacks;
+}
+
+#endif
diff --git a/src/decoder/FLACInput.cxx b/src/decoder/FLACInput.cxx
new file mode 100644
index 000000000..0383ac4ff
--- /dev/null
+++ b/src/decoder/FLACInput.cxx
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2003-2012 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 "FLACInput.hxx"
+#include "gcc.h"
+
+extern "C" {
+#include "input_stream.h"
+#include "decoder_api.h"
+}
+
+FLAC__StreamDecoderReadStatus
+FLACInput::Read(FLAC__byte buffer[], size_t *bytes)
+{
+ size_t r = decoder_read(decoder, input_stream, (void *)buffer, *bytes);
+ *bytes = r;
+
+ if (r == 0) {
+ if (input_stream_lock_eof(input_stream) ||
+ (decoder != nullptr &&
+ decoder_get_command(decoder) != DECODE_COMMAND_NONE))
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ }
+
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderSeekStatus
+FLACInput::Seek(FLAC__uint64 absolute_byte_offset)
+{
+ if (!input_stream->seekable)
+ return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+
+ if (!input_stream_lock_seek(input_stream,
+ absolute_byte_offset, SEEK_SET,
+ nullptr))
+ return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+
+ return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__StreamDecoderTellStatus
+FLACInput::Tell(FLAC__uint64 *absolute_byte_offset)
+{
+ if (!input_stream->seekable)
+ return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+
+ *absolute_byte_offset = (FLAC__uint64)input_stream->offset;
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+FLAC__StreamDecoderLengthStatus
+FLACInput::Length(FLAC__uint64 *stream_length)
+{
+ if (input_stream->size < 0)
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+
+ *stream_length = (FLAC__uint64)input_stream->size;
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+FLAC__bool
+FLACInput::Eof()
+{
+ return (decoder != nullptr &&
+ decoder_get_command(decoder) != DECODE_COMMAND_NONE &&
+ decoder_get_command(decoder) != DECODE_COMMAND_SEEK) ||
+ input_stream_lock_eof(input_stream);
+}
+
+void
+FLACInput::Error(FLAC__StreamDecoderErrorStatus status)
+{
+ if (decoder == nullptr ||
+ decoder_get_command(decoder) != DECODE_COMMAND_STOP)
+ g_warning("%s", FLAC__StreamDecoderErrorStatusString[status]);
+}
+
+FLAC__StreamDecoderReadStatus
+FLACInput::Read(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__byte buffer[], size_t *bytes,
+ void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Read(buffer, bytes);
+}
+
+FLAC__StreamDecoderSeekStatus
+FLACInput::Seek(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Seek(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderTellStatus
+FLACInput::Tell(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Tell(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderLengthStatus
+FLACInput::Length(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *stream_length, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Length(stream_length);
+}
+
+FLAC__bool
+FLACInput::Eof(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Eof();
+}
+
+void
+FLACInput::Error(gcc_unused const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ i->Error(status);
+}
+
diff --git a/src/decoder/FLACInput.hxx b/src/decoder/FLACInput.hxx
new file mode 100644
index 000000000..7661567d1
--- /dev/null
+++ b/src/decoder/FLACInput.hxx
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2003-2012 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_INPUT_HXX
+#define MPD_FLAC_INPUT_HXX
+
+#include <FLAC/stream_decoder.h>
+
+/**
+ * This class wraps an #input_stream in libFLAC stream decoder
+ * callbacks.
+ */
+class FLACInput {
+ struct decoder *decoder;
+
+ struct input_stream *input_stream;
+
+public:
+ FLACInput(struct input_stream *_input_stream,
+ struct decoder *_decoder=nullptr)
+ :decoder(_decoder), input_stream(_input_stream) {}
+
+protected:
+ FLAC__StreamDecoderReadStatus Read(FLAC__byte buffer[], size_t *bytes);
+ FLAC__StreamDecoderSeekStatus Seek(FLAC__uint64 absolute_byte_offset);
+ FLAC__StreamDecoderTellStatus Tell(FLAC__uint64 *absolute_byte_offset);
+ FLAC__StreamDecoderLengthStatus Length(FLAC__uint64 *stream_length);
+ FLAC__bool Eof();
+ void Error(FLAC__StreamDecoderErrorStatus status);
+
+public:
+ static FLAC__StreamDecoderReadStatus
+ Read(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__byte buffer[], size_t *bytes, void *client_data);
+
+ static FLAC__StreamDecoderSeekStatus
+ Seek(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data);
+
+ static FLAC__StreamDecoderTellStatus
+ Tell(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data);
+
+ static FLAC__StreamDecoderLengthStatus
+ Length(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *stream_length, void *client_data);
+
+ static FLAC__bool
+ Eof(const FLAC__StreamDecoder *flac_decoder, void *client_data);
+
+ static void
+ Error(const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data);
+};
+
+#endif
diff --git a/src/decoder/flac_metadata.c b/src/decoder/FLACMetaData.cxx
index bd1eaf323..561c1591f 100644
--- a/src/decoder/flac_metadata.c
+++ b/src/decoder/FLACMetaData.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,20 @@
*/
#include "config.h"
-#include "flac_metadata.h"
+#include "FLACMetaData.hxx"
+
+extern "C" {
+#include "XiphTags.h"
#include "replay_gain_info.h"
#include "tag.h"
+}
+
#include "tag_handler.h"
#include "tag_table.h"
#include <glib.h>
#include <assert.h>
-#include <stdbool.h>
#include <stdlib.h>
static bool
@@ -91,7 +95,7 @@ flac_find_string_comment(const FLAC__StreamMetadata *block,
int len;
const unsigned char *p;
- *str = NULL;
+ *str = nullptr;
offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
cmnt);
if (offset < 0)
@@ -128,36 +132,21 @@ flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
*/
static const char *
flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
- const char *name, const char *char_tnum, size_t *length_r)
+ const char *name, size_t *length_r)
{
size_t name_length = strlen(name);
- size_t char_tnum_length = 0;
const char *comment = (const char*)entry->entry;
if (entry->length <= name_length ||
g_ascii_strncasecmp(comment, name, name_length) != 0)
- return NULL;
-
- if (char_tnum != NULL) {
- char_tnum_length = strlen(char_tnum);
- if (entry->length > name_length + char_tnum_length + 2 &&
- comment[name_length] == '[' &&
- g_ascii_strncasecmp(comment + name_length + 1,
- char_tnum, char_tnum_length) == 0 &&
- comment[name_length + char_tnum_length + 1] == ']')
- name_length = name_length + char_tnum_length + 2;
- else if (entry->length > name_length + char_tnum_length &&
- g_ascii_strncasecmp(comment + name_length,
- char_tnum, char_tnum_length) == 0)
- name_length = name_length + char_tnum_length;
- }
+ return nullptr;
if (comment[name_length] == '=') {
*length_r = entry->length - name_length - 1;
return comment + name_length + 1;
}
- return NULL;
+ return nullptr;
}
/**
@@ -167,14 +156,13 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
static bool
flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, enum tag_type tag_type,
- const char *char_tnum,
const struct tag_handler *handler, void *handler_ctx)
{
const char *value;
size_t value_length;
- value = flac_comment_value(entry, name, char_tnum, &value_length);
- if (value != NULL) {
+ value = flac_comment_value(entry, name, &value_length);
+ if (value != nullptr) {
char *p = g_strndup(value, value_length);
tag_handler_invoke_tag(handler, handler_ctx, tag_type, p);
g_free(p);
@@ -184,23 +172,15 @@ flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
return false;
}
-static const struct tag_table flac_tags[] = {
- { "tracknumber", TAG_TRACK },
- { "discnumber", TAG_DISC },
- { "album artist", TAG_ALBUM_ARTIST },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
static void
-flac_scan_comment(const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment_Entry *entry,
+flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const struct tag_handler *handler, void *handler_ctx)
{
- if (handler->pair != NULL) {
+ if (handler->pair != nullptr) {
char *name = g_strdup((const char*)entry->entry);
char *value = strchr(name, '=');
- if (value != NULL && value > name) {
+ if (value != nullptr && value > name) {
*value++ = 0;
tag_handler_invoke_pair(handler, handler_ctx,
name, value);
@@ -209,36 +189,34 @@ flac_scan_comment(const char *char_tnum,
g_free(name);
}
- for (const struct tag_table *i = flac_tags; i->name != NULL; ++i)
- if (flac_copy_comment(entry, i->name, i->type, char_tnum,
+ for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
+ if (flac_copy_comment(entry, i->name, i->type,
handler, handler_ctx))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (flac_copy_comment(entry,
- tag_item_names[i], i, char_tnum,
+ tag_item_names[i], (enum tag_type)i,
handler, handler_ctx))
return;
}
static void
-flac_scan_comments(const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment *comment,
+flac_scan_comments(const FLAC__StreamMetadata_VorbisComment *comment,
const struct tag_handler *handler, void *handler_ctx)
{
for (unsigned i = 0; i < comment->num_comments; ++i)
- flac_scan_comment(char_tnum, &comment->comments[i],
+ flac_scan_comment(&comment->comments[i],
handler, handler_ctx);
}
void
-flac_scan_metadata(const char *track,
- const FLAC__StreamMetadata *block,
+flac_scan_metadata(const FLAC__StreamMetadata *block,
const struct tag_handler *handler, void *handler_ctx)
{
switch (block->type) {
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
- flac_scan_comments(track, &block->data.vorbis_comment,
+ flac_scan_comments(&block->data.vorbis_comment,
handler, handler_ctx);
break;
@@ -254,70 +232,22 @@ flac_scan_metadata(const char *track,
}
void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
+flac_vorbis_comments_to_tag(struct tag *tag,
const FLAC__StreamMetadata_VorbisComment *comment)
{
- flac_scan_comments(char_tnum, comment,
- &add_tag_handler, tag);
+ flac_scan_comments(comment, &add_tag_handler, tag);
}
-bool
-flac_scan_file2(const char *file, const char *char_tnum,
- const struct tag_handler *handler, void *handler_ctx)
+void
+FLACMetadataChain::Scan(const struct tag_handler *handler, void *handler_ctx)
{
- FLAC__Metadata_SimpleIterator *it;
- FLAC__StreamMetadata *block = NULL;
-
- it = FLAC__metadata_simple_iterator_new();
- if (!FLAC__metadata_simple_iterator_init(it, file, 1, 0)) {
- const char *err;
- FLAC_API FLAC__Metadata_SimpleIteratorStatus s;
-
- s = FLAC__metadata_simple_iterator_status(it);
-
- switch (s) { /* slightly more human-friendly messages: */
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
- err = "illegal input";
- break;
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
- err = "error opening file";
- break;
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
- err = "not a FLAC file";
- break;
- default:
- err = FLAC__Metadata_SimpleIteratorStatusString[s];
- }
- g_debug("Reading '%s' metadata gave the following error: %s\n",
- file, err);
- FLAC__metadata_simple_iterator_delete(it);
- return false;
- }
+ FLACMetadataIterator iterator(*this);
do {
- block = FLAC__metadata_simple_iterator_get_block(it);
- if (!block)
+ FLAC__StreamMetadata *block = iterator.GetBlock();
+ if (block == nullptr)
break;
- flac_scan_metadata(char_tnum, block, handler, handler_ctx);
- FLAC__metadata_object_delete(block);
- } while (FLAC__metadata_simple_iterator_next(it));
-
- FLAC__metadata_simple_iterator_delete(it);
-
- return true;
-}
-
-struct tag *
-flac_tag_load(const char *file, const char *char_tnum)
-{
- struct tag *tag = tag_new();
-
- if (!flac_scan_file2(file, char_tnum, &add_tag_handler, tag) ||
- tag_is_empty(tag)) {
- tag_free(tag);
- tag = NULL;
- }
-
- return tag;
+ flac_scan_metadata(block, handler, handler_ctx);
+ } while (iterator.Next());
}
diff --git a/src/decoder/FLACMetaData.hxx b/src/decoder/FLACMetaData.hxx
new file mode 100644
index 000000000..0eceec23c
--- /dev/null
+++ b/src/decoder/FLACMetaData.hxx
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2003-2012 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_METADATA_H
+#define MPD_FLAC_METADATA_H
+
+#include "gcc.h"
+#include "FLACIOHandle.hxx"
+
+#include <FLAC/metadata.h>
+
+#include <assert.h>
+
+class FLACMetadataChain {
+ FLAC__Metadata_Chain *chain;
+
+public:
+ FLACMetadataChain():chain(::FLAC__metadata_chain_new()) {}
+
+ ~FLACMetadataChain() {
+ ::FLAC__metadata_chain_delete(chain);
+ }
+
+ explicit operator FLAC__Metadata_Chain *() {
+ return chain;
+ }
+
+ bool Read(const char *path) {
+ return ::FLAC__metadata_chain_read(chain, path);
+ }
+
+ bool Read(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) {
+ return ::FLAC__metadata_chain_read_with_callbacks(chain,
+ handle,
+ callbacks);
+ }
+
+ bool Read(input_stream *is) {
+ return Read(::ToFLACIOHandle(is), ::GetFLACIOCallbacks(is));
+ }
+
+ bool ReadOgg(const char *path) {
+ return ::FLAC__metadata_chain_read_ogg(chain, path);
+ }
+
+ bool ReadOgg(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) {
+ return ::FLAC__metadata_chain_read_ogg_with_callbacks(chain,
+ handle,
+ callbacks);
+ }
+
+ bool ReadOgg(input_stream *is) {
+ return ReadOgg(::ToFLACIOHandle(is), ::GetFLACIOCallbacks(is));
+ }
+
+ gcc_pure
+ FLAC__Metadata_ChainStatus GetStatus() const {
+ return ::FLAC__metadata_chain_status(chain);
+ }
+
+ gcc_pure
+ const char *GetStatusString() const {
+ return FLAC__Metadata_ChainStatusString[GetStatus()];
+ }
+
+ void Scan(const struct tag_handler *handler, void *handler_ctx);
+};
+
+class FLACMetadataIterator {
+ FLAC__Metadata_Iterator *iterator;
+
+public:
+ FLACMetadataIterator():iterator(::FLAC__metadata_iterator_new()) {}
+
+ FLACMetadataIterator(FLACMetadataChain &chain)
+ :iterator(::FLAC__metadata_iterator_new()) {
+ ::FLAC__metadata_iterator_init(iterator,
+ (FLAC__Metadata_Chain *)chain);
+ }
+
+ ~FLACMetadataIterator() {
+ ::FLAC__metadata_iterator_delete(iterator);
+ }
+
+ bool Next() {
+ return ::FLAC__metadata_iterator_next(iterator);
+ }
+
+ gcc_pure
+ FLAC__StreamMetadata *GetBlock() {
+ return ::FLAC__metadata_iterator_get_block(iterator);
+ }
+};
+
+struct tag_handler;
+struct tag;
+struct replay_gain_info;
+
+static inline unsigned
+flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
+{
+ assert(stream_info->sample_rate > 0);
+
+ return (stream_info->total_samples + stream_info->sample_rate - 1) /
+ stream_info->sample_rate;
+}
+
+bool
+flac_parse_replay_gain(struct replay_gain_info *rgi,
+ const FLAC__StreamMetadata *block);
+
+bool
+flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
+ const FLAC__StreamMetadata *block);
+
+void
+flac_vorbis_comments_to_tag(struct tag *tag,
+ const FLAC__StreamMetadata_VorbisComment *comment);
+
+void
+flac_scan_metadata(const FLAC__StreamMetadata *block,
+ const struct tag_handler *handler, void *handler_ctx);
+
+#endif
diff --git a/src/decoder/flac_pcm.c b/src/decoder/FLAC_PCM.cxx
index 6964d8ac6..303530aa7 100644
--- a/src/decoder/flac_pcm.c
+++ b/src/decoder/FLAC_PCM.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 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 "flac_pcm.h"
+#include "FLAC_PCM.hxx"
#include <assert.h>
diff --git a/src/decoder/flac_pcm.h b/src/decoder/FLAC_PCM.hxx
index a931998c1..97d214c17 100644
--- a/src/decoder/flac_pcm.h
+++ b/src/decoder/FLAC_PCM.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 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_FLAC_PCM_H
-#define MPD_FLAC_PCM_H
+#ifndef MPD_FLAC_PCM_HXX
+#define MPD_FLAC_PCM_HXX
#include "audio_format.h"
diff --git a/src/decoder/OggUtil.cxx b/src/decoder/OggUtil.cxx
new file mode 100644
index 000000000..99f73d48e
--- /dev/null
+++ b/src/decoder/OggUtil.cxx
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2003-2012 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 "OggUtil.hxx"
+
+extern "C" {
+#include "decoder_api.h"
+}
+
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+ struct input_stream *input_stream, size_t size)
+{
+ char *buffer = ogg_sync_buffer(&oy, size);
+ if (buffer == nullptr)
+ return false;
+
+ size_t nbytes = decoder_read(decoder, input_stream,
+ buffer, size);
+ if (nbytes == 0)
+ return false;
+
+ ogg_sync_wrote(&oy, nbytes);
+ return true;
+}
+
+bool
+OggExpectPage(ogg_sync_state &oy, ogg_page &page,
+ struct decoder *decoder, struct input_stream *input_stream)
+{
+ while (true) {
+ int r = ogg_sync_pageout(&oy, &page);
+ if (r != 0)
+ return r > 0;
+
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ return false;
+ }
+}
diff --git a/src/decoder/OggUtil.hxx b/src/decoder/OggUtil.hxx
new file mode 100644
index 000000000..95bf6472f
--- /dev/null
+++ b/src/decoder/OggUtil.hxx
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2003-2012 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_OGG_UTIL_HXX
+#define MPD_OGG_UTIL_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+#include <stddef.h>
+
+/**
+ * Feed data from the #input_stream into the #ogg_sync_state.
+ *
+ * @return false on error or end-of-file
+ */
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+ struct input_stream *input_stream, size_t size);
+
+/**
+ * Feed into the #ogg_sync_state until a page gets available. Garbage
+ * data at the beginning is considered a fatal error.
+ *
+ * @return true if a page is available
+ */
+bool
+OggExpectPage(ogg_sync_state &oy, ogg_page &page,
+ struct decoder *decoder, struct input_stream *input_stream);
+
+#endif
diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/OpusDecoderPlugin.cxx
new file mode 100644
index 000000000..35e368ca9
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.cxx
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h" /* must be first for large file support */
+#include "OpusDecoderPlugin.h"
+#include "OpusHead.hxx"
+#include "OpusTags.hxx"
+#include "OggUtil.hxx"
+
+extern "C" {
+#include "ogg_codec.h"
+#include "decoder_api.h"
+}
+
+#include "audio_check.h"
+#include "tag_handler.h"
+
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#include <glib.h>
+
+#include <stdio.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "opus"
+
+static const opus_int32 opus_sample_rate = 48000;
+
+gcc_pure
+static bool
+IsOpusHead(const ogg_packet &packet)
+{
+ return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0;
+}
+
+gcc_pure
+static bool
+IsOpusTags(const ogg_packet &packet)
+{
+ return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0;
+}
+
+static bool
+mpd_opus_init(G_GNUC_UNUSED const struct config_param *param)
+{
+ g_debug("%s", opus_get_version_string());
+
+ return true;
+}
+
+class MPDOpusDecoder {
+ struct decoder *decoder;
+ struct input_stream *input_stream;
+
+ ogg_stream_state os;
+
+ OpusDecoder *opus_decoder = nullptr;
+ opus_int16 *output_buffer = nullptr;
+ unsigned output_size = 0;
+
+ bool os_initialized = false;
+ bool found_opus = false;
+
+ int opus_serialno;
+
+ size_t frame_size;
+
+public:
+ MPDOpusDecoder(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :decoder(_decoder), input_stream(_input_stream) {}
+ ~MPDOpusDecoder();
+
+ enum decoder_command HandlePage(ogg_page &page);
+ enum decoder_command HandlePacket(const ogg_packet &packet);
+ enum decoder_command HandleBOS(const ogg_packet &packet);
+ enum decoder_command HandleTags(const ogg_packet &packet);
+ enum decoder_command HandleAudio(const ogg_packet &packet);
+};
+
+MPDOpusDecoder::~MPDOpusDecoder()
+{
+ g_free(output_buffer);
+
+ if (opus_decoder != nullptr)
+ opus_decoder_destroy(opus_decoder);
+
+ if (os_initialized)
+ ogg_stream_clear(&os);
+}
+
+enum decoder_command
+MPDOpusDecoder::HandlePage(ogg_page &page)
+{
+ const auto page_serialno = ogg_page_serialno(&page);
+ if (!os_initialized) {
+ os_initialized = true;
+ ogg_stream_init(&os, page_serialno);
+ } else if (page_serialno != os.serialno)
+ ogg_stream_reset_serialno(&os, page_serialno);
+
+ ogg_stream_pagein(&os, &page);
+
+ ogg_packet packet;
+ while (ogg_stream_packetout(&os, &packet) == 1) {
+ enum decoder_command cmd = HandlePacket(packet);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+enum decoder_command
+MPDOpusDecoder::HandlePacket(const ogg_packet &packet)
+{
+ if (packet.e_o_s)
+ return DECODE_COMMAND_STOP;
+
+ if (packet.b_o_s)
+ return HandleBOS(packet);
+ else if (!found_opus)
+ return DECODE_COMMAND_STOP;
+
+ if (IsOpusTags(packet))
+ return HandleTags(packet);
+
+ return HandleAudio(packet);
+}
+
+enum decoder_command
+MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
+{
+ assert(packet.b_o_s);
+
+ if (found_opus || !IsOpusHead(packet))
+ return DECODE_COMMAND_STOP;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels))
+ return DECODE_COMMAND_STOP;
+
+ assert(opus_decoder == nullptr);
+ assert(output_buffer == nullptr);
+
+ opus_serialno = os.serialno;
+ found_opus = true;
+
+ /* TODO: parse attributes from the OpusHead (sample rate,
+ channels, ...) */
+
+ int opus_error;
+ opus_decoder = opus_decoder_create(opus_sample_rate, channels,
+ &opus_error);
+ if (opus_decoder == nullptr) {
+ g_warning("libopus error: %s",
+ opus_strerror(opus_error));
+ return DECODE_COMMAND_STOP;
+ }
+
+ struct audio_format audio_format;
+ audio_format_init(&audio_format, opus_sample_rate,
+ SAMPLE_FORMAT_S16, channels);
+ decoder_initialized(decoder, &audio_format, false, -1);
+ frame_size = audio_format_frame_size(&audio_format);
+
+ /* allocate an output buffer for 16 bit PCM samples big enough
+ to hold a quarter second, larger than 120ms required by
+ libopus */
+ output_size = audio_format.sample_rate / 4;
+ output_buffer = (opus_int16 *)
+ g_malloc(sizeof(*output_buffer) * output_size *
+ audio_format.channels);
+
+ return decoder_get_command(decoder);
+}
+
+enum decoder_command
+MPDOpusDecoder::HandleTags(const ogg_packet &packet)
+{
+ struct tag *tag = tag_new();
+
+ enum decoder_command cmd;
+ if (ScanOpusTags(packet.packet, packet.bytes, &add_tag_handler, tag) &&
+ !tag_is_empty(tag))
+ cmd = decoder_tag(decoder, input_stream, tag);
+ else
+ cmd = decoder_get_command(decoder);
+
+ tag_free(tag);
+ return cmd;
+}
+
+enum decoder_command
+MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
+{
+ assert(opus_decoder != nullptr);
+
+ int nframes = opus_decode(opus_decoder,
+ (const unsigned char*)packet.packet,
+ packet.bytes,
+ output_buffer, output_size,
+ 0);
+ if (nframes < 0) {
+ g_warning("%s", opus_strerror(nframes));
+ return DECODE_COMMAND_STOP;
+ }
+
+ if (nframes > 0) {
+ const size_t nbytes = nframes * frame_size;
+ enum decoder_command cmd =
+ decoder_data(decoder, input_stream,
+ output_buffer, nbytes,
+ 0);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+static void
+mpd_opus_stream_decode(struct decoder *decoder,
+ struct input_stream *input_stream)
+{
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_OPUS)
+ return;
+
+ /* rewind the stream, because ogg_codec_detect() has
+ moved it */
+ input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
+
+ MPDOpusDecoder d(decoder, input_stream);
+
+ ogg_sync_state oy;
+ ogg_sync_init(&oy);
+
+ while (true) {
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ break;
+
+ ogg_page page;
+ while (ogg_sync_pageout(&oy, &page) == 1) {
+ enum decoder_command cmd = d.HandlePage(page);
+ if (cmd != DECODE_COMMAND_NONE)
+ break;
+ }
+ }
+
+ ogg_sync_clear(&oy);
+}
+
+static bool
+mpd_opus_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ ogg_sync_state oy;
+ ogg_sync_init(&oy);
+
+ ogg_page page;
+ if (!OggExpectPage(oy, page, nullptr, is)) {
+ ogg_sync_clear(&oy);
+ return false;
+ }
+
+ /* read at most two more pages */
+ unsigned remaining_pages = 2;
+
+ bool result = false;
+
+ ogg_stream_state os;
+ ogg_stream_init(&os, ogg_page_serialno(&page));
+ ogg_stream_pagein(&os, &page);
+
+ ogg_packet packet;
+ while (true) {
+ int r = ogg_stream_packetout(&os, &packet);
+ if (r < 0) {
+ result = false;
+ break;
+ }
+
+ if (r == 0) {
+ if (remaining_pages-- == 0)
+ break;
+
+ if (!OggExpectPage(oy, page, nullptr, is)) {
+ result = false;
+ break;
+ }
+
+ ogg_stream_pagein(&os, &page);
+ continue;
+ }
+
+ if (packet.b_o_s) {
+ if (!IsOpusHead(packet))
+ break;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels)) {
+ result = false;
+ break;
+ }
+
+ result = true;
+ } else if (!result)
+ break;
+ else if (IsOpusTags(packet)) {
+ if (!ScanOpusTags(packet.packet, packet.bytes,
+ handler, handler_ctx))
+ result = false;
+
+ break;
+ }
+ }
+
+ ogg_stream_clear(&os);
+ ogg_sync_clear(&oy);
+
+ return result;
+}
+
+static const char *const opus_suffixes[] = {
+ "opus",
+ "ogg",
+ "oga",
+ nullptr
+};
+
+static const char *const opus_mime_types[] = {
+ "audio/opus",
+ nullptr
+};
+
+const struct decoder_plugin opus_decoder_plugin = {
+ "opus",
+ mpd_opus_init,
+ nullptr,
+ mpd_opus_stream_decode,
+ nullptr,
+ nullptr,
+ mpd_opus_scan_stream,
+ nullptr,
+ opus_suffixes,
+ opus_mime_types,
+};
diff --git a/src/decoder/OpusDecoderPlugin.h b/src/decoder/OpusDecoderPlugin.h
new file mode 100644
index 000000000..c95d6ded3
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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_DECODER_OPUS_H
+#define MPD_DECODER_OPUS_H
+
+extern const struct decoder_plugin opus_decoder_plugin;
+
+#endif
diff --git a/src/decoder/OpusHead.cxx b/src/decoder/OpusHead.cxx
new file mode 100644
index 000000000..c57e08e10
--- /dev/null
+++ b/src/decoder/OpusHead.cxx
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2012 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 "OpusHead.hxx"
+
+#include <stdint.h>
+#include <string.h>
+
+struct OpusHead {
+ char signature[8];
+ uint8_t version, channels;
+ uint16_t pre_skip;
+ uint32_t sample_rate;
+ uint16_t output_gain;
+ uint8_t channel_mapping;
+};
+
+bool
+ScanOpusHeader(const void *data, size_t size, unsigned &channels_r)
+{
+ const OpusHead *h = (const OpusHead *)data;
+ if (size < 19 || (h->version & 0xf0) != 0)
+ return false;
+
+ channels_r = h->channels;
+ return true;
+}
diff --git a/src/decoder/OpusHead.hxx b/src/decoder/OpusHead.hxx
new file mode 100644
index 000000000..9f75c4f70
--- /dev/null
+++ b/src/decoder/OpusHead.hxx
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2003-2012 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_OPUS_HEAD_HXX
+#define MPD_OPUS_HEAD_HXX
+
+#include "check.h"
+
+#include <stddef.h>
+
+bool
+ScanOpusHeader(const void *data, size_t size, unsigned &channels_r);
+
+#endif
diff --git a/src/decoder/OpusReader.hxx b/src/decoder/OpusReader.hxx
new file mode 100644
index 000000000..2cfc14118
--- /dev/null
+++ b/src/decoder/OpusReader.hxx
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2003-2012 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_OPUS_READER_HXX
+#define MPD_OPUS_READER_HXX
+
+#include "check.h"
+
+#include <stdint.h>
+#include <string.h>
+
+class OpusReader {
+ const uint8_t *p, *const end;
+
+public:
+ OpusReader(const void *_p, size_t size)
+ :p((const uint8_t *)_p), end(p + size) {}
+
+ bool Skip(size_t length) {
+ p += length;
+ return p <= end;
+ }
+
+ const void *Read(size_t length) {
+ const uint8_t *result = p;
+ return Skip(length)
+ ? result
+ : nullptr;
+ }
+
+ bool Expect(const void *value, size_t length) {
+ const void *data = Read(length);
+ return data != nullptr && memcmp(value, data, length) == 0;
+ }
+
+ bool ReadByte(uint8_t &value_r) {
+ if (p >= end)
+ return false;
+
+ value_r = *p++;
+ return true;
+ }
+
+ bool ReadShort(uint16_t &value_r) {
+ const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
+ if (value == nullptr)
+ return false;
+
+ value_r = value[0] | (value[1] << 8);
+ return true;
+ }
+
+ bool ReadWord(uint32_t &value_r) {
+ const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
+ if (value == nullptr)
+ return false;
+
+ value_r = value[0] | (value[1] << 8)
+ | (value[2] << 16) | (value[3] << 24);
+ return true;
+ }
+
+ bool SkipString() {
+ uint32_t length;
+ return ReadWord(length) && Skip(length);
+ }
+
+ char *ReadString() {
+ uint32_t length;
+ if (!ReadWord(length))
+ return nullptr;
+
+ const char *src = (const char *)Read(length);
+ if (src == nullptr)
+ return nullptr;
+
+ return strndup(src, length);
+ }
+};
+
+#endif
diff --git a/src/decoder/OpusTags.cxx b/src/decoder/OpusTags.cxx
new file mode 100644
index 000000000..cb35a6247
--- /dev/null
+++ b/src/decoder/OpusTags.cxx
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003-2012 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 "OpusTags.hxx"
+#include "OpusReader.hxx"
+#include "XiphTags.h"
+#include "tag_handler.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void
+ScanOneOpusTag(const char *name, const char *value,
+ const struct tag_handler *handler, void *ctx)
+{
+ tag_handler_invoke_pair(handler, ctx, name, value);
+
+ if (handler->tag != nullptr) {
+ enum tag_type t = tag_table_lookup_i(xiph_tags, name);
+ if (t != TAG_NUM_OF_ITEM_TYPES)
+ tag_handler_invoke_tag(handler, ctx, t, value);
+ }
+}
+
+bool
+ScanOpusTags(const void *data, size_t size,
+ const struct tag_handler *handler, void *ctx)
+{
+ OpusReader r(data, size);
+ if (!r.Expect("OpusTags", 8))
+ return false;
+
+ if (handler->pair == nullptr && handler->tag == nullptr)
+ return true;
+
+ if (!r.SkipString())
+ return false;
+
+ uint32_t n;
+ if (!r.ReadWord(n))
+ return false;
+
+ while (n-- > 0) {
+ char *p = r.ReadString();
+ if (p == nullptr)
+ return false;
+
+ char *eq = strchr(p, '=');
+ if (eq != nullptr && eq > p) {
+ *eq = 0;
+
+ ScanOneOpusTag(p, eq + 1, handler, ctx);
+ }
+
+ free(p);
+ }
+
+ return true;
+}
diff --git a/src/decoder/OpusTags.hxx b/src/decoder/OpusTags.hxx
new file mode 100644
index 000000000..2f3eec844
--- /dev/null
+++ b/src/decoder/OpusTags.hxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2012 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_OPUS_TAGS_HXX
+#define MPD_OPUS_TAGS_HXX
+
+#include "check.h"
+
+#include <stddef.h>
+
+bool
+ScanOpusTags(const void *data, size_t size,
+ const struct tag_handler *handler, void *ctx);
+
+#endif
diff --git a/src/decoder/XiphTags.c b/src/decoder/XiphTags.c
new file mode 100644
index 000000000..d55787b94
--- /dev/null
+++ b/src/decoder/XiphTags.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2012 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 "XiphTags.h"
+
+const struct tag_table xiph_tags[] = {
+ { "tracknumber", TAG_TRACK },
+ { "discnumber", TAG_DISC },
+ { "album artist", TAG_ALBUM_ARTIST },
+ { NULL, TAG_NUM_OF_ITEM_TYPES }
+};
diff --git a/src/decoder/XiphTags.h b/src/decoder/XiphTags.h
new file mode 100644
index 000000000..22a4e2204
--- /dev/null
+++ b/src/decoder/XiphTags.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2012 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_XIPH_TAGS_H
+#define MPD_XIPH_TAGS_H
+
+#include "check.h"
+#include "tag_table.h"
+
+extern const struct tag_table xiph_tags[];
+
+#endif
diff --git a/src/decoder/dsdiff_decoder_plugin.c b/src/decoder/dsdiff_decoder_plugin.c
index 84471fb3a..4e21e91de 100644
--- a/src/decoder/dsdiff_decoder_plugin.c
+++ b/src/decoder/dsdiff_decoder_plugin.c
@@ -52,10 +52,23 @@ struct dsdiff_chunk_header {
uint32_t size_high, size_low;
};
+/** struct for DSDIFF native Artist and Title tags */
+struct dsdiff_native_tag {
+ uint32_t size;
+};
+
struct dsdiff_metadata {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
+ /** offset for artist tag */
+ goffset diar_offset;
+ /** offset for title tag */
+ goffset diti_offset;
};
static bool lsbitfirst;
@@ -187,6 +200,127 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
return dsdlib_skip_to(decoder, is, end_offset);
}
+static void
+dsdiff_handle_native_tag(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset,
+ enum tag_type type)
+{
+ if (!dsdlib_skip_to(NULL, is, tagoffset))
+ return;
+
+ struct dsdiff_native_tag metatag;
+
+ if (!dsdlib_read(NULL, is, &metatag, sizeof(metatag)))
+ return;
+
+ uint32_t length = GUINT32_FROM_BE(metatag.size);
+
+ /* Check and limit size of the tag to prevent a stack overflow */
+ if (length == 0 || length > 60)
+ return;
+
+ char string[length];
+ char *label;
+ label = string;
+
+ if (!dsdlib_read(NULL, is, label, (size_t)length))
+ return;
+
+ string[length] = '\0';
+ tag_handler_invoke_tag(handler, handler_ctx, type, label);
+ return;
+}
+
+/**
+ * Read and parse additional metadata chunks for tagging purposes. By default
+ * dsdiff files only support equivalents for artist and title but some of the
+ * extract tools add an id3 tag to provide more tags. If such id3 is found
+ * this will be used for tagging otherwise the native tags (if any) will be
+ * used
+ */
+
+static bool
+dsdiff_read_metadata_extra(struct decoder *decoder, struct input_stream *is,
+ struct dsdiff_metadata *metadata,
+ struct dsdiff_chunk_header *chunk_header,
+ const struct tag_handler *handler,
+ void *handler_ctx)
+{
+
+ /* skip from DSD data to next chunk header */
+ if (!dsdlib_skip(decoder, is, metadata->chunk_size))
+ return false;
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+
+#ifdef HAVE_ID3TAG
+ metadata->id3_size = 0;
+#endif
+
+ /* Now process all the remaining chunk headers in the stream
+ and record their position and size */
+
+ while ( is->offset < is->size )
+ {
+ uint64_t chunk_size = dsdiff_chunk_size(chunk_header);
+
+ /* DIIN chunk, is directly followed by other chunks */
+ if (dsdlib_id_equals(&chunk_header->id, "DIIN"))
+ chunk_size = 0;
+
+ /* DIAR chunk - DSDIFF native tag for Artist */
+ if (dsdlib_id_equals(&chunk_header->id, "DIAR")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->diar_offset = is->offset;
+ }
+
+ /* DITI chunk - DSDIFF native tag for Title */
+ if (dsdlib_id_equals(&chunk_header->id, "DITI")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->diti_offset = is->offset;
+ }
+#ifdef HAVE_ID3TAG
+ /* 'ID3 ' chunk, offspec. Used by sacdextract */
+ if (dsdlib_id_equals(&chunk_header->id, "ID3 ")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->id3_offset = is->offset;
+ metadata->id3_size = chunk_size;
+ }
+#endif
+ if (chunk_size != 0) {
+ if (!dsdlib_skip(decoder, is, chunk_size))
+ break;
+ }
+
+ if ( is->offset < is->size ) {
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+ }
+ chunk_size = 0;
+ }
+ /* done processing chunk headers, process tags if any */
+
+#ifdef HAVE_ID3TAG
+ if (metadata->id3_offset != 0)
+ {
+ /* a ID3 tag has preference over the other tags, do not process
+ other tags if we have one */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata->id3_offset);
+ return true;
+ }
+#endif
+
+ if (metadata->diar_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diar_offset, TAG_ARTIST);
+
+ if (metadata->diti_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diti_offset, TAG_TITLE);
+ return true;
+}
+
/**
* Read and parse all metadata chunks at the beginning. Stop when the
* first "DSD" chunk is seen, and return its header in the
@@ -374,6 +508,10 @@ dsdiff_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+ /* Read additional metadata and created tags if available */
+ dsdiff_read_metadata_extra(NULL, is, &metadata, &chunk_header,
+ handler, handler_ctx);
+
return true;
}
diff --git a/src/decoder/dsdlib.c b/src/decoder/dsdlib.c
index 3df9497c4..c788184e2 100644
--- a/src/decoder/dsdlib.c
+++ b/src/decoder/dsdlib.c
@@ -27,12 +27,18 @@
#include "dsf_decoder_plugin.h"
#include "decoder_api.h"
#include "util/bit_reverse.h"
+#include "tag_handler.h"
+#include "tag_id3.h"
#include "dsdlib.h"
#include "dsdiff_decoder_plugin.h"
#include <unistd.h>
#include <stdio.h> /* for SEEK_SET, SEEK_CUR */
+#ifdef HAVE_ID3TAG
+#include <id3tag.h>
+#endif
+
bool
dsdlib_id_equals(const struct dsdlib_id *id, const char *s)
{
@@ -110,3 +116,53 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
return true;
}
+/**
+ * Add tags from ID3 tag. All tags commonly found in the ID3 tags of
+ * DSF and DSDIFF files are imported
+ */
+
+#ifdef HAVE_ID3TAG
+void
+dsdlib_tag_id3(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset)
+{
+ assert(tagoffset >= 0);
+
+ if (tagoffset == 0)
+ return;
+
+ if (!dsdlib_skip_to(NULL, is, tagoffset))
+ return;
+
+ struct id3_tag *id3_tag = NULL;
+ id3_length_t count;
+
+ /* Prevent broken files causing problems */
+ if (is->offset >= is->size)
+ return;
+
+ count = is->size - is->offset;
+
+ /* Check and limit id3 tag size to prevent a stack overflow */
+ if (count == 0 || count > 4096)
+ return;
+
+ id3_byte_t dsdid3[count];
+ id3_byte_t *dsdid3data;
+ dsdid3data = dsdid3;
+
+ if (!dsdlib_read(NULL, is, dsdid3data, count))
+ return;
+
+ id3_tag = id3_tag_parse(dsdid3data, count);
+ if (id3_tag == NULL)
+ return;
+
+ scan_id3_tag(id3_tag, handler, handler_ctx);
+
+ id3_tag_delete(id3_tag);
+
+ return;
+}
+#endif
diff --git a/src/decoder/dsdlib.h b/src/decoder/dsdlib.h
index d9675f5fe..0912740c3 100644
--- a/src/decoder/dsdlib.h
+++ b/src/decoder/dsdlib.h
@@ -39,4 +39,9 @@ bool
dsdlib_skip(struct decoder *decoder, struct input_stream *is,
goffset delta);
+void
+dsdlib_tag_id3(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset);
+
#endif
diff --git a/src/decoder/dsf_decoder_plugin.c b/src/decoder/dsf_decoder_plugin.c
index c0107eb30..6700f739a 100644
--- a/src/decoder/dsf_decoder_plugin.c
+++ b/src/decoder/dsf_decoder_plugin.c
@@ -45,6 +45,10 @@ struct dsf_metadata {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
};
struct dsf_header {
@@ -57,6 +61,7 @@ struct dsf_header {
/** pointer to id3v2 metadata, should be at the end of the file */
uint32_t pmeta_low, pmeta_high;
};
+
/** DSF file fmt chunk */
struct dsf_fmt_chunk {
@@ -109,6 +114,12 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
if (sizeof(dsf_header) != chunk_size)
return false;
+#ifdef HAVE_ID3TAG
+ uint64_t metadata_offset;
+ metadata_offset = (((uint64_t)GUINT32_FROM_LE(dsf_header.pmeta_high)) << 32) |
+ ((uint64_t)GUINT32_FROM_LE(dsf_header.pmeta_low));
+#endif
+
/* read the 'fmt ' chunk of the DSF file */
struct dsf_fmt_chunk dsf_fmt_chunk;
if (!dsdlib_read(decoder, is, &dsf_fmt_chunk, sizeof(dsf_fmt_chunk)) ||
@@ -153,9 +164,19 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
data_size -= sizeof(data_chunk);
metadata->chunk_size = data_size;
+ /* data_size cannot be bigger or equal to total file size */
+ if (data_size >= (unsigned) is->size)
+ return false;
+
metadata->channels = (unsigned) dsf_fmt_chunk.channelnum;
metadata->sample_rate = samplefreq;
-
+#ifdef HAVE_ID3TAG
+ /* metada_offset cannot be bigger then or equal to total file size */
+ if (metadata_offset >= (unsigned) is->size)
+ metadata->id3_offset = 0;
+ else
+ metadata->id3_offset = (goffset) metadata_offset;
+#endif
/* check bits per sample format, determine if bitreverse is needed */
metadata->bitreverse = dsf_fmt_chunk.bitssample == 1;
return true;
@@ -285,7 +306,7 @@ dsf_stream_decode(struct decoder *decoder, struct input_stream *is)
decoder_initialized(decoder, &audio_format, false, songtime);
if (!dsf_decode_chunk(decoder, is, metadata.channels,
- metadata.chunk_size,
+ chunk_size,
metadata.bitreverse))
return;
}
@@ -316,6 +337,10 @@ dsf_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+#ifdef HAVE_ID3TAG
+ /* Add available tags from the ID3 tag */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata.id3_offset);
+#endif
return true;
}
diff --git a/src/decoder/flac_compat.h b/src/decoder/flac_compat.h
deleted file mode 100644
index 9a30acc26..000000000
--- a/src/decoder/flac_compat.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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.
- */
-
-/*
- * Common data structures and functions used by FLAC and OggFLAC
- */
-
-#ifndef MPD_FLAC_COMPAT_H
-#define MPD_FLAC_COMPAT_H
-
-#include <FLAC/export.h>
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-# include <FLAC/seekable_stream_decoder.h>
-
-/* starting with libFLAC 1.1.3, the SeekableStreamDecoder has been
- merged into the StreamDecoder. The following macros try to emulate
- the new API for libFLAC 1.1.2 by mapping MPD's StreamDecoder calls
- to the old SeekableStreamDecoder API. */
-
-#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder
-#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new
-#define FLAC__stream_decoder_get_decode_position FLAC__seekable_stream_decoder_get_decode_position
-#define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state
-#define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single
-#define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata
-#define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute
-#define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish
-#define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete
-
-#define FLAC__STREAM_DECODER_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM
-
-typedef unsigned flac_read_status_size_t;
-
-#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus
-#define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
-#define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
-#define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR
-
-#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus
-#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK
-#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
-#define FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
-
-#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus
-#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK
-#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
-#define FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
-
-#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
-
-typedef enum {
- FLAC__STREAM_DECODER_INIT_STATUS_OK,
- FLAC__STREAM_DECODER_INIT_STATUS_ERROR,
-} FLAC__StreamDecoderInitStatus;
-
-static inline FLAC__StreamDecoderInitStatus
-FLAC__stream_decoder_init_stream(FLAC__SeekableStreamDecoder *decoder,
- FLAC__SeekableStreamDecoderReadCallback read_cb,
- FLAC__SeekableStreamDecoderSeekCallback seek_cb,
- FLAC__SeekableStreamDecoderTellCallback tell_cb,
- FLAC__SeekableStreamDecoderLengthCallback length_cb,
- FLAC__SeekableStreamDecoderEofCallback eof_cb,
- FLAC__SeekableStreamDecoderWriteCallback write_cb,
- FLAC__SeekableStreamDecoderMetadataCallback metadata_cb,
- FLAC__SeekableStreamDecoderErrorCallback error_cb,
- void *data)
-{
- return FLAC__seekable_stream_decoder_set_read_callback(decoder, read_cb) &&
- FLAC__seekable_stream_decoder_set_seek_callback(decoder, seek_cb) &&
- FLAC__seekable_stream_decoder_set_tell_callback(decoder, tell_cb) &&
- FLAC__seekable_stream_decoder_set_length_callback(decoder, length_cb) &&
- FLAC__seekable_stream_decoder_set_eof_callback(decoder, eof_cb) &&
- FLAC__seekable_stream_decoder_set_write_callback(decoder, write_cb) &&
- FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadata_cb) &&
- FLAC__seekable_stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT) &&
- FLAC__seekable_stream_decoder_set_error_callback(decoder, error_cb) &&
- FLAC__seekable_stream_decoder_set_client_data(decoder, data) &&
- FLAC__seekable_stream_decoder_init(decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK
- ? FLAC__STREAM_DECODER_INIT_STATUS_OK
- : FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-}
-
-#else /* FLAC_API_VERSION_CURRENT > 7 */
-
-# include <FLAC/stream_decoder.h>
-
-# define flac_init(a,b,c,d,e,f,g,h,i,j) \
- (FLAC__stream_decoder_init_stream(a,b,c,d,e,f,g,h,i,j) \
- == FLAC__STREAM_DECODER_INIT_STATUS_OK)
-
-typedef size_t flac_read_status_size_t;
-
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-
-#endif /* _FLAC_COMMON_H */
diff --git a/src/decoder/flac_metadata.h b/src/decoder/flac_metadata.h
deleted file mode 100644
index 3c463d5d6..000000000
--- a/src/decoder/flac_metadata.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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_METADATA_H
-#define MPD_FLAC_METADATA_H
-
-#include <assert.h>
-#include <stdbool.h>
-#include <FLAC/metadata.h>
-
-struct tag_handler;
-struct tag;
-struct replay_gain_info;
-
-static inline unsigned
-flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
-{
- assert(stream_info->sample_rate > 0);
-
- return (stream_info->total_samples + stream_info->sample_rate - 1) /
- stream_info->sample_rate;
-}
-
-bool
-flac_parse_replay_gain(struct replay_gain_info *rgi,
- const FLAC__StreamMetadata *block);
-
-bool
-flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
- const FLAC__StreamMetadata *block);
-
-void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment *comment);
-
-void
-flac_scan_metadata(const char *track,
- const FLAC__StreamMetadata *block,
- const struct tag_handler *handler, void *handler_ctx);
-
-bool
-flac_scan_file2(const char *file, const char *char_tnum,
- const struct tag_handler *handler, void *handler_ctx);
-
-struct tag *
-flac_tag_load(const char *file, const char *char_tnum);
-
-#endif
diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c
index 62c371642..0c27ac119 100644
--- a/src/decoder/mad_decoder_plugin.c
+++ b/src/decoder/mad_decoder_plugin.c
@@ -76,9 +76,9 @@ mad_fixed_to_24_sample(mad_fixed_t sample)
sample = sample + (1L << (MAD_F_FRACBITS - bits));
/* clip */
- if (sample > MAX)
+ if (gcc_unlikely(sample > MAX))
sample = MAX;
- else if (sample < MIN)
+ else if (gcc_unlikely(sample < MIN))
sample = MIN;
/* quantize */
@@ -359,15 +359,14 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
- float replay_gain_db = 0;
if (parse_id3_replay_gain_info(&rgi, id3_tag)) {
- replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
+ decoder_replay_gain(data->decoder, &rgi);
data->found_replay_gain = true;
}
if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag))
- decoder_mixramp(data->decoder, replay_gain_db,
+ decoder_mixramp(data->decoder,
mixramp_start, mixramp_end);
}
diff --git a/src/decoder/_ogg_common.c b/src/decoder/ogg_codec.c
index 09d2712da..7416f27da 100644
--- a/src/decoder/_ogg_common.c
+++ b/src/decoder/ogg_codec.c
@@ -22,25 +22,27 @@
*/
#include "config.h"
-#include "_ogg_common.h"
+#include "ogg_codec.h"
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream)
+enum ogg_codec
+ogg_codec_detect(struct decoder *decoder, struct input_stream *is)
{
/* oggflac detection based on code in ogg123 and this post
* http://lists.xiph.org/pipermail/flac/2004-December/000393.html
* ogg123 trunk still doesn't have this patch as of June 2005 */
unsigned char buf[41];
- size_t r;
-
- r = decoder_read(NULL, inStream, buf, sizeof(buf));
+ size_t r = decoder_read(decoder, is, buf, sizeof(buf));
if (r < sizeof(buf) || memcmp(buf, "OggS", 4) != 0)
- return VORBIS;
+ return OGG_CODEC_UNKNOWN;
if ((memcmp(buf + 29, "FLAC", 4) == 0 &&
memcmp(buf + 37, "fLaC", 4) == 0) ||
memcmp(buf + 28, "FLAC", 4) == 0 ||
memcmp(buf + 28, "fLaC", 4) == 0)
- return FLAC;
+ return OGG_CODEC_FLAC;
+
+ if (memcmp(buf + 28, "Opus", 4) == 0)
+ return OGG_CODEC_OPUS;
- return VORBIS;
+ return OGG_CODEC_VORBIS;
}
diff --git a/src/decoder/_ogg_common.h b/src/decoder/ogg_codec.h
index 85e4ebba6..fd1fecfbb 100644
--- a/src/decoder/_ogg_common.h
+++ b/src/decoder/ogg_codec.h
@@ -21,13 +21,19 @@
* Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC)
*/
-#ifndef MPD_OGG_COMMON_H
-#define MPD_OGG_COMMON_H
+#ifndef MPD_OGG_CODEC_H
+#define MPD_OGG_CODEC_H
#include "decoder_api.h"
-typedef enum _ogg_stream_type { VORBIS, FLAC } ogg_stream_type;
+enum ogg_codec {
+ OGG_CODEC_UNKNOWN,
+ OGG_CODEC_VORBIS,
+ OGG_CODEC_FLAC,
+ OGG_CODEC_OPUS,
+};
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream);
+enum ogg_codec
+ogg_codec_detect(struct decoder *decoder, struct input_stream *is);
#endif /* _OGG_COMMON_H */
diff --git a/src/decoder/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx
index 5d162f179..de2e599e9 100644
--- a/src/decoder/sidplay_decoder_plugin.cxx
+++ b/src/decoder/sidplay_decoder_plugin.cxx
@@ -104,7 +104,7 @@ sidplay_init(const struct config_param *param)
return true;
}
-void
+static void
sidplay_finish()
{
g_pattern_spec_free(path_with_subtune);
@@ -136,7 +136,7 @@ get_container_name(const char *path_fs)
* returns tune number from file.sid/tune_xxx.sid style path or 1 if
* no subtune is appended
*/
-static int
+static unsigned
get_song_num(const char *path_fs)
{
if(g_pattern_match(path_with_subtune,
@@ -172,7 +172,7 @@ get_song_length(const char *path_fs)
char md5sum[SIDTUNE_MD5_LENGTH+1];
tune.createMD5(md5sum);
- int song_num=get_song_num(path_fs);
+ const unsigned song_num = get_song_num(path_fs);
gsize num_items;
gchar **values=g_key_file_get_string_list(songlength_database,
@@ -330,7 +330,7 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
decoder_command_finished(decoder);
}
- if (song_len > 0 && player.time() >= song_len)
+ if (song_len > 0 && player.time() >= (unsigned)song_len)
break;
} while (cmd != DECODE_COMMAND_STOP);
diff --git a/src/decoder/vorbis_comments.c b/src/decoder/vorbis_comments.c
index 6c2d57b72..84f7c5014 100644
--- a/src/decoder/vorbis_comments.c
+++ b/src/decoder/vorbis_comments.c
@@ -19,6 +19,7 @@
#include "config.h"
#include "vorbis_comments.h"
+#include "XiphTags.h"
#include "tag.h"
#include "tag_table.h"
#include "tag_handler.h"
@@ -95,13 +96,6 @@ vorbis_copy_comment(const char *comment,
return false;
}
-static const struct tag_table vorbis_tags[] = {
- { "tracknumber", TAG_TRACK },
- { "discnumber", TAG_DISC },
- { "album artist", TAG_ALBUM_ARTIST },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
static void
vorbis_scan_comment(const char *comment,
const struct tag_handler *handler, void *handler_ctx)
@@ -119,7 +113,7 @@ vorbis_scan_comment(const char *comment,
g_free(name);
}
- for (const struct tag_table *i = vorbis_tags; i->name != NULL; ++i)
+ for (const struct tag_table *i = xiph_tags; i->name != NULL; ++i)
if (vorbis_copy_comment(comment, i->name, i->type,
handler, handler_ctx))
return;
diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/vorbis_decoder_plugin.c
index 15cdc0ca9..f180a6559 100644
--- a/src/decoder/vorbis_decoder_plugin.c
+++ b/src/decoder/vorbis_decoder_plugin.c
@@ -19,7 +19,7 @@
#include "config.h"
#include "vorbis_comments.h"
-#include "_ogg_common.h"
+#include "ogg_codec.h"
#include "audio_check.h"
#include "uri.h"
#include "tag_handler.h"
@@ -48,12 +48,11 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "vorbis"
-#define OGG_CHUNK_SIZE 4096
#if G_BYTE_ORDER == G_BIG_ENDIAN
-#define OGG_DECODE_USE_BIGENDIAN 1
+#define VORBIS_BIG_ENDIAN true
#else
-#define OGG_DECODE_USE_BIGENDIAN 0
+#define VORBIS_BIG_ENDIAN false
#endif
struct vorbis_input_stream {
@@ -66,9 +65,8 @@ struct vorbis_input_stream {
static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
{
struct vorbis_input_stream *vis = data;
- size_t ret;
-
- ret = decoder_read(vis->decoder, vis->input_stream, ptr, size * nmemb);
+ size_t ret = decoder_read(vis->decoder, vis->input_stream,
+ ptr, size * nmemb);
errno = 0;
@@ -155,9 +153,7 @@ static void
vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
char **comments)
{
- struct tag *tag;
-
- tag = vorbis_comments_to_tag(comments);
+ struct tag *tag = vorbis_comments_to_tag(comments);
if (!tag)
return;
@@ -165,55 +161,79 @@ vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
tag_free(tag);
}
+#ifndef HAVE_TREMOR
+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 *d = dest;
+ for (const float *s = *src, *s_end = s + nframes;
+ s != s_end; ++s, d += channels)
+ *d = *s;
+ }
+}
+#endif
+
/* public */
static void
vorbis_stream_decode(struct decoder *decoder,
struct input_stream *input_stream)
{
GError *error = NULL;
- OggVorbis_File vf;
- struct vorbis_input_stream vis;
- struct audio_format audio_format;
- float total_time;
- int current_section;
- int prev_section = -1;
- long ret;
- char chunk[OGG_CHUNK_SIZE];
- long bitRate = 0;
- long test;
- const vorbis_info *vi;
- enum decoder_command cmd = DECODE_COMMAND_NONE;
-
- if (ogg_stream_type_detect(input_stream) != VORBIS)
+
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_VORBIS)
return;
- /* rewind the stream, because ogg_stream_type_detect() has
+ /* rewind the stream, because ogg_codec_detect() has
moved it */
input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL);
+ struct vorbis_input_stream vis;
+ OggVorbis_File vf;
if (!vorbis_is_open(&vis, &vf, decoder, input_stream))
return;
- vi = ov_info(&vf, -1);
+ const vorbis_info *vi = ov_info(&vf, -1);
if (vi == NULL) {
g_warning("ov_info() has failed");
return;
}
+ struct audio_format audio_format;
if (!audio_format_init_checked(&audio_format, vi->rate,
+#ifdef HAVE_TREMOR
SAMPLE_FORMAT_S16,
+#else
+ SAMPLE_FORMAT_FLOAT,
+#endif
vi->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
return;
}
- total_time = ov_time_total(&vf, -1);
+ float total_time = ov_time_total(&vf, -1);
if (total_time < 0)
total_time = 0;
decoder_initialized(decoder, &audio_format, vis.seekable, total_time);
+ enum decoder_command cmd = decoder_get_command(decoder);
+
+#ifdef HAVE_TREMOR
+ char buffer[4096];
+#else
+ float buffer[2048];
+ const int frames_per_buffer =
+ G_N_ELEMENTS(buffer) / audio_format.channels;
+ const unsigned frame_size = sizeof(buffer[0]) * audio_format.channels;
+#endif
+
+ int prev_section = -1;
+ unsigned kbit_rate = 0;
+
do {
if (cmd == DECODE_COMMAND_SEEK) {
double seek_where = decoder_seek_where(decoder);
@@ -223,17 +243,33 @@ vorbis_stream_decode(struct decoder *decoder,
decoder_seek_error(decoder);
}
- ret = ov_read(&vf, chunk, sizeof(chunk),
- OGG_DECODE_USE_BIGENDIAN, 2, 1, &current_section);
- if (ret == OV_HOLE) /* bad packet */
- ret = 0;
- else if (ret <= 0)
+ int current_section;
+
+#ifdef HAVE_TREMOR
+ long nbytes = ov_read(&vf, buffer, sizeof(buffer),
+ VORBIS_BIG_ENDIAN, 2, 1,
+ &current_section);
+#else
+ float **per_channel;
+ long nframes = ov_read_float(&vf, &per_channel,
+ frames_per_buffer,
+ &current_section);
+ long nbytes = nframes;
+ if (nframes > 0) {
+ vorbis_interleave(buffer,
+ (const float*const*)per_channel,
+ nframes, audio_format.channels);
+ nbytes *= frame_size;
+ }
+#endif
+
+ if (nbytes == OV_HOLE) /* bad packet */
+ nbytes = 0;
+ else if (nbytes <= 0)
/* break on EOF or other error */
break;
if (current_section != prev_section) {
- char **comments;
-
vi = ov_info(&vf, -1);
if (vi == NULL) {
g_warning("ov_info() has failed");
@@ -248,7 +284,7 @@ vorbis_stream_decode(struct decoder *decoder,
break;
}
- comments = ov_comment(&vf, -1)->user_comments;
+ char **comments = ov_comment(&vf, -1)->user_comments;
vorbis_send_comments(decoder, input_stream, comments);
struct replay_gain_info rgi;
@@ -258,12 +294,13 @@ vorbis_stream_decode(struct decoder *decoder,
prev_section = current_section;
}
- if ((test = ov_bitrate_instant(&vf)) > 0)
- bitRate = test / 1000;
+ long test = ov_bitrate_instant(&vf);
+ if (test > 0)
+ kbit_rate = test / 1000;
cmd = decoder_data(decoder, input_stream,
- chunk, ret,
- bitRate);
+ buffer, nbytes,
+ kbit_rate);
} while (cmd != DECODE_COMMAND_STOP);
ov_clear(&vf);
diff --git a/src/decoder_api.h b/src/decoder_api.h
index 6e011c395..76cf03920 100644
--- a/src/decoder_api.h
+++ b/src/decoder_api.h
@@ -152,9 +152,8 @@ decoder_tag(struct decoder *decoder, struct input_stream *is,
* @param decoder the decoder object
* @param rgi the replay_gain_info object; may be NULL to invalidate
* the previous replay gain values
- * @return the replay gain adjustment used
*/
-float
+void
decoder_replay_gain(struct decoder *decoder,
const struct replay_gain_info *replay_gain_info);
@@ -162,12 +161,11 @@ decoder_replay_gain(struct decoder *decoder,
* Store MixRamp tags.
*
* @param decoder the decoder object
- * @param replay_gain_db the ReplayGain adjustment used for this song
* @param mixramp_start the mixramp_start tag; may be NULL to invalidate
* @param mixramp_end the mixramp_end tag; may be NULL to invalidate
*/
void
-decoder_mixramp(struct decoder *decoder, float replay_gain_db,
+decoder_mixramp(struct decoder *decoder,
char *mixramp_start, char *mixramp_end);
#endif
diff --git a/src/decoder_error.h b/src/decoder_error.h
new file mode 100644
index 000000000..a12a31937
--- /dev/null
+++ b/src/decoder_error.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003-2012 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_DECODER_ERROR_H
+#define MPD_DECODER_ERROR_H
+
+#include <glib.h>
+
+/**
+ * Quark for GError.domain.
+ */
+G_GNUC_CONST
+static inline GQuark
+decoder_quark(void)
+{
+ return g_quark_from_static_string("decoder");
+}
+
+#endif
diff --git a/src/decoder_list.c b/src/decoder_list.c
index 177b632ad..575346c98 100644
--- a/src/decoder_list.c
+++ b/src/decoder_list.c
@@ -26,6 +26,9 @@
#include "decoder/pcm_decoder_plugin.h"
#include "decoder/dsdiff_decoder_plugin.h"
#include "decoder/dsf_decoder_plugin.h"
+#include "decoder/FLACDecoderPlugin.h"
+#include "decoder/OpusDecoderPlugin.h"
+#include "decoder/AdPlugDecoderPlugin.h"
#include <glib.h>
@@ -34,8 +37,6 @@
extern const struct decoder_plugin mad_decoder_plugin;
extern const struct decoder_plugin mpg123_decoder_plugin;
extern const struct decoder_plugin vorbis_decoder_plugin;
-extern const struct decoder_plugin flac_decoder_plugin;
-extern const struct decoder_plugin oggflac_decoder_plugin;
extern const struct decoder_plugin sndfile_decoder_plugin;
extern const struct decoder_plugin audiofile_decoder_plugin;
extern const struct decoder_plugin mp4ff_decoder_plugin;
@@ -66,6 +67,9 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef HAVE_FLAC
&flac_decoder_plugin,
#endif
+#ifdef HAVE_OPUS
+ &opus_decoder_plugin,
+#endif
#ifdef ENABLE_SNDFILE
&sndfile_decoder_plugin,
#endif
@@ -101,6 +105,9 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef ENABLE_FLUIDSYNTH
&fluidsynth_decoder_plugin,
#endif
+#ifdef HAVE_ADPLUG
+ &adplug_decoder_plugin,
+#endif
#ifdef HAVE_FFMPEG
&ffmpeg_decoder_plugin,
#endif
diff --git a/src/directory.c b/src/directory.c
deleted file mode 100644
index e886698d6..000000000
--- a/src/directory.c
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "directory.h"
-#include "song.h"
-#include "song_sort.h"
-#include "playlist_vector.h"
-#include "path.h"
-#include "util/list_sort.h"
-#include "db_visitor.h"
-#include "db_lock.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-struct directory *
-directory_new(const char *path, struct directory *parent)
-{
- struct directory *directory;
- size_t pathlen = strlen(path);
-
- assert(path != NULL);
- assert((*path == 0) == (parent == NULL));
-
- directory = g_malloc0(sizeof(*directory) -
- sizeof(directory->path) + pathlen + 1);
- INIT_LIST_HEAD(&directory->children);
- INIT_LIST_HEAD(&directory->songs);
- INIT_LIST_HEAD(&directory->playlists);
-
- directory->parent = parent;
- memcpy(directory->path, path, pathlen + 1);
-
- return directory;
-}
-
-void
-directory_free(struct directory *directory)
-{
- playlist_vector_deinit(&directory->playlists);
-
- struct song *song, *ns;
- directory_for_each_song_safe(song, ns, directory)
- song_free(song);
-
- struct directory *child, *n;
- directory_for_each_child_safe(child, n, directory)
- directory_free(child);
-
- g_free(directory);
- /* this resets last dir returned */
- /*directory_get_path(NULL); */
-}
-
-void
-directory_delete(struct directory *directory)
-{
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(directory->parent != NULL);
-
- list_del(&directory->siblings);
- directory_free(directory);
-}
-
-const char *
-directory_get_name(const struct directory *directory)
-{
- assert(!directory_is_root(directory));
- assert(directory->path != NULL);
-
- const char *slash = strrchr(directory->path, '/');
- assert((slash == NULL) == directory_is_root(directory->parent));
-
- return slash != NULL
- ? slash + 1
- : directory->path;
-}
-
-struct directory *
-directory_new_child(struct directory *parent, const char *name_utf8)
-{
- assert(holding_db_lock());
- assert(parent != NULL);
- assert(name_utf8 != NULL);
- assert(*name_utf8 != 0);
-
- char *allocated;
- const char *path_utf8;
- if (directory_is_root(parent)) {
- allocated = NULL;
- path_utf8 = name_utf8;
- } else {
- allocated = g_strconcat(directory_get_path(parent),
- "/", name_utf8, NULL);
- path_utf8 = allocated;
- }
-
- struct directory *directory = directory_new(path_utf8, parent);
- g_free(allocated);
-
- list_add_tail(&directory->siblings, &parent->children);
- return directory;
-}
-
-struct directory *
-directory_get_child(const struct directory *directory, const char *name)
-{
- assert(holding_db_lock());
-
- struct directory *child;
- directory_for_each_child(child, directory)
- if (strcmp(directory_get_name(child), name) == 0)
- return child;
-
- return NULL;
-}
-
-void
-directory_prune_empty(struct directory *directory)
-{
- assert(holding_db_lock());
-
- struct directory *child, *n;
- directory_for_each_child_safe(child, n, directory) {
- directory_prune_empty(child);
-
- if (directory_is_empty(child))
- directory_delete(child);
- }
-}
-
-struct directory *
-directory_lookup_directory(struct directory *directory, const char *uri)
-{
- assert(holding_db_lock());
- assert(uri != NULL);
-
- if (isRootDirectory(uri))
- return directory;
-
- char *duplicated = g_strdup(uri), *name = duplicated;
-
- while (1) {
- char *slash = strchr(name, '/');
- if (slash == name) {
- directory = NULL;
- break;
- }
-
- if (slash != NULL)
- *slash = '\0';
-
- directory = directory_get_child(directory, name);
- if (directory == NULL || slash == NULL)
- break;
-
- name = slash + 1;
- }
-
- g_free(duplicated);
-
- return directory;
-}
-
-void
-directory_add_song(struct directory *directory, struct song *song)
-{
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(song != NULL);
- assert(song->parent == directory);
-
- list_add_tail(&song->siblings, &directory->songs);
-}
-
-void
-directory_remove_song(G_GNUC_UNUSED struct directory *directory,
- struct song *song)
-{
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(song != NULL);
- assert(song->parent == directory);
-
- list_del(&song->siblings);
-}
-
-struct song *
-directory_get_song(const struct directory *directory, const char *name_utf8)
-{
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(name_utf8 != NULL);
-
- struct song *song;
- directory_for_each_song(song, directory) {
- assert(song->parent == directory);
-
- if (strcmp(song->uri, name_utf8) == 0)
- return song;
- }
-
- return NULL;
-}
-
-struct song *
-directory_lookup_song(struct directory *directory, const char *uri)
-{
- char *duplicated, *base;
-
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(uri != NULL);
-
- duplicated = g_strdup(uri);
- base = strrchr(duplicated, '/');
-
- if (base != NULL) {
- *base++ = 0;
- directory = directory_lookup_directory(directory, duplicated);
- if (directory == NULL) {
- g_free(duplicated);
- return NULL;
- }
- } else
- base = duplicated;
-
- struct song *song = directory_get_song(directory, base);
- assert(song == NULL || song->parent == directory);
-
- g_free(duplicated);
- return song;
-
-}
-
-static int
-directory_cmp(G_GNUC_UNUSED void *priv,
- struct list_head *_a, struct list_head *_b)
-{
- const struct directory *a = (const struct directory *)_a;
- const struct directory *b = (const struct directory *)_b;
- return g_utf8_collate(a->path, b->path);
-}
-
-void
-directory_sort(struct directory *directory)
-{
- assert(holding_db_lock());
-
- list_sort(NULL, &directory->children, directory_cmp);
- song_list_sort(&directory->songs);
-
- struct directory *child;
- directory_for_each_child(child, directory)
- directory_sort(child);
-}
-
-bool
-directory_walk(const struct directory *directory, bool recursive,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
-{
- assert(directory != NULL);
- assert(visitor != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- if (visitor->song != NULL) {
- struct song *song;
- directory_for_each_song(song, directory)
- if (!visitor->song(song, ctx, error_r))
- return false;
- }
-
- if (visitor->playlist != NULL) {
- struct playlist_metadata *i;
- directory_for_each_playlist(i, directory)
- if (!visitor->playlist(i, directory, ctx, error_r))
- return false;
- }
-
- struct directory *child;
- directory_for_each_child(child, directory) {
- if (visitor->directory != NULL &&
- !visitor->directory(child, ctx, error_r))
- return false;
-
- if (recursive &&
- !directory_walk(child, recursive, visitor, ctx, error_r))
- return false;
- }
-
- return true;
-}
diff --git a/src/directory.h b/src/directory.h
deleted file mode 100644
index b3cd9c8c9..000000000
--- a/src/directory.h
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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_DIRECTORY_H
-#define MPD_DIRECTORY_H
-
-#include "check.h"
-#include "util/list.h"
-
-#include <glib.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-#define DEVICE_INARCHIVE (dev_t)(-1)
-#define DEVICE_CONTAINER (dev_t)(-2)
-
-#define directory_for_each_child(pos, directory) \
- list_for_each_entry(pos, &directory->children, siblings)
-
-#define directory_for_each_child_safe(pos, n, directory) \
- list_for_each_entry_safe(pos, n, &directory->children, siblings)
-
-#define directory_for_each_song(pos, directory) \
- list_for_each_entry(pos, &directory->songs, siblings)
-
-#define directory_for_each_song_safe(pos, n, directory) \
- list_for_each_entry_safe(pos, n, &directory->songs, siblings)
-
-#define directory_for_each_playlist(pos, directory) \
- list_for_each_entry(pos, &directory->playlists, siblings)
-
-#define directory_for_each_playlist_safe(pos, n, directory) \
- list_for_each_entry_safe(pos, n, &directory->playlists, siblings)
-
-struct song;
-struct db_visitor;
-
-struct directory {
- /**
- * Pointers to the siblings of this directory within the
- * parent directory. It is unused (undefined) in the root
- * directory.
- *
- * This attribute is protected with the global #db_mutex.
- * Read access in the update thread does not need protection.
- */
- struct list_head siblings;
-
- /**
- * A doubly linked list of child directories.
- *
- * This attribute is protected with the global #db_mutex.
- * Read access in the update thread does not need protection.
- */
- struct list_head children;
-
- /**
- * A doubly linked list of songs within this directory.
- *
- * This attribute is protected with the global #db_mutex.
- * Read access in the update thread does not need protection.
- */
- struct list_head songs;
-
- struct list_head playlists;
-
- struct directory *parent;
- time_t mtime;
- ino_t inode;
- dev_t device;
- bool have_stat; /* not needed if ino_t == dev_t == 0 is impossible */
- char path[sizeof(long)];
-};
-
-static inline bool
-isRootDirectory(const char *name)
-{
- return name[0] == 0 || (name[0] == '/' && name[1] == 0);
-}
-
-/**
- * Generic constructor for #directory object.
- */
-G_GNUC_MALLOC
-struct directory *
-directory_new(const char *dirname, struct directory *parent);
-
-/**
- * Create a new root #directory object.
- */
-G_GNUC_MALLOC
-static inline struct directory *
-directory_new_root(void)
-{
- return directory_new("", NULL);
-}
-
-/**
- * Free this #directory object (and the whole object tree within it),
- * assuming it was already removed from the parent.
- */
-void
-directory_free(struct directory *directory);
-
-/**
- * Remove this #directory object from its parent and free it. This
- * must not be called with the root directory.
- *
- * Caller must lock the #db_mutex.
- */
-void
-directory_delete(struct directory *directory);
-
-static inline bool
-directory_is_empty(const struct directory *directory)
-{
- return list_empty(&directory->children) &&
- list_empty(&directory->songs) &&
- list_empty(&directory->playlists);
-}
-
-static inline const char *
-directory_get_path(const struct directory *directory)
-{
- return directory->path;
-}
-
-/**
- * Is this the root directory of the music database?
- */
-static inline bool
-directory_is_root(const struct directory *directory)
-{
- return directory->parent == NULL;
-}
-
-/**
- * Returns the base name of the directory.
- */
-G_GNUC_PURE
-const char *
-directory_get_name(const struct directory *directory);
-
-/**
- * Caller must lock the #db_mutex.
- */
-G_GNUC_PURE
-struct directory *
-directory_get_child(const struct directory *directory, const char *name);
-
-/**
- * Create a new #directory object as a child of the given one.
- *
- * Caller must lock the #db_mutex.
- *
- * @param parent the parent directory the new one will be added to
- * @param name_utf8 the UTF-8 encoded name of the new sub directory
- */
-G_GNUC_MALLOC
-struct directory *
-directory_new_child(struct directory *parent, const char *name_utf8);
-
-/**
- * Look up a sub directory, and create the object if it does not
- * exist.
- *
- * Caller must lock the #db_mutex.
- */
-static inline struct directory *
-directory_make_child(struct directory *directory, const char *name_utf8)
-{
- struct directory *child = directory_get_child(directory, name_utf8);
- if (child == NULL)
- child = directory_new_child(directory, name_utf8);
- return child;
-}
-
-/**
- * Caller must lock the #db_mutex.
- */
-void
-directory_prune_empty(struct directory *directory);
-
-/**
- * Looks up a directory by its relative URI.
- *
- * @param directory the parent (or grandparent, ...) directory
- * @param uri the relative URI
- * @return the directory, or NULL if none was found
- */
-struct directory *
-directory_lookup_directory(struct directory *directory, const char *uri);
-
-/**
- * Add a song object to this directory. Its "parent" attribute must
- * be set already.
- */
-void
-directory_add_song(struct directory *directory, struct song *song);
-
-/**
- * Remove a song object from this directory (which effectively
- * invalidates the song object, because the "parent" attribute becomes
- * stale), but does not free it.
- */
-void
-directory_remove_song(struct directory *directory, struct song *song);
-
-/**
- * Look up a song in this directory by its name.
- *
- * Caller must lock the #db_mutex.
- */
-G_GNUC_PURE
-struct song *
-directory_get_song(const struct directory *directory, const char *name_utf8);
-
-/**
- * Looks up a song by its relative URI.
- *
- * Caller must lock the #db_mutex.
- *
- * @param directory the parent (or grandparent, ...) directory
- * @param uri the relative URI
- * @return the song, or NULL if none was found
- */
-struct song *
-directory_lookup_song(struct directory *directory, const char *uri);
-
-/**
- * Sort all directory entries recursively.
- *
- * Caller must lock the #db_mutex.
- */
-void
-directory_sort(struct directory *directory);
-
-/**
- * Caller must lock #db_mutex.
- */
-bool
-directory_walk(const struct directory *directory, bool recursive,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r);
-
-#endif
diff --git a/src/dsd2pcm/dsd2pcm.hpp b/src/dsd2pcm/dsd2pcm.hpp
index b1b2ae1c5..8f3f55197 100644
--- a/src/dsd2pcm/dsd2pcm.hpp
+++ b/src/dsd2pcm/dsd2pcm.hpp
@@ -13,11 +13,9 @@ class dxd
{
dsd2pcm_ctx *handle;
public:
- dxd() : handle(dsd2pcm_init())
- { if (!handle) throw std::runtime_error("wtf?!"); }
+ dxd() : handle(dsd2pcm_init()) {}
- dxd(dxd const& x) : handle(dsd2pcm_clone(x.handle))
- { if (!handle) throw std::runtime_error("wtf?!"); }
+ dxd(dxd const& x) : handle(dsd2pcm_clone(x.handle)) {}
~dxd() { dsd2pcm_destroy(handle); }
diff --git a/src/dsd2pcm/noiseshape.hpp b/src/dsd2pcm/noiseshape.hpp
index 726272f91..1fc698b36 100644
--- a/src/dsd2pcm/noiseshape.hpp
+++ b/src/dsd2pcm/noiseshape.hpp
@@ -14,14 +14,12 @@ class noise_shaper
public:
noise_shaper(int sos_count, const float *bbaa)
{
- if (noise_shape_init(&ctx,sos_count,bbaa))
- throw std::runtime_error("noise shaper initialization failed");
+ noise_shape_init(&ctx, sos_count, bbaa);
}
noise_shaper(noise_shaper const& x)
{
- if (noise_shape_clone(&x.ctx,&ctx))
- throw std::runtime_error("noise shaper initialization failed");
+ noise_shape_clone(&x.ctx,&ctx);
}
~noise_shaper()
@@ -31,8 +29,7 @@ public:
{
if (this != &x) {
noise_shape_destroy(&ctx);
- if (noise_shape_clone(&x.ctx,&ctx))
- throw std::runtime_error("noise shaper initialization failed");
+ noise_shape_clone(&x.ctx,&ctx);
}
return *this;
}
diff --git a/src/encoder/OggStream.hxx b/src/encoder/OggStream.hxx
new file mode 100644
index 000000000..ce847f491
--- /dev/null
+++ b/src/encoder/OggStream.hxx
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2003-2012 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_OGG_STREAM_HXX
+#define MPD_OGG_STREAM_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+
+class OggStream {
+ ogg_stream_state state;
+
+ bool flush;
+
+#ifndef NDEBUG
+ bool initialized;
+#endif
+
+public:
+#ifndef NDEBUG
+ OggStream():initialized(false) {}
+ ~OggStream() {
+ assert(!initialized);
+ }
+#endif
+
+ void Initialize(int serialno) {
+ assert(!initialized);
+
+ ogg_stream_init(&state, serialno);
+
+ /* set "flush" to true, so the caller gets the full
+ headers on the first read() */
+ flush = true;
+
+#ifndef NDEBUG
+ initialized = true;
+#endif
+ }
+
+ void Reinitialize(int serialno) {
+ assert(initialized);
+
+ ogg_stream_reset_serialno(&state, serialno);
+
+ /* set "flush" to true, so the caller gets the full
+ headers on the first read() */
+ flush = true;
+ }
+
+ void Deinitialize() {
+ assert(initialized);
+
+ ogg_stream_clear(&state);
+
+#ifndef NDEBUG
+ initialized = false;
+#endif
+ }
+
+ void Flush() {
+ assert(initialized);
+
+ flush = true;
+ }
+
+ void PacketIn(const ogg_packet &packet) {
+ assert(initialized);
+
+ ogg_stream_packetin(&state,
+ const_cast<ogg_packet *>(&packet));
+ }
+
+ bool PageOut(ogg_page &page) {
+ int result = ogg_stream_pageout(&state, &page);
+ if (result == 0 && flush) {
+ flush = false;
+ result = ogg_stream_flush(&state, &page);
+ }
+
+ return result != 0;
+ }
+
+ size_t PageOut(void *_buffer, size_t size) {
+ ogg_page page;
+ if (!PageOut(page))
+ return 0;
+
+ assert(page.header_len > 0 || page.body_len > 0);
+
+ size_t header_len = (size_t)page.header_len;
+ size_t body_len = (size_t)page.body_len;
+ assert(header_len <= size);
+
+ if (header_len + body_len > size)
+ /* TODO: better overflow handling */
+ body_len = size - header_len;
+
+ uint8_t *buffer = (uint8_t *)_buffer;
+ memcpy(buffer, page.header, header_len);
+ memcpy(buffer + header_len, page.body, body_len);
+
+ return header_len + body_len;
+ }
+};
+
+#endif
diff --git a/src/encoder/OpusEncoderPlugin.cxx b/src/encoder/OpusEncoderPlugin.cxx
new file mode 100644
index 000000000..9f46e8681
--- /dev/null
+++ b/src/encoder/OpusEncoderPlugin.cxx
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2003-2012 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 "OpusEncoderPlugin.hxx"
+#include "OggStream.hxx"
+
+extern "C" {
+#include "encoder_api.h"
+}
+
+#include "encoder_plugin.h"
+#include "audio_format.h"
+#include "mpd_error.h"
+
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "opus_encoder"
+
+struct opus_encoder {
+ /** the base class */
+ struct encoder encoder;
+
+ /* configuration */
+
+ opus_int32 bitrate;
+ int complexity;
+ int signal;
+
+ /* runtime information */
+
+ struct audio_format audio_format;
+
+ size_t frame_size;
+
+ size_t buffer_frames, buffer_size, buffer_position;
+ uint8_t *buffer;
+
+ OpusEncoder *enc;
+
+ unsigned char buffer2[1275 * 3 + 7];
+
+ OggStream stream;
+
+ int lookahead;
+
+ ogg_int64_t packetno;
+
+ ogg_int64_t granulepos;
+};
+
+gcc_const
+static inline GQuark
+opus_encoder_quark(void)
+{
+ return g_quark_from_static_string("opus_encoder");
+}
+
+static bool
+opus_encoder_configure(struct opus_encoder *encoder,
+ const struct config_param *param, GError **error_r)
+{
+ const char *value = config_get_block_string(param, "bitrate", "auto");
+ if (strcmp(value, "auto") == 0)
+ encoder->bitrate = OPUS_AUTO;
+ else if (strcmp(value, "max") == 0)
+ encoder->bitrate = OPUS_BITRATE_MAX;
+ else {
+ char *endptr;
+ encoder->bitrate = strtoul(value, &endptr, 10);
+ if (endptr == value || *endptr != 0 ||
+ encoder->bitrate < 500 || encoder->bitrate > 512000) {
+ g_set_error(error_r, opus_encoder_quark(), 0,
+ "Invalid bit rate");
+ return false;
+ }
+ }
+
+ encoder->complexity = config_get_block_unsigned(param, "complexity",
+ 10);
+ if (encoder->complexity > 10) {
+ g_set_error(error_r, opus_encoder_quark(), 0,
+ "Invalid complexity");
+ return false;
+ }
+
+ value = config_get_block_string(param, "signal", "auto");
+ if (strcmp(value, "auto") == 0)
+ encoder->bitrate = OPUS_AUTO;
+ else if (strcmp(value, "voice") == 0)
+ encoder->bitrate = OPUS_SIGNAL_VOICE;
+ else if (strcmp(value, "music") == 0)
+ encoder->bitrate = OPUS_SIGNAL_MUSIC;
+ else {
+ g_set_error(error_r, opus_encoder_quark(), 0,
+ "Invalid signal");
+ return false;
+ }
+
+ return true;
+}
+
+static struct encoder *
+opus_encoder_init(const struct config_param *param, GError **error)
+{
+ struct opus_encoder *encoder;
+
+ encoder = g_new(struct opus_encoder, 1);
+ encoder_struct_init(&encoder->encoder, &opus_encoder_plugin);
+
+ /* load configuration from "param" */
+ if (!opus_encoder_configure(encoder, param, error)) {
+ /* configuration has failed, roll back and return error */
+ g_free(encoder);
+ return NULL;
+ }
+
+ return &encoder->encoder;
+}
+
+static void
+opus_encoder_finish(struct encoder *_encoder)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ /* the real libopus cleanup was already performed by
+ opus_encoder_close(), so no real work here */
+ g_free(encoder);
+}
+
+static bool
+opus_encoder_open(struct encoder *_encoder,
+ struct audio_format *audio_format,
+ GError **error_r)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ /* libopus supports only 48 kHz */
+ audio_format->sample_rate = 48000;
+
+ if (audio_format->channels > 2)
+ audio_format->channels = 1;
+
+ switch ((enum sample_format)audio_format->format) {
+ case SAMPLE_FORMAT_S16:
+ case SAMPLE_FORMAT_FLOAT:
+ break;
+
+ case SAMPLE_FORMAT_S8:
+ audio_format->format = SAMPLE_FORMAT_S16;
+ break;
+
+ default:
+ audio_format->format = SAMPLE_FORMAT_FLOAT;
+ break;
+ }
+
+ encoder->audio_format = *audio_format;
+ encoder->frame_size = audio_format_frame_size(audio_format);
+
+ int error;
+ encoder->enc = opus_encoder_create(audio_format->sample_rate,
+ audio_format->channels,
+ OPUS_APPLICATION_AUDIO,
+ &error);
+ if (encoder->enc == nullptr) {
+ g_set_error_literal(error_r, opus_encoder_quark(), error,
+ opus_strerror(error));
+ return false;
+ }
+
+ opus_encoder_ctl(encoder->enc, OPUS_SET_BITRATE(encoder->bitrate));
+ opus_encoder_ctl(encoder->enc,
+ OPUS_SET_COMPLEXITY(encoder->complexity));
+ opus_encoder_ctl(encoder->enc, OPUS_SET_SIGNAL(encoder->signal));
+
+ opus_encoder_ctl(encoder->enc, OPUS_GET_LOOKAHEAD(&encoder->lookahead));
+
+ encoder->buffer_frames = audio_format->sample_rate / 50;
+ encoder->buffer_size = encoder->frame_size * encoder->buffer_frames;
+ encoder->buffer_position = 0;
+ encoder->buffer = (unsigned char *)g_malloc(encoder->buffer_size);
+
+ encoder->stream.Initialize(g_random_int());
+ encoder->packetno = 0;
+
+ return true;
+}
+
+static void
+opus_encoder_close(struct encoder *_encoder)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ encoder->stream.Deinitialize();
+ g_free(encoder->buffer);
+ opus_encoder_destroy(encoder->enc);
+}
+
+static bool
+opus_encoder_do_encode(struct opus_encoder *encoder, bool eos,
+ GError **error_r)
+{
+ assert(encoder->buffer_position == encoder->buffer_size);
+
+ opus_int32 result =
+ encoder->audio_format.format == SAMPLE_FORMAT_S16
+ ? opus_encode(encoder->enc,
+ (const opus_int16 *)encoder->buffer,
+ encoder->buffer_frames,
+ encoder->buffer2,
+ sizeof(encoder->buffer2))
+ : opus_encode_float(encoder->enc,
+ (const float *)encoder->buffer,
+ encoder->buffer_frames,
+ encoder->buffer2,
+ sizeof(encoder->buffer2));
+ if (result < 0) {
+ g_set_error_literal(error_r, opus_encoder_quark(), 0,
+ "Opus encoder error");
+ return false;
+ }
+
+ encoder->granulepos += encoder->buffer_frames;
+
+ ogg_packet packet;
+ packet.packet = encoder->buffer2;
+ packet.bytes = result;
+ packet.b_o_s = false;
+ packet.e_o_s = eos;
+ packet.granulepos = encoder->granulepos;
+ packet.packetno = encoder->packetno++;
+ encoder->stream.PacketIn(packet);
+
+ encoder->buffer_position = 0;
+
+ return true;
+}
+
+static bool
+opus_encoder_end(struct encoder *_encoder, GError **error_r)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ encoder->stream.Flush();
+
+ memset(encoder->buffer + encoder->buffer_position, 0,
+ encoder->buffer_size - encoder->buffer_position);
+ encoder->buffer_position = encoder->buffer_size;
+
+ return opus_encoder_do_encode(encoder, true, error_r);
+}
+
+static bool
+opus_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ encoder->stream.Flush();
+ return true;
+}
+
+static bool
+opus_encoder_write_silence(struct opus_encoder *encoder, unsigned fill_frames,
+ GError **error_r)
+{
+ size_t fill_bytes = fill_frames * encoder->frame_size;
+
+ while (fill_bytes > 0) {
+ size_t nbytes =
+ encoder->buffer_size - encoder->buffer_position;
+ if (nbytes > fill_bytes)
+ nbytes = fill_bytes;
+
+ memset(encoder->buffer + encoder->buffer_position,
+ 0, nbytes);
+ encoder->buffer_position += nbytes;
+ fill_bytes -= nbytes;
+
+ if (encoder->buffer_position == encoder->buffer_size &&
+ !opus_encoder_do_encode(encoder, false, error_r))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+opus_encoder_write(struct encoder *_encoder,
+ const void *_data, size_t length,
+ GError **error_r)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+ const uint8_t *data = (const uint8_t *)_data;
+
+ if (encoder->lookahead > 0) {
+ /* generate some silence at the beginning of the
+ stream */
+
+ assert(encoder->buffer_position == 0);
+
+ if (!opus_encoder_write_silence(encoder, encoder->lookahead,
+ error_r))
+ return false;
+
+ encoder->lookahead = 0;
+ }
+
+ while (length > 0) {
+ size_t nbytes =
+ encoder->buffer_size - encoder->buffer_position;
+ if (nbytes > length)
+ nbytes = length;
+
+ memcpy(encoder->buffer + encoder->buffer_position,
+ data, nbytes);
+ data += nbytes;
+ length -= nbytes;
+ encoder->buffer_position += nbytes;
+
+ if (encoder->buffer_position == encoder->buffer_size &&
+ !opus_encoder_do_encode(encoder, false, error_r))
+ return false;
+ }
+
+ return true;
+}
+
+static void
+opus_encoder_generate_head(struct opus_encoder *encoder)
+{
+ unsigned char header[19];
+ memcpy(header, "OpusHead", 8);
+ header[8] = 1;
+ header[9] = encoder->audio_format.channels;
+ *(uint16_t *)(header + 10) = GUINT16_TO_LE(encoder->lookahead);
+ *(uint32_t *)(header + 12) =
+ GUINT32_TO_LE(encoder->audio_format.sample_rate);
+ header[16] = 0;
+ header[17] = 0;
+ header[18] = 0;
+
+ ogg_packet packet;
+ packet.packet = header;
+ packet.bytes = 19;
+ packet.b_o_s = true;
+ packet.e_o_s = false;
+ packet.granulepos = 0;
+ packet.packetno = encoder->packetno++;
+ encoder->stream.PacketIn(packet);
+ encoder->stream.Flush();
+}
+
+static void
+opus_encoder_generate_tags(struct opus_encoder *encoder)
+{
+ const char *version = opus_get_version_string();
+ size_t version_length = strlen(version);
+
+ size_t comments_size = 8 + 4 + version_length + 4;
+ unsigned char *comments = (unsigned char *)g_malloc(comments_size);
+ memcpy(comments, "OpusTags", 8);
+ *(uint32_t *)(comments + 8) = GUINT32_TO_LE(version_length);
+ memcpy(comments + 12, version, version_length);
+ *(uint32_t *)(comments + 12 + version_length) = GUINT32_TO_LE(0);
+
+ ogg_packet packet;
+ packet.packet = comments;
+ packet.bytes = comments_size;
+ packet.b_o_s = false;
+ packet.e_o_s = false;
+ packet.granulepos = 0;
+ packet.packetno = encoder->packetno++;
+ encoder->stream.PacketIn(packet);
+ encoder->stream.Flush();
+
+ g_free(comments);
+}
+
+static size_t
+opus_encoder_read(struct encoder *_encoder, void *dest, size_t length)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ if (encoder->packetno == 0)
+ opus_encoder_generate_head(encoder);
+ else if (encoder->packetno == 1)
+ opus_encoder_generate_tags(encoder);
+
+ return encoder->stream.PageOut(dest, length);
+}
+
+static const char *
+opus_encoder_get_mime_type(G_GNUC_UNUSED struct encoder *_encoder)
+{
+ return "audio/ogg";
+}
+
+const struct encoder_plugin opus_encoder_plugin = {
+ "opus",
+ opus_encoder_init,
+ opus_encoder_finish,
+ opus_encoder_open,
+ opus_encoder_close,
+ opus_encoder_end,
+ opus_encoder_flush,
+ nullptr,
+ nullptr,
+ opus_encoder_write,
+ opus_encoder_read,
+ opus_encoder_get_mime_type,
+};
diff --git a/src/encoder/OpusEncoderPlugin.hxx b/src/encoder/OpusEncoderPlugin.hxx
new file mode 100644
index 000000000..f54377202
--- /dev/null
+++ b/src/encoder/OpusEncoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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_OPUS_H
+#define MPD_ENCODER_OPUS_H
+
+extern const struct encoder_plugin opus_encoder_plugin;
+
+#endif
diff --git a/src/encoder/vorbis_encoder.c b/src/encoder/VorbisEncoderPlugin.cxx
index 468cf38ee..bc0f47fd0 100644
--- a/src/encoder/vorbis_encoder.c
+++ b/src/encoder/VorbisEncoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,13 @@
*/
#include "config.h"
+#include "VorbisEncoderPlugin.hxx"
+#include "OggStream.hxx"
+
+extern "C" {
#include "encoder_api.h"
+}
+
#include "encoder_plugin.h"
#include "tag.h"
#include "audio_format.h"
@@ -44,17 +50,13 @@ struct vorbis_encoder {
struct audio_format audio_format;
- ogg_stream_state os;
-
vorbis_dsp_state vd;
vorbis_block vb;
vorbis_info vi;
- bool flush;
+ OggStream stream;
};
-extern const struct encoder_plugin vorbis_encoder_plugin;
-
static inline GQuark
vorbis_encoder_quark(void)
{
@@ -65,8 +67,8 @@ static bool
vorbis_encoder_configure(struct vorbis_encoder *encoder,
const struct config_param *param, GError **error)
{
- const char *value = config_get_block_string(param, "quality", NULL);
- if (value != NULL) {
+ const char *value = config_get_block_string(param, "quality", nullptr);
+ if (value != nullptr) {
/* a quality was configured (VBR) */
char *endptr;
@@ -81,7 +83,7 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
return false;
}
- if (config_get_block_string(param, "bitrate", NULL) != NULL) {
+ if (config_get_block_string(param, "bitrate", nullptr) != nullptr) {
g_set_error(error, vorbis_encoder_quark(), 0,
"quality and bitrate are "
"both defined (line %i)",
@@ -91,8 +93,8 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
} else {
/* a bit rate was configured */
- value = config_get_block_string(param, "bitrate", NULL);
- if (value == NULL) {
+ value = config_get_block_string(param, "bitrate", nullptr);
+ if (value == nullptr) {
g_set_error(error, vorbis_encoder_quark(), 0,
"neither bitrate nor quality defined "
"at line %i",
@@ -125,7 +127,7 @@ vorbis_encoder_init(const struct config_param *param, GError **error)
if (!vorbis_encoder_configure(encoder, param, error)) {
/* configuration has failed, roll back and return error */
g_free(encoder);
- return NULL;
+ return nullptr;
}
return &encoder->encoder;
@@ -174,7 +176,7 @@ vorbis_encoder_reinit(struct vorbis_encoder *encoder, GError **error)
vorbis_analysis_init(&encoder->vd, &encoder->vi);
vorbis_block_init(&encoder->vd, &encoder->vb);
- ogg_stream_init(&encoder->os, g_random_int());
+ encoder->stream.Initialize(g_random_int());
return true;
}
@@ -187,9 +189,9 @@ vorbis_encoder_headerout(struct vorbis_encoder *encoder, vorbis_comment *vc)
vorbis_analysis_headerout(&encoder->vd, vc,
&packet, &comments, &codebooks);
- ogg_stream_packetin(&encoder->os, &packet);
- ogg_stream_packetin(&encoder->os, &comments);
- ogg_stream_packetin(&encoder->os, &codebooks);
+ encoder->stream.PacketIn(packet);
+ encoder->stream.PacketIn(comments);
+ encoder->stream.PacketIn(codebooks);
}
static void
@@ -209,7 +211,7 @@ vorbis_encoder_open(struct encoder *_encoder,
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
- audio_format->format = SAMPLE_FORMAT_S16;
+ audio_format->format = SAMPLE_FORMAT_FLOAT;
encoder->audio_format = *audio_format;
@@ -218,17 +220,13 @@ vorbis_encoder_open(struct encoder *_encoder,
vorbis_encoder_send_header(encoder);
- /* set "flush" to true, so the caller gets the full headers on
- the first read() */
- encoder->flush = true;
-
return true;
}
static void
vorbis_encoder_clear(struct vorbis_encoder *encoder)
{
- ogg_stream_clear(&encoder->os);
+ encoder->stream.Deinitialize();
vorbis_block_clear(&encoder->vb);
vorbis_dsp_clear(&encoder->vd);
vorbis_info_clear(&encoder->vi);
@@ -246,12 +244,12 @@ static void
vorbis_encoder_blockout(struct vorbis_encoder *encoder)
{
while (vorbis_analysis_blockout(&encoder->vd, &encoder->vb) == 1) {
- vorbis_analysis(&encoder->vb, NULL);
+ vorbis_analysis(&encoder->vb, nullptr);
vorbis_bitrate_addblock(&encoder->vb);
ogg_packet packet;
while (vorbis_bitrate_flushpacket(&encoder->vd, &packet))
- ogg_stream_packetin(&encoder->os, &packet);
+ encoder->stream.PacketIn(packet);
}
}
@@ -260,7 +258,7 @@ vorbis_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
- encoder->flush = true;
+ encoder->stream.Flush();
return true;
}
@@ -279,7 +277,7 @@ vorbis_encoder_pre_tag(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
vorbis_analysis_init(&encoder->vd, &encoder->vi);
vorbis_block_init(&encoder->vd, &encoder->vb);
- encoder->flush = true;
+ encoder->stream.Flush();
return true;
}
@@ -308,28 +306,23 @@ vorbis_encoder_tag(struct encoder *_encoder, const struct tag *tag,
/* reset ogg_stream_state and begin a new stream */
- ogg_stream_reset_serialno(&encoder->os, g_random_int());
+ encoder->stream.Reinitialize(g_random_int());
/* send that vorbis_comment to the ogg_stream_state */
vorbis_encoder_headerout(encoder, &comment);
vorbis_comment_clear(&comment);
- /* the next vorbis_encoder_read() call should flush the
- ogg_stream_state */
-
- encoder->flush = true;
-
return true;
}
static void
-pcm16_to_vorbis_buffer(float **dest, const int16_t *src,
- unsigned num_frames, unsigned num_channels)
+interleaved_to_vorbis_buffer(float **dest, const float *src,
+ unsigned num_frames, unsigned num_channels)
{
for (unsigned i = 0; i < num_frames; i++)
for (unsigned j = 0; j < num_channels; j++)
- dest[j][i] = *src++ / 32768.0;
+ dest[j][i] = *src++;
}
static bool
@@ -344,10 +337,11 @@ vorbis_encoder_write(struct encoder *_encoder,
/* this is for only 16-bit audio */
- pcm16_to_vorbis_buffer(vorbis_analysis_buffer(&encoder->vd,
- num_frames),
- (const int16_t *)data,
- num_frames, encoder->audio_format.channels);
+ interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&encoder->vd,
+ num_frames),
+ (const float *)data,
+ num_frames,
+ encoder->audio_format.channels);
vorbis_analysis_wrote(&encoder->vd, num_frames);
vorbis_encoder_blockout(encoder);
@@ -355,34 +349,11 @@ vorbis_encoder_write(struct encoder *_encoder,
}
static size_t
-vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
+vorbis_encoder_read(struct encoder *_encoder, void *dest, size_t length)
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
- unsigned char *dest = _dest;
-
- ogg_page page;
- int ret = ogg_stream_pageout(&encoder->os, &page);
- if (ret == 0 && encoder->flush) {
- encoder->flush = false;
- ret = ogg_stream_flush(&encoder->os, &page);
-
- }
-
- if (ret == 0)
- return 0;
-
- assert(page.header_len > 0 || page.body_len > 0);
-
- size_t nbytes = (size_t)page.header_len + (size_t)page.body_len;
-
- if (nbytes > length)
- /* XXX better error handling */
- MPD_ERROR("buffer too small");
-
- memcpy(dest, page.header, page.header_len);
- memcpy(dest + page.header_len, page.body, page.body_len);
- return nbytes;
+ return encoder->stream.PageOut(dest, length);
}
static const char *
@@ -392,16 +363,16 @@ vorbis_encoder_get_mime_type(G_GNUC_UNUSED struct encoder *_encoder)
}
const struct encoder_plugin vorbis_encoder_plugin = {
- .name = "vorbis",
- .init = vorbis_encoder_init,
- .finish = vorbis_encoder_finish,
- .open = vorbis_encoder_open,
- .close = vorbis_encoder_close,
- .end = vorbis_encoder_pre_tag,
- .flush = vorbis_encoder_flush,
- .pre_tag = vorbis_encoder_pre_tag,
- .tag = vorbis_encoder_tag,
- .write = vorbis_encoder_write,
- .read = vorbis_encoder_read,
- .get_mime_type = vorbis_encoder_get_mime_type,
+ "vorbis",
+ vorbis_encoder_init,
+ vorbis_encoder_finish,
+ vorbis_encoder_open,
+ vorbis_encoder_close,
+ vorbis_encoder_pre_tag,
+ vorbis_encoder_flush,
+ vorbis_encoder_pre_tag,
+ vorbis_encoder_tag,
+ vorbis_encoder_write,
+ vorbis_encoder_read,
+ vorbis_encoder_get_mime_type,
};
diff --git a/src/encoder/VorbisEncoderPlugin.hxx b/src/encoder/VorbisEncoderPlugin.hxx
new file mode 100644
index 000000000..4cddf1b11
--- /dev/null
+++ b/src/encoder/VorbisEncoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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_VORBIS_H
+#define MPD_ENCODER_VORBIS_H
+
+extern const struct encoder_plugin vorbis_encoder_plugin;
+
+#endif
diff --git a/src/encoder/flac_encoder.c b/src/encoder/flac_encoder.c
index e32588e29..6b09bcd66 100644
--- a/src/encoder/flac_encoder.c
+++ b/src/encoder/flac_encoder.c
@@ -30,6 +30,10 @@
#include <FLAC/stream_encoder.h>
+#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
+#error libFLAC is too old
+#endif
+
struct flac_encoder {
struct encoder encoder;
@@ -98,8 +102,6 @@ static bool
flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
GError **error)
{
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-#else
if ( !FLAC__stream_encoder_set_compression_level(encoder->fse,
encoder->compression)) {
g_set_error(error, flac_encoder_quark(), 0,
@@ -107,7 +109,7 @@ flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
encoder->compression);
return false;
}
-#endif
+
if ( !FLAC__stream_encoder_set_channels(encoder->fse,
encoder->audio_format.channels)) {
g_set_error(error, flac_encoder_quark(), 0,
@@ -135,11 +137,7 @@ flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
static FLAC__StreamEncoderWriteStatus
flac_write_callback(G_GNUC_UNUSED const FLAC__StreamEncoder *fse,
const FLAC__byte data[],
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
- unsigned bytes,
-#else
size_t bytes,
-#endif
G_GNUC_UNUSED unsigned samples,
G_GNUC_UNUSED unsigned current_frame, void *client_data)
{
@@ -209,24 +207,6 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
/* this immediately outputs data through callback */
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
- {
- FLAC__StreamEncoderState init_status;
-
- FLAC__stream_encoder_set_write_callback(encoder->fse,
- flac_write_callback);
-
- init_status = FLAC__stream_encoder_init(encoder->fse);
-
- if (init_status != FLAC__STREAM_ENCODER_OK) {
- g_set_error(error, flac_encoder_quark(), 0,
- "failed to initialize encoder: %s\n",
- FLAC__StreamEncoderStateString[init_status]);
- flac_encoder_close(_encoder);
- return false;
- }
- }
-#else
{
FLAC__StreamEncoderInitStatus init_status;
@@ -242,7 +222,6 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
return false;
}
}
-#endif
return true;
}
diff --git a/src/encoder_list.c b/src/encoder_list.c
index 2326c1099..029b4be34 100644
--- a/src/encoder_list.c
+++ b/src/encoder_list.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,11 +20,12 @@
#include "config.h"
#include "encoder_list.h"
#include "encoder_plugin.h"
+#include "encoder/VorbisEncoderPlugin.hxx"
+#include "encoder/OpusEncoderPlugin.hxx"
#include <string.h>
extern const struct encoder_plugin null_encoder_plugin;
-extern const struct encoder_plugin vorbis_encoder_plugin;
extern const struct encoder_plugin lame_encoder_plugin;
extern const struct encoder_plugin twolame_encoder_plugin;
extern const struct encoder_plugin wave_encoder_plugin;
@@ -35,6 +36,9 @@ const struct encoder_plugin *const encoder_plugins[] = {
#ifdef ENABLE_VORBIS_ENCODER
&vorbis_encoder_plugin,
#endif
+#ifdef HAVE_OPUS
+ &opus_encoder_plugin,
+#endif
#ifdef ENABLE_LAME_ENCODER
&lame_encoder_plugin,
#endif
diff --git a/src/encoder_plugin.h b/src/encoder_plugin.h
index 3a42d79f4..e0748a136 100644
--- a/src/encoder_plugin.h
+++ b/src/encoder_plugin.h
@@ -20,7 +20,7 @@
#ifndef MPD_ENCODER_PLUGIN_H
#define MPD_ENCODER_PLUGIN_H
-#include <glib.h>
+#include "gerror.h"
#include <assert.h>
#include <stdbool.h>
diff --git a/src/event_pipe.h b/src/event_pipe.h
index 3734bb86c..3749ccf79 100644
--- a/src/event_pipe.h
+++ b/src/event_pipe.h
@@ -20,8 +20,6 @@
#ifndef EVENT_PIPE_H
#define EVENT_PIPE_H
-#include <glib.h>
-
enum pipe_event {
/** database update was finished */
PIPE_EVENT_UPDATE,
diff --git a/src/fd_util.h b/src/fd_util.h
index dd4df7a13..c8a17c7ef 100644
--- a/src/fd_util.h
+++ b/src/fd_util.h
@@ -51,6 +51,10 @@
struct sockaddr;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Wrapper for dup(), which sets the CLOEXEC flag on the new
* descriptor.
@@ -146,4 +150,8 @@ inotify_init_cloexec(void);
int
close_socket(int fd);
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
#endif
diff --git a/src/filter/null_filter_plugin.c b/src/filter/null_filter_plugin.c
index e7c998827..7728c55bf 100644
--- a/src/filter/null_filter_plugin.c
+++ b/src/filter/null_filter_plugin.c
@@ -29,6 +29,7 @@
#include "filter_internal.h"
#include "filter_registry.h"
+#include <glib.h>
#include <assert.h>
struct null_filter {
diff --git a/src/filter/replay_gain_filter_plugin.c b/src/filter/replay_gain_filter_plugin.c
index 583a09f90..9fd3cbc07 100644
--- a/src/filter/replay_gain_filter_plugin.c
+++ b/src/filter/replay_gain_filter_plugin.c
@@ -119,7 +119,7 @@ replay_gain_filter_init(G_GNUC_UNUSED const struct config_param *param,
filter_init(&filter->filter, &replay_gain_filter_plugin);
filter->mixer = NULL;
- filter->mode = replay_gain_get_real_mode();
+ filter->mode = REPLAY_GAIN_OFF;
replay_gain_info_init(&filter->info);
filter->volume = PCM_VOLUME_1;
@@ -164,16 +164,6 @@ replay_gain_filter_filter(struct filter *_filter,
(struct replay_gain_filter *)_filter;
bool success;
void *dest;
- enum replay_gain_mode rg_mode;
-
- /* check if the mode has been changed since the last call */
- rg_mode = replay_gain_get_real_mode();
-
- if (filter->mode != rg_mode) {
- g_debug("replay gain mode has changed %d->%d\n", filter->mode, rg_mode);
- filter->mode = rg_mode;
- replay_gain_filter_update(filter);
- }
*dest_size_r = src_size;
@@ -243,3 +233,19 @@ replay_gain_filter_set_info(struct filter *_filter,
replay_gain_filter_update(filter);
}
+
+void
+replay_gain_filter_set_mode(struct filter *_filter, enum replay_gain_mode mode)
+{
+ struct replay_gain_filter *filter =
+ (struct replay_gain_filter *)_filter;
+
+ if (mode == filter->mode)
+ /* no change */
+ return;
+
+ g_debug("replay gain mode has changed %d->%d\n", filter->mode, mode);
+
+ filter->mode = mode;
+ replay_gain_filter_update(filter);
+}
diff --git a/src/filter/replay_gain_filter_plugin.h b/src/filter/replay_gain_filter_plugin.h
index 45b738e40..da2df9d6a 100644
--- a/src/filter/replay_gain_filter_plugin.h
+++ b/src/filter/replay_gain_filter_plugin.h
@@ -47,4 +47,7 @@ void
replay_gain_filter_set_info(struct filter *filter,
const struct replay_gain_info *info);
+void
+replay_gain_filter_set_mode(struct filter *filter, enum replay_gain_mode mode);
+
#endif
diff --git a/src/filter_plugin.h b/src/filter_plugin.h
index 58e34dfb2..d45faee1f 100644
--- a/src/filter_plugin.h
+++ b/src/filter_plugin.h
@@ -26,7 +26,7 @@
#ifndef MPD_FILTER_PLUGIN_H
#define MPD_FILTER_PLUGIN_H
-#include <glib.h>
+#include "gerror.h"
#include <stdbool.h>
#include <stddef.h>
diff --git a/src/gcc.h b/src/gcc.h
index 45f7101f3..e921b4646 100644
--- a/src/gcc.h
+++ b/src/gcc.h
@@ -32,6 +32,9 @@
*/
#if GCC_CHECK_VERSION(3,0)
+# define gcc_const __attribute__((const))
+# define gcc_pure __attribute__((pure))
+# define gcc_malloc __attribute__((malloc))
# define gcc_must_check __attribute__ ((warn_unused_result))
# define gcc_packed __attribute__ ((packed))
/* these are very useful for type checking */
@@ -41,11 +44,21 @@
# define gcc_fprintf__ __attribute__ ((format(printf,4,5)))
# define gcc_scanf __attribute__ ((format(scanf,1,2)))
# define gcc_used __attribute__ ((used))
+# define gcc_unused __attribute__((unused))
+# define gcc_warn_unused_result __attribute__((warn_unused_result))
/* # define inline inline __attribute__ ((always_inline)) */
# define gcc_noinline __attribute__ ((noinline))
# define gcc_nonnull(...) __attribute__((nonnull(__VA_ARGS__)))
# define gcc_nonnull_all __attribute__((nonnull))
+
+# define gcc_likely(x) __builtin_expect (!!(x), 1)
+# define gcc_unlikely(x) __builtin_expect (!!(x), 0)
+
#else
+# define gcc_unused
+# define gcc_const
+# define gcc_pure
+# define gcc_malloc
# define gcc_must_check
# define gcc_packed
# define gcc_printf
@@ -54,10 +67,29 @@
# define gcc_fprintf__
# define gcc_scanf
# define gcc_used
+# define gcc_unused
+# define gcc_warn_unused_result
/* # define inline */
# define gcc_noinline
# define gcc_nonnull(...)
# define gcc_nonnull_all
+
+# define gcc_likely(x) (x)
+# define gcc_unlikely(x) (x)
+
+#endif
+
+#ifdef __cplusplus
+
+#if !defined(__clang__) && defined(__GNUC__) && !GCC_CHECK_VERSION(4,6)
+#error Your gcc version is too old. MPD requires gcc 4.6 or newer.
+#endif
+
+/* support for C++11 "override" was added in gcc 4.7 */
+#if !defined(__clang__) && defined(__GNUC__) && !GCC_CHECK_VERSION(4,7)
+#define override
+#endif
+
#endif
#endif /* MPD_GCC_H */
diff --git a/src/decoder_print.h b/src/gerror.h
index 31713d5d8..fe4c54da9 100644
--- a/src/decoder_print.h
+++ b/src/gerror.h
@@ -17,12 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_PRINT_H
-#define MPD_DECODER_PRINT_H
+#ifndef MPD_GERROR_H
+#define MPD_GERROR_H
-struct client;
-
-void
-decoder_list_print(struct client *client);
+typedef struct _GError GError;
#endif
diff --git a/src/input/file_input_plugin.c b/src/input/file_input_plugin.c
index 5ee3f200b..e130230a7 100644
--- a/src/input/file_input_plugin.c
+++ b/src/input/file_input_plugin.c
@@ -23,6 +23,7 @@
#include "input_plugin.h"
#include "fd_util.h"
#include "open.h"
+#include "io_error.h"
#include <sys/stat.h>
#include <unistd.h>
@@ -39,12 +40,6 @@ struct file_input_stream {
int fd;
};
-static inline GQuark
-file_quark(void)
-{
- return g_quark_from_static_string("file");
-}
-
static struct input_stream *
input_file_open(const char *filename,
GMutex *mutex, GCond *cond,
@@ -60,7 +55,7 @@ input_file_open(const char *filename,
fd = open_cloexec(filename, O_RDONLY|O_BINARY, 0);
if (fd < 0) {
if (errno != ENOENT && errno != ENOTDIR)
- g_set_error(error_r, file_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to open \"%s\": %s",
filename, g_strerror(errno));
return NULL;
@@ -68,7 +63,7 @@ input_file_open(const char *filename,
ret = fstat(fd, &st);
if (ret < 0) {
- g_set_error(error_r, file_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to stat \"%s\": %s",
filename, g_strerror(errno));
close(fd);
@@ -76,7 +71,7 @@ input_file_open(const char *filename,
}
if (!S_ISREG(st.st_mode)) {
- g_set_error(error_r, file_quark(), 0,
+ g_set_error(error_r, errno_quark(), 0,
"Not a regular file: %s", filename);
close(fd);
return NULL;
@@ -107,7 +102,7 @@ input_file_seek(struct input_stream *is, goffset offset, int whence,
offset = (goffset)lseek(fis->fd, (off_t)offset, whence);
if (offset < 0) {
- g_set_error(error_r, file_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to seek: %s", g_strerror(errno));
return false;
}
@@ -125,7 +120,7 @@ input_file_read(struct input_stream *is, void *ptr, size_t size,
nbytes = read(fis->fd, ptr, size);
if (nbytes < 0) {
- g_set_error(error_r, file_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to read: %s", g_strerror(errno));
return 0;
}
diff --git a/src/input_init.h b/src/input_init.h
index ad92cda08..1a73e5ef9 100644
--- a/src/input_init.h
+++ b/src/input_init.h
@@ -20,9 +20,8 @@
#ifndef MPD_INPUT_INIT_H
#define MPD_INPUT_INIT_H
-#include "check.h"
+#include "gerror.h"
-#include <glib.h>
#include <stdbool.h>
/**
diff --git a/src/io_error.h b/src/io_error.h
new file mode 100644
index 000000000..c89748360
--- /dev/null
+++ b/src/io_error.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2012 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_IO_ERROR_H
+#define MPD_IO_ERROR_H
+
+#include <glib.h>
+
+#include <errno.h>
+
+/**
+ * A GQuark for GError for I/O errors. The code is an errno value.
+ */
+G_GNUC_CONST
+static inline GQuark
+errno_quark(void)
+{
+ return g_quark_from_static_string("errno");
+}
+
+static inline void
+set_error_errno(GError **error_r)
+{
+ g_set_error_literal(error_r, errno_quark(), errno,
+ g_strerror(errno));
+}
+
+#endif
diff --git a/src/locate.c b/src/locate.c
deleted file mode 100644
index c9684d2b6..000000000
--- a/src/locate.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "locate.h"
-#include "path.h"
-#include "tag.h"
-#include "song.h"
-
-#include <glib.h>
-
-#include <stdlib.h>
-
-#define LOCATE_TAG_FILE_KEY "file"
-#define LOCATE_TAG_FILE_KEY_OLD "filename"
-#define LOCATE_TAG_ANY_KEY "any"
-
-int
-locate_parse_type(const char *str)
-{
- if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_FILE_KEY) ||
- 0 == g_ascii_strcasecmp(str, LOCATE_TAG_FILE_KEY_OLD))
- return LOCATE_TAG_FILE_TYPE;
-
- if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_ANY_KEY))
- return LOCATE_TAG_ANY_TYPE;
-
- enum tag_type i = tag_name_parse_i(str);
- if (i != TAG_NUM_OF_ITEM_TYPES)
- return i;
-
- return -1;
-}
-
-static bool
-locate_item_init(struct locate_item *item,
- const char *type_string, const char *needle)
-{
- item->tag = locate_parse_type(type_string);
-
- if (item->tag < 0)
- return false;
-
- item->needle = g_strdup(needle);
-
- return true;
-}
-
-void
-locate_item_list_free(struct locate_item_list *list)
-{
- for (unsigned i = 0; i < list->length; ++i)
- g_free(list->items[i].needle);
-
- g_free(list);
-}
-
-struct locate_item_list *
-locate_item_list_new(unsigned length)
-{
- struct locate_item_list *list =
- g_malloc0(sizeof(*list) - sizeof(list->items[0]) +
- length * sizeof(list->items[0]));
- list->length = length;
-
- return list;
-}
-
-struct locate_item_list *
-locate_item_list_parse(char *argv[], int argc)
-{
- if (argc % 2 != 0)
- return NULL;
-
- struct locate_item_list *list = locate_item_list_new(argc / 2);
-
- for (unsigned i = 0; i < list->length; ++i) {
- if (!locate_item_init(&list->items[i], argv[i * 2],
- argv[i * 2 + 1])) {
- locate_item_list_free(list);
- return NULL;
- }
- }
-
- return list;
-}
-
-struct locate_item_list *
-locate_item_list_casefold(const struct locate_item_list *list)
-{
- struct locate_item_list *new_list = locate_item_list_new(list->length);
-
- for (unsigned i = 0; i < list->length; i++){
- new_list->items[i].needle =
- g_utf8_casefold(list->items[i].needle, -1);
- new_list->items[i].tag = list->items[i].tag;
- }
-
- return new_list;
-}
-
-void
-locate_item_free(struct locate_item *item)
-{
- g_free(item->needle);
- g_free(item);
-}
-
-static bool
-locate_tag_search(const struct song *song, enum tag_type type, const char *str)
-{
- bool ret = false;
-
- if (type == LOCATE_TAG_FILE_TYPE || (int)type == LOCATE_TAG_ANY_TYPE) {
- char *uri = song_get_uri(song);
- char *p = g_utf8_casefold(uri, -1);
- g_free(uri);
-
- if (strstr(p, str))
- ret = true;
- g_free(p);
- if (ret == 1 || type == LOCATE_TAG_FILE_TYPE)
- return ret;
- }
-
- if (!song->tag)
- return false;
-
- bool visited_types[TAG_NUM_OF_ITEM_TYPES];
- memset(visited_types, 0, sizeof(visited_types));
-
- for (unsigned i = 0; i < song->tag->num_items && !ret; i++) {
- visited_types[song->tag->items[i]->type] = true;
- if ((int)type != LOCATE_TAG_ANY_TYPE &&
- song->tag->items[i]->type != type) {
- continue;
- }
-
- char *duplicate = g_utf8_casefold(song->tag->items[i]->value, -1);
- if (*str && strstr(duplicate, str))
- ret = true;
- g_free(duplicate);
- }
-
- /** If the search critieron was not visited during the sweep
- * through the song's tag, it means this field is absent from
- * the tag or empty. Thus, if the searched string is also
- * empty (first char is a \0), then it's a match as well and
- * we should return true.
- */
- if (!*str && !visited_types[type])
- return true;
-
- return ret;
-}
-
-bool
-locate_song_search(const struct song *song,
- const struct locate_item_list *criteria)
-{
- for (unsigned i = 0; i < criteria->length; i++)
- if (!locate_tag_search(song, criteria->items[i].tag,
- criteria->items[i].needle))
- return false;
-
- return true;
-}
-
-static bool
-locate_tag_match(const struct song *song, enum tag_type type, const char *str)
-{
- if (type == LOCATE_TAG_FILE_TYPE || (int)type == LOCATE_TAG_ANY_TYPE) {
- char *uri = song_get_uri(song);
- bool matches = strcmp(str, uri) == 0;
- g_free(uri);
-
- if (matches)
- return true;
-
- if (type == LOCATE_TAG_FILE_TYPE)
- return false;
- }
-
- if (!song->tag)
- return false;
-
- bool visited_types[TAG_NUM_OF_ITEM_TYPES];
- memset(visited_types, 0, sizeof(visited_types));
-
- for (unsigned i = 0; i < song->tag->num_items; i++) {
- visited_types[song->tag->items[i]->type] = true;
- if ((int)type != LOCATE_TAG_ANY_TYPE &&
- song->tag->items[i]->type != type) {
- continue;
- }
-
- if (0 == strcmp(str, song->tag->items[i]->value))
- return true;
- }
-
- /** If the search critieron was not visited during the sweep
- * through the song's tag, it means this field is absent from
- * the tag or empty. Thus, if the searched string is also
- * empty (first char is a \0), then it's a match as well and
- * we should return true.
- */
- if (!*str && !visited_types[type])
- return true;
-
- return false;
-}
-
-bool
-locate_song_match(const struct song *song,
- const struct locate_item_list *criteria)
-{
- for (unsigned i = 0; i < criteria->length; i++)
- if (!locate_tag_match(song, criteria->items[i].tag,
- criteria->items[i].needle))
- return false;
-
- return true;
-}
diff --git a/src/locate.h b/src/locate.h
deleted file mode 100644
index ec20ded24..000000000
--- a/src/locate.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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_H
-#define MPD_LOCATE_H
-
-#include "gcc.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10
-#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
-
-struct song;
-
-/* struct used for search, find, list queries */
-struct locate_item {
- int8_t tag;
- /* what we are looking for */
- char *needle;
-};
-
-/**
- * An array of struct locate_item objects.
- */
-struct locate_item_list {
- /** number of items */
- unsigned length;
-
- /** this is a variable length array */
- struct locate_item items[1];
-};
-
-int
-locate_parse_type(const char *str);
-
-/**
- * Allocates a new struct locate_item_list, and initializes all
- * members with zero bytes.
- */
-struct locate_item_list *
-locate_item_list_new(unsigned length);
-
-/* return number of items or -1 on error */
-gcc_nonnull(1)
-struct locate_item_list *
-locate_item_list_parse(char *argv[], int argc);
-
-/**
- * Duplicate the struct locate_item_list object and convert all
- * needles with g_utf8_casefold().
- */
-gcc_nonnull(1)
-struct locate_item_list *
-locate_item_list_casefold(const struct locate_item_list *list);
-
-gcc_nonnull(1)
-void
-locate_item_list_free(struct locate_item_list *list);
-
-gcc_nonnull(1)
-void
-locate_item_free(struct locate_item *item);
-
-gcc_nonnull(1,2)
-bool
-locate_song_search(const struct song *song,
- const struct locate_item_list *criteria);
-
-gcc_nonnull(1,2)
-bool
-locate_song_match(const struct song *song,
- const struct locate_item_list *criteria);
-
-#endif
diff --git a/src/ls.c b/src/ls.cxx
index 310c2d7b6..9bb56c898 100644
--- a/src/ls.c
+++ b/src/ls.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,15 @@
*/
#include "config.h"
-#include "ls.h"
+#include "ls.hxx"
+
+extern "C" {
#include "uri.h"
-#include "client.h"
+}
+
+#include "Client.hxx"
+
+#include <glib.h>
#include <assert.h>
#include <string.h>
@@ -72,7 +78,7 @@ void print_supported_uri_schemes_to_fp(FILE *fp)
fprintf(fp,"\n");
}
-void print_supported_uri_schemes(struct client *client)
+void print_supported_uri_schemes(Client *client)
{
const char **prefixes = remoteUrlPrefixes;
diff --git a/src/ls.h b/src/ls.hxx
index 15cb01160..8ae5a58fd 100644
--- a/src/ls.h
+++ b/src/ls.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_LS_H
-#define MPD_LS_H
+#ifndef MPD_LS_HXX
+#define MPD_LS_HXX
-#include <stdbool.h>
#include <stdio.h>
-struct client;
+class Client;
/**
* Checks whether the scheme of the specified URI is supported by MPD.
@@ -36,7 +35,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(struct client *client);
+void print_supported_uri_schemes(Client *client);
/**
* Send a list of supported URI schemes to a file pointer.
diff --git a/src/mixer_control.h b/src/mixer_control.h
index 6c3468aca..307298e47 100644
--- a/src/mixer_control.h
+++ b/src/mixer_control.h
@@ -25,7 +25,7 @@
#ifndef MPD_MIXER_CONTROL_H
#define MPD_MIXER_CONTROL_H
-#include <glib.h>
+#include "gerror.h"
#include <stdbool.h>
diff --git a/src/mixer_plugin.h b/src/mixer_plugin.h
index 9532b95cb..2f3beed1d 100644
--- a/src/mixer_plugin.h
+++ b/src/mixer_plugin.h
@@ -27,7 +27,7 @@
#ifndef MPD_MIXER_PLUGIN_H
#define MPD_MIXER_PLUGIN_H
-#include <glib.h>
+#include "gerror.h"
#include <stdbool.h>
diff --git a/src/mpd_error.h b/src/mpd_error.h
index 219738ced..e0b7d29a4 100644
--- a/src/mpd_error.h
+++ b/src/mpd_error.h
@@ -20,6 +20,7 @@
#ifndef MPD_ERROR_H
#define MPD_ERROR_H
+#include <glib.h>
#include <stdlib.h>
/* This macro is used as an intermediate step to a proper error handling
diff --git a/src/output/httpd_client.h b/src/output/httpd_client.h
index 739163f42..f0df829db 100644
--- a/src/output/httpd_client.h
+++ b/src/output/httpd_client.h
@@ -20,9 +20,8 @@
#ifndef MPD_OUTPUT_HTTPD_CLIENT_H
#define MPD_OUTPUT_HTTPD_CLIENT_H
-#include <glib.h>
-
#include <stdbool.h>
+#include <stddef.h>
struct httpd_client;
struct httpd_output;
diff --git a/src/output/pulse_output_plugin.h b/src/output/pulse_output_plugin.h
index 02a51f27b..b285b5e4d 100644
--- a/src/output/pulse_output_plugin.h
+++ b/src/output/pulse_output_plugin.h
@@ -20,9 +20,9 @@
#ifndef MPD_PULSE_OUTPUT_PLUGIN_H
#define MPD_PULSE_OUTPUT_PLUGIN_H
-#include <stdbool.h>
+#include "gerror.h"
-#include <glib.h>
+#include <stdbool.h>
struct pulse_output;
struct pulse_mixer;
diff --git a/src/output/shout_output_plugin.c b/src/output/shout_output_plugin.c
index 56456a0ea..8f9df2ccf 100644
--- a/src/output/shout_output_plugin.c
+++ b/src/output/shout_output_plugin.c
@@ -105,15 +105,10 @@ static void free_shout_data(struct shout_data *sd)
} \
}
-static struct audio_output *
-my_shout_init_driver(const struct config_param *param,
- GError **error)
+static bool
+my_shout_configure(struct shout_data *sd, const struct config_param *param,
+ GError **error)
{
- struct shout_data *sd = new_shout_data();
- if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
- free_shout_data(sd);
- return NULL;
- }
const struct audio_format *audio_format =
&sd->base.config_audio_format;
@@ -125,11 +120,6 @@ my_shout_init_driver(const struct config_param *param,
return NULL;
}
- if (shout_init_count == 0)
- shout_init();
-
- shout_init_count++;
-
const struct block_param *block_param;
check_block_param("host");
char *host = block_param->value;
@@ -141,7 +131,7 @@ my_shout_init_driver(const struct config_param *param,
if (port == 0) {
g_set_error(error, shout_output_quark(), 0,
"shout port must be configured");
- goto failure;
+ return false;
}
check_block_param("password");
@@ -164,21 +154,21 @@ my_shout_init_driver(const struct config_param *param,
"shout quality \"%s\" is not a number in the "
"range -1 to 10, line %i",
value, param->line);
- goto failure;
+ return false;
}
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
g_set_error(error, shout_output_quark(), 0,
"quality and bitrate are "
"both defined");
- goto failure;
+ return false;
}
} else {
value = config_get_block_string(param, "bitrate", NULL);
if (value == NULL) {
g_set_error(error, shout_output_quark(), 0,
"neither bitrate nor quality defined");
- goto failure;
+ return false;
}
char *test;
@@ -187,7 +177,7 @@ my_shout_init_driver(const struct config_param *param,
if (*test != '\0' || sd->bitrate <= 0) {
g_set_error(error, shout_output_quark(), 0,
"bitrate must be a positive integer");
- goto failure;
+ return false;
}
}
@@ -199,12 +189,12 @@ my_shout_init_driver(const struct config_param *param,
g_set_error(error, shout_output_quark(), 0,
"couldn't find shout encoder plugin \"%s\"",
encoding);
- goto failure;
+ return false;
}
sd->encoder = encoder_init(encoder_plugin, param, error);
if (sd->encoder == NULL)
- goto failure;
+ return false;
unsigned shout_format;
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
@@ -220,7 +210,7 @@ my_shout_init_driver(const struct config_param *param,
g_set_error(error, shout_output_quark(), 0,
"you cannot stream \"%s\" to shoutcast, use mp3",
encoding);
- goto failure;
+ return false;
} else if (0 == strcmp(value, "shoutcast"))
protocol = SHOUT_PROTOCOL_ICY;
else if (0 == strcmp(value, "icecast1"))
@@ -232,7 +222,7 @@ my_shout_init_driver(const struct config_param *param,
"shout protocol \"%s\" is not \"shoutcast\" or "
"\"icecast1\"or \"icecast2\"",
value);
- goto failure;
+ return false;
}
} else {
protocol = SHOUT_PROTOCOL_HTTP;
@@ -251,7 +241,7 @@ my_shout_init_driver(const struct config_param *param,
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
/* optional paramters */
@@ -262,21 +252,21 @@ my_shout_init_driver(const struct config_param *param,
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
value = config_get_block_string(param, "description", NULL);
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
value = config_get_block_string(param, "url", NULL);
if (value != NULL && shout_set_url(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
{
@@ -301,12 +291,31 @@ my_shout_init_driver(const struct config_param *param,
}
}
- return &sd->base;
+ return true;
+}
-failure:
- ao_base_finish(&sd->base);
- free_shout_data(sd);
- return NULL;
+static struct audio_output *
+my_shout_init_driver(const struct config_param *param,
+ GError **error)
+{
+ struct shout_data *sd = new_shout_data();
+ if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
+ free_shout_data(sd);
+ return NULL;
+ }
+
+ if (!my_shout_configure(sd, param, error)) {
+ ao_base_finish(&sd->base);
+ free_shout_data(sd);
+ return NULL;
+ }
+
+ if (shout_init_count == 0)
+ shout_init();
+
+ shout_init_count++;
+
+ return &sd->base;
}
static bool
diff --git a/src/output_all.h b/src/output_all.h
index 4eeb94f13..becf4b695 100644
--- a/src/output_all.h
+++ b/src/output_all.h
@@ -26,6 +26,9 @@
#ifndef OUTPUT_ALL_H
#define OUTPUT_ALL_H
+#include "replay_gain_info.h"
+#include "gerror.h"
+
#include <stdbool.h>
#include <stddef.h>
@@ -84,7 +87,8 @@ audio_output_all_enable_disable(void);
*/
bool
audio_output_all_open(const struct audio_format *audio_format,
- struct music_buffer *buffer);
+ struct music_buffer *buffer,
+ GError **error_r);
/**
* Closes all audio outputs.
@@ -99,6 +103,9 @@ audio_output_all_close(void);
void
audio_output_all_release(void);
+void
+audio_output_all_set_replay_gain_mode(enum replay_gain_mode mode);
+
/**
* Enqueue a #music_chunk object for playing, i.e. pushes it to a
* #music_pipe.
@@ -108,7 +115,7 @@ audio_output_all_release(void);
* (all closed then)
*/
bool
-audio_output_all_play(struct music_chunk *chunk);
+audio_output_all_play(struct music_chunk *chunk, GError **error_r);
/**
* Checks if the output devices have drained their music pipe, and
diff --git a/src/output_internal.h b/src/output_internal.h
index 9d975d789..1a3e8aa1f 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -73,6 +73,13 @@ struct audio_output {
struct mixer *mixer;
/**
+ * Will this output receive tags from the decoder? The
+ * default is true, but it may be configured to false to
+ * suppress sending tags to the output.
+ */
+ bool tags;
+
+ /**
* Shall this output always play something (i.e. silence),
* even when playback is stopped?
*/
diff --git a/src/output_plugin.h b/src/output_plugin.h
index 209ca6221..a47296566 100644
--- a/src/output_plugin.h
+++ b/src/output_plugin.h
@@ -20,7 +20,8 @@
#ifndef MPD_OUTPUT_PLUGIN_H
#define MPD_OUTPUT_PLUGIN_H
-#include <glib.h>
+#include "gcc.h"
+#include "gerror.h"
#include <stdbool.h>
#include <stddef.h>
@@ -165,7 +166,7 @@ ao_plugin_test_default_device(const struct audio_output_plugin *plugin)
: false;
}
-G_GNUC_MALLOC
+gcc_malloc
struct audio_output *
ao_plugin_init(const struct audio_output_plugin *plugin,
const struct config_param *param,
@@ -187,7 +188,7 @@ ao_plugin_open(struct audio_output *ao, struct audio_format *audio_format,
void
ao_plugin_close(struct audio_output *ao);
-G_GNUC_PURE
+gcc_pure
unsigned
ao_plugin_delay(struct audio_output *ao);
diff --git a/src/pcm_channels.c b/src/pcm_channels.c
index ec2bd69a5..9d166a437 100644
--- a/src/pcm_channels.c
+++ b/src/pcm_channels.c
@@ -244,3 +244,74 @@ pcm_convert_channels_32(struct pcm_buffer *buffer,
return dest;
}
+
+static void
+pcm_convert_channels_float_1_to_2(float *dest, const float *src,
+ const float *src_end)
+{
+ pcm_convert_channels_24_1_to_2((int32_t *)dest,
+ (const int32_t *)src,
+ (const int32_t *)src_end);
+}
+
+static void
+pcm_convert_channels_float_2_to_1(float *restrict dest,
+ const float *restrict src,
+ const float *restrict src_end)
+{
+ while (src < src_end) {
+ double a = *src++, b = *src++;
+
+ *dest++ = (a + b) / 2;
+ }
+}
+
+static void
+pcm_convert_channels_float_n_to_2(float *dest,
+ unsigned src_channels, const float *src,
+ const float *src_end)
+{
+ unsigned c;
+
+ assert(src_channels > 0);
+
+ while (src < src_end) {
+ double sum = 0;
+ float value;
+
+ for (c = 0; c < src_channels; ++c)
+ sum += *src++;
+ value = sum / (double)src_channels;
+
+ /* XXX this is actually only mono ... */
+ *dest++ = value;
+ *dest++ = value;
+ }
+}
+
+const float *
+pcm_convert_channels_float(struct pcm_buffer *buffer,
+ unsigned dest_channels,
+ unsigned src_channels, const float *src,
+ size_t src_size, size_t *dest_size_r)
+{
+ assert(src_size % (sizeof(*src) * src_channels) == 0);
+
+ size_t dest_size = src_size / src_channels * dest_channels;
+ *dest_size_r = dest_size;
+
+ float *dest = pcm_buffer_get(buffer, dest_size);
+ const float *src_end = pcm_end_pointer(src, src_size);
+
+ if (src_channels == 1 && dest_channels == 2)
+ pcm_convert_channels_float_1_to_2(dest, src, src_end);
+ else if (src_channels == 2 && dest_channels == 1)
+ pcm_convert_channels_float_2_to_1(dest, src, src_end);
+ else if (dest_channels == 2)
+ pcm_convert_channels_float_n_to_2(dest, src_channels, src,
+ src_end);
+ else
+ return NULL;
+
+ return dest;
+}
diff --git a/src/pcm_channels.h b/src/pcm_channels.h
index 1e4a0991f..6da00316d 100644
--- a/src/pcm_channels.h
+++ b/src/pcm_channels.h
@@ -77,4 +77,21 @@ pcm_convert_channels_32(struct pcm_buffer *buffer,
unsigned src_channels, const int32_t *src,
size_t src_size, size_t *dest_size_r);
+/**
+ * Changes the number of channels in 32 bit float PCM data.
+ *
+ * @param buffer the destination pcm_buffer object
+ * @param dest_channels the number of channels requested
+ * @param src_channels the number of channels in the source buffer
+ * @param src the source PCM buffer
+ * @param src_size the number of bytes in #src
+ * @param dest_size_r returns the number of bytes of the destination buffer
+ * @return the destination buffer
+ */
+const float *
+pcm_convert_channels_float(struct pcm_buffer *buffer,
+ unsigned dest_channels,
+ unsigned src_channels, const float *src,
+ size_t src_size, size_t *dest_size_r);
+
#endif
diff --git a/src/pcm_convert.c b/src/pcm_convert.c
index 63f9a1b98..32425143a 100644
--- a/src/pcm_convert.c
+++ b/src/pcm_convert.c
@@ -61,55 +61,6 @@ pcm_convert_reset(struct pcm_convert_state *state)
pcm_resample_reset(&state->resample);
}
-static const void *
-pcm_convert_channels(struct pcm_buffer *buffer, enum sample_format format,
- uint8_t dest_channels,
- uint8_t src_channels, const void *src,
- size_t src_size, size_t *dest_size_r,
- GError **error_r)
-{
- const void *dest = NULL;
-
- switch (format) {
- case SAMPLE_FORMAT_UNDEFINED:
- case SAMPLE_FORMAT_S8:
- case SAMPLE_FORMAT_FLOAT:
- case SAMPLE_FORMAT_DSD:
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Channel conversion not implemented for format '%s'",
- sample_format_to_string(format));
- return NULL;
-
- case SAMPLE_FORMAT_S16:
- dest = pcm_convert_channels_16(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
-
- case SAMPLE_FORMAT_S24_P32:
- dest = pcm_convert_channels_24(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
-
- case SAMPLE_FORMAT_S32:
- dest = pcm_convert_channels_32(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
- }
-
- if (dest == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_channels, dest_channels);
- return NULL;
- }
-
- return dest;
-}
-
static const int16_t *
pcm_convert_16(struct pcm_convert_state *state,
const struct audio_format *src_format,
@@ -273,19 +224,6 @@ pcm_convert_float(struct pcm_convert_state *state,
assert(dest_format->format == SAMPLE_FORMAT_FLOAT);
- /* convert channels first, hoping the source format is
- supported (float is not) */
-
- if (dest_format->channels != src_format->channels) {
- buffer = pcm_convert_channels(&state->channels_buffer,
- src_format->format,
- dest_format->channels,
- src_format->channels,
- buffer, size, &size, error_r);
- if (buffer == NULL)
- return NULL;
- }
-
/* convert to float now */
buffer = pcm_convert_to_float(&state->format_buffer,
@@ -298,6 +236,23 @@ pcm_convert_float(struct pcm_convert_state *state,
return NULL;
}
+ /* convert channels */
+
+ if (src_format->channels != dest_format->channels) {
+ buffer = pcm_convert_channels_float(&state->channels_buffer,
+ dest_format->channels,
+ src_format->channels,
+ buffer, size, &size);
+ if (buffer == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %u to %u channels "
+ "is not implemented",
+ src_format->channels,
+ dest_format->channels);
+ return NULL;
+ }
+ }
+
/* resample with float, because this is the best format for
libsamplerate */
diff --git a/src/pcm_convert.h b/src/pcm_convert.h
index be11a6e41..0668a2b66 100644
--- a/src/pcm_convert.h
+++ b/src/pcm_convert.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -25,6 +25,8 @@
#include "pcm_dither.h"
#include "pcm_buffer.h"
+#include <glib.h>
+
struct audio_format;
/**
@@ -52,6 +54,8 @@ pcm_convert_quark(void)
return g_quark_from_static_string("pcm_convert");
}
+G_BEGIN_DECLS
+
/**
* Initializes a pcm_convert_state object.
*/
@@ -91,4 +95,6 @@ pcm_convert(struct pcm_convert_state *state,
size_t *dest_size_r,
GError **error_r);
+G_END_DECLS
+
#endif
diff --git a/src/pcm_mix.h b/src/pcm_mix.h
index 0e58d01ee..0cf557680 100644
--- a/src/pcm_mix.h
+++ b/src/pcm_mix.h
@@ -21,6 +21,7 @@
#define PCM_MIX_H
#include "audio_format.h"
+#include "gcc.h"
#include <stdbool.h>
#include <stddef.h>
@@ -41,7 +42,7 @@
*
* @return true on success, false if the format is not supported
*/
-G_GNUC_WARN_UNUSED_RESULT
+gcc_warn_unused_result
bool
pcm_mix(void *buffer1, const void *buffer2, size_t size,
enum sample_format format, float portion1);
diff --git a/src/pcm_volume.h b/src/pcm_volume.h
index 64e3c7641..4a4a4e45a 100644
--- a/src/pcm_volume.h
+++ b/src/pcm_volume.h
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include <stddef.h>
enum {
/** this value means "100% volume" */
diff --git a/src/playlist_vector.c b/src/playlist_vector.c
deleted file mode 100644
index 74c7bf089..000000000
--- a/src/playlist_vector.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "playlist_vector.h"
-#include "db_lock.h"
-
-#include <assert.h>
-#include <string.h>
-#include <glib.h>
-
-static struct playlist_metadata *
-playlist_metadata_new(const char *name, time_t mtime)
-{
- assert(name != NULL);
-
- struct playlist_metadata *pm = g_slice_new(struct playlist_metadata);
- pm->name = g_strdup(name);
- pm->mtime = mtime;
- return pm;
-}
-
-static void
-playlist_metadata_free(struct playlist_metadata *pm)
-{
- assert(pm != NULL);
- assert(pm->name != NULL);
-
- g_free(pm->name);
- g_slice_free(struct playlist_metadata, pm);
-}
-
-void
-playlist_vector_deinit(struct list_head *pv)
-{
- assert(pv != NULL);
-
- struct playlist_metadata *pm, *n;
- playlist_vector_for_each_safe(pm, n, pv)
- playlist_metadata_free(pm);
-}
-
-struct playlist_metadata *
-playlist_vector_find(struct list_head *pv, const char *name)
-{
- assert(holding_db_lock());
- assert(pv != NULL);
- assert(name != NULL);
-
- struct playlist_metadata *pm;
- playlist_vector_for_each(pm, pv)
- if (strcmp(pm->name, name) == 0)
- return pm;
-
- return NULL;
-}
-
-void
-playlist_vector_add(struct list_head *pv,
- const char *name, time_t mtime)
-{
- assert(holding_db_lock());
-
- struct playlist_metadata *pm = playlist_metadata_new(name, mtime);
- list_add_tail(&pm->siblings, pv);
-}
-
-bool
-playlist_vector_update_or_add(struct list_head *pv,
- const char *name, time_t mtime)
-{
- assert(holding_db_lock());
-
- struct playlist_metadata *pm = playlist_vector_find(pv, name);
- if (pm != NULL) {
- if (mtime == pm->mtime)
- return false;
-
- pm->mtime = mtime;
- } else
- playlist_vector_add(pv, name, mtime);
-
- return true;
-}
-
-bool
-playlist_vector_remove(struct list_head *pv, const char *name)
-{
- assert(holding_db_lock());
-
- struct playlist_metadata *pm = playlist_vector_find(pv, name);
- if (pm == NULL)
- return false;
-
- list_del(&pm->siblings);
- playlist_metadata_free(pm);
- return true;
-}
diff --git a/src/playlist_vector.h b/src/playlist_vector.h
deleted file mode 100644
index 0af6df8b4..000000000
--- a/src/playlist_vector.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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_PLAYLIST_VECTOR_H
-#define MPD_PLAYLIST_VECTOR_H
-
-#include "util/list.h"
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <sys/time.h>
-
-#define playlist_vector_for_each(pos, head) \
- list_for_each_entry(pos, head, siblings)
-
-#define playlist_vector_for_each_safe(pos, n, head) \
- list_for_each_entry_safe(pos, n, head, siblings)
-
-/**
- * A directory entry pointing to a playlist file.
- */
-struct playlist_metadata {
- struct list_head siblings;
-
- /**
- * The UTF-8 encoded name of the playlist file.
- */
- char *name;
-
- time_t mtime;
-};
-
-void
-playlist_vector_deinit(struct list_head *pv);
-
-/**
- * Caller must lock the #db_mutex.
- */
-struct playlist_metadata *
-playlist_vector_find(struct list_head *pv, const char *name);
-
-/**
- * Caller must lock the #db_mutex.
- */
-void
-playlist_vector_add(struct list_head *pv,
- const char *name, time_t mtime);
-
-/**
- * Caller must lock the #db_mutex.
- *
- * @return true if the vector or one of its items was modified
- */
-bool
-playlist_vector_update_or_add(struct list_head *pv,
- const char *name, time_t mtime);
-
-/**
- * Caller must lock the #db_mutex.
- */
-bool
-playlist_vector_remove(struct list_head *pv, const char *name);
-
-#endif /* SONGVEC_H */
diff --git a/src/protocol/argparser.c b/src/protocol/ArgParser.cxx
index b21d4c53c..0ab19f7d4 100644
--- a/src/protocol/argparser.c
+++ b/src/protocol/ArgParser.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,14 @@
*/
#include "config.h"
-#include "argparser.h"
-#include "result.h"
+#include "ArgParser.hxx"
+#include "Result.hxx"
#include <glib.h>
#include <stdlib.h>
bool
-check_uint32(struct client *client, uint32_t *dst, const char *s)
+check_uint32(Client *client, uint32_t *dst, const char *s)
{
char *test;
@@ -39,7 +39,7 @@ check_uint32(struct client *client, uint32_t *dst, const char *s)
}
bool
-check_int(struct client *client, int *value_r, const char *s)
+check_int(Client *client, int *value_r, const char *s)
{
char *test;
long value;
@@ -64,7 +64,7 @@ check_int(struct client *client, int *value_r, const char *s)
}
bool
-check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
+check_range(Client *client, unsigned *value_r1, unsigned *value_r2,
const char *s)
{
char *test, *test2;
@@ -134,7 +134,7 @@ check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
}
bool
-check_unsigned(struct client *client, unsigned *value_r, const char *s)
+check_unsigned(Client *client, unsigned *value_r, const char *s)
{
unsigned long value;
char *endptr;
@@ -157,7 +157,7 @@ check_unsigned(struct client *client, unsigned *value_r, const char *s)
}
bool
-check_bool(struct client *client, bool *value_r, const char *s)
+check_bool(Client *client, bool *value_r, const char *s)
{
long value;
char *endptr;
@@ -174,7 +174,7 @@ check_bool(struct client *client, bool *value_r, const char *s)
}
bool
-check_float(struct client *client, float *value_r, const char *s)
+check_float(Client *client, float *value_r, const char *s)
{
float value;
char *endptr;
diff --git a/src/protocol/argparser.h b/src/protocol/ArgParser.hxx
index e88aea478..b6feb3e67 100644
--- a/src/protocol/argparser.h
+++ b/src/protocol/ArgParser.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,33 +17,33 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PROTOCOL_ARGPARSER_H
-#define MPD_PROTOCOL_ARGPARSER_H
+#ifndef MPD_PROTOCOL_ARGPARSER_HXX
+#define MPD_PROTOCOL_ARGPARSER_HXX
#include "check.h"
#include <stdbool.h>
#include <stdint.h>
-struct client;
+class Client;
bool
-check_uint32(struct client *client, uint32_t *dst, const char *s);
+check_uint32(Client *client, uint32_t *dst, const char *s);
bool
-check_int(struct client *client, int *value_r, const char *s);
+check_int(Client *client, int *value_r, const char *s);
bool
-check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
+check_range(Client *client, unsigned *value_r1, unsigned *value_r2,
const char *s);
bool
-check_unsigned(struct client *client, unsigned *value_r, const char *s);
+check_unsigned(Client *client, unsigned *value_r, const char *s);
bool
-check_bool(struct client *client, bool *value_r, const char *s);
+check_bool(Client *client, bool *value_r, const char *s);
bool
-check_float(struct client *client, float *value_r, const char *s);
+check_float(Client *client, float *value_r, const char *s);
#endif
diff --git a/src/protocol/result.c b/src/protocol/Result.cxx
index 30cd0a266..e10a731cc 100644
--- a/src/protocol/result.c
+++ b/src/protocol/Result.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "result.h"
-#include "client.h"
+#include "Result.hxx"
+#include "Client.hxx"
#include <assert.h>
@@ -27,13 +27,13 @@ const char *current_command;
int command_list_num;
void
-command_success(struct client *client)
+command_success(Client *client)
{
client_puts(client, "OK\n");
}
void
-command_error_v(struct client *client, enum ack error,
+command_error_v(Client *client, enum ack error,
const char *fmt, va_list args)
{
assert(client != NULL);
@@ -48,7 +48,7 @@ command_error_v(struct client *client, enum ack error,
}
void
-command_error(struct client *client, enum ack error, const char *fmt, ...)
+command_error(Client *client, enum ack error, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
diff --git a/src/protocol/result.h b/src/protocol/Result.hxx
index 8b9e44bfd..99d9a2fa0 100644
--- a/src/protocol/result.h
+++ b/src/protocol/Result.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,28 +17,27 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PROTOCOL_RESULT_H
-#define MPD_PROTOCOL_RESULT_H
+#ifndef MPD_PROTOCOL_RESULT_HXX
+#define MPD_PROTOCOL_RESULT_HXX
#include "check.h"
+#include "gcc.h"
#include "ack.h"
-#include <glib.h>
-
-struct client;
+class Client;
extern const char *current_command;
extern int command_list_num;
void
-command_success(struct client *client);
+command_success(Client *client);
void
-command_error_v(struct client *client, enum ack error,
+command_error_v(Client *client, enum ack error,
const char *fmt, va_list args);
-G_GNUC_PRINTF(3, 4)
+gcc_fprintf_
void
-command_error(struct client *client, enum ack error, const char *fmt, ...);
+command_error(Client *client, enum ack error, const char *fmt, ...);
#endif
diff --git a/src/queue.h b/src/queue.h
deleted file mode 100644
index e4bfcdffa..000000000
--- a/src/queue.h
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 QUEUE_H
-#define QUEUE_H
-
-#include <glib.h>
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-enum {
- /**
- * reserve max_length * QUEUE_HASH_MULT elements in the id
- * number space
- */
- QUEUE_HASH_MULT = 4,
-};
-
-/**
- * One element of the queue: basically a song plus some queue specific
- * information attached.
- */
-struct queue_item {
- struct song *song;
-
- /** the unique id of this item in the queue */
- unsigned id;
-
- /** when was this item last changed? */
- uint32_t version;
-
- /**
- * The priority of this item, between 0 and 255. High
- * priority value means that this song gets played first in
- * "random" mode.
- */
- uint8_t priority;
-};
-
-/**
- * A queue of songs. This is the backend of the playlist: it contains
- * an ordered list of songs.
- *
- * Songs can be addressed in three possible ways:
- *
- * - the position in the queue
- * - the unique id (which stays the same, regardless of moves)
- * - the order number (which only differs from "position" in random mode)
- */
-struct queue {
- /** configured maximum length of the queue */
- unsigned max_length;
-
- /** number of songs in the queue */
- unsigned length;
-
- /** the current version number */
- uint32_t version;
-
- /** all songs in "position" order */
- struct queue_item *items;
-
- /** map order numbers to positions */
- unsigned *order;
-
- /** map song ids to positions */
- int *id_to_position;
-
- /** repeat playback when the end of the queue has been
- reached? */
- bool repeat;
-
- /** play only current song. */
- bool single;
-
- /** remove each played files. */
- bool consume;
-
- /** play back songs in random order? */
- bool random;
-
- /** random number generator for shuffle and random mode */
- GRand *rand;
-};
-
-static inline unsigned
-queue_length(const struct queue *queue)
-{
- assert(queue->length <= queue->max_length);
-
- return queue->length;
-}
-
-/**
- * Determine if the queue is empty, i.e. there are no songs.
- */
-static inline bool
-queue_is_empty(const struct queue *queue)
-{
- return queue->length == 0;
-}
-
-/**
- * Determine if the maximum number of songs has been reached.
- */
-static inline bool
-queue_is_full(const struct queue *queue)
-{
- assert(queue->length <= queue->max_length);
-
- return queue->length >= queue->max_length;
-}
-
-/**
- * Is that a valid position number?
- */
-static inline bool
-queue_valid_position(const struct queue *queue, unsigned position)
-{
- return position < queue->length;
-}
-
-/**
- * Is that a valid order number?
- */
-static inline bool
-queue_valid_order(const struct queue *queue, unsigned order)
-{
- return order < queue->length;
-}
-
-static inline int
-queue_id_to_position(const struct queue *queue, unsigned id)
-{
- if (id >= queue->max_length * QUEUE_HASH_MULT)
- return -1;
-
- assert(queue->id_to_position[id] >= -1);
- assert(queue->id_to_position[id] < (int)queue->length);
-
- return queue->id_to_position[id];
-}
-
-static inline int
-queue_position_to_id(const struct queue *queue, unsigned position)
-{
- assert(position < queue->length);
-
- return queue->items[position].id;
-}
-
-static inline unsigned
-queue_order_to_position(const struct queue *queue, unsigned order)
-{
- assert(order < queue->length);
-
- return queue->order[order];
-}
-
-static inline unsigned
-queue_position_to_order(const struct queue *queue, unsigned position)
-{
- assert(position < queue->length);
-
- for (unsigned i = 0;; ++i) {
- assert(i < queue->length);
-
- if (queue->order[i] == position)
- return i;
- }
-}
-
-G_GNUC_PURE
-static inline uint8_t
-queue_get_priority_at_position(const struct queue *queue, unsigned position)
-{
- assert(position < queue->length);
-
- return queue->items[position].priority;
-}
-
-/**
- * Returns the song at the specified position.
- */
-static inline struct song *
-queue_get(const struct queue *queue, unsigned position)
-{
- assert(position < queue->length);
-
- return queue->items[position].song;
-}
-
-/**
- * Returns the song at the specified order number.
- */
-static inline struct song *
-queue_get_order(const struct queue *queue, unsigned order)
-{
- return queue_get(queue, queue_order_to_position(queue, order));
-}
-
-/**
- * Is the song at the specified position newer than the specified
- * version?
- */
-static inline bool
-queue_song_newer(const struct queue *queue, unsigned position,
- uint32_t version)
-{
- assert(position < queue->length);
-
- return version > queue->version ||
- queue->items[position].version >= version ||
- queue->items[position].version == 0;
-}
-
-/**
- * Initialize a queue object.
- */
-void
-queue_init(struct queue *queue, unsigned max_length);
-
-/**
- * Deinitializes a queue object. It does not free the queue pointer
- * itself.
- */
-void
-queue_finish(struct queue *queue);
-
-/**
- * Returns the order number following the specified one. This takes
- * end of queue and "repeat" mode into account.
- *
- * @return the next order number, or -1 to stop playback
- */
-int
-queue_next_order(const struct queue *queue, unsigned order);
-
-/**
- * Increments the queue's version number. This handles integer
- * overflow well.
- */
-void
-queue_increment_version(struct queue *queue);
-
-/**
- * Marks the specified song as "modified" and increments the version
- * number.
- */
-void
-queue_modify(struct queue *queue, unsigned order);
-
-/**
- * Marks all songs as "modified" and increments the version number.
- */
-void
-queue_modify_all(struct queue *queue);
-
-/**
- * Appends a song to the queue and returns its position. Prior to
- * that, the caller must check if the queue is already full.
- *
- * If a song is not in the database (determined by
- * song_in_database()), it is freed when removed from the queue.
- *
- * @param priority the priority of this new queue item
- */
-unsigned
-queue_append(struct queue *queue, struct song *song, uint8_t priority);
-
-/**
- * Swaps two songs, addressed by their position.
- */
-void
-queue_swap(struct queue *queue, unsigned position1, unsigned position2);
-
-/**
- * Swaps two songs, addressed by their order number.
- */
-static inline void
-queue_swap_order(struct queue *queue, unsigned order1, unsigned order2)
-{
- unsigned tmp = queue->order[order1];
- queue->order[order1] = queue->order[order2];
- queue->order[order2] = tmp;
-}
-
-/**
- * Moves a song to a new position.
- */
-void
-queue_move(struct queue *queue, unsigned from, unsigned to);
-
-/**
- * Moves a range of songs to a new position.
- */
-void
-queue_move_range(struct queue *queue, unsigned start, unsigned end, unsigned to);
-
-/**
- * Removes a song from the playlist.
- */
-void
-queue_delete(struct queue *queue, unsigned position);
-
-/**
- * Removes all songs from the playlist.
- */
-void
-queue_clear(struct queue *queue);
-
-/**
- * Initializes the "order" array, and restores "normal" order.
- */
-static inline void
-queue_restore_order(struct queue *queue)
-{
- for (unsigned i = 0; i < queue->length; ++i)
- queue->order[i] = i;
-}
-
-/**
- * Shuffle the order of items in the specified range, taking their
- * priorities into account.
- */
-void
-queue_shuffle_order_range_with_priority(struct queue *queue,
- unsigned start, unsigned end);
-
-/**
- * Shuffles the virtual order of songs, but does not move them
- * physically. This is used in random mode.
- */
-void
-queue_shuffle_order(struct queue *queue);
-
-/**
- * Shuffles the virtual order of the last song in the specified
- * (order) range. This is used in random mode after a song has been
- * appended by queue_append().
- */
-void
-queue_shuffle_order_last(struct queue *queue, unsigned start, unsigned end);
-
-/**
- * Shuffles a (position) range in the queue. The songs are physically
- * shuffled, not by using the "order" mapping.
- */
-void
-queue_shuffle_range(struct queue *queue, unsigned start, unsigned end);
-
-bool
-queue_set_priority(struct queue *queue, unsigned position,
- uint8_t priority, int after_order);
-
-bool
-queue_set_priority_range(struct queue *queue,
- unsigned start_position, unsigned end_position,
- uint8_t priority, int after_order);
-
-#endif
diff --git a/src/queue_print.c b/src/queue_print.c
deleted file mode 100644
index d149e8b6f..000000000
--- a/src/queue_print.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "queue_print.h"
-#include "queue.h"
-#include "song.h"
-#include "song_print.h"
-#include "locate.h"
-#include "client.h"
-#include "mapper.h"
-
-/**
- * Send detailed information about a range of songs in the queue to a
- * client.
- *
- * @param client the client which has requested information
- * @param start the index of the first song (including)
- * @param end the index of the last song (excluding)
- */
-static void
-queue_print_song_info(struct client *client, const struct queue *queue,
- unsigned position)
-{
- song_print_info(client, queue_get(queue, position));
- client_printf(client, "Pos: %u\nId: %u\n",
- position, queue_position_to_id(queue, position));
-
- uint8_t priority = queue_get_priority_at_position(queue, position);
- if (priority != 0)
- client_printf(client, "Prio: %u\n", priority);
-}
-
-void
-queue_print_info(struct client *client, const struct queue *queue,
- unsigned start, unsigned end)
-{
- assert(start <= end);
- assert(end <= queue_length(queue));
-
- for (unsigned i = start; i < end; ++i)
- queue_print_song_info(client, queue, i);
-}
-
-void
-queue_print_uris(struct client *client, const struct queue *queue,
- unsigned start, unsigned end)
-{
- assert(start <= end);
- assert(end <= queue_length(queue));
-
- for (unsigned i = start; i < end; ++i) {
- client_printf(client, "%i:", i);
- song_print_uri(client, queue_get(queue, i));
- }
-}
-
-void
-queue_print_changes_info(struct client *client, const struct queue *queue,
- uint32_t version)
-{
- for (unsigned i = 0; i < queue_length(queue); i++) {
- if (queue_song_newer(queue, i, version))
- queue_print_song_info(client, queue, i);
- }
-}
-
-void
-queue_print_changes_position(struct client *client, const struct queue *queue,
- uint32_t version)
-{
- for (unsigned i = 0; i < queue_length(queue); i++)
- if (queue_song_newer(queue, i, version))
- client_printf(client, "cpos: %i\nId: %i\n",
- i, queue_position_to_id(queue, i));
-}
-
-void
-queue_search(struct client *client, const struct queue *queue,
- const struct locate_item_list *criteria)
-{
- unsigned i;
- struct locate_item_list *new_list =
- locate_item_list_casefold(criteria);
-
- for (i = 0; i < queue_length(queue); i++) {
- const struct song *song = queue_get(queue, i);
-
- if (locate_song_search(song, new_list))
- queue_print_song_info(client, queue, i);
- }
-
- locate_item_list_free(new_list);
-}
-
-void
-queue_find(struct client *client, const struct queue *queue,
- const struct locate_item_list *criteria)
-{
- for (unsigned i = 0; i < queue_length(queue); i++) {
- const struct song *song = queue_get(queue, i);
-
- if (locate_song_match(song, criteria))
- queue_print_song_info(client, queue, i);
- }
-}
diff --git a/src/replay_gain_config.h b/src/replay_gain_config.h
index 18747cef2..2bf4f32f2 100644
--- a/src/replay_gain_config.h
+++ b/src/replay_gain_config.h
@@ -50,6 +50,6 @@ replay_gain_set_mode_string(const char *p);
* Returns the "real" mode according to the "auto" setting"
*/
enum replay_gain_mode
-replay_gain_get_real_mode(void);
+replay_gain_get_real_mode(bool random_mode);
#endif
diff --git a/src/resolver.h b/src/resolver.h
index e5ad06754..666b6d5b2 100644
--- a/src/resolver.h
+++ b/src/resolver.h
@@ -20,12 +20,14 @@
#ifndef MPD_RESOLVER_H
#define MPD_RESOLVER_H
+#include "gcc.h"
+
#include <glib.h>
struct sockaddr;
struct addrinfo;
-G_GNUC_CONST
+gcc_const
static inline GQuark
resolver_quark(void)
{
@@ -42,7 +44,7 @@ resolver_quark(void)
* @param error location to store the error occurring, or NULL to
* ignore errors
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
sockaddr_to_string(const struct sockaddr *sa, size_t length, GError **error);
diff --git a/src/server_socket.h b/src/server_socket.h
index 7caa4bbf2..f7e9aa4cf 100644
--- a/src/server_socket.h
+++ b/src/server_socket.h
@@ -20,9 +20,10 @@
#ifndef MPD_SERVER_SOCKET_H
#define MPD_SERVER_SOCKET_H
-#include <stdbool.h>
+#include "gerror.h"
-#include <glib.h>
+#include <stdbool.h>
+#include <stddef.h>
struct sockaddr;
diff --git a/src/sig_handlers.c b/src/sig_handlers.c
index b23f9e778..eabca1997 100644
--- a/src/sig_handlers.c
+++ b/src/sig_handlers.c
@@ -23,7 +23,7 @@
#ifndef WIN32
#include "log.h"
-#include "main.h"
+#include "Main.hxx"
#include "event_pipe.h"
#include "mpd_error.h"
diff --git a/src/socket_util.c b/src/socket_util.c
index a06a0cbd5..ee8bf7e1a 100644
--- a/src/socket_util.c
+++ b/src/socket_util.c
@@ -21,6 +21,8 @@
#include "socket_util.h"
#include "fd_util.h"
+#include <glib.h>
+
#include <errno.h>
#include <unistd.h>
diff --git a/src/socket_util.h b/src/socket_util.h
index 93bd27362..4cf845d8e 100644
--- a/src/socket_util.h
+++ b/src/socket_util.h
@@ -26,7 +26,9 @@
#ifndef SOCKET_UTIL_H
#define SOCKET_UTIL_H
-#include <glib.h>
+#include "gerror.h"
+
+#include <stddef.h>
struct sockaddr;
diff --git a/src/song.h b/src/song.h
index 8b97d45d0..119d89ccd 100644
--- a/src/song.h
+++ b/src/song.h
@@ -21,7 +21,9 @@
#define MPD_SONG_H
#include "util/list.h"
+#include "gcc.h"
+#include <assert.h>
#include <stddef.h>
#include <stdbool.h>
#include <sys/time.h>
@@ -41,7 +43,7 @@ struct song {
struct list_head siblings;
struct tag *tag;
- struct directory *parent;
+ struct Directory *parent;
time_t mtime;
/**
@@ -58,13 +60,21 @@ struct song {
char uri[sizeof(int)];
};
+/**
+ * A dummy #directory instance that is used for "detached" song
+ * copies.
+ */
+extern struct Directory detached_root;
+
+G_BEGIN_DECLS
+
/** allocate a new song with a remote URL */
struct song *
song_remote_new(const char *uri);
/** allocate a new song with a local file name */
struct song *
-song_file_new(const char *path, struct directory *parent);
+song_file_new(const char *path, struct Directory *parent);
/**
* allocate a new song structure with a local file name and attempt to
@@ -72,7 +82,7 @@ song_file_new(const char *path, struct directory *parent);
* data, NULL is returned.
*/
struct song *
-song_file_load(const char *path, struct directory *parent);
+song_file_load(const char *path, struct Directory *parent);
/**
* Replaces the URI of a song object. The given song object is
@@ -83,9 +93,52 @@ song_file_load(const char *path, struct directory *parent);
struct song *
song_replace_uri(struct song *song, const char *uri);
+/**
+ * Creates a "detached" song object.
+ */
+struct song *
+song_detached_new(const char *uri);
+
+/**
+ * Creates a duplicate of the song object. If the object is in the
+ * database, it creates a "detached" copy of this song, see
+ * song_is_detached().
+ */
+gcc_malloc
+struct song *
+song_dup_detached(const struct song *src);
+
void
song_free(struct song *song);
+static inline bool
+song_in_database(const struct song *song)
+{
+ return song->parent != NULL;
+}
+
+static inline bool
+song_is_file(const struct song *song)
+{
+ return song_in_database(song) || song->uri[0] == '/';
+}
+
+static inline bool
+song_is_detached(const struct song *song)
+{
+ assert(song != NULL);
+ assert(song_in_database(song));
+
+ return song->parent == &detached_root;
+}
+
+/**
+ * Returns true if both objects refer to the same physical song.
+ */
+gcc_pure
+bool
+song_equals(const struct song *a, const struct song *b);
+
bool
song_file_update(struct song *song);
@@ -105,16 +158,6 @@ song_get_uri(const struct song *song);
double
song_get_duration(const struct song *song);
-static inline bool
-song_in_database(const struct song *song)
-{
- return song->parent != NULL;
-}
-
-static inline bool
-song_is_file(const struct song *song)
-{
- return song_in_database(song) || song->uri[0] == '/';
-}
+G_END_DECLS
#endif
diff --git a/src/stats.c b/src/stats.c
deleted file mode 100644
index fe6a064a6..000000000
--- a/src/stats.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "stats.h"
-#include "database.h"
-#include "db_visitor.h"
-#include "tag.h"
-#include "song.h"
-#include "client.h"
-#include "player_control.h"
-#include "strset.h"
-#include "client_internal.h"
-
-struct stats stats;
-
-void stats_global_init(void)
-{
- stats.timer = g_timer_new();
-}
-
-void stats_global_finish(void)
-{
- g_timer_destroy(stats.timer);
-}
-
-struct visit_data {
- struct strset *artists;
- struct strset *albums;
-};
-
-static void
-visit_tag(struct visit_data *data, const struct tag *tag)
-{
- if (tag->time > 0)
- stats.song_duration += tag->time;
-
- for (unsigned i = 0; i < tag->num_items; ++i) {
- const struct tag_item *item = tag->items[i];
-
- switch (item->type) {
- case TAG_ARTIST:
- strset_add(data->artists, item->value);
- break;
-
- case TAG_ALBUM:
- strset_add(data->albums, item->value);
- break;
-
- default:
- break;
- }
- }
-}
-
-static bool
-collect_stats_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct visit_data *data = _data;
-
- ++stats.song_count;
-
- if (song->tag != NULL)
- visit_tag(data, song->tag);
-
- return true;
-}
-
-static const struct db_visitor collect_stats_visitor = {
- .song = collect_stats_song,
-};
-
-void stats_update(void)
-{
- struct visit_data data;
-
- stats.song_count = 0;
- stats.song_duration = 0;
- stats.artist_count = 0;
-
- data.artists = strset_new();
- data.albums = strset_new();
-
- db_walk("", &collect_stats_visitor, &data, NULL);
-
- stats.artist_count = strset_size(data.artists);
- stats.album_count = strset_size(data.albums);
-
- strset_free(data.artists);
- strset_free(data.albums);
-}
-
-int stats_print(struct client *client)
-{
- client_printf(client,
- "artists: %u\n"
- "albums: %u\n"
- "songs: %i\n"
- "uptime: %li\n"
- "playtime: %li\n"
- "db_playtime: %li\n"
- "db_update: %li\n",
- stats.artist_count,
- stats.album_count,
- stats.song_count,
- (long)g_timer_elapsed(stats.timer, NULL),
- (long)(pc_get_total_play_time(client->player_control) + 0.5),
- stats.song_duration,
- (long)db_get_mtime());
- return 0;
-}
diff --git a/src/stats.h b/src/stats.h
index a686477de..eb723bcf3 100644
--- a/src/stats.h
+++ b/src/stats.h
@@ -22,7 +22,7 @@
#include <glib.h>
-struct client;
+class Client;
struct stats {
GTimer *timer;
@@ -49,6 +49,7 @@ void stats_global_finish(void);
void stats_update(void);
-int stats_print(struct client *client);
+void
+stats_print(Client *client);
#endif
diff --git a/src/string_util.h b/src/string_util.h
index dc80a46ef..c1d316f0c 100644
--- a/src/string_util.h
+++ b/src/string_util.h
@@ -20,7 +20,7 @@
#ifndef MPD_STRING_UTIL_H
#define MPD_STRING_UTIL_H
-#include <glib.h>
+#include "gcc.h"
#include <stdbool.h>
@@ -28,10 +28,13 @@
* Remove the "const" attribute from a string pointer. This is a
* dirty hack, don't use it unless you know what you're doing!
*/
-G_GNUC_CONST
+gcc_const
static inline char *
deconst_string(const char *p)
{
+#ifdef __cplusplus
+ return const_cast<char *>(p);
+#else
union {
const char *in;
char *out;
@@ -40,6 +43,7 @@ deconst_string(const char *p)
};
return u.out;
+#endif
}
/**
@@ -49,14 +53,14 @@ deconst_string(const char *p)
* This is a faster version of g_strchug(), because it does not move
* data.
*/
-G_GNUC_PURE
+gcc_pure
const char *
strchug_fast_c(const char *p);
/**
* Same as strchug_fast_c(), but works with a writable pointer.
*/
-G_GNUC_PURE
+gcc_pure
static inline char *
strchug_fast(char *p)
{
diff --git a/src/strset.c b/src/strset.c
deleted file mode 100644
index 5862e4075..000000000
--- a/src/strset.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 "strset.h"
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-#define NUM_SLOTS 16384
-
-struct strset_slot {
- struct strset_slot *next;
- const char *value;
-};
-
-struct strset {
- unsigned size;
-
- struct strset_slot *current_slot;
- unsigned next_slot;
-
- struct strset_slot slots[NUM_SLOTS];
-};
-
-static unsigned calc_hash(const char *p) {
- unsigned hash = 5381;
-
- assert(p != NULL);
-
- while (*p != 0)
- hash = (hash << 5) + hash + *p++;
-
- return hash;
-}
-
-G_GNUC_MALLOC struct strset *strset_new(void)
-{
- struct strset *set = g_new0(struct strset, 1);
- return set;
-}
-
-void strset_free(struct strset *set)
-{
- unsigned i;
-
- for (i = 0; i < NUM_SLOTS; ++i) {
- struct strset_slot *slot = set->slots[i].next, *next;
-
- while (slot != NULL) {
- next = slot->next;
- g_free(slot);
- slot = next;
- }
- }
-
- g_free(set);
-}
-
-void strset_add(struct strset *set, const char *value)
-{
- struct strset_slot *base_slot
- = &set->slots[calc_hash(value) % NUM_SLOTS];
- struct strset_slot *slot = base_slot;
-
- if (base_slot->value == NULL) {
- /* empty slot - put into base_slot */
- assert(base_slot->next == NULL);
-
- base_slot->value = value;
- ++set->size;
- return;
- }
-
- for (slot = base_slot; slot != NULL; slot = slot->next)
- if (strcmp(slot->value, value) == 0)
- /* found it - do nothing */
- return;
-
- /* insert it into the slot chain */
- slot = g_new(struct strset_slot, 1);
- slot->next = base_slot->next;
- slot->value = value;
- base_slot->next = slot;
- ++set->size;
-}
-
-int strset_get(const struct strset *set, const char *value)
-{
- const struct strset_slot *slot = &set->slots[calc_hash(value)];
-
- if (slot->value == NULL)
- return 0;
-
- for (slot = slot->next; slot != NULL; slot = slot->next)
- if (strcmp(slot->value, value) == 0)
- /* found it - do nothing */
- return 1;
-
- return 0;
-}
-
-unsigned strset_size(const struct strset *set)
-{
- return set->size;
-}
-
-void strset_rewind(struct strset *set)
-{
- set->current_slot = NULL;
- set->next_slot = 0;
-}
-
-const char *strset_next(struct strset *set)
-{
- if (set->current_slot != NULL && set->current_slot->next != NULL) {
- set->current_slot = set->current_slot->next;
- return set->current_slot->value;
- }
-
- while (set->next_slot < NUM_SLOTS &&
- set->slots[set->next_slot].value == NULL)
- ++set->next_slot;
-
- if (set->next_slot >= NUM_SLOTS)
- return NULL;
-
- set->current_slot = &set->slots[set->next_slot++];
- return set->current_slot->value;
-}
-
diff --git a/src/strset.h b/src/strset.h
deleted file mode 100644
index 5382e59b8..000000000
--- a/src/strset.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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.
- */
-
-/**
- * "struct strset" is a hashed string set: you can add strings to this
- * library, and it stores them as a set of unique strings. You can
- * get the size of the set, and you can enumerate through all values.
- *
- * It is important to note that the strset does not copy the string
- * values - it stores the exact pointers it was given in strset_add().
- */
-
-#ifndef MPD_STRSET_H
-#define MPD_STRSET_H
-
-#include <glib.h>
-
-struct strset;
-
-G_GNUC_MALLOC struct strset *strset_new(void);
-
-void strset_free(struct strset *set);
-
-void strset_add(struct strset *set, const char *value);
-
-int strset_get(const struct strset *set, const char *value);
-
-unsigned strset_size(const struct strset *set);
-
-void strset_rewind(struct strset *set);
-
-const char *strset_next(struct strset *set);
-
-#endif
diff --git a/src/tag.c b/src/tag.c
index c0faa7ab2..b59c23b23 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -168,9 +168,9 @@ static void tag_delete_item(struct tag *tag, unsigned idx)
assert(idx < tag->num_items);
tag->num_items--;
- g_mutex_lock(tag_pool_lock);
+ g_static_mutex_lock(&tag_pool_lock);
tag_pool_put_item(tag->items[idx]);
- g_mutex_unlock(tag_pool_lock);
+ g_static_mutex_unlock(&tag_pool_lock);
if (tag->num_items - idx > 0) {
memmove(tag->items + idx, tag->items + idx + 1,
@@ -202,10 +202,10 @@ void tag_free(struct tag *tag)
assert(tag != NULL);
- g_mutex_lock(tag_pool_lock);
+ g_static_mutex_lock(&tag_pool_lock);
for (i = tag->num_items; --i >= 0; )
tag_pool_put_item(tag->items[i]);
- g_mutex_unlock(tag_pool_lock);
+ g_static_mutex_unlock(&tag_pool_lock);
if (tag->items == bulk.items) {
#ifndef NDEBUG
@@ -231,10 +231,10 @@ struct tag *tag_dup(const struct tag *tag)
ret->num_items = tag->num_items;
ret->items = ret->num_items > 0 ? g_malloc(items_size(tag)) : NULL;
- g_mutex_lock(tag_pool_lock);
+ g_static_mutex_lock(&tag_pool_lock);
for (unsigned i = 0; i < tag->num_items; i++)
ret->items[i] = tag_pool_dup_item(tag->items[i]);
- g_mutex_unlock(tag_pool_lock);
+ g_static_mutex_unlock(&tag_pool_lock);
return ret;
}
@@ -255,7 +255,7 @@ tag_merge(const struct tag *base, const struct tag *add)
ret->num_items = base->num_items + add->num_items;
ret->items = ret->num_items > 0 ? g_malloc(items_size(ret)) : NULL;
- g_mutex_lock(tag_pool_lock);
+ g_static_mutex_lock(&tag_pool_lock);
/* copy all items from "add" */
@@ -270,7 +270,7 @@ tag_merge(const struct tag *base, const struct tag *add)
if (!tag_has_type(add, base->items[i]->type))
ret->items[n++] = tag_pool_dup_item(base->items[i]);
- g_mutex_unlock(tag_pool_lock);
+ g_static_mutex_unlock(&tag_pool_lock);
assert(n <= ret->num_items);
@@ -502,9 +502,9 @@ tag_add_item_internal(struct tag *tag, enum tag_type type,
items_size(tag) - sizeof(struct tag_item *));
}
- g_mutex_lock(tag_pool_lock);
+ g_static_mutex_lock(&tag_pool_lock);
tag->items[i] = tag_pool_get_item(type, value, len);
- g_mutex_unlock(tag_pool_lock);
+ g_static_mutex_unlock(&tag_pool_lock);
g_free(p);
}
diff --git a/src/tag_id3.c b/src/tag_id3.c
index 0971829f0..5744e0e62 100644
--- a/src/tag_id3.c
+++ b/src/tag_id3.c
@@ -25,6 +25,7 @@
#include "riff.h"
#include "aiff.h"
#include "conf.h"
+#include "io_error.h"
#include <glib.h>
#include <id3tag.h>
@@ -343,7 +344,7 @@ tag_id3_import_ufid(struct id3_tag *id3_tag,
}
}
-static void
+void
scan_id3_tag(struct id3_tag *tag,
const struct tag_handler *handler, void *handler_ctx)
{
@@ -546,7 +547,7 @@ tag_id3_load(const char *path_fs, GError **error_r)
{
FILE *file = fopen(path_fs, "rb");
if (file == NULL) {
- g_set_error(error_r, g_file_error_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to open file %s: %s",
path_fs, g_strerror(errno));
return NULL;
diff --git a/src/tag_id3.h b/src/tag_id3.h
index 049c53ad9..1907c13fc 100644
--- a/src/tag_id3.h
+++ b/src/tag_id3.h
@@ -21,8 +21,8 @@
#define MPD_TAG_ID3_H
#include "check.h"
-
-#include <glib.h>
+#include "gcc.h"
+#include "gerror.h"
#include <stdbool.h>
@@ -48,12 +48,20 @@ struct tag *tag_id3_import(struct id3_tag *);
struct id3_tag *
tag_id3_load(const char *path_fs, GError **error_r);
+/**
+ * Import all tags from the provided id3_tag *tag
+ *
+ */
+void
+scan_id3_tag(struct id3_tag *tag,
+ const struct tag_handler *handler, void *handler_ctx);
+
#else
static inline bool
-tag_id3_scan(G_GNUC_UNUSED const char *path_fs,
- G_GNUC_UNUSED const struct tag_handler *handler,
- G_GNUC_UNUSED void *handler_ctx)
+tag_id3_scan(gcc_unused const char *path_fs,
+ gcc_unused const struct tag_handler *handler,
+ gcc_unused void *handler_ctx)
{
return false;
}
diff --git a/src/tag_pool.c b/src/tag_pool.c
index eabf3e369..2f9b39486 100644
--- a/src/tag_pool.c
+++ b/src/tag_pool.c
@@ -22,7 +22,17 @@
#include <assert.h>
-GMutex *tag_pool_lock = NULL;
+#if GCC_CHECK_VERSION(4, 2)
+/* workaround for a warning caused by G_STATIC_MUTEX_INIT */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+GStaticMutex tag_pool_lock = G_STATIC_MUTEX_INIT;
+
+#if GCC_CHECK_VERSION(4, 2)
+#pragma GCC diagnostic pop
+#endif
#define NUM_SLOTS 4096
@@ -81,19 +91,6 @@ static struct slot *slot_alloc(struct slot *next,
return slot;
}
-void tag_pool_init(void)
-{
- g_assert(tag_pool_lock == NULL);
- tag_pool_lock = g_mutex_new();
-}
-
-void tag_pool_deinit(void)
-{
- g_assert(tag_pool_lock != NULL);
- g_mutex_free(tag_pool_lock);
- tag_pool_lock = NULL;
-}
-
struct tag_item *
tag_pool_get_item(enum tag_type type, const char *value, size_t length)
{
diff --git a/src/tag_pool.h b/src/tag_pool.h
index a96c00d85..a717f704d 100644
--- a/src/tag_pool.h
+++ b/src/tag_pool.h
@@ -24,14 +24,10 @@
#include <glib.h>
-extern GMutex *tag_pool_lock;
+extern GStaticMutex tag_pool_lock;
struct tag_item;
-void tag_pool_init(void);
-
-void tag_pool_deinit(void);
-
struct tag_item *
tag_pool_get_item(enum tag_type type, const char *value, size_t length);
diff --git a/src/text_file.h b/src/text_file.h
deleted file mode 100644
index 9dd810943..000000000
--- a/src/text_file.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_TEXT_FILE_H
-#define MPD_TEXT_FILE_H
-
-#include <glib.h>
-
-#include <stdio.h>
-
-/**
- * Reads a line from the input file, and strips trailing space. There
- * is a reasonable maximum line length, only to prevent denial of
- * service.
- *
- * @param file the source file, opened in text mode
- * @param buffer an allocator for the buffer
- * @return a pointer to the line, or NULL on end-of-file or error
- */
-char *
-read_text_line(FILE *file, GString *buffer);
-
-#endif
diff --git a/src/thread/CriticalSection.hxx b/src/thread/CriticalSection.hxx
new file mode 100644
index 000000000..f3faee943
--- /dev/null
+++ b/src/thread/CriticalSection.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009-2013 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 MPD_THREAD_CRITICAL_SECTION_HXX
+#define MPD_THREAD_CRITICAL_SECTION_HXX
+
+#include <windows.h>
+
+class CriticalSection {
+ CRITICAL_SECTION critical_section;
+
+public:
+ CriticalSection() {
+ ::InitializeCriticalSection(&critical_section);
+ }
+
+ ~CriticalSection() {
+ ::DeleteCriticalSection(&critical_section);
+ }
+
+ CriticalSection(const CriticalSection &other) = delete;
+ CriticalSection &operator=(const CriticalSection &other) = delete;
+
+ void lock() {
+ ::EnterCriticalSection(&critical_section);
+ };
+
+ bool try_lock() {
+ return ::TryEnterCriticalSection(&critical_section) != 0;
+ };
+
+ void unlock() {
+ ::LeaveCriticalSection(&critical_section);
+ }
+};
+
+#endif
diff --git a/src/thread/Mutex.hxx b/src/thread/Mutex.hxx
new file mode 100644
index 000000000..df8d140c6
--- /dev/null
+++ b/src/thread/Mutex.hxx
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_THREAD_MUTEX_HXX
+#define MPD_THREAD_MUTEX_HXX
+
+#ifdef WIN32
+
+/* mingw-w64 4.6.3 lacks a std::mutex implementation */
+
+#include "CriticalSection.hxx"
+typedef CriticalSection Mutex;
+
+class ScopeLock {
+ Mutex &mutex;
+
+public:
+ ScopeLock(Mutex &_mutex):mutex(_mutex) {
+ mutex.lock();
+ };
+
+ ~ScopeLock() {
+ mutex.unlock();
+ };
+
+ ScopeLock(const ScopeLock &other) = delete;
+ ScopeLock &operator=(const ScopeLock &other) = delete;
+};
+
+#else
+
+#include <mutex>
+typedef std::mutex Mutex;
+typedef std::lock_guard<std::mutex> ScopeLock;
+
+#endif
+
+#endif
diff --git a/src/tokenizer.c b/src/tokenizer.c
index bbb34e100..4a98e882f 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -21,6 +21,8 @@
#include "tokenizer.h"
#include "string_util.h"
+#include <glib.h>
+
#include <stdbool.h>
#include <assert.h>
#include <string.h>
diff --git a/src/tokenizer.h b/src/tokenizer.h
index d55eb3ca6..2026e5ad6 100644
--- a/src/tokenizer.h
+++ b/src/tokenizer.h
@@ -20,7 +20,7 @@
#ifndef MPD_TOKENIZER_H
#define MPD_TOKENIZER_H
-#include <glib.h>
+#include "gerror.h"
/**
* Reads the next word from the input string. This function modifies
diff --git a/src/uri.h b/src/uri.h
index 5a9b472f5..6713f1698 100644
--- a/src/uri.h
+++ b/src/uri.h
@@ -20,7 +20,7 @@
#ifndef MPD_URI_H
#define MPD_URI_H
-#include <glib.h>
+#include "gcc.h"
#include <stdbool.h>
@@ -28,10 +28,10 @@
* Checks whether the specified URI has a scheme in the form
* "scheme://".
*/
-G_GNUC_PURE
+gcc_pure
bool uri_has_scheme(const char *uri);
-G_GNUC_PURE
+gcc_pure
const char *
uri_get_suffix(const char *uri);
@@ -43,7 +43,7 @@ uri_get_suffix(const char *uri);
* - no double slashes
* - no path component begins with a dot
*/
-G_GNUC_PURE
+gcc_pure
bool
uri_safe_local(const char *uri);
@@ -53,7 +53,7 @@ uri_safe_local(const char *uri);
* NULL if nothing needs to be removed, or if the URI is not
* recognized.
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
uri_remove_auth(const char *uri);
diff --git a/src/util/HugeAllocator.cxx b/src/util/HugeAllocator.cxx
new file mode 100644
index 000000000..d1c55c965
--- /dev/null
+++ b/src/util/HugeAllocator.cxx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "HugeAllocator.hxx"
+
+#ifdef __linux__
+#include <sys/mman.h>
+#include <unistd.h>
+#else
+#include <stdlib.h>
+#endif
+
+#ifdef __linux__
+
+/**
+ * Round up the parameter, make it page-aligned.
+ */
+gcc_const
+static size_t
+AlignToPageSize(size_t size)
+{
+ static const long page_size = sysconf(_SC_PAGESIZE);
+ if (page_size > 0)
+ return size;
+
+ size_t ps(page_size);
+ return (size + ps - 1) / ps * ps;
+}
+
+void *
+HugeAllocate(size_t size)
+{
+ size = AlignToPageSize(size);
+
+ constexpr int flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE;
+ void *p = mmap(nullptr, size,
+ PROT_READ|PROT_WRITE, flags,
+ -1, 0);
+ if (p == (void *)-1)
+ return nullptr;
+
+#ifdef MADV_HUGEPAGE
+ /* allow the Linux kernel to use "Huge Pages", which reduces page
+ table overhead for this big chunk of data */
+ madvise(p, size, MADV_HUGEPAGE);
+#endif
+
+#ifdef MADV_DONTFORK
+ /* just in case MPD needs to fork, don't copy this allocation
+ to the child process, to reduce overhead */
+ madvise(p, size, MADV_DONTFORK);
+#endif
+
+ return p;
+}
+
+void
+HugeFree(void *p, size_t size)
+{
+ munmap(p, AlignToPageSize(size));
+}
+
+void
+HugeDiscard(void *p, size_t size)
+{
+#ifdef MADV_DONTNEED
+ madvise(p, AlignToPageSize(size), MADV_DONTNEED);
+#endif
+}
+
+#endif
diff --git a/src/util/HugeAllocator.hxx b/src/util/HugeAllocator.hxx
new file mode 100644
index 000000000..01c92cd43
--- /dev/null
+++ b/src/util/HugeAllocator.hxx
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_HUGE_ALLOCATOR_HXX
+#define MPD_HUGE_ALLOCATOR_HXX
+
+#include "gcc.h"
+
+#include <stddef.h>
+
+#ifdef __linux__
+
+/**
+ * Allocate a huge amount of memory. This will be done in a way that
+ * allows giving the memory back to the kernel as soon as we don't
+ * need it anymore. On the downside, this call is expensive.
+ */
+gcc_malloc
+void *
+HugeAllocate(size_t size);
+
+/**
+ * @param p an allocation returned by HugeAllocate()
+ * @param size the allocation's size as passed to HugeAllocate()
+ */
+void
+HugeFree(void *p, size_t size);
+
+/**
+ * Discard any data stored in the allocation and give the memory back
+ * to the kernel. After returning, the allocation still exists and
+ * can be reused at any time, but its contents are undefined.
+ *
+ * @param p an allocation returned by HugeAllocate()
+ * @param size the allocation's size as passed to HugeAllocate()
+ */
+void
+HugeDiscard(void *p, size_t size);
+
+#else
+
+/* not Linux: fall back to standard C calls */
+
+#include <stdlib.h>
+
+gcc_malloc
+static inline void *
+HugeAllocate(size_t size)
+{
+ return malloc(size);
+}
+
+static inline void
+HugeFree(void *p, size_t)
+{
+ free(p);
+}
+
+static inline void
+HugeDiscard(void *, size_t)
+{
+}
+
+#endif
+
+#endif
diff --git a/src/util/SliceBuffer.hxx b/src/util/SliceBuffer.hxx
new file mode 100644
index 000000000..c61f164f4
--- /dev/null
+++ b/src/util/SliceBuffer.hxx
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SLICE_BUFFER_HXX
+#define MPD_SLICE_BUFFER_HXX
+
+#include "HugeAllocator.hxx"
+#include "gcc.h"
+
+#include <utility>
+#include <new>
+
+#include <assert.h>
+#include <stddef.h>
+
+/**
+ * This class pre-allocates a certain number of objects, and allows
+ * callers to allocate and free these objects ("slices").
+ */
+template<typename T>
+class SliceBuffer {
+ union Slice {
+ Slice *next;
+
+ T value;
+ };
+
+ /**
+ * The maximum number of slices in this container.
+ */
+ const unsigned n_max;
+
+ /**
+ * The number of slices that are initialized. This is used to
+ * avoid page faulting on the new allocation, so the kernel
+ * does not need to reserve physical memory pages.
+ */
+ unsigned n_initialized;
+
+ /**
+ * The number of slices currently allocated.
+ */
+ unsigned n_allocated;
+
+ Slice *const data;
+
+ /**
+ * Pointer to the first free element in the chain.
+ */
+ Slice *available;
+
+ size_t CalcAllocationSize() const {
+ return n_max * sizeof(Slice);
+ }
+
+public:
+ SliceBuffer(unsigned _count)
+ :n_max(_count), n_initialized(0), n_allocated(0),
+ data((Slice *)HugeAllocate(CalcAllocationSize())),
+ available(nullptr) {
+ assert(n_max > 0);
+ }
+
+ ~SliceBuffer() {
+ /* all slices must be freed explicitly, and this
+ assertion checks for leaks */
+ assert(n_allocated == 0);
+
+ HugeFree(data, CalcAllocationSize());
+ }
+
+ SliceBuffer(const SliceBuffer &other) = delete;
+ SliceBuffer &operator=(const SliceBuffer &other) = delete;
+
+ /**
+ * @return true if buffer allocation (by the constructor) has failed
+ */
+ bool IsOOM() {
+ return data == nullptr;
+ }
+
+ unsigned GetCapacity() const {
+ return n_max;
+ }
+
+ bool IsEmpty() const {
+ return n_allocated == 0;
+ }
+
+ bool IsFull() const {
+ return n_allocated == n_max;
+ }
+
+ template<typename... Args>
+ T *Allocate(Args&&... args) {
+ assert(n_initialized <= n_max);
+ assert(n_allocated <= n_initialized);
+
+ if (available == nullptr) {
+ if (n_initialized == n_max) {
+ /* out of (internal) memory, buffer is full */
+ assert(n_allocated == n_max);
+ return nullptr;
+ }
+
+ available = &data[n_initialized++];
+ available->next = nullptr;
+ }
+
+ /* allocate a slice */
+ T *value = &available->value;
+ available = available->next;
+ ++n_allocated;
+
+ /* construct the object */
+ return ::new((void *)value) T(std::forward<Args>(args)...);
+ }
+
+ void Free(T *value) {
+ assert(n_initialized <= n_max);
+ assert(n_allocated > 0);
+ assert(n_allocated <= n_initialized);
+
+ Slice *slice = reinterpret_cast<Slice *>(value);
+ assert(slice >= data && slice < data + n_max);
+
+ /* destruct the object */
+ value->~T();
+
+ /* insert the slice in the "available" linked list */
+ slice->next = available;
+ available = slice;
+ --n_allocated;
+
+ /* give memory back to the kernel when the last slice
+ was freed */
+ if (n_allocated == 0) {
+ HugeDiscard(data, CalcAllocationSize());
+ n_initialized = 0;
+ available = nullptr;
+ }
+ }
+};
+
+#endif
diff --git a/src/util/bit_reverse.h b/src/util/bit_reverse.h
index e44693b1d..54cb789bb 100644
--- a/src/util/bit_reverse.h
+++ b/src/util/bit_reverse.h
@@ -20,12 +20,13 @@
#ifndef MPD_BIT_REVERSE_H
#define MPD_BIT_REVERSE_H
-#include <glib.h>
+#include "gcc.h"
+
#include <stdint.h>
extern const uint8_t bit_reverse_table[256];
-G_GNUC_CONST
+gcc_const
static inline uint8_t
bit_reverse(uint8_t x)
{
diff --git a/src/util/list.h b/src/util/list.h
index fdab47675..28c70954f 100644
--- a/src/util/list.h
+++ b/src/util/list.h
@@ -47,8 +47,8 @@
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
-#define LIST_POISON1 ((void *) 0x00100100)
-#define LIST_POISON2 ((void *) 0x00200200)
+#define LIST_POISON1 ((struct list_head *)(void *) 0x00100100)
+#define LIST_POISON2 ((struct list_head *)(void *) 0x00200200)
/*
* Simple doubly linked list implementation.
@@ -82,46 +82,47 @@ static inline void INIT_LIST_HEAD(struct list_head *list)
* the prev/next entries already!
*/
#ifndef CONFIG_DEBUG_LIST
-static inline void __list_add(struct list_head *new,
+static inline void __list_add(struct list_head *new_item,
struct list_head *prev,
struct list_head *next)
{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
+ next->prev = new_item;
+ new_item->next = next;
+ new_item->prev = prev;
+ prev->next = new_item;
}
#else
-extern void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next);
+extern void __list_add(struct list_head *new_item,
+ struct list_head *prev,
+ struct list_head *next);
#endif
/**
* list_add - add a new entry
- * @new: new entry to be added
+ * @new_item: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
-static inline void list_add(struct list_head *new, struct list_head *head)
+static inline void list_add(struct list_head *new_item, struct list_head *head)
{
- __list_add(new, head, head->next);
+ __list_add(new_item, head, head->next);
}
/**
* list_add_tail - add a new entry
- * @new: new entry to be added
+ * @new_item: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
+static inline void
+list_add_tail(struct list_head *new_item, struct list_head *head)
{
- __list_add(new, head->prev, head);
+ __list_add(new_item, head->prev, head);
}
/*
@@ -163,23 +164,23 @@ extern void list_del(struct list_head *entry);
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
- * @new : the new element to insert
+ * @new_item : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
- struct list_head *new)
+ struct list_head *new_item)
{
- new->next = old->next;
- new->next->prev = new;
- new->prev = old->prev;
- new->prev->next = new;
+ new_item->next = old->next;
+ new_item->next->prev = new_item;
+ new_item->prev = old->prev;
+ new_item->prev->next = new_item;
}
static inline void list_replace_init(struct list_head *old,
- struct list_head *new)
+ struct list_head *new_item)
{
- list_replace(old, new);
+ list_replace(old, new_item);
INIT_LIST_HEAD(old);
}
diff --git a/src/utils.h b/src/utils.h
index f8d6657f2..ba7307ded 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -20,7 +20,8 @@
#ifndef MPD_UTILS_H
#define MPD_UTILS_H
-#include <glib.h>
+#include "gerror.h"
+
#include <stdbool.h>
#ifndef assert_static
diff --git a/src/zeroconf-avahi.c b/src/zeroconf-avahi.c
index f2cc5359b..1f93ddb72 100644
--- a/src/zeroconf-avahi.c
+++ b/src/zeroconf-avahi.c
@@ -19,7 +19,7 @@
#include "config.h"
#include "zeroconf-internal.h"
-#include "listen.h"
+#include "Listen.hxx"
#include "mpd_error.h"
#include <glib.h>
diff --git a/src/zeroconf.c b/src/zeroconf.c
index 4a399e4a2..a4611b67e 100644
--- a/src/zeroconf.c
+++ b/src/zeroconf.c
@@ -21,7 +21,7 @@
#include "zeroconf.h"
#include "zeroconf-internal.h"
#include "conf.h"
-#include "listen.h"
+#include "Listen.hxx"
#include <glib.h>
diff --git a/test/DumpDatabase.cxx b/test/DumpDatabase.cxx
new file mode 100644
index 000000000..0678d7076
--- /dev/null
+++ b/test/DumpDatabase.cxx
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2003-2012 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 "DatabaseRegistry.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseSelection.hxx"
+#include "Directory.hxx"
+#include "song.h"
+#include "PlaylistVector.hxx"
+
+extern "C" {
+#include "conf.h"
+#include "tag.h"
+}
+
+#include <iostream>
+using std::cout;
+using std::cerr;
+using std::endl;
+
+#include <stdlib.h>
+
+static void
+my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
+ const gchar *message, G_GNUC_UNUSED gpointer user_data)
+{
+ if (log_domain != NULL)
+ g_printerr("%s: %s\n", log_domain, message);
+ else
+ g_printerr("%s\n", message);
+}
+
+static bool
+DumpDirectory(const Directory &directory, GError **)
+{
+ cout << "D " << directory.path << endl;
+ return true;
+}
+
+static bool
+DumpSong(song &song, GError **)
+{
+ cout << "S " << song.parent->path << "/" << song.uri << endl;
+ return true;
+}
+
+static bool
+DumpPlaylist(const PlaylistInfo &playlist,
+ const Directory &directory, GError **)
+{
+ cout << "P " << directory.path << "/" << playlist.name.c_str() << endl;
+ return true;
+}
+
+int
+main(int argc, char **argv)
+{
+ GError *error = nullptr;
+
+ if (argc != 3) {
+ cerr << "Usage: DumpDatabase CONFIG PLUGIN" << endl;
+ return 1;
+ }
+
+ const char *const config_path = argv[1];
+ const char *const plugin_name = argv[2];
+
+ const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
+ if (plugin == NULL) {
+ cerr << "No such database plugin: " << plugin_name << endl;
+ return EXIT_FAILURE;
+ }
+
+ /* initialize GLib */
+
+ g_thread_init(nullptr);
+ g_log_set_default_handler(my_log_func, nullptr);
+
+ /* initialize MPD */
+
+ config_global_init();
+
+ if (!config_read_file(config_path, &error)) {
+ cerr << error->message << endl;
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ tag_lib_init();
+
+ /* do it */
+
+ const struct config_param *path = config_get_param(CONF_DB_FILE);
+ struct config_param *param = config_new_param("database", path->line);
+ if (path != nullptr)
+ config_add_block_param(param, "path", path->value, path->line);
+
+ Database *db = plugin->create(param, &error);
+
+ config_param_free(param);
+
+ if (db == nullptr) {
+ cerr << error->message << endl;
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ if (!db->Open(&error)) {
+ delete db;
+ cerr << error->message << endl;
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ const DatabaseSelection selection("", true);
+
+ if (!db->Visit(selection, DumpDirectory, DumpSong, DumpPlaylist,
+ &error)) {
+ db->Close();
+ delete db;
+ cerr << error->message << endl;
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ db->Close();
+ delete db;
+
+ /* deinitialize everything */
+
+ config_global_finish();
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/FakeSong.cxx b/test/FakeSong.cxx
new file mode 100644
index 000000000..927a07652
--- /dev/null
+++ b/test/FakeSong.cxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2012 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 "song.h"
+#include "directory.h"
+#include "gcc.h"
+
+#include <stdlib.h>
+
+struct directory detached_root;
+
+struct song *
+song_dup_detached(gcc_unused const struct song *src)
+{
+ abort();
+}
diff --git a/test/run_inotify.c b/test/RunInotify.cxx
index 3e7c70dba..2b77130d8 100644
--- a/test/run_inotify.c
+++ b/test/RunInotify.cxx
@@ -18,9 +18,10 @@
*/
#include "config.h"
-#include "inotify_source.h"
+#include "InotifySource.hxx"
+
+#include <glib.h>
-#include <stdbool.h>
#include <sys/inotify.h>
#include <signal.h>
diff --git a/test/TestQueuePriority.cxx b/test/TestQueuePriority.cxx
new file mode 100644
index 000000000..cab56d82f
--- /dev/null
+++ b/test/TestQueuePriority.cxx
@@ -0,0 +1,186 @@
+#include "config.h"
+#include "Queue.hxx"
+#include "song.h"
+#include "Directory.hxx"
+
+Directory detached_root;
+
+Directory::Directory() {}
+Directory::~Directory() {}
+
+struct song *
+song_dup_detached(const struct song *src)
+{
+ return const_cast<song *>(src);
+}
+
+void
+song_free(G_GNUC_UNUSED struct song *song)
+{
+}
+
+G_GNUC_UNUSED
+static void
+dump_order(const struct queue *queue)
+{
+ g_printerr("queue length=%u, order:\n", queue->GetLength());
+ for (unsigned i = 0; i < queue->GetLength(); ++i)
+ g_printerr(" [%u] -> %u (prio=%u)\n", i, queue->order[i],
+ queue->items[queue->order[i]].priority);
+}
+
+static void
+check_descending_priority(const struct queue *queue,
+ unsigned start_order)
+{
+ assert(start_order < queue->GetLength());
+
+ uint8_t last_priority = 0xff;
+ for (unsigned order = start_order; order < queue->GetLength(); ++order) {
+ unsigned position = queue->OrderToPosition(order);
+ uint8_t priority = queue->items[position].priority;
+ assert(priority <= last_priority);
+ (void)last_priority;
+ last_priority = priority;
+ }
+}
+
+int
+main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
+{
+ static struct song songs[16];
+
+ struct queue queue(32);
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(songs); ++i)
+ queue.Append(&songs[i], 0);
+
+ assert(queue.GetLength() == G_N_ELEMENTS(songs));
+
+ /* priority=10 for 4 items */
+
+ queue.SetPriorityRange(4, 8, 10, -1);
+
+ queue.random = true;
+ queue.ShuffleOrder();
+ check_descending_priority(&queue, 0);
+
+ for (unsigned i = 0; i < 4; ++i) {
+ assert(queue.PositionToOrder(i) >= 4);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue.PositionToOrder(i) < 4);
+ }
+
+ for (unsigned i = 8; i < G_N_ELEMENTS(songs); ++i) {
+ assert(queue.PositionToOrder(i) >= 4);
+ }
+
+ /* priority=50 one more item */
+
+ queue.SetPriorityRange(15, 16, 50, -1);
+ check_descending_priority(&queue, 0);
+
+ assert(queue.PositionToOrder(15) == 0);
+
+ for (unsigned i = 0; i < 4; ++i) {
+ assert(queue.PositionToOrder(i) >= 4);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue.PositionToOrder(i) >= 1 &&
+ queue.PositionToOrder(i) < 5);
+ }
+
+ for (unsigned i = 8; i < 15; ++i) {
+ assert(queue.PositionToOrder(i) >= 5);
+ }
+
+ /* priority=20 for one of the 4 priority=10 items */
+
+ queue.SetPriorityRange(3, 4, 20, -1);
+ check_descending_priority(&queue, 0);
+
+ assert(queue.PositionToOrder(3) == 1);
+ assert(queue.PositionToOrder(15) == 0);
+
+ for (unsigned i = 0; i < 3; ++i) {
+ assert(queue.PositionToOrder(i) >= 5);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue.PositionToOrder(i) >= 2 &&
+ queue.PositionToOrder(i) < 6);
+ }
+
+ for (unsigned i = 8; i < 15; ++i) {
+ assert(queue.PositionToOrder(i) >= 6);
+ }
+
+ /* priority=20 for another one of the 4 priority=10 items;
+ pass "after_order" (with priority=10) and see if it's moved
+ after that one */
+
+ unsigned current_order = 4;
+ unsigned current_position =
+ queue.OrderToPosition(current_order);
+
+ unsigned a_order = 3;
+ unsigned a_position = queue.OrderToPosition(a_order);
+ assert(queue.items[a_position].priority == 10);
+ queue.SetPriority(a_position, 20, current_order);
+
+ current_order = queue.PositionToOrder(current_position);
+ assert(current_order == 3);
+
+ a_order = queue.PositionToOrder(a_position);
+ assert(a_order == 4);
+
+ check_descending_priority(&queue, current_order + 1);
+
+ /* priority=70 for one of the last items; must be inserted
+ right after the current song, before the priority=20 one we
+ just created */
+
+ unsigned b_order = 10;
+ unsigned b_position = queue.OrderToPosition(b_order);
+ assert(queue.items[b_position].priority == 0);
+ queue.SetPriority(b_position, 70, current_order);
+
+ current_order = queue.PositionToOrder(current_position);
+ assert(current_order == 3);
+
+ b_order = queue.PositionToOrder(b_position);
+ assert(b_order == 4);
+
+ check_descending_priority(&queue, current_order + 1);
+
+ /* priority=60 for the old prio50 item; must not be moved,
+ because it's before the current song, and it's status
+ hasn't changed (it was already higher before) */
+
+ unsigned c_order = 0;
+ unsigned c_position = queue.OrderToPosition(c_order);
+ assert(queue.items[c_position].priority == 50);
+ queue.SetPriority(c_position, 60, current_order);
+
+ current_order = queue.PositionToOrder(current_position);
+ assert(current_order == 3);
+
+ c_order = queue.PositionToOrder(c_position);
+ assert(c_order == 0);
+
+ /* move the prio=20 item back */
+
+ a_order = queue.PositionToOrder(a_position);
+ assert(a_order == 5);
+ assert(queue.items[a_position].priority == 20);
+ queue.SetPriority(a_position, 5, current_order);
+
+ current_order = queue.PositionToOrder(current_position);
+ assert(current_order == 3);
+
+ a_order = queue.PositionToOrder(a_position);
+ assert(a_order == 6);
+}
diff --git a/test/dump_playlist.c b/test/dump_playlist.cxx
index 84ac69045..7d4aa96e6 100644
--- a/test/dump_playlist.c
+++ b/test/dump_playlist.cxx
@@ -18,23 +18,29 @@
*/
#include "config.h"
+#include "TagSave.hxx"
+#include "song.h"
+#include "Directory.hxx"
+
+extern "C" {
#include "io_thread.h"
#include "input_init.h"
#include "input_stream.h"
-#include "tag_pool.h"
-#include "tag_save.h"
#include "conf.h"
-#include "song.h"
#include "decoder_api.h"
#include "decoder_list.h"
#include "playlist_list.h"
#include "playlist_plugin.h"
+}
#include <glib.h>
#include <unistd.h>
#include <stdlib.h>
+Directory::Directory() {}
+Directory::~Directory() {}
+
static void
my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
const gchar *message, G_GNUC_UNUSED gpointer user_data)
@@ -107,7 +113,7 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder,
return DECODE_COMMAND_NONE;
}
-float
+void
decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
const struct replay_gain_info *replay_gain_info)
{
@@ -121,13 +127,10 @@ decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
if (replay_gain_tuple_defined(tuple))
g_printerr("replay_gain[track]: gain=%f peak=%f\n",
tuple->gain, tuple->peak);
-
- return 0.0;
}
void
decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder,
- G_GNUC_UNUSED float replay_gain_db,
char *mixramp_start, char *mixramp_end)
{
g_free(mixramp_start);
@@ -157,7 +160,6 @@ int main(int argc, char **argv)
/* initialize MPD */
- tag_pool_init();
config_global_init();
success = config_read_file(argv[1], &error);
if (!success) {
@@ -249,7 +251,6 @@ int main(int argc, char **argv)
input_stream_global_finish();
io_thread_deinit();
config_global_finish();
- tag_pool_deinit();
return 0;
}
diff --git a/test/dump_text_file.c b/test/dump_text_file.c
index f14371441..f13e648b2 100644
--- a/test/dump_text_file.c
+++ b/test/dump_text_file.c
@@ -22,7 +22,6 @@
#include "input_init.h"
#include "input_stream.h"
#include "text_input_stream.h"
-#include "tag_pool.h"
#include "conf.h"
#include "stdbin.h"
@@ -112,7 +111,6 @@ int main(int argc, char **argv)
/* initialize MPD */
- tag_pool_init();
config_global_init();
io_thread_init();
@@ -164,7 +162,6 @@ int main(int argc, char **argv)
io_thread_deinit();
config_global_finish();
- tag_pool_deinit();
return ret;
}
diff --git a/test/read_tags.c b/test/read_tags.c
index faf9a45c0..2471610e6 100644
--- a/test/read_tags.c
+++ b/test/read_tags.c
@@ -118,16 +118,14 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder,
return DECODE_COMMAND_NONE;
}
-float
+void
decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
G_GNUC_UNUSED const struct replay_gain_info *replay_gain_info)
{
- return 0.0;
}
void
decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder,
- G_GNUC_UNUSED float replay_gain_db,
char *mixramp_start, char *mixramp_end)
{
g_free(mixramp_start);
@@ -218,6 +216,25 @@ int main(int argc, char **argv)
return 1;
}
+ g_mutex_lock(mutex);
+
+ while (!is->ready) {
+ g_cond_wait(cond, mutex);
+ input_stream_update(is);
+ }
+
+ if (!input_stream_check(is, &error)) {
+ g_mutex_unlock(mutex);
+
+ g_printerr("Failed to read %s: %s\n",
+ path, error->message);
+ g_error_free(error);
+
+ return EXIT_FAILURE;
+ }
+
+ g_mutex_unlock(mutex);
+
success = decoder_plugin_scan_stream(plugin, is,
&print_handler, NULL);
input_stream_close(is);
diff --git a/test/run_decoder.c b/test/run_decoder.c
index e6712c75b..3da5216d5 100644
--- a/test/run_decoder.c
+++ b/test/run_decoder.c
@@ -21,7 +21,6 @@
#include "io_thread.h"
#include "decoder_list.h"
#include "decoder_api.h"
-#include "tag_pool.h"
#include "input_init.h"
#include "input_stream.h"
#include "audio_format.h"
@@ -140,7 +139,7 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder,
return DECODE_COMMAND_NONE;
}
-float
+void
decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
const struct replay_gain_info *replay_gain_info)
{
@@ -154,13 +153,10 @@ decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
if (replay_gain_tuple_defined(tuple))
g_printerr("replay_gain[track]: gain=%f peak=%f\n",
tuple->gain, tuple->peak);
-
- return 0.0;
}
void
decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder,
- G_GNUC_UNUSED float replay_gain_db,
char *mixramp_start, char *mixramp_end)
{
g_free(mixramp_start);
@@ -191,8 +187,6 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- tag_pool_init();
-
if (!input_stream_global_init(&error)) {
g_warning("%s", error->message);
g_error_free(error);
@@ -248,7 +242,5 @@ int main(int argc, char **argv)
return 1;
}
- tag_pool_deinit();
-
return 0;
}
diff --git a/test/run_filter.c b/test/run_filter.c
index 8e793b768..d319a9a22 100644
--- a/test/run_filter.c
+++ b/test/run_filter.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,6 @@
#include "pcm_volume.h"
#include "idle.h"
#include "mixer_control.h"
-#include "playlist.h"
#include "stdbin.h"
#include <glib.h>
@@ -35,8 +34,6 @@
#include <errno.h>
#include <unistd.h>
-struct playlist g_playlist;
-
void
idle_add(G_GNUC_UNUSED unsigned flags)
{
diff --git a/test/run_input.c b/test/run_input.cxx
index 676e4e618..52b53db5a 100644
--- a/test/run_input.c
+++ b/test/run_input.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,20 @@
*/
#include "config.h"
+#include "TagSave.hxx"
+#include "stdbin.h"
+
+extern "C" {
#include "io_thread.h"
#include "input_init.h"
#include "input_stream.h"
-#include "tag_pool.h"
-#include "tag_save.h"
+#include "tag.h"
#include "conf.h"
-#include "stdbin.h"
#ifdef ENABLE_ARCHIVE
#include "archive_list.h"
#endif
+}
#include <glib.h>
@@ -127,7 +130,6 @@ int main(int argc, char **argv)
/* initialize MPD */
- tag_pool_init();
config_global_init();
io_thread_init();
@@ -179,7 +181,6 @@ int main(int argc, char **argv)
io_thread_deinit();
config_global_finish();
- tag_pool_deinit();
return ret;
}
diff --git a/test/run_output.c b/test/run_output.cxx
index bbb1be7d2..04374537e 100644
--- a/test/run_output.c
+++ b/test/run_output.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,18 +18,21 @@
*/
#include "config.h"
-#include "io_thread.h"
+#include "OutputControl.hxx"
+
+extern "C" {
#include "output_plugin.h"
#include "output_internal.h"
-#include "output_control.h"
+#include "io_thread.h"
#include "conf.h"
#include "audio_parser.h"
#include "filter_registry.h"
#include "pcm_convert.h"
#include "event_pipe.h"
#include "idle.h"
-#include "playlist.h"
-#include "player_control.h"
+}
+
+#include "PlayerControl.hxx"
#include "stdbin.h"
#include <glib.h>
@@ -39,8 +42,6 @@
#include <unistd.h>
#include <stdlib.h>
-struct playlist g_playlist;
-
void
idle_add(G_GNUC_UNUSED unsigned flags)
{
@@ -94,6 +95,10 @@ find_named_config_block(const char *block, const char *name)
return NULL;
}
+player_control::player_control(gcc_unused unsigned _buffer_chunks,
+ gcc_unused unsigned _buffered_before_play) {}
+player_control::~player_control() {}
+
static struct audio_output *
load_audio_output(const char *name)
{
@@ -103,10 +108,10 @@ load_audio_output(const char *name)
param = find_named_config_block(CONF_AUDIO_OUTPUT, name);
if (param == NULL) {
g_printerr("No such configured audio output: %s\n", name);
- return false;
+ return nullptr;
}
- static struct player_control dummy_player_control;
+ static struct player_control dummy_player_control(32, 4);
struct audio_output *ao =
audio_output_new(param, &dummy_player_control, &error);
diff --git a/test/test_queue_priority.c b/test/test_queue_priority.c
deleted file mode 100644
index 5543edbba..000000000
--- a/test/test_queue_priority.c
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "queue.h"
-#include "song.h"
-
-void
-song_free(G_GNUC_UNUSED struct song *song)
-{
-}
-
-G_GNUC_UNUSED
-static void
-dump_order(const struct queue *queue)
-{
- g_printerr("queue length=%u, order:\n", queue_length(queue));
- for (unsigned i = 0; i < queue_length(queue); ++i)
- g_printerr(" [%u] -> %u (prio=%u)\n", i, queue->order[i],
- queue->items[queue->order[i]].priority);
-}
-
-static void
-check_descending_priority(G_GNUC_UNUSED const struct queue *queue,
- unsigned start_order)
-{
- assert(start_order < queue_length(queue));
-
- uint8_t last_priority = 0xff;
- for (unsigned order = start_order; order < queue_length(queue); ++order) {
- unsigned position = queue_order_to_position(queue, order);
- uint8_t priority = queue->items[position].priority;
- assert(priority <= last_priority);
- (void)last_priority;
- last_priority = priority;
- }
-}
-
-int
-main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
-{
- struct song songs[16];
-
- struct queue queue;
- queue_init(&queue, 32);
-
- for (unsigned i = 0; i < G_N_ELEMENTS(songs); ++i)
- queue_append(&queue, &songs[i], 0);
-
- assert(queue_length(&queue) == G_N_ELEMENTS(songs));
-
- /* priority=10 for 4 items */
-
- queue_set_priority_range(&queue, 4, 8, 10, -1);
-
- queue.random = true;
- queue_shuffle_order(&queue);
- check_descending_priority(&queue, 0);
-
- for (unsigned i = 0; i < 4; ++i) {
- assert(queue_position_to_order(&queue, i) >= 4);
- }
-
- for (unsigned i = 4; i < 8; ++i) {
- assert(queue_position_to_order(&queue, i) < 4);
- }
-
- for (unsigned i = 8; i < G_N_ELEMENTS(songs); ++i) {
- assert(queue_position_to_order(&queue, i) >= 4);
- }
-
- /* priority=50 one more item */
-
- queue_set_priority_range(&queue, 15, 16, 50, -1);
- check_descending_priority(&queue, 0);
-
- assert(queue_position_to_order(&queue, 15) == 0);
-
- for (unsigned i = 0; i < 4; ++i) {
- assert(queue_position_to_order(&queue, i) >= 4);
- }
-
- for (unsigned i = 4; i < 8; ++i) {
- assert(queue_position_to_order(&queue, i) >= 1 &&
- queue_position_to_order(&queue, i) < 5);
- }
-
- for (unsigned i = 8; i < 15; ++i) {
- assert(queue_position_to_order(&queue, i) >= 5);
- }
-
- /* priority=20 for one of the 4 priority=10 items */
-
- queue_set_priority_range(&queue, 3, 4, 20, -1);
- check_descending_priority(&queue, 0);
-
- assert(queue_position_to_order(&queue, 3) == 1);
- assert(queue_position_to_order(&queue, 15) == 0);
-
- for (unsigned i = 0; i < 3; ++i) {
- assert(queue_position_to_order(&queue, i) >= 5);
- }
-
- for (unsigned i = 4; i < 8; ++i) {
- assert(queue_position_to_order(&queue, i) >= 2 &&
- queue_position_to_order(&queue, i) < 6);
- }
-
- for (unsigned i = 8; i < 15; ++i) {
- assert(queue_position_to_order(&queue, i) >= 6);
- }
-
- /* priority=20 for another one of the 4 priority=10 items;
- pass "after_order" (with priority=10) and see if it's moved
- after that one */
-
- unsigned current_order = 4;
- unsigned current_position =
- queue_order_to_position(&queue, current_order);
-
- unsigned a_order = 3;
- unsigned a_position = queue_order_to_position(&queue, a_order);
- assert(queue.items[a_position].priority == 10);
- queue_set_priority(&queue, a_position, 20, current_order);
-
- current_order = queue_position_to_order(&queue, current_position);
- assert(current_order == 3);
-
- a_order = queue_position_to_order(&queue, a_position);
- assert(a_order == 4);
-
- check_descending_priority(&queue, current_order + 1);
-
- /* priority=70 for one of the last items; must be inserted
- right after the current song, before the priority=20 one we
- just created */
-
- unsigned b_order = 10;
- unsigned b_position = queue_order_to_position(&queue, b_order);
- assert(queue.items[b_position].priority == 0);
- queue_set_priority(&queue, b_position, 70, current_order);
-
- current_order = queue_position_to_order(&queue, current_position);
- assert(current_order == 3);
-
- b_order = queue_position_to_order(&queue, b_position);
- assert(b_order == 4);
-
- check_descending_priority(&queue, current_order + 1);
-
- /* priority=60 for the old prio50 item; must not be moved,
- because it's before the current song, and it's status
- hasn't changed (it was already higher before) */
-
- unsigned c_order = 0;
- unsigned c_position = queue_order_to_position(&queue, c_order);
- assert(queue.items[c_position].priority == 50);
- queue_set_priority(&queue, c_position, 60, current_order);
-
- current_order = queue_position_to_order(&queue, current_position);
- assert(current_order == 3);
-
- c_order = queue_position_to_order(&queue, c_position);
- assert(c_order == 0);
-
- /* move the prio=20 item back */
-
- a_order = queue_position_to_order(&queue, a_position);
- assert(a_order == 5);
- assert(queue.items[a_position].priority == 20);
- queue_set_priority(&queue, a_position, 5, current_order);
-
-
- current_order = queue_position_to_order(&queue, current_position);
- assert(current_order == 3);
-
- a_order = queue_position_to_order(&queue, a_position);
- assert(a_order == 6);
-}
diff --git a/valgrind.suppressions b/valgrind.suppressions
index 2cce34186..04651b7ad 100644
--- a/valgrind.suppressions
+++ b/valgrind.suppressions
@@ -309,6 +309,29 @@
}
{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:malloc
+ fun:strdup
+ ...
+ fun:ao_initialize
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:calloc
+ fun:ao_initialize
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Addr4
+ ...
+ fun:WildMidi_Init
+}
+
+{
g_quark_from_string
Memcheck:Leak
fun:*alloc