aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AllCommands.cxx387
-rw-r--r--src/AllCommands.hxx34
-rw-r--r--src/Client.cxx (renamed from src/client.c)15
-rw-r--r--src/Client.hxx (renamed from src/client.h)39
-rw-r--r--src/ClientEvent.cxx (renamed from src/song_print.h)25
-rw-r--r--src/ClientExpire.cxx (renamed from src/client_expire.c)32
-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)24
-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)131
-rw-r--r--src/ClientList.cxx (renamed from src/client_list.c)33
-rw-r--r--src/ClientList.hxx43
-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)103
-rw-r--r--src/ClientProcess.cxx (renamed from src/client_process.c)53
-rw-r--r--src/ClientRead.cxx66
-rw-r--r--src/ClientSubscribe.cxx93
-rw-r--r--src/ClientSubscribe.hxx (renamed from src/client_subscribe.h)26
-rw-r--r--src/ClientWrite.cxx87
-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.cxx43
-rw-r--r--src/CommandListBuilder.hxx109
-rw-r--r--src/ConfigFile.cxx (renamed from src/conf.c)135
-rw-r--r--src/CrossFade.cxx (renamed from src/crossfade.c)16
-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)128
-rw-r--r--src/DatabaseGlue.hxx (renamed from src/notify.h)48
-rw-r--r--src/DatabaseHelpers.cxx134
-rw-r--r--src/DatabaseHelpers.hxx41
-rw-r--r--src/DatabaseLock.cxx (renamed from src/db_lock.c)6
-rw-r--r--src/DatabaseLock.hxx (renamed from src/db_lock.h)29
-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.cxx54
-rw-r--r--src/DatabaseQueue.hxx (renamed from src/db_internal.h)19
-rw-r--r--src/DatabaseRegistry.cxx43
-rw-r--r--src/DatabaseRegistry.hxx37
-rw-r--r--src/DatabaseSave.cxx (renamed from src/db_save.c)34
-rw-r--r--src/DatabaseSave.hxx36
-rw-r--r--src/DatabaseSelection.cxx (renamed from src/decoder_print.h)15
-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.hxx38
-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)44
-rw-r--r--src/DecoderControl.hxx (renamed from src/decoder_control.h)102
-rw-r--r--src/DecoderInternal.cxx (renamed from src/decoder_internal.c)32
-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)82
-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.hxx (renamed from src/directory_save.h)15
-rw-r--r--src/ExcludeList.cxx (renamed from src/exclude.c)41
-rw-r--r--src/ExcludeList.hxx78
-rw-r--r--src/GlobalEvents.cxx114
-rw-r--r--src/GlobalEvents.hxx71
-rw-r--r--src/IOThread.cxx (renamed from src/io_thread.c)104
-rw-r--r--src/IOThread.hxx (renamed from src/io_thread.h)31
-rw-r--r--src/IcyMetaDataParser.cxx (renamed from src/icy_metadata.c)93
-rw-r--r--src/IcyMetaDataParser.hxx83
-rw-r--r--src/IdTable.hxx91
-rw-r--r--src/Idle.cxx (renamed from src/idle.c)8
-rw-r--r--src/Idle.hxx (renamed from src/idle.h)6
-rw-r--r--src/InotifyQueue.cxx (renamed from src/inotify_queue.c)74
-rw-r--r--src/InotifyQueue.hxx41
-rw-r--r--src/InotifySource.cxx (renamed from src/inotify_source.c)106
-rw-r--r--src/InotifySource.hxx74
-rw-r--r--src/InotifyUpdate.cxx (renamed from src/inotify_update.c)56
-rw-r--r--src/InotifyUpdate.hxx (renamed from src/inotify_update.h)6
-rw-r--r--src/InputInit.cxx (renamed from src/input_init.c)6
-rw-r--r--src/InputInit.hxx (renamed from src/input_init.h)11
-rw-r--r--src/InputRegistry.cxx (renamed from src/input_registry.c)8
-rw-r--r--src/InputRegistry.hxx (renamed from src/input_registry.h)8
-rw-r--r--src/InputStream.cxx (renamed from src/input_stream.c)14
-rw-r--r--src/Listen.cxx (renamed from src/listen.c)17
-rw-r--r--src/Listen.hxx (renamed from src/listen.h)8
-rw-r--r--src/Log.cxx (renamed from src/log.c)5
-rw-r--r--src/Log.hxx (renamed from src/log.h)6
-rw-r--r--src/Main.cxx (renamed from src/main.c)176
-rw-r--r--src/Main.hxx (renamed from src/main.h)16
-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.cxx167
-rw-r--r--src/MessageCommands.hxx42
-rw-r--r--src/MixerAll.cxx (renamed from src/mixer_all.c)13
-rw-r--r--src/MixerAll.hxx (renamed from src/mixer_all.h)8
-rw-r--r--src/MusicBuffer.cxx79
-rw-r--r--src/MusicBuffer.hxx (renamed from src/buffer.h)6
-rw-r--r--src/MusicChunk.cxx84
-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.cxx308
-rw-r--r--src/OtherCommands.hxx69
-rw-r--r--src/OutputAll.cxx (renamed from src/output_all.c)58
-rw-r--r--src/OutputAll.hxx (renamed from src/output_all.h)11
-rw-r--r--src/OutputCommand.cxx (renamed from src/output_command.c)11
-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)23
-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)19
-rw-r--r--src/OutputList.cxx (renamed from src/output_list.c)6
-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)12
-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)6
-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)48
-rw-r--r--src/OutputThread.hxx (renamed from src/output_thread.h)6
-rw-r--r--src/Partition.hxx165
-rw-r--r--src/Permission.cxx (renamed from src/permission.c)34
-rw-r--r--src/Permission.hxx (renamed from src/permission.h)8
-rw-r--r--src/PlayerCommands.cxx394
-rw-r--r--src/PlayerCommands.hxx90
-rw-r--r--src/PlayerControl.cxx (renamed from src/player_control.c)170
-rw-r--r--src/PlayerControl.hxx (renamed from src/player_control.h)124
-rw-r--r--src/PlayerThread.cxx (renamed from src/player_thread.c)150
-rw-r--r--src/PlayerThread.hxx (renamed from src/player_thread.h)6
-rw-r--r--src/Playlist.cxx346
-rw-r--r--src/Playlist.hxx256
-rw-r--r--src/PlaylistAny.cxx (renamed from src/playlist_any.c)11
-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.cxx265
-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.cxx423
-rw-r--r--src/PlaylistFile.cxx (renamed from src/stored_playlist.c)256
-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)31
-rw-r--r--src/PlaylistGlobal.hxx26
-rw-r--r--src/PlaylistInfo.hxx63
-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)116
-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)23
-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)49
-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)33
-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)74
-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.cxx499
-rw-r--r--src/Queue.hxx368
-rw-r--r--src/QueueCommands.cxx375
-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)21
-rw-r--r--src/ReplayGainInfo.cxx (renamed from src/replay_gain_info.c)2
-rw-r--r--src/ServerSocket.cxx (renamed from src/server_socket.c)323
-rw-r--r--src/ServerSocket.hxx (renamed from src/server_socket.h)14
-rw-r--r--src/SignalHandlers.cxx (renamed from src/sig_handlers.c)15
-rw-r--r--src/SignalHandlers.hxx25
-rw-r--r--src/SocketError.hxx156
-rw-r--r--src/SocketUtil.cxx (renamed from src/socket_util.c)31
-rw-r--r--src/SocketUtil.hxx (renamed from src/socket_util.h)10
-rw-r--r--src/Song.cxx (renamed from src/song.c)99
-rw-r--r--src/SongFilter.cxx167
-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)21
-rw-r--r--src/StateFile.cxx122
-rw-r--r--src/StateFile.hxx53
-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)95
-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/Tag.cxx (renamed from src/tag.c)135
-rw-r--r--src/TagInternal.hxx (renamed from src/tag_internal.h)8
-rw-r--r--src/TagNames.c44
-rw-r--r--src/TagPool.cxx (renamed from src/tag_pool.c)30
-rw-r--r--src/TagPool.hxx (renamed from src/tag_pool.h)13
-rw-r--r--src/TagPrint.cxx (renamed from src/tag_print.c)12
-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)6
-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)36
-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)38
-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)20
-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)111
-rw-r--r--src/UpdateWalk.hxx (renamed from src/update_walk.h)8
-rw-r--r--src/Volume.cxx (renamed from src/volume.c)21
-rw-r--r--src/Volume.hxx (renamed from src/volume.h)7
-rw-r--r--src/Win32Main.cxx (renamed from src/main_win32.c)35
-rw-r--r--src/audio_check.h2
-rw-r--r--src/audio_config.c1
-rw-r--r--src/audio_format.h27
-rw-r--r--src/audio_parser.h2
-rw-r--r--src/buffer.c137
-rw-r--r--src/chunk.c102
-rw-r--r--src/client_event.c108
-rw-r--r--src/client_message.c96
-rw-r--r--src/client_message.h72
-rw-r--r--src/client_read.c113
-rw-r--r--src/client_subscribe.c123
-rw-r--r--src/client_write.c284
-rw-r--r--src/clock.h6
-rw-r--r--src/command.c2298
-rw-r--r--src/command.h49
-rw-r--r--src/conf.h4
-rw-r--r--src/cue/cue_parser.c2
-rw-r--r--src/db/ProxyDatabasePlugin.cxx475
-rw-r--r--src/db/ProxyDatabasePlugin.hxx27
-rw-r--r--src/db/SimpleDatabasePlugin.cxx344
-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)291
-rw-r--r--src/decoder/FLACDecoderPlugin.h26
-rw-r--r--src/decoder/FLACIOHandle.cxx114
-rw-r--r--src/decoder/FLACIOHandle.hxx45
-rw-r--r--src/decoder/FLACInput.cxx149
-rw-r--r--src/decoder/FLACInput.hxx72
-rw-r--r--src/decoder/FLACMetaData.cxx (renamed from src/decoder/flac_metadata.c)136
-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/OggCodec.cxx (renamed from src/decoder/_ogg_common.c)20
-rw-r--r--src/decoder/OggCodec.hxx (renamed from src/decoder/_ogg_common.h)16
-rw-r--r--src/decoder/OggFind.cxx37
-rw-r--r--src/decoder/OggFind.hxx38
-rw-r--r--src/decoder/OggSyncState.hxx78
-rw-r--r--src/decoder/OggUtil.cxx118
-rw-r--r--src/decoder/OggUtil.hxx87
-rw-r--r--src/decoder/OpusDecoderPlugin.cxx396
-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.hxx98
-rw-r--r--src/decoder/OpusTags.cxx77
-rw-r--r--src/decoder/OpusTags.hxx31
-rw-r--r--src/decoder/VorbisComments.cxx (renamed from src/decoder/vorbis_comments.c)16
-rw-r--r--src/decoder/VorbisComments.hxx (renamed from src/decoder/vorbis_comments.h)6
-rw-r--r--src/decoder/VorbisDecoderPlugin.cxx (renamed from src/decoder/vorbis_decoder_plugin.c)156
-rw-r--r--src/decoder/VorbisDecoderPlugin.h25
-rw-r--r--src/decoder/WavpackDecoderPlugin.cxx (renamed from src/decoder/wavpack_decoder_plugin.c)59
-rw-r--r--src/decoder/WavpackDecoderPlugin.hxx25
-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/sidplay_decoder_plugin.cxx10
-rw-r--r--src/decoder_api.h14
-rw-r--r--src/decoder_error.h35
-rw-r--r--src/decoder_list.c16
-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.cxx433
-rw-r--r--src/encoder/OpusEncoderPlugin.hxx25
-rw-r--r--src/encoder/VorbisEncoderPlugin.cxx (renamed from src/encoder/vorbis_encoder.c)134
-rw-r--r--src/encoder/VorbisEncoderPlugin.hxx25
-rw-r--r--src/encoder/flac_encoder.c35
-rw-r--r--src/encoder/null_encoder.c4
-rw-r--r--src/encoder/wave_encoder.c4
-rw-r--r--src/encoder_list.c8
-rw-r--r--src/encoder_list.h8
-rw-r--r--src/encoder_plugin.h2
-rw-r--r--src/event/BufferedSocket.cxx256
-rw-r--r--src/event/BufferedSocket.hxx125
-rw-r--r--src/event/Loop.hxx84
-rw-r--r--src/event/MultiSocketMonitor.cxx107
-rw-r--r--src/event/MultiSocketMonitor.hxx124
-rw-r--r--src/event/SocketMonitor.cxx121
-rw-r--r--src/event/SocketMonitor.hxx123
-rw-r--r--src/event/TimeoutMonitor.cxx65
-rw-r--r--src/event/TimeoutMonitor.hxx60
-rw-r--r--src/event/WakeFD.cxx225
-rw-r--r--src/event/WakeFD.hxx80
-rw-r--r--src/event_pipe.c164
-rw-r--r--src/event_pipe.h71
-rw-r--r--src/fd_util.c14
-rw-r--r--src/fd_util.h19
-rw-r--r--src/filter/ReplayGainFilterPlugin.cxx (renamed from src/filter/replay_gain_filter_plugin.c)54
-rw-r--r--src/filter/ReplayGainFilterPlugin.hxx (renamed from src/filter/replay_gain_filter_plugin.h)9
-rw-r--r--src/filter/null_filter_plugin.c1
-rw-r--r--src/filter_plugin.h2
-rw-r--r--src/gcc.h33
-rw-r--r--src/gerror.h (renamed from src/sig_handlers.h)6
-rw-r--r--src/icy_metadata.h99
-rw-r--r--src/icy_server.h8
-rw-r--r--src/inotify_source.h61
-rw-r--r--src/input/CurlInputPlugin.cxx (renamed from src/input/curl_input_plugin.c)556
-rw-r--r--src/input/CurlInputPlugin.hxx (renamed from src/input/curl_input_plugin.h)6
-rw-r--r--src/input/SoupInputPlugin.cxx (renamed from src/input/soup_input_plugin.c)58
-rw-r--r--src/input/SoupInputPlugin.hxx (renamed from src/input/soup_input_plugin.h)6
-rw-r--r--src/input/file_input_plugin.c17
-rw-r--r--src/input_stream.h15
-rw-r--r--src/io_error.h51
-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/AlsaMixerPlugin.cxx (renamed from src/mixer/alsa_mixer_plugin.c)189
-rw-r--r--src/mixer/PulseMixerPlugin.cxx (renamed from src/mixer/pulse_mixer_plugin.c)30
-rw-r--r--src/mixer/PulseMixerPlugin.h (renamed from src/mixer/pulse_mixer_plugin.h)10
-rw-r--r--src/mixer/winmm_mixer_plugin.c2
-rw-r--r--src/mixer_api.h8
-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/notify.c58
-rw-r--r--src/notify.cxx (renamed from src/state_file.h)32
-rw-r--r--src/notify.hxx53
-rw-r--r--src/output/HttpdClient.cxx592
-rw-r--r--src/output/HttpdClient.hxx215
-rw-r--r--src/output/HttpdInternal.hxx (renamed from src/output/httpd_internal.h)14
-rw-r--r--src/output/HttpdOutputPlugin.cxx (renamed from src/output/httpd_output_plugin.c)241
-rw-r--r--src/output/HttpdOutputPlugin.hxx (renamed from src/output/httpd_output_plugin.h)6
-rw-r--r--src/output/fifo_output_plugin.c1
-rw-r--r--src/output/httpd_client.c764
-rw-r--r--src/output/httpd_client.h71
-rw-r--r--src/output/osx_output_plugin.c2
-rw-r--r--src/output/pulse_output_plugin.c2
-rw-r--r--src/output/pulse_output_plugin.h12
-rw-r--r--src/output/shout_output_plugin.c104
-rw-r--r--src/output/winmm_output_plugin.c1
-rw-r--r--src/output/winmm_output_plugin.h1
-rw-r--r--src/output_internal.h15
-rw-r--r--src/output_plugin.h7
-rw-r--r--src/page.h8
-rw-r--r--src/path.c23
-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.c452
-rw-r--r--src/playlist.h256
-rw-r--r--src/playlist_control.c288
-rw-r--r--src/playlist_edit.c487
-rw-r--r--src/playlist_internal.h56
-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.c603
-rw-r--r--src/queue.h379
-rw-r--r--src/queue_print.c122
-rw-r--r--src/replay_gain_config.h10
-rw-r--r--src/replay_gain_info.h16
-rw-r--r--src/resolver.h14
-rw-r--r--src/song.h73
-rw-r--r--src/state_file.c162
-rw-r--r--src/stats.c128
-rw-r--r--src/stats.h5
-rw-r--r--src/string_util.c21
-rw-r--r--src/string_util.h38
-rw-r--r--src/strset.c148
-rw-r--r--src/strset.h50
-rw-r--r--src/tag.h10
-rw-r--r--src/tag_id3.c5
-rw-r--r--src/tag_id3.h18
-rw-r--r--src/text_file.h39
-rw-r--r--src/text_input_stream.c2
-rw-r--r--src/thread/Cond.hxx37
-rw-r--r--src/thread/CriticalSection.hxx68
-rw-r--r--src/thread/GLibCond.hxx88
-rw-r--r--src/thread/GLibMutex.hxx90
-rw-r--r--src/thread/Mutex.hxx54
-rw-r--r--src/thread/PosixCond.hxx60
-rw-r--r--src/thread/PosixMutex.hxx62
-rw-r--r--src/thread/WindowsCond.hxx63
-rw-r--r--src/timer.h8
-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/LazyRandomEngine.cxx (renamed from src/inotify_queue.h)21
-rw-r--r--src/util/LazyRandomEngine.hxx67
-rw-r--r--src/util/PeakBuffer.cxx143
-rw-r--r--src/util/PeakBuffer.hxx66
-rw-r--r--src/util/SliceBuffer.hxx161
-rw-r--r--src/util/bit_reverse.h5
-rw-r--r--src/util/fifo_buffer.c (renamed from src/fifo_buffer.c)8
-rw-r--r--src/util/fifo_buffer.h (renamed from src/fifo_buffer.h)11
-rw-r--r--src/util/growing_fifo.c (renamed from src/growing_fifo.c)0
-rw-r--r--src/util/growing_fifo.h (renamed from src/growing_fifo.h)0
-rw-r--r--src/util/list.h53
-rw-r--r--src/utils.h12
-rw-r--r--src/zeroconf-avahi.c9
-rw-r--r--src/zeroconf-bonjour.c2
-rw-r--r--src/zeroconf.c2
456 files changed, 20921 insertions, 14938 deletions
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/AllCommands.hxx b/src/AllCommands.hxx
new file mode 100644
index 000000000..a55eb5a3b
--- /dev/null
+++ b/src/AllCommands.hxx
@@ -0,0 +1,34 @@
+/*
+ * 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_ALL_COMMANDS_HXX
+#define MPD_ALL_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+void command_init(void);
+
+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..be121dfe8 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,19 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
-bool client_is_expired(const struct 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..bf2a2521f 100644
--- a/src/client.h
+++ b/src/Client.hxx
@@ -20,60 +20,61 @@
#ifndef MPD_CLIENT_H
#define MPD_CLIENT_H
-#include <glib.h>
-#include <stdbool.h>
+#include "gcc.h"
+
#include <stddef.h>
#include <stdarg.h>
-struct client;
struct sockaddr;
-struct player_control;
+class EventLoop;
+struct Partition;
+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);
-
-G_GNUC_PURE
-bool client_is_expired(const struct client *client);
+void
+client_new(EventLoop &loop, Partition &partition,
+ int fd, const struct sockaddr *sa, size_t sa_length, int uid);
/**
* 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/song_print.h b/src/ClientEvent.cxx
index 8f1f0cc65..905cf0c0a 100644
--- a/src/song_print.h
+++ 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
@@ -17,17 +17,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SONG_PRINT_H
-#define MPD_SONG_PRINT_H
-
-struct client;
-struct song;
-struct songvec;
+#include "config.h"
+#include "ClientInternal.hxx"
void
-song_print_info(struct client *client, struct song *song);
+Client::OnSocketError(GError *error)
+{
+ g_warning("error on client %d: %s", num, error->message);
+ g_error_free(error);
-void
-song_print_uri(struct client *client, struct song *song);
+ SetExpired();
+}
-#endif
+void
+Client::OnSocketClosed()
+{
+ SetExpired();
+}
diff --git a/src/client_expire.c b/src/ClientExpire.cxx
index 1ca32ebcc..56b003df8 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,41 +18,33 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+#include "ClientList.hxx"
static guint expire_source_id;
void
-client_set_expired(struct client *client)
+Client::SetExpired()
{
- if (!client_is_expired(client))
- client_schedule_expire();
+ if (IsExpired())
+ return;
- if (client->source_id != 0) {
- g_source_remove(client->source_id);
- client->source_id = 0;
- }
-
- if (client->channel != NULL) {
- g_io_channel_unref(client->channel);
- client->channel = NULL;
- }
+ client_schedule_expire();
+ BufferedSocket::Close();
}
static void
-client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
+client_check_expired_callback(Client *client, G_GNUC_UNUSED gpointer user_data)
{
- struct client *client = data;
-
- if (client_is_expired(client)) {
+ if (client->IsExpired()) {
g_debug("[%u] expired", client->num);
- client_close(client);
+ client->Close();
} else if (!client->idle_waiting && /* idle clients
never expire */
(int)g_timer_elapsed(client->last_activity, NULL) >
client_timeout) {
g_debug("[%u] timeout", client->num);
- client_close(client);
+ client->Close();
}
}
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..1aa82b435 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,7 +18,8 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+#include "ClientList.hxx"
#include "conf.h"
#include <assert.h>
@@ -55,9 +56,9 @@ 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);
+ client->Close();
}
assert(client_list_is_empty());
diff --git a/src/client_idle.c b/src/ClientIdle.cxx
index 930911d6e..f9818b278 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,10 @@
*/
#include "config.h"
-#include "client_idle.h"
-#include "client_internal.h"
-#include "idle.h"
+#include "ClientIdle.hxx"
+#include "ClientInternal.hxx"
+#include "ClientList.hxx"
+#include "Idle.hxx"
#include <assert.h>
@@ -28,7 +29,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,23 +53,20 @@ 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))
+ if (client->IsExpired())
return;
client->idle_flags |= flags;
if (client->idle_waiting
- && (client->idle_flags & client->idle_subscriptions)) {
+ && (client->idle_flags & client->idle_subscriptions))
client_idle_notify(client);
- client_write_output(client);
- }
}
static void
-client_idle_callback(gpointer data, gpointer user_data)
+client_idle_callback(Client *client, gpointer user_data)
{
- struct client *client = data;
unsigned flags = GPOINTER_TO_UINT(user_data);
client_idle_add(client, flags);
@@ -81,7 +79,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..dee5d9c71 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,22 @@
* 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 "check.h"
+#include "Client.hxx"
+#include "ClientMessage.hxx"
+#include "CommandListBuilder.hxx"
+#include "event/BufferedSocket.hxx"
#include "command.h"
+#include <set>
+#include <string>
+#include <list>
+
+#include <glib.h>
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "client"
@@ -32,20 +41,14 @@ enum {
CLIENT_MAX_MESSAGES = 64,
};
-struct deferred_buffer {
- size_t size;
- char data[sizeof(long)];
-};
+struct Partition;
-struct client {
+class Client final : private BufferedSocket {
+public:
+ Partition &partition;
+ struct playlist &playlist;
struct player_control *player_control;
- GIOChannel *channel;
- guint source_id;
-
- /** the buffer for reading lines from the #channel */
- struct fifo_buffer *input;
-
unsigned permission;
/** the uid of the client process, or -1 if unknown */
@@ -56,15 +59,9 @@ 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 */
- GQueue *deferred_send; /* for output if client is slow */
- size_t deferred_bytes; /* mem deferred_send consumes */
- unsigned int num; /* client number */
+ CommandListBuilder cmd_list;
- char send_buf[16384];
- size_t send_buf_used; /* bytes used this instance */
+ unsigned int num; /* client number */
/** is this client waiting for an "idle" response? */
bool idle_waiting;
@@ -79,7 +76,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,60 +85,45 @@ 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(EventLoop &loop, Partition &partition,
+ int fd, int uid, int num);
+ ~Client();
-extern unsigned int client_max_connections;
-extern int client_timeout;
-extern size_t client_max_command_list_size;
-extern size_t client_max_output_buffer_size;
+ bool IsConnected() const {
+ return BufferedSocket::IsDefined();
+ }
-bool
-client_list_is_empty(void);
+ gcc_pure
+ bool IsSubscribed(const char *channel_name) const {
+ return subscriptions.find(channel_name) != subscriptions.end();
+ }
-bool
-client_list_is_full(void);
+ gcc_pure
+ bool IsExpired() const {
+ return !BufferedSocket::IsDefined();
+ }
-struct client *
-client_list_get_first(void);
+ void Close();
+ void SetExpired();
-void
-client_list_add(struct client *client);
+ using BufferedSocket::Write;
-void
-client_list_foreach(GFunc func, gpointer user_data);
-
-void
-client_list_remove(struct 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);
-}
+private:
+ /* virtual methods from class BufferedSocket */
+ virtual InputResult OnSocketInput(const void *data,
+ size_t length) override;
+ virtual void OnSocketError(GError *error) override;
+ virtual void OnSocketClosed() override;
+};
-void
-client_set_expired(struct client *client);
+extern unsigned int client_max_connections;
+extern int client_timeout;
+extern size_t client_max_command_list_size;
+extern size_t client_max_output_buffer_size;
/**
* Schedule an "expired" check for all clients: permanently delete
@@ -157,16 +139,13 @@ 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);
-
-void
-client_write_deferred(struct client *client);
+client_process_line(Client *client, char *line);
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..a1248c8ec 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,11 +18,15 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientList.hxx"
+#include "ClientInternal.hxx"
+
+#include <list>
+#include <algorithm>
#include <assert.h>
-static GList *clients;
+static std::list<Client *> clients;
static unsigned num_clients;
bool
@@ -37,33 +41,36 @@ client_list_is_full(void)
return num_clients >= client_max_connections;
}
-struct client *
+Client *
client_list_get_first(void)
{
- assert(clients != NULL);
+ assert(!clients.empty());
- return clients->data;
+ return clients.front();
}
void
-client_list_add(struct client *client)
+client_list_add(Client *client)
{
- clients = g_list_prepend(clients, client);
+ clients.push_front(client);
++num_clients;
}
void
-client_list_foreach(GFunc func, gpointer user_data)
+client_list_foreach(void (*callback)(Client *client, void *ctx), void *ctx)
{
- g_list_foreach(clients, func, user_data);
+ for (auto client = clients.begin(); client != clients.end();)
+ callback(*(client++), ctx);
}
void
-client_list_remove(struct client *client)
+client_list_remove(Client *client)
{
assert(num_clients > 0);
- assert(clients != NULL);
+ assert(!clients.empty());
- clients = g_list_remove(clients, client);
+ auto i = std::find(clients.begin(), clients.end(), client);
+ assert(i != clients.end());
+ clients.erase(i);
--num_clients;
}
diff --git a/src/ClientList.hxx b/src/ClientList.hxx
new file mode 100644
index 000000000..fccc01d81
--- /dev/null
+++ b/src/ClientList.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_CLIENT_LIST_HXX
+#define MPD_CLIENT_LIST_HXX
+
+class Client;
+
+bool
+client_list_is_empty(void);
+
+bool
+client_list_is_full(void);
+
+Client *
+client_list_get_first(void);
+
+void
+client_list_add(Client *client);
+
+void
+client_list_foreach(void (*callback)(Client *client, void *ctx), void *ctx);
+
+void
+client_list_remove(Client *client);
+
+#endif
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..42cc3470d 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,14 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+#include "ClientList.hxx"
+#include "Partition.hxx"
#include "fd_util.h"
-#include "fifo_buffer.h"
+extern "C" {
#include "resolver.h"
-#include "permission.h"
+}
+#include "Permission.hxx"
#include "glib_socket.h"
#include <assert.h>
@@ -43,15 +46,32 @@
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
+Client::Client(EventLoop &_loop, Partition &_partition,
+ int _fd, int _uid, int _num)
+ :BufferedSocket(_fd, _loop, 16384, client_max_output_buffer_size),
+ partition(_partition),
+ playlist(partition.playlist), player_control(&partition.pc),
+ permission(getDefaultPermissions()),
+ uid(_uid),
+ last_activity(g_timer_new()),
+ num(_num),
+ idle_waiting(false), idle_flags(0),
+ num_subscriptions(0)
+{
+}
+
+Client::~Client()
+{
+ g_timer_destroy(last_activity);
+}
+
void
-client_new(struct player_control *player_control,
+client_new(EventLoop &loop, Partition &partition,
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);
assert(fd >= 0);
#ifdef HAVE_LIBWRAP
@@ -85,43 +105,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(loop, partition, fd, uid,
+ next_client_num++);
(void)send(fd, GREETING, sizeof(GREETING) - 1, 0);
@@ -133,33 +118,13 @@ 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_list_remove(client);
+ client_list_remove(this);
- client_set_expired(client);
+ SetExpired();
- 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);
+ g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE, "[%u] closed", num);
+ delete this;
}
diff --git a/src/client_process.c b/src/ClientProcess.cxx
index 57a8a7824..bcd20d1b7 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,19 +29,20 @@
#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);
ret = command_process(client, num++, cmd);
g_debug("command_process_list: command returned %i", ret);
- if (ret != COMMAND_RETURN_OK || client_is_expired(client))
+ if (ret != COMMAND_RETURN_OK || client->IsExpired())
break;
else if (list_ok)
client_puts(client, "list_OK\n");
@@ -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;
@@ -58,7 +61,6 @@ client_process_line(struct client *client, char *line)
/* send empty idle response and leave idle mode */
client->idle_waiting = false;
command_success(client);
- client_write_output(client);
}
/* do nothing if the client wasn't idling: the client
@@ -74,55 +76,44 @@ 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);
if (ret == COMMAND_RETURN_CLOSE ||
- client_is_expired(client))
+ client->IsExpired())
return COMMAND_RETURN_CLOSE;
if (ret == COMMAND_RETURN_OK)
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\"",
@@ -132,13 +123,11 @@ client_process_line(struct client *client, char *line)
client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
- client_is_expired(client))
+ client->IsExpired())
return COMMAND_RETURN_CLOSE;
if (ret == COMMAND_RETURN_OK)
command_success(client);
-
- client_write_output(client);
}
}
diff --git a/src/ClientRead.cxx b/src/ClientRead.cxx
new file mode 100644
index 000000000..363363d1f
--- /dev/null
+++ b/src/ClientRead.cxx
@@ -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.
+ */
+
+#include "config.h"
+#include "ClientInternal.hxx"
+#include "Main.hxx"
+#include "event/Loop.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+BufferedSocket::InputResult
+Client::OnSocketInput(const void *data, size_t length)
+{
+ g_timer_start(last_activity);
+
+ const char *p = (const char *)data;
+ const char *newline = (const char *)memchr(p, '\n', length);
+ if (newline == NULL)
+ return InputResult::MORE;
+
+ char *line = g_strndup(p, newline - p);
+ BufferedSocket::ConsumeInput(newline + 1 - p);
+
+ enum command_return result = client_process_line(this, line);
+ g_free(line);
+
+ switch (result) {
+ case COMMAND_RETURN_OK:
+ case COMMAND_RETURN_IDLE:
+ case COMMAND_RETURN_ERROR:
+ break;
+
+ case COMMAND_RETURN_KILL:
+ Close();
+ main_loop->Break();
+ return InputResult::CLOSED;
+
+ case COMMAND_RETURN_CLOSE:
+ Close();
+ return InputResult::CLOSED;
+ }
+
+ if (IsExpired()) {
+ Close();
+ return InputResult::CLOSED;
+ }
+
+ return InputResult::AGAIN;
+}
diff --git a/src/ClientSubscribe.cxx b/src/ClientSubscribe.cxx
new file mode 100644
index 000000000..d8420266c
--- /dev/null
+++ b/src/ClientSubscribe.cxx
@@ -0,0 +1,93 @@
+/*
+ * 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 "ClientSubscribe.hxx"
+#include "ClientIdle.hxx"
+#include "ClientInternal.hxx"
+#include "Idle.hxx"
+
+#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/ClientWrite.cxx b/src/ClientWrite.cxx
new file mode 100644
index 000000000..23b515a3d
--- /dev/null
+++ b/src/ClientWrite.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 "ClientInternal.hxx"
+
+#include <string.h>
+#include <stdio.h>
+
+/**
+ * Write a block of data to the client.
+ */
+static void
+client_write(Client *client, const char *data, size_t length)
+{
+ /* if the client is going to be closed, do nothing */
+ if (client->IsExpired() || length == 0)
+ return;
+
+ client->Write(data, length);
+}
+
+void
+client_puts(Client *client, const char *s)
+{
+ client_write(client, s, strlen(s));
+}
+
+void
+client_vprintf(Client *client, const char *fmt, va_list args)
+{
+#ifndef G_OS_WIN32
+ va_list tmp;
+ int length;
+
+ va_copy(tmp, args);
+ length = vsnprintf(NULL, 0, fmt, tmp);
+ va_end(tmp);
+
+ if (length <= 0)
+ /* wtf.. */
+ return;
+
+ char *buffer = (char *)g_malloc(length + 1);
+ vsnprintf(buffer, length + 1, fmt, args);
+ client_write(client, buffer, length);
+ g_free(buffer);
+#else
+ /* On mingw32, snprintf() expects a 64 bit integer instead of
+ a "long int" for "%li". This is not consistent with our
+ expectation, so we're using plain sprintf() here, hoping
+ the static buffer is large enough. Sorry for this hack,
+ but WIN32 development is so painful, I'm not in the mood to
+ do it properly now. */
+
+ static char buffer[4096];
+ vsprintf(buffer, fmt, args);
+ client_write(client, buffer, strlen(buffer));
+#endif
+}
+
+G_GNUC_PRINTF(2, 3)
+void
+client_printf(Client *client, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ client_vprintf(client, fmt, args);
+ va_end(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..a8608af9c 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,19 @@
*/
#include "config.h"
-#include "cmdline.h"
+#include "CommandLine.hxx"
#include "path.h"
-#include "log.h"
+#include "ls.hxx"
+#include "Log.hxx"
#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 "InputRegistry.hxx"
#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 +159,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..cc10f7205
--- /dev/null
+++ b/src/CommandListBuilder.cxx
@@ -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.
+ */
+
+#include "config.h"
+#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/conf.c b/src/ConfigFile.cxx
index 167f2da92..fd08f52b1 100644
--- a/src/conf.c
+++ b/src/ConfigFile.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
@@ -19,9 +19,13 @@
#include "config.h"
#include "conf.h"
+
+extern "C" {
#include "utils.h"
#include "string_util.h"
#include "tokenizer.h"
+}
+
#include "path.h"
#include "mpd_error.h"
@@ -46,62 +50,68 @@ struct config_entry {
const bool block;
GSList *params;
+
+ constexpr config_entry(const char *_name,
+ bool _repeatable, bool _block)
+ :name(_name), repeatable(_repeatable), block(_block),
+ params(nullptr) {}
};
static struct config_entry config_entries[] = {
- { .name = CONF_MUSIC_DIR, false, false },
- { .name = CONF_PLAYLIST_DIR, false, false },
- { .name = CONF_FOLLOW_INSIDE_SYMLINKS, false, false },
- { .name = CONF_FOLLOW_OUTSIDE_SYMLINKS, false, false },
- { .name = CONF_DB_FILE, false, false },
- { .name = CONF_STICKER_FILE, false, false },
- { .name = CONF_LOG_FILE, false, false },
- { .name = CONF_PID_FILE, false, false },
- { .name = CONF_STATE_FILE, false, false },
- { .name = "restore_paused", false, false },
- { .name = CONF_USER, false, false },
- { .name = CONF_GROUP, false, false },
- { .name = CONF_BIND_TO_ADDRESS, true, false },
- { .name = CONF_PORT, false, false },
- { .name = CONF_LOG_LEVEL, false, false },
- { .name = CONF_ZEROCONF_NAME, false, false },
- { .name = CONF_ZEROCONF_ENABLED, false, false },
- { .name = CONF_PASSWORD, true, false },
- { .name = CONF_DEFAULT_PERMS, false, false },
- { .name = CONF_AUDIO_OUTPUT, true, true },
- { .name = CONF_AUDIO_OUTPUT_FORMAT, false, false },
- { .name = CONF_MIXER_TYPE, false, false },
- { .name = CONF_REPLAYGAIN, false, false },
- { .name = CONF_REPLAYGAIN_PREAMP, false, false },
- { .name = CONF_REPLAYGAIN_MISSING_PREAMP, false, false },
- { .name = CONF_REPLAYGAIN_LIMIT, false, false },
- { .name = CONF_VOLUME_NORMALIZATION, false, false },
- { .name = CONF_SAMPLERATE_CONVERTER, false, false },
- { .name = CONF_AUDIO_BUFFER_SIZE, false, false },
- { .name = CONF_BUFFER_BEFORE_PLAY, false, false },
- { .name = CONF_HTTP_PROXY_HOST, false, false },
- { .name = CONF_HTTP_PROXY_PORT, false, false },
- { .name = CONF_HTTP_PROXY_USER, false, false },
- { .name = CONF_HTTP_PROXY_PASSWORD, false, false },
- { .name = CONF_CONN_TIMEOUT, false, false },
- { .name = CONF_MAX_CONN, false, false },
- { .name = CONF_MAX_PLAYLIST_LENGTH, false, false },
- { .name = CONF_MAX_COMMAND_LIST_SIZE, false, false },
- { .name = CONF_MAX_OUTPUT_BUFFER_SIZE, false, false },
- { .name = CONF_FS_CHARSET, false, false },
- { .name = CONF_ID3V1_ENCODING, false, false },
- { .name = CONF_METADATA_TO_USE, false, false },
- { .name = CONF_SAVE_ABSOLUTE_PATHS, false, false },
- { .name = CONF_DECODER, true, true },
- { .name = CONF_INPUT, true, true },
- { .name = CONF_GAPLESS_MP3_PLAYBACK, false, false },
- { .name = CONF_PLAYLIST_PLUGIN, true, true },
- { .name = CONF_AUTO_UPDATE, false, false },
- { .name = CONF_AUTO_UPDATE_DEPTH, false, false },
- { .name = CONF_DESPOTIFY_USER, false, false },
- { .name = CONF_DESPOTIFY_PASSWORD, false, false},
- { .name = CONF_DESPOTIFY_HIGH_BITRATE, false, false },
- { .name = "filter", true, true },
+ { CONF_MUSIC_DIR, false, false },
+ { CONF_PLAYLIST_DIR, false, false },
+ { CONF_FOLLOW_INSIDE_SYMLINKS, false, false },
+ { CONF_FOLLOW_OUTSIDE_SYMLINKS, false, false },
+ { CONF_DB_FILE, false, false },
+ { CONF_STICKER_FILE, false, false },
+ { CONF_LOG_FILE, false, false },
+ { CONF_PID_FILE, false, false },
+ { CONF_STATE_FILE, false, false },
+ { "restore_paused", false, false },
+ { CONF_USER, false, false },
+ { CONF_GROUP, false, false },
+ { CONF_BIND_TO_ADDRESS, true, false },
+ { CONF_PORT, false, false },
+ { CONF_LOG_LEVEL, false, false },
+ { CONF_ZEROCONF_NAME, false, false },
+ { CONF_ZEROCONF_ENABLED, false, false },
+ { CONF_PASSWORD, true, false },
+ { CONF_DEFAULT_PERMS, false, false },
+ { CONF_AUDIO_OUTPUT, true, true },
+ { CONF_AUDIO_OUTPUT_FORMAT, false, false },
+ { CONF_MIXER_TYPE, false, false },
+ { CONF_REPLAYGAIN, false, false },
+ { CONF_REPLAYGAIN_PREAMP, false, false },
+ { CONF_REPLAYGAIN_MISSING_PREAMP, false, false },
+ { CONF_REPLAYGAIN_LIMIT, false, false },
+ { CONF_VOLUME_NORMALIZATION, false, false },
+ { CONF_SAMPLERATE_CONVERTER, false, false },
+ { CONF_AUDIO_BUFFER_SIZE, false, false },
+ { CONF_BUFFER_BEFORE_PLAY, false, false },
+ { CONF_HTTP_PROXY_HOST, false, false },
+ { CONF_HTTP_PROXY_PORT, false, false },
+ { CONF_HTTP_PROXY_USER, false, false },
+ { CONF_HTTP_PROXY_PASSWORD, false, false },
+ { CONF_CONN_TIMEOUT, false, false },
+ { CONF_MAX_CONN, false, false },
+ { CONF_MAX_PLAYLIST_LENGTH, false, false },
+ { CONF_MAX_COMMAND_LIST_SIZE, false, false },
+ { CONF_MAX_OUTPUT_BUFFER_SIZE, false, false },
+ { CONF_FS_CHARSET, false, false },
+ { CONF_ID3V1_ENCODING, false, false },
+ { CONF_METADATA_TO_USE, false, false },
+ { CONF_SAVE_ABSOLUTE_PATHS, false, false },
+ { CONF_DECODER, true, true },
+ { CONF_INPUT, true, true },
+ { CONF_GAPLESS_MP3_PLAYBACK, false, false },
+ { CONF_PLAYLIST_PLUGIN, true, true },
+ { CONF_AUTO_UPDATE, false, false },
+ { CONF_AUTO_UPDATE_DEPTH, false, false },
+ { CONF_DESPOTIFY_USER, false, false },
+ { CONF_DESPOTIFY_PASSWORD, false, false},
+ { CONF_DESPOTIFY_HIGH_BITRATE, false, false },
+ { "filter", true, true },
+ { "database", false, true },
};
static bool
@@ -161,7 +171,7 @@ config_param_free(struct config_param *param)
static void
config_param_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
- struct config_param *param = data;
+ struct config_param *param = (struct config_param *)data;
config_param_free(param);
}
@@ -196,7 +206,7 @@ void config_global_init(void)
static void
config_param_check(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
- struct config_param *param = data;
+ struct config_param *param = (struct config_param *)data;
if (!param->used)
/* this whole config_param was not queried at all -
@@ -232,9 +242,10 @@ config_add_block_param(struct config_param * param, const char *name,
param->num_block_params++;
- param->block_params = g_realloc(param->block_params,
- param->num_block_params *
- sizeof(param->block_params[0]));
+ param->block_params = (struct block_param *)
+ g_realloc(param->block_params,
+ param->num_block_params *
+ sizeof(param->block_params[0]));
bp = &param->block_params[param->num_block_params - 1];
@@ -317,7 +328,7 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
g_set_error(error_r, config_quark(), 0,
"line %i: Unknown tokens after '}'",
*count);
- return false;
+ return nullptr;
}
return ret;
@@ -389,7 +400,7 @@ config_read_file(const char *file, GError **error_r)
}
if (entry->params != NULL && !entry->repeatable) {
- param = entry->params->data;
+ param = (struct config_param *)entry->params->data;
g_set_error(error_r, config_quark(), 0,
"config parameter \"%s\" is first defined "
"on line %i and redefined on line %i\n",
@@ -486,7 +497,7 @@ config_get_next_param(const char *name, const struct config_param * last)
if (node == NULL)
return NULL;
- param = node->data;
+ param = (struct config_param *)node->data;
param->used = true;
return param;
}
diff --git a/src/crossfade.c b/src/CrossFade.cxx
index 46a0dff30..0bdcc43d6 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,11 +18,13 @@
*/
#include "config.h"
-#include "crossfade.h"
-#include "chunk.h"
+#include "CrossFade.hxx"
+#include "MusicChunk.hxx"
#include "audio_format.h"
#include "tag.h"
+#include <cmath>
+
#include <assert.h>
#include <string.h>
#include <stdlib.h>
@@ -82,9 +84,8 @@ static float mixramp_interpolate(char *ramp_list, float required_db)
}
/* If required db < any stored value, use the least. */
- if (isnan(last_db)) {
+ if (std::isnan(last_db))
return secs;
- }
/* Finally, interpolate linearly. */
secs = last_secs + (required_db - last_db) * (secs - last_secs) / (db - last_db);
@@ -114,13 +115,14 @@ unsigned cross_fade_calc(float duration, float total_time,
chunks_f = (float)audio_format_time_to_size(af) / (float)CHUNK_SIZE;
- if (isnan(mixramp_delay) || !(mixramp_start) || !(mixramp_prev_end)) {
+ if (std::isnan(mixramp_delay) || !mixramp_start || !mixramp_prev_end) {
chunks = (chunks_f * duration + 0.5);
} else {
/* Calculate mixramp overlap. */
mixramp_overlap = mixramp_interpolate(mixramp_start, mixramp_db - replay_gain_db)
+ mixramp_interpolate(mixramp_prev_end, mixramp_db - replay_gain_prev_db);
- if (!isnan(mixramp_overlap) && (mixramp_delay <= mixramp_overlap)) {
+ if (!std::isnan(mixramp_overlap) &&
+ mixramp_delay <= mixramp_overlap) {
chunks = (chunks_f * (mixramp_overlap - mixramp_delay));
g_debug("will overlap %d chunks, %fs", chunks,
mixramp_overlap - mixramp_delay);
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..bd5a48b35
--- /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;
+ }
+
+ const DatabaseSelection selection("", true, &filter);
+ GError *error = NULL;
+ return AddFromDatabase(client->partition, selection, &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..853e67b07 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"
+#include "conf.h"
+
+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;
+ const char *plugin_name =
+ config_get_block_string(param, "plugin", "simple");
+ is_simple = strcmp(plugin_name, "simple") == 0;
- 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);
-
- 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);
+ assert(db == NULL || db_is_open);
- g_debug("get song: %s", file);
-
- 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/notify.h b/src/DatabaseGlue.hxx
index 40821690c..ea26f3242 100644
--- a/src/notify.h
+++ b/src/DatabaseGlue.hxx
@@ -17,37 +17,43 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_NOTIFY_H
-#define MPD_NOTIFY_H
+#ifndef MPD_DATABASE_GLUE_HXX
+#define MPD_DATABASE_GLUE_HXX
-#include <glib.h>
+#include "gcc.h"
+#include "gerror.h"
-#include <stdbool.h>
-
-struct notify {
- GMutex *mutex;
- GCond *cond;
- bool pending;
-};
-
-void notify_init(struct notify *notify);
-
-void notify_deinit(struct notify *notify);
+struct config_param;
+class Database;
/**
- * Wait for a notification. Return immediately if we have already
- * been notified since we last returned from notify_wait().
+ * Initialize the database library.
+ *
+ * @param param the database configuration block
*/
-void notify_wait(struct notify *notify);
+bool
+DatabaseGlobalInit(const config_param *param, GError **error_r);
+
+void
+DatabaseGlobalDeinit(void);
+
+bool
+DatabaseGlobalOpen(GError **error);
/**
- * Notify the thread. This function never blocks.
+ * Returns the global #Database instance. May return NULL if this MPD
+ * configuration has no database (no music_directory was configured).
*/
-void notify_signal(struct notify *notify);
+gcc_pure
+const Database *
+GetDatabase();
/**
- * Clears a pending notification.
+ * Returns the global #Database instance. May return NULL if this MPD
+ * configuration has no database (no music_directory was configured).
*/
-void notify_clear(struct notify *notify);
+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..d0327d794 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)
@@ -26,7 +26,7 @@
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
-GStaticMutex db_mutex = G_STATIC_MUTEX_INIT;
+Mutex db_mutex;
#ifndef NDEBUG
GThread *db_mutex_holder;
diff --git a/src/db_lock.h b/src/DatabaseLock.hxx
index 4640502f3..371a7d7b2 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,16 +23,16 @@
* 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 "thread/Mutex.hxx"
#include <glib.h>
#include <assert.h>
-#include <stdbool.h>
-extern GStaticMutex db_mutex;
+extern Mutex db_mutex;
#ifndef NDEBUG
@@ -59,7 +59,7 @@ db_lock(void)
{
assert(!holding_db_lock());
- g_static_mutex_lock(&db_mutex);
+ db_mutex.lock();
assert(db_mutex_holder == NULL);
#ifndef NDEBUG
@@ -78,7 +78,22 @@ db_unlock(void)
db_mutex_holder = NULL;
#endif
- g_static_mutex_unlock(&db_mutex);
+ db_mutex.unlock();
}
+#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..2384d5c14
--- /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"
+#include "tag.h"
+
+extern "C" {
+#include "song.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..e22144c07
--- /dev/null
+++ b/src/DatabaseQueue.cxx
@@ -0,0 +1,54 @@
+/*
+ * 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 "Partition.hxx"
+
+#include <functional>
+
+static bool
+AddToQueue(Partition &partition, song &song, GError **error_r)
+{
+ enum playlist_result result =
+ partition.playlist.AppendSong(partition.pc, &song, NULL);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ g_set_error(error_r, playlist_quark(), result,
+ "Playlist error");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+AddFromDatabase(Partition &partition, const DatabaseSelection &selection,
+ GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ using namespace std::placeholders;
+ const auto f = std::bind(AddToQueue, std::ref(partition), _1, _2);
+ return db->Visit(selection, f, error_r);
+}
diff --git a/src/db_internal.h b/src/DatabaseQueue.hxx
index a33351524..bae5b1f05 100644
--- a/src/db_internal.h
+++ b/src/DatabaseQueue.hxx
@@ -17,19 +17,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DB_INTERNAL_H
-#define MPD_DB_INTERNAL_H
+#ifndef MPD_DATABASE_QUEUE_HXX
+#define MPD_DATABASE_QUEUE_HXX
-#include "db_plugin.h"
+#include "gerror.h"
-#include <assert.h>
+struct Partition;
+struct DatabaseSelection;
-static inline void
-db_base_init(struct db *db, const struct db_plugin *plugin)
-{
- assert(plugin != NULL);
-
- db->plugin = plugin;
-}
+bool
+AddFromDatabase(Partition &partition, const DatabaseSelection &selection,
+ 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..5100c2a0b 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 "path.h"
-#include "text_file.h"
+#include "TextFile.hxx"
+#include "TagInternal.hxx"
#include "tag.h"
-#include "tag_internal.h"
+
+extern "C" {
+#include "path.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/decoder_print.h b/src/DatabaseSelection.cxx
index 31713d5d8..bd756f5f9 100644
--- a/src/decoder_print.h
+++ b/src/DatabaseSelection.cxx
@@ -17,12 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_PRINT_H
-#define MPD_DECODER_PRINT_H
+#include "DatabaseSelection.hxx"
+#include "SongFilter.hxx"
-struct client;
-
-void
-decoder_list_print(struct client *client);
-
-#endif
+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/DatabaseVisitor.hxx b/src/DatabaseVisitor.hxx
new file mode 100644
index 000000000..c90441415
--- /dev/null
+++ b/src/DatabaseVisitor.hxx
@@ -0,0 +1,38 @@
+/*
+ * 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_VISITOR_HXX
+#define MPD_DATABASE_VISITOR_HXX
+
+#include "gerror.h"
+
+#include <functional>
+
+struct Directory;
+struct song;
+struct PlaylistInfo;
+
+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;
+
+typedef std::function<bool(const char *, GError **)> VisitString;
+
+#endif
diff --git a/src/decoder_api.c b/src/DecoderAPI.cxx
index a45d0f1e6..49ebacd49 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
@@ -19,14 +19,18 @@
#include "config.h"
#include "decoder_api.h"
-#include "decoder_internal.h"
-#include "decoder_control.h"
+
+extern "C" {
#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..2ffaf116f 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>
@@ -27,7 +28,7 @@
#define G_LOG_DOMAIN "decoder_control"
struct decoder_control *
-dc_new(GCond *client_cond)
+dc_new()
{
struct decoder_control *dc = g_new(struct decoder_control, 1);
@@ -35,11 +36,13 @@ dc_new(GCond *client_cond)
dc->mutex = g_mutex_new();
dc->cond = g_cond_new();
- dc->client_cond = client_cond;
+ dc->client_cond = g_cond_new();
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,12 @@ 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->client_cond);
g_cond_free(dc->cond);
g_mutex_free(dc->mutex);
g_free(dc->mixramp_start);
@@ -79,6 +88,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 +104,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 +135,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..42c28d785 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
@@ -122,7 +133,7 @@ struct decoder_control {
G_GNUC_MALLOC
struct decoder_control *
-dc_new(GCond *client_cond);
+dc_new();
void
dc_free(struct decoder_control *dc);
@@ -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..6970a220f 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,33 @@
*/
#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"
+#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 +105,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..21653830b 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 "decoder_api.h"
-#include "replay_gain_ape.h"
-#include "input_stream.h"
-#include "pipe.h"
#include "song.h"
+#include "mpd_error.h"
+#include "Mapper.hxx"
+#include "decoder_api.h"
#include "tag.h"
-#include "mapper.h"
-#include "path.h"
+#include "input_stream.h"
+
+extern "C" {
+#include "decoder_list.h"
+#include "replay_gain_ape.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,51 @@ 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->error = g_error_new(decoder_quark(), 0,
+ "Failed to decode %s", error_uri);
+ g_free(allocated);
+ }
- dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR;
+ g_cond_signal(dc->client_cond);
}
static void
decoder_run(struct decoder_control *dc)
{
+ dc_clear_error(dc);
+
const struct song *song = dc->song;
char *uri;
@@ -446,6 +437,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 +452,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/directory_save.h b/src/DirectorySave.hxx
index 2d0056830..a7f3034a7 100644
--- a/src/directory_save.h
+++ b/src/DirectorySave.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,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_DIRECTORY_SAVE_HXX
+#define MPD_DIRECTORY_SAVE_HXX
#include <glib.h>
-#include <stdbool.h>
#include <stdio.h>
-struct directory;
+struct Directory;
+class TextFile;
void
-directory_save(FILE *fp, const struct directory *directory);
+directory_save(FILE *fp, const Directory *directory);
bool
-directory_load(FILE *fp, struct directory *directory,
- GString *buffer, GError **error);
+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/GlobalEvents.cxx b/src/GlobalEvents.cxx
new file mode 100644
index 000000000..e4f335c9e
--- /dev/null
+++ b/src/GlobalEvents.cxx
@@ -0,0 +1,114 @@
+/*
+ * 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 "GlobalEvents.hxx"
+#include "event/WakeFD.hxx"
+#include "mpd_error.h"
+
+#include <atomic>
+
+#include <assert.h>
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "global_events"
+
+namespace GlobalEvents {
+ static WakeFD wake_fd;
+ static guint source_id;
+ static std::atomic_uint flags;
+ static Handler handlers[MAX];
+}
+
+/**
+ * Invoke the callback for a certain event.
+ */
+static void
+InvokeGlobalEvent(GlobalEvents::Event event)
+{
+ assert((unsigned)event < GlobalEvents::MAX);
+ assert(GlobalEvents::handlers[event] != NULL);
+
+ GlobalEvents::handlers[event]();
+}
+
+static gboolean
+GlobalEventCallback(G_GNUC_UNUSED GIOChannel *source,
+ G_GNUC_UNUSED GIOCondition condition,
+ G_GNUC_UNUSED gpointer data)
+{
+ if (!GlobalEvents::wake_fd.Read())
+ return true;
+
+ const unsigned flags = GlobalEvents::flags.fetch_and(0);
+
+ for (unsigned i = 0; i < GlobalEvents::MAX; ++i)
+ if (flags & (1u << i))
+ /* invoke the event handler */
+ InvokeGlobalEvent(GlobalEvents::Event(i));
+
+ return true;
+}
+
+void
+GlobalEvents::Initialize()
+{
+ if (!wake_fd.Create())
+ MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
+
+#ifndef G_OS_WIN32
+ GIOChannel *channel = g_io_channel_unix_new(wake_fd.Get());
+#else
+ GIOChannel *channel = g_io_channel_win32_new_socket(wake_fd.Get());
+#endif
+
+ source_id = g_io_add_watch(channel, G_IO_IN,
+ GlobalEventCallback, NULL);
+ g_io_channel_unref(channel);
+}
+
+void
+GlobalEvents::Deinitialize()
+{
+ g_source_remove(source_id);
+
+ wake_fd.Destroy();
+}
+
+void
+GlobalEvents::Register(Event event, Handler callback)
+{
+ assert((unsigned)event < MAX);
+ assert(handlers[event] == NULL);
+
+ handlers[event] = callback;
+}
+
+void
+GlobalEvents::Emit(Event event)
+{
+ assert((unsigned)event < MAX);
+
+ const unsigned mask = 1u << unsigned(event);
+ if ((GlobalEvents::flags.fetch_or(mask) & mask) == 0)
+ wake_fd.Write();
+}
diff --git a/src/GlobalEvents.hxx b/src/GlobalEvents.hxx
new file mode 100644
index 000000000..2e549305b
--- /dev/null
+++ b/src/GlobalEvents.hxx
@@ -0,0 +1,71 @@
+/*
+ * 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_GLOBAL_EVENTS_HXX
+#define MPD_GLOBAL_EVENTS_HXX
+
+#ifdef WIN32
+/* DELETE is a WIN32 macro that poisons our namespace; this is a
+ kludge to allow us to use it anyway */
+#ifdef DELETE
+#undef DELETE
+#endif
+#endif
+
+namespace GlobalEvents {
+ enum Event {
+ /** database update was finished */
+ UPDATE,
+
+ /** during database update, a song was deleted */
+ DELETE,
+
+ /** an idle event was emitted */
+ IDLE,
+
+ /** must call playlist_sync() */
+ PLAYLIST,
+
+ /** the current song's tag has changed */
+ TAG,
+
+ /** SIGHUP received: reload configuration, roll log file */
+ RELOAD,
+
+ /** a hardware mixer plugin has detected a change */
+ MIXER,
+
+ /** shutdown requested */
+ SHUTDOWN,
+
+ MAX
+ };
+
+ typedef void (*Handler)();
+
+ void Initialize();
+
+ void Deinitialize();
+
+ void Register(Event event, Handler handler);
+
+ void Emit(Event event);
+}
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/io_thread.c b/src/IOThread.cxx
index 7c080adcb..192d4cc49 100644
--- a/src/io_thread.c
+++ b/src/IOThread.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,16 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "io_thread.h"
+#include "config.h"
+#include "IOThread.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+#include "event/Loop.hxx"
#include <assert.h>
static struct {
- GMutex *mutex;
- GCond *cond;
+ Mutex mutex;
+ Cond cond;
- GMainContext *context;
- GMainLoop *loop;
+ EventLoop *loop;
GThread *thread;
} io;
@@ -34,10 +37,9 @@ void
io_thread_run(void)
{
assert(io_thread_inside());
- assert(io.context != NULL);
assert(io.loop != NULL);
- g_main_loop_run(io.loop);
+ io.loop->Run();
}
static gpointer
@@ -45,8 +47,8 @@ io_thread_func(G_GNUC_UNUSED gpointer arg)
{
/* lock+unlock to synchronize with io_thread_start(), to be
sure that io.thread is set */
- g_mutex_lock(io.mutex);
- g_mutex_unlock(io.mutex);
+ io.mutex.lock();
+ io.mutex.unlock();
io_thread_run();
return NULL;
@@ -55,26 +57,21 @@ io_thread_func(G_GNUC_UNUSED gpointer arg)
void
io_thread_init(void)
{
- assert(io.context == NULL);
assert(io.loop == NULL);
assert(io.thread == NULL);
- io.mutex = g_mutex_new();
- io.cond = g_cond_new();
- io.context = g_main_context_new();
- io.loop = g_main_loop_new(io.context, false);
+ io.loop = new EventLoop();
}
bool
io_thread_start(GError **error_r)
{
- assert(io.context != NULL);
assert(io.loop != NULL);
assert(io.thread == NULL);
- g_mutex_lock(io.mutex);
+ io.mutex.lock();
io.thread = g_thread_create(io_thread_func, NULL, true, error_r);
- g_mutex_unlock(io.mutex);
+ io.mutex.unlock();
if (io.thread == NULL)
return false;
@@ -86,7 +83,7 @@ io_thread_quit(void)
{
assert(io.loop != NULL);
- g_main_loop_quit(io.loop);
+ io.loop->Break();
}
void
@@ -98,20 +95,15 @@ io_thread_deinit(void)
g_thread_join(io.thread);
}
- if (io.loop != NULL)
- g_main_loop_unref(io.loop);
-
- if (io.context != NULL)
- g_main_context_unref(io.context);
-
- g_cond_free(io.cond);
- g_mutex_free(io.mutex);
+ delete io.loop;
}
-GMainContext *
-io_thread_context(void)
+EventLoop &
+io_thread_get()
{
- return io.context;
+ assert(io.loop != nullptr);
+
+ return *io.loop;
}
bool
@@ -120,35 +112,6 @@ io_thread_inside(void)
return io.thread != NULL && g_thread_self() == io.thread;
}
-guint
-io_thread_idle_add(GSourceFunc function, gpointer data)
-{
- GSource *source = g_idle_source_new();
- g_source_set_callback(source, function, data, NULL);
- guint id = g_source_attach(source, io.context);
- g_source_unref(source);
- return id;
-}
-
-GSource *
-io_thread_timeout_add(guint interval_ms, GSourceFunc function, gpointer data)
-{
- GSource *source = g_timeout_source_new(interval_ms);
- g_source_set_callback(source, function, data, NULL);
- g_source_attach(source, io.context);
- return source;
-}
-
-GSource *
-io_thread_timeout_add_seconds(guint interval,
- GSourceFunc function, gpointer data)
-{
- GSource *source = g_timeout_source_new_seconds(interval);
- g_source_set_callback(source, function, data, NULL);
- g_source_attach(source, io.context);
- return source;
-}
-
struct call_data {
GThreadFunc function;
gpointer data;
@@ -159,15 +122,15 @@ struct call_data {
static gboolean
io_thread_call_func(gpointer _data)
{
- struct call_data *data = _data;
+ struct call_data *data = (struct call_data *)_data;
gpointer result = data->function(data->data);
- g_mutex_lock(io.mutex);
+ io.mutex.lock();
data->done = true;
data->result = result;
- g_cond_broadcast(io.cond);
- g_mutex_unlock(io.mutex);
+ io.cond.broadcast();
+ io.mutex.unlock();
return false;
}
@@ -183,17 +146,18 @@ io_thread_call(GThreadFunc function, gpointer _data)
return function(_data);
struct call_data data = {
- .function = function,
- .data = _data,
- .done = false,
+ function,
+ _data,
+ false,
+ nullptr,
};
- io_thread_idle_add(io_thread_call_func, &data);
+ io.loop->AddIdle(io_thread_call_func, &data);
- g_mutex_lock(io.mutex);
+ io.mutex.lock();
while (!data.done)
- g_cond_wait(io.cond, io.mutex);
- g_mutex_unlock(io.mutex);
+ io.cond.wait(io.mutex);
+ io.mutex.unlock();
return data.result;
}
diff --git a/src/io_thread.h b/src/IOThread.hxx
index 8ff5a71e5..a9401dc7f 100644
--- a/src/io_thread.h
+++ b/src/IOThread.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,11 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_IO_THREAD_H
-#define MPD_IO_THREAD_H
+#ifndef MPD_IO_THREAD_HXX
+#define MPD_IO_THREAD_HXX
+
+#include "gcc.h"
#include <glib.h>
-#include <stdbool.h>
+
+class EventLoop;
void
io_thread_init(void);
@@ -48,29 +51,17 @@ io_thread_quit(void);
void
io_thread_deinit(void);
-G_GNUC_PURE
-GMainContext *
-io_thread_context(void);
+gcc_pure
+EventLoop &
+io_thread_get();
/**
* Is the current thread the I/O thread?
*/
-G_GNUC_PURE
+gcc_pure
bool
io_thread_inside(void);
-guint
-io_thread_idle_add(GSourceFunc function, gpointer data);
-
-G_GNUC_MALLOC
-GSource *
-io_thread_timeout_add(guint interval_ms, GSourceFunc function, gpointer data);
-
-G_GNUC_MALLOC
-GSource *
-io_thread_timeout_add_seconds(guint interval,
- GSourceFunc function, gpointer data);
-
/**
* Call a function synchronously in the I/O thread.
*/
diff --git a/src/icy_metadata.c b/src/IcyMetaDataParser.cxx
index 32953e69f..cda63da44 100644
--- a/src/icy_metadata.c
+++ b/src/IcyMetaDataParser.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 "icy_metadata.h"
+#include "IcyMetaDataParser.hxx"
#include "tag.h"
#include <glib.h>
@@ -30,46 +30,37 @@
#define G_LOG_DOMAIN "icy_metadata"
void
-icy_deinit(struct icy_metadata *im)
+IcyMetaDataParser::Reset()
{
- if (!icy_defined(im))
+ if (!IsDefined())
return;
- if (im->data_rest == 0 && im->meta_size > 0)
- g_free(im->meta_data);
+ if (data_rest == 0 && meta_size > 0)
+ g_free(meta_data);
- if (im->tag != NULL)
- tag_free(im->tag);
-}
-
-void
-icy_reset(struct icy_metadata *im)
-{
- if (!icy_defined(im))
- return;
-
- icy_deinit(im);
+ if (tag != nullptr)
+ tag_free(tag);
- im->data_rest = im->data_size;
- im->meta_size = 0;
+ data_rest = data_size;
+ meta_size = 0;
}
size_t
-icy_data(struct icy_metadata *im, size_t length)
+IcyMetaDataParser::Data(size_t length)
{
assert(length > 0);
- if (!icy_defined(im))
+ if (!IsDefined())
return length;
- if (im->data_rest == 0)
+ if (data_rest == 0)
return 0;
- if (length >= im->data_rest) {
- length = im->data_rest;
- im->data_rest = 0;
+ if (length >= data_rest) {
+ length = data_rest;
+ data_rest = 0;
} else
- im->data_rest -= length;
+ data_rest -= length;
return length;
}
@@ -94,7 +85,7 @@ icy_parse_tag_item(struct tag *tag, const char *item)
{
gchar **p = g_strsplit(item, "=", 0);
- if (p[0] != NULL && p[1] != NULL) {
+ if (p[0] != nullptr && p[1] != nullptr) {
if (strcmp(p[0], "StreamTitle") == 0)
icy_add_item(tag, TAG_TITLE, p[1]);
else
@@ -110,7 +101,7 @@ icy_parse_tag(const char *p)
struct tag *tag = tag_new();
gchar **items = g_strsplit(p, ";", 0);
- for (unsigned i = 0; items[i] != NULL; ++i)
+ for (unsigned i = 0; items[i] != nullptr; ++i)
icy_parse_tag_item(tag, items[i]);
g_strfreev(items);
@@ -119,21 +110,21 @@ icy_parse_tag(const char *p)
}
size_t
-icy_meta(struct icy_metadata *im, const void *data, size_t length)
+IcyMetaDataParser::Meta(const void *data, size_t length)
{
- const unsigned char *p = data;
+ const unsigned char *p = (const unsigned char *)data;
- assert(icy_defined(im));
- assert(im->data_rest == 0);
+ assert(IsDefined());
+ assert(data_rest == 0);
assert(length > 0);
- if (im->meta_size == 0) {
+ if (meta_size == 0) {
/* read meta_size from the first byte of a meta
block */
- im->meta_size = *p++ * 16;
- if (im->meta_size == 0) {
+ meta_size = *p++ * 16;
+ if (meta_size == 0) {
/* special case: no metadata */
- im->data_rest = im->data_size;
+ data_rest = data_size;
return 1;
}
@@ -143,39 +134,39 @@ icy_meta(struct icy_metadata *im, const void *data, size_t length)
/* initialize metadata reader, allocate enough
memory (+1 for the null terminator) */
- im->meta_position = 0;
- im->meta_data = g_malloc(im->meta_size + 1);
+ meta_position = 0;
+ meta_data = (char *)g_malloc(meta_size + 1);
}
- assert(im->meta_position < im->meta_size);
+ assert(meta_position < meta_size);
- if (length > im->meta_size - im->meta_position)
- length = im->meta_size - im->meta_position;
+ if (length > meta_size - meta_position)
+ length = meta_size - meta_position;
- memcpy(im->meta_data + im->meta_position, p, length);
- im->meta_position += length;
+ memcpy(meta_data + meta_position, p, length);
+ meta_position += length;
if (p != data)
/* re-add the first byte (which contained meta_size) */
++length;
- if (im->meta_position == im->meta_size) {
+ if (meta_position == meta_size) {
/* null-terminate the string */
- im->meta_data[im->meta_size] = 0;
+ meta_data[meta_size] = 0;
/* parse */
- if (im->tag != NULL)
- tag_free(im->tag);
+ if (tag != nullptr)
+ tag_free(tag);
- im->tag = icy_parse_tag(im->meta_data);
- g_free(im->meta_data);
+ tag = icy_parse_tag(meta_data);
+ g_free(meta_data);
/* change back to normal data mode */
- im->meta_size = 0;
- im->data_rest = im->data_size;
+ meta_size = 0;
+ data_rest = data_size;
}
return length;
diff --git a/src/IcyMetaDataParser.hxx b/src/IcyMetaDataParser.hxx
new file mode 100644
index 000000000..6ccc73f52
--- /dev/null
+++ b/src/IcyMetaDataParser.hxx
@@ -0,0 +1,83 @@
+/*
+ * 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_ICY_META_DATA_PARSER_HXX
+#define MPD_ICY_META_DATA_PARSER_HXX
+
+#include <stddef.h>
+
+class IcyMetaDataParser {
+ size_t data_size, data_rest;
+
+ size_t meta_size, meta_position;
+ char *meta_data;
+
+ struct tag *tag;
+
+public:
+ IcyMetaDataParser():data_size(0) {}
+ ~IcyMetaDataParser() {
+ Reset();
+ }
+
+ /**
+ * Initialize an enabled icy_metadata object with the specified
+ * data_size (from the icy-metaint HTTP response header).
+ */
+ void Start(size_t _data_size) {
+ data_size = data_rest = _data_size;
+ meta_size = 0;
+ tag = nullptr;
+ }
+
+ /**
+ * Resets the icy_metadata. Call this after rewinding the stream.
+ */
+ void Reset();
+
+ /**
+ * Checks whether the icy_metadata object is enabled.
+ */
+ bool IsDefined() const {
+ return data_size > 0;
+ }
+
+ /**
+ * Evaluates data. Returns the number of bytes of normal data which
+ * can be read by the caller, but not more than "length". If the
+ * return value is smaller than "length", the caller should invoke
+ * icy_meta().
+ */
+ size_t Data(size_t length);
+
+ /**
+ * Reads metadata from the stream. Returns the number of bytes
+ * consumed. If the return value is smaller than "length", the caller
+ * should invoke icy_data().
+ */
+ size_t Meta(const void *data, size_t length);
+
+ struct tag *ReadTag() {
+ struct tag *result = tag;
+ tag = nullptr;
+ return result;
+ }
+};
+
+#endif
diff --git a/src/IdTable.hxx b/src/IdTable.hxx
new file mode 100644
index 000000000..8925fe8ab
--- /dev/null
+++ b/src/IdTable.hxx
@@ -0,0 +1,91 @@
+/*
+ * 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_ID_TABLE_HXX
+#define MPD_ID_TABLE_HXX
+
+#include "gcc.h"
+
+#include <algorithm>
+
+#include <assert.h>
+
+/**
+ * A table that maps id numbers to position numbers.
+ */
+class IdTable {
+ unsigned size;
+
+ unsigned next;
+
+ int *data;
+
+public:
+ IdTable(unsigned _size):size(_size), next(1), data(new int[size]) {
+ std::fill(data, data + size, -1);
+ }
+
+ ~IdTable() {
+ delete[] data;
+ }
+
+ int IdToPosition(unsigned id) const {
+ return id < size
+ ? data[id]
+ : -1;
+ }
+
+ unsigned GenerateId() {
+ assert(next > 0);
+ assert(next < size);
+
+ while (true) {
+ unsigned id = next;
+
+ ++next;
+ if (next == size)
+ next = 1;
+
+ if (data[id] < 0)
+ return id;
+ }
+ }
+
+ unsigned Insert(unsigned position) {
+ unsigned id = GenerateId();
+ data[id] = position;
+ return id;
+ }
+
+ void Move(unsigned id, unsigned position) {
+ assert(id < size);
+ assert(data[id] >= 0);
+
+ data[id] = position;
+ }
+
+ void Erase(unsigned id) {
+ assert(id < size);
+ assert(data[id] >= 0);
+
+ data[id] = -1;
+ }
+};
+
+#endif
diff --git a/src/idle.c b/src/Idle.cxx
index 2d174d78a..fce0cb7da 100644
--- a/src/idle.c
+++ b/src/Idle.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,8 +23,8 @@
*/
#include "config.h"
-#include "idle.h"
-#include "event_pipe.h"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
#include <assert.h>
#include <glib.h>
@@ -71,7 +71,7 @@ idle_add(unsigned flags)
idle_flags |= flags;
g_mutex_unlock(idle_mutex);
- event_pipe_emit(PIPE_EVENT_IDLE);
+ GlobalEvents::Emit(GlobalEvents::IDLE);
}
unsigned
diff --git a/src/idle.h b/src/Idle.hxx
index 0156933c0..e77061300 100644
--- a/src/idle.h
+++ b/src/Idle.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,8 +22,8 @@
*
*/
-#ifndef MPD_IDLE_H
-#define MPD_IDLE_H
+#ifndef MPD_IDLE_HXX
+#define MPD_IDLE_HXX
enum {
/** song database has been updated*/
diff --git a/src/inotify_queue.c b/src/InotifyQueue.cxx
index d5e2228c3..3212f95f9 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,9 @@
*/
#include "config.h"
-#include "inotify_queue.h"
-#include "update.h"
+#include "InotifyQueue.hxx"
+#include "UpdateGlue.hxx"
+#include "event/Loop.hxx"
#include <glib.h>
@@ -37,37 +38,13 @@ enum {
INOTIFY_UPDATE_DELAY_S = 5,
};
-static GSList *inotify_queue;
-static guint queue_source_id;
-
-void
-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
-mpd_inotify_run_update(G_GNUC_UNUSED gpointer data)
+bool
+InotifyQueue::OnTimeout()
{
unsigned id;
- while (inotify_queue != NULL) {
- char *uri_utf8 = inotify_queue->data;
+ while (!queue.empty()) {
+ const char *uri_utf8 = queue.front().c_str();
id = update_enqueue(uri_utf8, false);
if (id == 0)
@@ -76,13 +53,10 @@ 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);
+ queue.pop_front();
}
/* done, remove the timer event by returning false */
- queue_source_id = 0;
return false;
}
@@ -97,39 +71,25 @@ path_in(const char *path, const char *possible_parent)
}
void
-mpd_inotify_enqueue(char *uri_utf8)
+InotifyQueue::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);
+ ScheduleSeconds(INOTIFY_UPDATE_DELAY_S);
- inotify_queue = NULL;
- while (old_queue != NULL) {
- char *current_uri = old_queue->data;
+ for (auto i = queue.begin(), end = 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 = 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);
+ queue.emplace_back(uri_utf8);
}
diff --git a/src/InotifyQueue.hxx b/src/InotifyQueue.hxx
new file mode 100644
index 000000000..761df574a
--- /dev/null
+++ b/src/InotifyQueue.hxx
@@ -0,0 +1,41 @@
+/*
+ * 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_INOTIFY_QUEUE_HXX
+#define MPD_INOTIFY_QUEUE_HXX
+
+#include "event/TimeoutMonitor.hxx"
+#include "gcc.h"
+
+#include <list>
+#include <string>
+
+class InotifyQueue final : private TimeoutMonitor {
+ std::list<std::string> queue;
+
+public:
+ InotifyQueue(EventLoop &_loop):TimeoutMonitor(_loop) {}
+
+ void Enqueue(const char *uri_utf8);
+
+private:
+ virtual bool OnTimeout() override;
+};
+
+#endif
diff --git a/src/inotify_source.c b/src/InotifySource.cxx
index e415f5e72..5da32c9bd 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,35 +18,20 @@
*/
#include "config.h"
-#include "inotify_source.h"
-#include "fifo_buffer.h"
+#include "InotifySource.hxx"
+#include "util/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"
-struct mpd_inotify_source {
- int fd;
-
- GIOChannel *channel;
-
- /**
- * The channel's source id in the GLib main loop.
- */
- guint id;
-
- struct fifo_buffer *buffer;
-
- mpd_inotify_callback_t callback;
- void *callback_ctx;
-};
-
/**
* A GQuark for GError instances.
*/
@@ -56,34 +41,32 @@ mpd_inotify_quark(void)
return g_quark_from_static_string("inotify");
}
-static gboolean
-mpd_inotify_in_event(G_GNUC_UNUSED GIOChannel *_source,
- G_GNUC_UNUSED GIOCondition condition,
- gpointer data)
+void
+InotifySource::OnSocketReady(gcc_unused unsigned flags)
{
- struct mpd_inotify_source *source = data;
void *dest;
size_t length;
ssize_t nbytes;
- const struct inotify_event *event;
- dest = fifo_buffer_write(source->buffer, &length);
+ dest = fifo_buffer_write(buffer, &length);
if (dest == NULL)
MPD_ERROR("buffer full");
- nbytes = read(source->fd, dest, length);
+ nbytes = read(Get(), dest, length);
if (nbytes < 0)
MPD_ERROR("failed to read from inotify: %s",
g_strerror(errno));
if (nbytes == 0)
MPD_ERROR("end of file from inotify");
- fifo_buffer_append(source->buffer, nbytes);
+ fifo_buffer_append(buffer, nbytes);
while (true) {
const char *name;
- event = fifo_buffer_read(source->buffer, &length);
+ const struct inotify_event *event =
+ (const struct inotify_event *)
+ fifo_buffer_read(buffer, &length);
if (event == NULL || length < sizeof(*event) ||
length < sizeof(*event) + event->len)
break;
@@ -93,59 +76,48 @@ mpd_inotify_in_event(G_GNUC_UNUSED GIOChannel *_source,
else
name = NULL;
- source->callback(event->wd, event->mask, name,
- source->callback_ctx);
- fifo_buffer_consume(source->buffer,
- sizeof(*event) + event->len);
+ callback(event->wd, event->mask, name, callback_ctx);
+ fifo_buffer_consume(buffer, sizeof(*event) + event->len);
}
-
- return true;
}
-struct mpd_inotify_source *
-mpd_inotify_source_new(mpd_inotify_callback_t callback, void *callback_ctx,
- GError **error_r)
+inline
+InotifySource::InotifySource(EventLoop &_loop,
+ mpd_inotify_callback_t _callback, void *_ctx,
+ int _fd)
+ :SocketMonitor(_fd, _loop),
+ callback(_callback), callback_ctx(_ctx),
+ buffer(fifo_buffer_new(4096))
{
- struct mpd_inotify_source *source =
- g_new(struct mpd_inotify_source, 1);
+ ScheduleRead();
- source->fd = inotify_init_cloexec();
- if (source->fd < 0) {
+}
+
+InotifySource *
+InotifySource::Create(EventLoop &loop,
+ mpd_inotify_callback_t callback, void *callback_ctx,
+ GError **error_r)
+{
+ int fd = inotify_init_cloexec();
+ if (fd < 0) {
g_set_error(error_r, mpd_inotify_quark(), errno,
"inotify_init() has failed: %s",
g_strerror(errno));
- g_free(source);
return NULL;
}
- source->buffer = fifo_buffer_new(4096);
-
- source->channel = g_io_channel_unix_new(source->fd);
- source->id = g_io_add_watch(source->channel, G_IO_IN,
- mpd_inotify_in_event, source);
-
- source->callback = callback;
- source->callback_ctx = callback_ctx;
-
- return source;
+ return new InotifySource(loop, callback, callback_ctx, fd);
}
-void
-mpd_inotify_source_free(struct mpd_inotify_source *source)
+InotifySource::~InotifySource()
{
- g_source_remove(source->id);
- g_io_channel_unref(source->channel);
- fifo_buffer_free(source->buffer);
- close(source->fd);
- g_free(source);
+ fifo_buffer_free(buffer);
}
int
-mpd_inotify_source_add(struct mpd_inotify_source *source,
- const char *path_fs, unsigned mask,
- GError **error_r)
+InotifySource::Add(const char *path_fs, unsigned mask, GError **error_r)
{
- int wd = inotify_add_watch(source->fd, path_fs, mask);
+ int wd = inotify_add_watch(Get(), path_fs, mask);
if (wd < 0)
g_set_error(error_r, mpd_inotify_quark(), errno,
"inotify_add_watch() has failed: %s",
@@ -155,9 +127,9 @@ mpd_inotify_source_add(struct mpd_inotify_source *source,
}
void
-mpd_inotify_source_rm(struct mpd_inotify_source *source, unsigned wd)
+InotifySource::Remove(unsigned wd)
{
- int ret = inotify_rm_watch(source->fd, wd);
+ int ret = inotify_rm_watch(Get(), wd);
if (ret < 0 && errno != EINVAL)
g_warning("inotify_rm_watch() has failed: %s",
g_strerror(errno));
diff --git a/src/InotifySource.hxx b/src/InotifySource.hxx
new file mode 100644
index 000000000..0f29ae754
--- /dev/null
+++ b/src/InotifySource.hxx
@@ -0,0 +1,74 @@
+/*
+ * 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_INOTIFY_SOURCE_HXX
+#define MPD_INOTIFY_SOURCE_HXX
+
+#include "event/SocketMonitor.hxx"
+#include "gerror.h"
+#include "gcc.h"
+
+#include <glib.h>
+
+typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask,
+ const char *name, void *ctx);
+
+class InotifySource final : private SocketMonitor {
+ mpd_inotify_callback_t callback;
+ void *callback_ctx;
+
+ struct fifo_buffer *buffer;
+
+ InotifySource(EventLoop &_loop,
+ mpd_inotify_callback_t callback, void *ctx, int fd);
+
+public:
+ /**
+ * Creates a new inotify source and registers it in the GLib main
+ * loop.
+ *
+ * @param a callback invoked for events received from the kernel
+ */
+ static InotifySource *Create(EventLoop &_loop,
+ mpd_inotify_callback_t callback,
+ void *ctx,
+ GError **error_r);
+
+ ~InotifySource();
+
+
+ /**
+ * Adds a path to the notify list.
+ *
+ * @return a watch descriptor or -1 on error
+ */
+ int Add(const char *path_fs, unsigned mask, GError **error_r);
+
+ /**
+ * Removes a path from the notify list.
+ *
+ * @param wd the watch descriptor returned by mpd_inotify_source_add()
+ */
+ void Remove(unsigned wd);
+
+private:
+ virtual void OnSocketReady(unsigned flags) override;
+};
+
+#endif
diff --git a/src/inotify_update.c b/src/InotifyUpdate.cxx
index 3f4a8c0c4..7ed0e84ba 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,20 @@
*/
#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"
+#include "Main.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>
@@ -54,7 +57,8 @@ struct watch_directory {
GList *children;
};
-static struct mpd_inotify_source *inotify_source;
+static InotifySource *inotify_source;
+static InotifyQueue *inotify_queue;
static unsigned inotify_max_depth;
static struct watch_directory inotify_root;
@@ -90,7 +94,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,12 +114,12 @@ 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);
- mpd_inotify_source_rm(inotify_source, directory->descriptor);
+ inotify_source->Remove(directory->descriptor);
g_free(directory->name);
g_slice_free(struct watch_directory, directory);
}
@@ -192,8 +197,7 @@ recursive_watch_subdirectories(struct watch_directory *directory,
continue;
}
- ret = mpd_inotify_source_add(inotify_source, child_path_fs,
- IN_MASK, &error);
+ ret = inotify_source->Add(child_path_fs, IN_MASK, &error);
if (ret < 0) {
g_warning("Failed to register %s: %s",
child_path_fs, error->message);
@@ -292,10 +296,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 */
- mpd_inotify_enqueue(uri_utf8);
+ if (uri_utf8 != NULL) {
+ inotify_queue->Enqueue(uri_utf8);
+ g_free(uri_utf8);
+ }
}
g_free(uri_fs);
@@ -314,8 +318,9 @@ mpd_inotify_init(unsigned max_depth)
return;
}
- inotify_source = mpd_inotify_source_new(mpd_inotify_callback, NULL,
- &error);
+ inotify_source = InotifySource::Create(*main_loop,
+ mpd_inotify_callback, nullptr,
+ &error);
if (inotify_source == NULL) {
g_warning("%s", error->message);
g_error_free(error);
@@ -325,12 +330,11 @@ mpd_inotify_init(unsigned max_depth)
inotify_max_depth = max_depth;
inotify_root.name = g_strdup(path);
- inotify_root.descriptor = mpd_inotify_source_add(inotify_source, path,
- IN_MASK, &error);
+ inotify_root.descriptor = inotify_source->Add(path, IN_MASK, &error);
if (inotify_root.descriptor < 0) {
g_warning("%s", error->message);
g_error_free(error);
- mpd_inotify_source_free(inotify_source);
+ delete inotify_source;
inotify_source = NULL;
return;
}
@@ -340,7 +344,7 @@ mpd_inotify_init(unsigned max_depth)
recursive_watch_subdirectories(&inotify_root, path, 0);
- mpd_inotify_queue_init();
+ inotify_queue = new InotifyQueue(*main_loop);
g_debug("watching music directory");
}
@@ -349,7 +353,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);
@@ -366,8 +370,8 @@ mpd_inotify_finish(void)
if (inotify_source == NULL)
return;
- mpd_inotify_queue_finish();
- mpd_inotify_source_free(inotify_source);
+ delete inotify_queue;
+ delete inotify_source;
g_tree_foreach(inotify_directories, free_watch_directory, NULL);
g_tree_destroy(inotify_directories);
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/input_init.c b/src/InputInit.cxx
index 771d648d1..6714cc727 100644
--- a/src/input_init.c
+++ b/src/InputInit.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,9 @@
*/
#include "config.h"
-#include "input_init.h"
+#include "InputInit.hxx"
+#include "InputRegistry.hxx"
#include "input_plugin.h"
-#include "input_registry.h"
#include "conf.h"
#include <assert.h>
diff --git a/src/input_init.h b/src/InputInit.hxx
index ad92cda08..9d503e5a8 100644
--- a/src/input_init.h
+++ b/src/InputInit.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,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_INIT_H
-#define MPD_INPUT_INIT_H
+#ifndef MPD_INPUT_INIT_HXX
+#define MPD_INPUT_INIT_HXX
-#include "check.h"
-
-#include <glib.h>
-#include <stdbool.h>
+#include "gerror.h"
/**
* Initializes this library and all input_stream implementations.
diff --git a/src/input_registry.c b/src/InputRegistry.cxx
index 5987d5da2..3dd825915 100644
--- a/src/input_registry.c
+++ b/src/InputRegistry.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 "input_registry.h"
+#include "InputRegistry.hxx"
#include "input/file_input_plugin.h"
#ifdef ENABLE_ARCHIVE
@@ -26,11 +26,11 @@
#endif
#ifdef ENABLE_CURL
-#include "input/curl_input_plugin.h"
+#include "input/CurlInputPlugin.hxx"
#endif
#ifdef ENABLE_SOUP
-#include "input/soup_input_plugin.h"
+#include "input/SoupInputPlugin.hxx"
#endif
#ifdef HAVE_FFMPEG
diff --git a/src/input_registry.h b/src/InputRegistry.hxx
index 4f5fff8da..a080d108b 100644
--- a/src/input_registry.h
+++ b/src/InputRegistry.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 MPD_INPUT_REGISTRY_H
-#define MPD_INPUT_REGISTRY_H
+#ifndef MPD_INPUT_REGISTRY_HXX
+#define MPD_INPUT_REGISTRY_HXX
#include "check.h"
-#include <stdbool.h>
-
/**
* NULL terminated list of all input plugins which were enabled at
* compile time.
diff --git a/src/input_stream.c b/src/InputStream.cxx
index e445dca6c..64f347555 100644
--- a/src/input_stream.c
+++ b/src/InputStream.cxx
@@ -19,9 +19,13 @@
#include "config.h"
#include "input_stream.h"
-#include "input_registry.h"
+#include "InputRegistry.hxx"
#include "input_plugin.h"
+
+extern "C" {
#include "input/rewind_input_plugin.h"
+#include "uri.h"
+}
#include <glib.h>
#include <assert.h>
@@ -115,6 +119,12 @@ input_stream_lock_wait_ready(struct input_stream *is)
}
bool
+input_stream_cheap_seeking(const struct input_stream *is)
+{
+ return is->seekable && (is->uri == NULL || !uri_has_scheme(is->uri));
+}
+
+bool
input_stream_seek(struct input_stream *is, goffset offset, int whence,
GError **error_r)
{
@@ -165,7 +175,7 @@ input_stream_lock_tag(struct input_stream *is)
assert(is->plugin != NULL);
if (is->plugin->tag == NULL)
- return false;
+ return nullptr;
if (is->mutex == NULL)
/* no locking */
diff --git a/src/listen.c b/src/Listen.cxx
index 90e13b9c1..7bc10dc18 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,11 @@
*/
#include "config.h"
-#include "listen.h"
-#include "server_socket.h"
-#include "client.h"
+#include "Listen.hxx"
+#include "Main.hxx"
+#include "Client.hxx"
#include "conf.h"
-#include "main.h"
+#include "ServerSocket.hxx"
#include <string.h>
#include <assert.h>
@@ -43,7 +43,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(*main_loop, *global_partition,
+ fd, address, address_length, uid);
}
static bool
@@ -91,13 +92,15 @@ listen_systemd_activation(GError **error_r)
bool
listen_global_init(GError **error_r)
{
+ assert(main_loop != nullptr);
+
int port = config_get_positive(CONF_PORT, DEFAULT_PORT);
const struct config_param *param =
config_get_next_param(CONF_BIND_TO_ADDRESS, NULL);
bool success;
GError *error = NULL;
- listen_socket = server_socket_new(listen_callback, NULL);
+ listen_socket = server_socket_new(*main_loop, listen_callback, NULL);
if (listen_systemd_activation(&error))
return true;
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/log.c b/src/Log.cxx
index 2d3c7cafd..76b365778 100644
--- a/src/log.c
+++ b/src/Log.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,8 @@
*/
#include "config.h"
-#include "log.h"
+#include "Log.hxx"
#include "conf.h"
-#include "utils.h"
#include "fd_util.h"
#include "mpd_error.h"
diff --git a/src/log.h b/src/Log.hxx
index 683ff3e9f..cfcece104 100644
--- a/src/log.h
+++ b/src/Log.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_LOG_H
-#define MPD_LOG_H
+#ifndef MPD_LOG_HXX
+#define MPD_LOG_HXX
#include <glib.h>
#include <stdbool.h>
diff --git a/src/main.c b/src/Main.cxx
index 12f8d86f6..917d89457 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,62 @@
*/
#include "config.h"
-#include "main.h"
-#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 "Main.hxx"
+#include "CommandLine.hxx"
+#include "PlaylistFile.hxx"
+#include "PlaylistGlobal.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"
+#include "Volume.hxx"
+#include "OutputAll.hxx"
+#include "tag.h"
#include "conf.h"
+#include "replay_gain_config.h"
+#include "Idle.hxx"
+#include "SignalHandlers.hxx"
+#include "Log.hxx"
+#include "GlobalEvents.hxx"
+#include "InputInit.hxx"
+#include "event/Loop.hxx"
+#include "IOThread.hxx"
+
+extern "C" {
+#include "daemon.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>
@@ -91,11 +97,11 @@ enum {
};
GThread *main_task;
-GMainLoop *main_loop;
+EventLoop *main_loop;
-GCond *main_cond;
+Partition *global_partition;
-struct player_control *global_player_control;
+static StateFile *state_file;
static bool
glue_daemonize_init(const struct options *options, GError **error_r)
@@ -153,31 +159,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();
}
/**
@@ -205,14 +227,18 @@ glue_state_file_init(GError **error_r)
GError *error = NULL;
char *path = config_dup_path(CONF_STATE_FILE, &error);
- if (path == NULL && error != NULL) {
- g_propagate_error(error_r, error);
- return false;
+ if (path == nullptr) {
+ if (error != nullptr) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ return true;
}
- state_file_init(path, global_player_control);
+ state_file = new StateFile(path, *global_partition, *main_loop);
g_free(path);
-
+ state_file->Read();
return true;
}
@@ -285,11 +311,17 @@ 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);
}
/**
- * event_pipe callback function for PIPE_EVENT_IDLE
+ * Handler for GlobalEvents::IDLE.
*/
static void
idle_event_emitted(void)
@@ -302,12 +334,12 @@ idle_event_emitted(void)
}
/**
- * event_pipe callback function for PIPE_EVENT_SHUTDOWN
+ * Handler for GlobalEvents::SHUTDOWN.
*/
static void
shutdown_event_emitted(void)
{
- g_main_loop_quit(main_loop);
+ main_loop->Break();
}
int main(int argc, char *argv[])
@@ -342,7 +374,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);
@@ -367,6 +398,9 @@ int mpd_main(int argc, char *argv[])
return EXIT_FAILURE;
}
+ main_task = g_thread_self();
+ main_loop = new EventLoop(EventLoop::Default());
+
success = listen_global_init(&error);
if (!success) {
g_warning("%s", error->message);
@@ -376,13 +410,9 @@ int mpd_main(int argc, char *argv[])
daemonize_set_user();
- main_task = g_thread_self();
- main_loop = g_main_loop_new(NULL, FALSE);
- main_cond = g_cond_new();
-
- event_pipe_init();
- event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted);
- event_pipe_register(PIPE_EVENT_SHUTDOWN, shutdown_event_emitted);
+ GlobalEvents::Initialize();
+ GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted);
+ GlobalEvents::Register(GlobalEvents::SHUTDOWN, shutdown_event_emitted);
path_global_init();
@@ -416,7 +446,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 +472,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 +488,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,14 +504,14 @@ 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();
#endif
/* run the main loop */
- g_main_loop_run(main_loop);
+ main_loop->Run();
#ifdef WIN32
win32_app_stopping();
@@ -487,21 +519,24 @@ int mpd_main(int argc, char *argv[])
/* cleanup */
- g_main_loop_unref(main_loop);
+ delete main_loop;
#ifdef ENABLE_INOTIFY
mpd_inotify_finish();
#endif
- state_file_finish(global_player_control);
- pc_kill(global_player_control);
+ if (state_file != nullptr) {
+ state_file->Write();
+ delete state_file;
+ }
+
+ 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);
@@ -509,8 +544,7 @@ int mpd_main(int argc, char *argv[])
sticker_global_finish();
#endif
- g_cond_free(main_cond);
- event_pipe_deinit();
+ GlobalEvents::Deinitialize();
playlist_list_global_finish();
input_stream_global_finish();
@@ -518,8 +552,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 +560,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..6ce079434 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,18 +17,18 @@
* 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>
-extern GThread *main_task;
+class EventLoop;
-extern GMainLoop *main_loop;
+extern GThread *main_task;
-extern GCond *main_cond;
+extern EventLoop *main_loop;
-extern struct player_control *global_player_control;
+extern struct Partition *global_partition;
/**
* A entry point for application.
@@ -52,7 +52,7 @@ win32_main(int argc, char *argv[]);
* When running as a service reports to service control manager
* that our service is started.
* When running as a console application enables console handler that will
- * trigger PIPE_EVENT_SHUTDOWN when user closes console window
+ * trigger GlobalEvents::SHUTDOWN when user closes console window
* or presses Ctrl+C.
* This function should be called just before entering main loop.
*/
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..6a967f95b
--- /dev/null
+++ b/src/MessageCommands.cxx
@@ -0,0 +1,167 @@
+/*
+ * 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 "ClientList.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(Client *client, gpointer user_data)
+{
+ struct channels_context *context =
+ (struct channels_context *)user_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(Client *client, gpointer user_data)
+{
+ struct send_message_context *context =
+ (struct send_message_context *)user_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/mixer_all.c b/src/MixerAll.cxx
index 95ba90793..a214c1e24 100644
--- a/src/mixer_all.c
+++ b/src/MixerAll.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,15 @@
*/
#include "config.h"
-#include "mixer_all.h"
+#include "MixerAll.hxx"
+#include "pcm_volume.h"
+#include "OutputAll.hxx"
+
+extern "C" {
#include "mixer_control.h"
-#include "output_all.h"
-#include "output_plugin.h"
#include "output_internal.h"
-#include "pcm_volume.h"
#include "mixer_api.h"
-#include "mixer_list.h"
+}
#include <glib.h>
diff --git a/src/mixer_all.h b/src/MixerAll.hxx
index fe873e713..23350a843 100644
--- a/src/mixer_all.h
+++ b/src/MixerAll.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,8 @@
* Functions which affect the mixers of all audio outputs.
*/
-#ifndef MPD_MIXER_ALL_H
-#define MPD_MIXER_ALL_H
-
-#include <stdbool.h>
+#ifndef MPD_MIXER_ALL_HXX
+#define MPD_MIXER_ALL_HXX
/**
* Returns the average volume of all available mixers (range 0..100).
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..eefda24b5
--- /dev/null
+++ b/src/MusicChunk.cxx
@@ -0,0 +1,84 @@
+/*
+ * 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"
+#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..b8cc36cc5
--- /dev/null
+++ b/src/OtherCommands.cxx
@@ -0,0 +1,308 @@
+/*
+ * 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"
+#include "Volume.hxx"
+
+extern "C" {
+#include "uri.h"
+#include "stats.h"
+}
+
+#include "Permission.hxx"
+#include "PlaylistFile.hxx"
+#include "ClientIdle.hxx"
+#include "ClientFile.hxx"
+#include "Client.hxx"
+#include "Idle.hxx"
+
+#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..a18a63385 100644
--- a/src/output_all.c
+++ b/src/OutputAll.cxx
@@ -18,20 +18,21 @@
*/
#include "config.h"
-#include "output_all.h"
+#include "OutputAll.hxx"
+
+extern "C" {
#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 "mpd_error.h"
-#include "notify.h"
+}
-#ifndef NDEBUG
-#include "chunk.h"
-#endif
+#include "PlayerControl.hxx"
+#include "OutputControl.hxx"
+#include "OutputError.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicPipe.hxx"
+#include "MusicChunk.hxx"
+#include "mpd_error.h"
+#include "conf.h"
+#include "notify.hxx"
#include <assert.h>
#include <string.h>
@@ -109,8 +110,6 @@ audio_output_all_init(struct player_control *pc)
unsigned int i;
GError *error = NULL;
- notify_init(&audio_output_client_notify);
-
num_audio_outputs = audio_output_config_count();
audio_outputs = g_new(struct audio_output *, num_audio_outputs);
@@ -157,8 +156,6 @@ audio_output_all_finish(void)
g_free(audio_outputs);
audio_outputs = NULL;
num_audio_outputs = 0;
-
- notify_deinit(&audio_output_client_notify);
}
void
@@ -207,7 +204,7 @@ audio_output_all_finished(void)
static void audio_output_wait_all(void)
{
while (!audio_output_all_finished())
- notify_wait(&audio_output_client_notify);
+ audio_output_client_notify.Wait();
}
/**
@@ -269,8 +266,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 +282,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 +302,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 +343,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_all.h b/src/OutputAll.hxx
index 4eeb94f13..becf4b695 100644
--- a/src/output_all.h
+++ b/src/OutputAll.hxx
@@ -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_command.c b/src/OutputCommand.cxx
index 3988f350a..f4edaa4a1 100644
--- a/src/output_command.c
+++ b/src/OutputCommand.cxx
@@ -25,13 +25,16 @@
*/
#include "config.h"
-#include "output_command.h"
-#include "output_all.h"
+#include "OutputCommand.hxx"
+#include "OutputAll.hxx"
+#include "PlayerControl.hxx"
+#include "Idle.hxx"
+
+extern "C" {
#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..13625ade2 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 "notify.hxx"
+#include "filter/ReplayGainFilterPlugin.hxx"
#include "filter_plugin.h"
-#include "notify.h"
#include <assert.h>
#include <stdlib.h>
@@ -47,7 +52,7 @@ static void ao_command_wait(struct audio_output *ao)
{
while (ao->command != AO_COMMAND_NONE) {
g_mutex_unlock(ao->mutex);
- notify_wait(&audio_output_client_notify);
+ audio_output_client_notify.Wait();
g_mutex_lock(ao->mutex);
}
}
@@ -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..3c394ba44 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"
@@ -32,7 +34,9 @@
#include "filter_config.h"
#include "filter/chain_filter_plugin.h"
#include "filter/autoconvert_filter_plugin.h"
-#include "filter/replay_gain_filter_plugin.h"
+}
+
+#include "filter/ReplayGainFilterPlugin.hxx"
#include <glib.h>
@@ -165,6 +169,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 +302,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 +317,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..df064219f 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,13 +18,13 @@
*/
#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"
#include "output/ffado_output_plugin.h"
#include "output/fifo_output_plugin.h"
-#include "output/httpd_output_plugin.h"
+#include "output/HttpdOutputPlugin.hxx"
#include "output/jack_output_plugin.h"
#include "output/mvp_output_plugin.h"
#include "output/null_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..240ea967b 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,15 @@
*/
#include "config.h"
-#include "output_print.h"
+#include "OutputPrint.hxx"
+#include "OutputAll.hxx"
#include "output_internal.h"
-#include "output_all.h"
-#include "client.h"
+#include "Client.hxx"
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..27fa34f8f 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,9 @@
*/
#include "config.h"
-#include "output_state.h"
+#include "OutputState.hxx"
+#include "OutputAll.hxx"
#include "output_internal.h"
-#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..59394e072 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 "notify.hxx"
+#include "filter/ReplayGainFilterPlugin.hxx"
+#include "PlayerControl.hxx"
+#include "MusicPipe.hxx"
+#include "MusicChunk.hxx"
+
#include "mpd_error.h"
-#include "notify.h"
#include "gcc.h"
#include <glib.h>
@@ -47,7 +52,7 @@ static void ao_command_finished(struct audio_output *ao)
ao->command = AO_COMMAND_NONE;
g_mutex_unlock(ao->mutex);
- notify_signal(&audio_output_client_notify);
+ audio_output_client_notify.Signal();
g_mutex_lock(ao->mutex);
}
@@ -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..9efde274a
--- /dev/null
+++ b/src/Partition.hxx
@@ -0,0 +1,165 @@
+/*
+ * 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) {
+ }
+
+ void ClearQueue() {
+ playlist.Clear(pc);
+ }
+
+ enum playlist_result AppendFile(const char *path_fs,
+ unsigned *added_id=nullptr) {
+ return playlist.AppendFile(pc, path_fs, added_id);
+ }
+
+ enum playlist_result AppendURI(const char *uri_utf8,
+ unsigned *added_id=nullptr) {
+ return playlist.AppendURI(pc, uri_utf8, added_id);
+ }
+
+ enum playlist_result DeletePosition(unsigned position) {
+ return playlist.DeletePosition(pc, position);
+ }
+
+ enum playlist_result DeleteId(unsigned id) {
+ return playlist.DeleteId(pc, id);
+ }
+
+ /**
+ * Deletes a range of songs from the playlist.
+ *
+ * @param start the position of the first song to delete
+ * @param end the position after the last song to delete
+ */
+ enum playlist_result DeleteRange(unsigned start, unsigned end) {
+ return playlist.DeleteRange(pc, start, end);
+ }
+
+ void DeleteSong(const song &song) {
+ playlist.DeleteSong(pc, song);
+ }
+
+ void Shuffle(unsigned start, unsigned end) {
+ playlist.Shuffle(pc, start, end);
+ }
+
+ enum playlist_result MoveRange(unsigned start, unsigned end, int to) {
+ return playlist.MoveRange(pc, start, end, to);
+ }
+
+ enum playlist_result MoveId(unsigned id, int to) {
+ return playlist.MoveId(pc, id, to);
+ }
+
+ enum playlist_result SwapPositions(unsigned song1, unsigned song2) {
+ return playlist.SwapPositions(pc, song1, song2);
+ }
+
+ enum playlist_result SwapIds(unsigned id1, unsigned id2) {
+ return playlist.SwapIds(pc, id1, id2);
+ }
+
+ enum playlist_result SetPriorityRange(unsigned start_position,
+ unsigned end_position,
+ uint8_t priority) {
+ return playlist.SetPriorityRange(pc,
+ start_position, end_position,
+ priority);
+ }
+
+ enum playlist_result SetPriorityId(unsigned song_id,
+ uint8_t priority) {
+ return playlist.SetPriorityId(pc, song_id, priority);
+ }
+
+ void Stop() {
+ playlist.Stop(pc);
+ }
+
+ enum playlist_result PlayPosition(int position) {
+ return playlist.PlayPosition(pc, position);
+ }
+
+ enum playlist_result PlayId(int id) {
+ return playlist.PlayId(pc, id);
+ }
+
+ void PlayNext() {
+ return playlist.PlayNext(pc);
+ }
+
+ void PlayPrevious() {
+ return playlist.PlayPrevious(pc);
+ }
+
+ enum playlist_result SeekSongPosition(unsigned song_position,
+ float seek_time) {
+ return playlist.SeekSongPosition(pc, song_position, seek_time);
+ }
+
+ enum playlist_result SeekSongId(unsigned song_id, float seek_time) {
+ return playlist.SeekSongId(pc, song_id, seek_time);
+ }
+
+ enum playlist_result SeekCurrent(float seek_time, bool relative) {
+ return playlist.SeekCurrent(pc, seek_time, relative);
+ }
+
+ void SetRepeat(bool new_value) {
+ playlist.SetRepeat(pc, new_value);
+ }
+
+ bool GetRandom() const {
+ return playlist.GetRandom();
+ }
+
+ void SetRandom(bool new_value) {
+ playlist.SetRandom(pc, new_value);
+ }
+
+ void SetSingle(bool new_value) {
+ playlist.SetSingle(pc, new_value);
+ }
+
+ void SetConsume(bool new_value) {
+ playlist.SetConsume(new_value);
+ }
+};
+
+#endif
diff --git a/src/permission.c b/src/Permission.cxx
index cd52b9c86..e6cf5cfb8 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,9 +18,12 @@
*/
#include "config.h"
-#include "permission.h"
-#include "conf.h"
+#include "Permission.hxx"
#include "mpd_error.h"
+#include "conf.h"
+
+#include <map>
+#include <string>
#include <glib.h>
@@ -35,7 +38,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 +78,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 +101,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 +114,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..99ede9bd8
--- /dev/null
+++ b/src/PlayerCommands.cxx
@@ -0,0 +1,394 @@
+/*
+ * 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 "Volume.hxx"
+#include "OutputAll.hxx"
+#include "Partition.hxx"
+#include "protocol/Result.hxx"
+#include "protocol/ArgParser.hxx"
+
+extern "C" {
+#include "audio_format.h"
+}
+
+#include "replay_gain_config.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"
+
+enum command_return
+handle_play(Client *client, int argc, char *argv[])
+{
+ int song = -1;
+
+ if (argc == 2 && !check_int(client, &song, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ enum playlist_result result = client->partition.PlayPosition(song);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_playid(Client *client, int argc, char *argv[])
+{
+ int id = -1;
+
+ if (argc == 2 && !check_int(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result = client->partition.PlayId(id);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_stop(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ client->partition.Stop();
+ 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.GetRepeat(),
+ playlist.GetRandom(),
+ playlist.GetSingle(),
+ playlist.GetConsume(),
+ (unsigned long)playlist.GetVersion(),
+ playlist.GetLength(),
+ (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.GetCurrentPosition();
+ if (song >= 0) {
+ client_printf(client,
+ COMMAND_STATUS_SONG ": %i\n"
+ COMMAND_STATUS_SONGID ": %u\n",
+ song, playlist.PositionToId(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.GetNextPosition();
+ if (song >= 0) {
+ client_printf(client,
+ COMMAND_STATUS_NEXTSONG ": %i\n"
+ COMMAND_STATUS_NEXTSONGID ": %u\n",
+ song, playlist.PositionToId(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;
+
+ client->partition.PlayNext();
+
+ 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[])
+{
+ client->partition.PlayPrevious();
+ 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;
+
+ client->partition.SetRepeat(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;
+
+ client->partition.SetSingle(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;
+
+ client->partition.SetConsume(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;
+
+ client->partition.SetRandom(status);
+ audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(client->partition.GetRandom()));
+ 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;
+
+ if (!check_unsigned(client, &song, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &seek_time, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SeekSongPosition(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;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &seek_time, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SeekSongId(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 =
+ client->partition.SeekCurrent(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..62a8a4f80 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,78 +18,61 @@
*/
#include "config.h"
-#include "player_control.h"
-#include "decoder_control.h"
-#include "path.h"
-#include "log.h"
-#include "tag.h"
+#include "PlayerControl.hxx"
+#include "Idle.hxx"
#include "song.h"
-#include "idle.h"
-#include "pcm_volume.h"
-#include "main.h"
+#include "DecoderControl.hxx"
+#include "Main.hxx"
+
+#include <cmath>
#include <assert.h>
#include <stdio.h>
-#include <math.h>
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),
+ 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(std::nanf("")),
+ total_play_time(0),
+ border_pause(false)
{
- 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);
}
void
-player_wait_decoder(struct player_control *pc, struct decoder_control *dc)
+player_wait_decoder(gcc_unused struct player_control *pc,
+ struct decoder_control *dc)
{
assert(pc != NULL);
assert(dc != NULL);
- assert(dc->client_cond == pc->cond);
/* during this function, the decoder lock is held, because
we're waiting for the decoder thread */
- 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;
- }
+ g_cond_wait(dc->client_cond, dc->mutex);
}
static void
player_command_wait_locked(struct player_control *pc)
{
while (pc->command != PLAYER_COMMAND_NONE)
- g_cond_wait(main_cond, pc->mutex);
+ pc->cond.wait(pc->mutex);
}
static void
@@ -236,70 +219,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 +284,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 +316,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 +324,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 +331,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..322b95c84 100644
--- a/src/player_control.h
+++ b/src/PlayerControl.hxx
@@ -21,6 +21,8 @@
#define MPD_PLAYER_H
#include "audio_format.h"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
#include <glib.h>
@@ -66,12 +68,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 {
@@ -94,22 +101,39 @@ struct player_control {
/**
* This lock protects #command, #state, #error.
*/
- GMutex *mutex;
+ Mutex mutex;
/**
* Trigger this object after you have modified #command.
*/
- GCond *cond;
+ Cond cond;
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 +148,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.
@@ -138,7 +160,7 @@ pc_free(struct player_control *pc);
static inline void
player_lock(struct player_control *pc)
{
- g_mutex_lock(pc->mutex);
+ pc->mutex.lock();
}
/**
@@ -147,7 +169,7 @@ player_lock(struct player_control *pc)
static inline void
player_unlock(struct player_control *pc)
{
- g_mutex_unlock(pc->mutex);
+ pc->mutex.unlock();
}
/**
@@ -158,7 +180,7 @@ player_unlock(struct player_control *pc)
static inline void
player_wait(struct player_control *pc)
{
- g_cond_wait(pc->cond, pc->mutex);
+ pc->cond.wait(pc->mutex);
}
/**
@@ -178,7 +200,7 @@ player_wait_decoder(struct player_control *pc, struct decoder_control *dc);
static inline void
player_signal(struct player_control *pc)
{
- g_cond_signal(pc->cond);
+ pc->cond.signal();
}
/**
@@ -194,14 +216,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 +246,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 +276,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 +288,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 +315,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..fb7432368 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,23 @@
*/
#include "config.h"
-#include "player_thread.h"
-#include "player_control.h"
-#include "decoder_control.h"
-#include "decoder_thread.h"
-#include "output_all.h"
-#include "pcm_volume.h"
-#include "path.h"
-#include "event_pipe.h"
-#include "crossfade.h"
+#include "PlayerThread.hxx"
+#include "DecoderThread.hxx"
+#include "DecoderControl.hxx"
+#include "MusicPipe.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
#include "song.h"
-#include "tag.h"
-#include "pipe.h"
-#include "chunk.h"
-#include "idle.h"
-#include "main.h"
-#include "buffer.h"
+#include "Main.hxx"
#include "mpd_error.h"
+#include "CrossFade.hxx"
+#include "PlayerControl.hxx"
+#include "OutputAll.hxx"
+#include "tag.h"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
+
+#include <cmath>
#include <glib.h>
@@ -123,6 +123,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;
@@ -133,7 +147,7 @@ player_command_finished_locked(struct player_control *pc)
assert(pc->command != PLAYER_COMMAND_NONE);
pc->command = PLAYER_COMMAND_NONE;
- g_cond_signal(main_cond);
+ pc->cond.signal();
}
static void
@@ -162,7 +176,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 +249,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;
@@ -265,7 +285,7 @@ player_wait_for_decoder(struct player *player)
player_unlock(pc);
/* call syncPlaylistWithQueue() in the main thread */
- event_pipe_emit(PIPE_EVENT_PLAYLIST);
+ GlobalEvents::Emit(GlobalEvents::PLAYLIST);
return true;
}
@@ -305,7 +325,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 +337,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 +346,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 +371,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 +453,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 +480,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 +506,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 +627,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);
@@ -635,7 +665,7 @@ update_song_tag(struct song *song, const struct tag *new_tag)
/* the main thread will update the playlist version when he
receives this event */
- event_pipe_emit(PIPE_EVENT_TAG);
+ GlobalEvents::Emit(GlobalEvents::TAG);
/* notify all clients that the tag of the current song has
changed */
@@ -652,9 +682,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 +701,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 /
@@ -727,14 +758,14 @@ play_next_chunk(struct player *player)
other_chunk->tag);
other_chunk->tag = NULL;
- if (isnan(pc->mixramp_delay_seconds)) {
+ if (std::isnan(pc->mixramp_delay_seconds)) {
chunk->mix_ratio = ((float)cross_fade_position)
/ player->cross_fade_chunks;
} else {
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 +816,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 +896,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,10 +904,12 @@ 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);
- event_pipe_emit(PIPE_EVENT_PLAYLIST);
+ GlobalEvents::Emit(GlobalEvents::PLAYLIST);
player_lock(pc);
return;
}
@@ -1049,10 +1071,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;
}
@@ -1060,7 +1086,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
player_unlock(pc);
- event_pipe_emit(PIPE_EVENT_PLAYLIST);
+ GlobalEvents::Emit(GlobalEvents::PLAYLIST);
player_lock(pc);
}
@@ -1068,9 +1094,9 @@ 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);
+ struct decoder_control *dc = dc_new();
decoder_thread_start(dc);
player_buffer = music_buffer_new(pc->buffer_chunks);
@@ -1094,7 +1120,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 +1165,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.cxx b/src/Playlist.cxx
new file mode 100644
index 000000000..1977188da
--- /dev/null
+++ b/src/Playlist.cxx
@@ -0,0 +1,346 @@
+/*
+ * 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 "Playlist.hxx"
+#include "PlayerControl.hxx"
+#include "song.h"
+#include "Idle.hxx"
+
+#include <glib.h>
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "playlist"
+
+void
+playlist::FullIncrementVersions()
+{
+ queue.ModifyAll();
+ idle_add(IDLE_PLAYLIST);
+}
+
+void
+playlist::TagChanged()
+{
+ if (!playing)
+ return;
+
+ assert(current >= 0);
+
+ queue.ModifyAtOrder(current);
+ idle_add(IDLE_PLAYLIST);
+}
+
+/**
+ * Queue a song, addressed by its order number.
+ */
+static void
+playlist_queue_song_order(struct playlist *playlist, struct player_control *pc,
+ unsigned order)
+{
+ char *uri;
+
+ assert(playlist->queue.IsValidOrder(order));
+
+ playlist->queued = 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);
+
+ pc_enqueue_song(pc, song);
+}
+
+/**
+ * Called if the player thread has started playing the "queued" song.
+ */
+static void
+playlist_song_started(struct playlist *playlist, struct player_control *pc)
+{
+ assert(pc->next_song == NULL);
+ assert(playlist->queued >= -1);
+
+ /* queued song has started: copy queued to current,
+ and notify the clients */
+
+ int current = playlist->current;
+ playlist->current = playlist->queued;
+ playlist->queued = -1;
+
+ if(playlist->queue.consume)
+ playlist->DeleteOrder(*pc, current);
+
+ idle_add(IDLE_PLAYER);
+}
+
+const struct song *
+playlist::GetQueuedSong() const
+{
+ return playing && queued >= 0
+ ? queue.GetOrder(queued)
+ : nullptr;
+}
+
+void
+playlist::UpdateQueuedSong(player_control &pc, const song *prev)
+{
+ if (!playing)
+ return;
+
+ assert(!queue.IsEmpty());
+ assert((queued < 0) == (prev == NULL));
+
+ const int next_order = current >= 0
+ ? queue.GetNextOrder(current)
+ : 0;
+
+ if (next_order == 0 && queue.random && !queue.single) {
+ /* shuffle the song order again, so we get a different
+ order each time the playlist is played
+ completely */
+ const unsigned current_position =
+ queue.OrderToPosition(current);
+
+ queue.ShuffleOrder();
+
+ /* make sure that the current still points to
+ the current song, after the song order has been
+ shuffled */
+ current = queue.PositionToOrder(current_position);
+ }
+
+ const struct song *const next_song = next_order >= 0
+ ? queue.GetOrder(next_order)
+ : nullptr;
+
+ if (prev != NULL && next_song != prev) {
+ /* clear the currently queued song */
+ pc_cancel(&pc);
+ queued = -1;
+ }
+
+ if (next_order >= 0) {
+ if (next_song != prev)
+ playlist_queue_song_order(this, &pc, next_order);
+ else
+ queued = next_order;
+ }
+}
+
+void
+playlist::PlayOrder(player_control &pc, int order)
+{
+ playing = true;
+ queued = -1;
+
+ struct song *song = song_dup_detached(queue.GetOrder(order));
+
+ char *uri = song_get_uri(song);
+ g_debug("play %i:\"%s\"", order, uri);
+ g_free(uri);
+
+ pc_play(&pc, song);
+ current = order;
+}
+
+static void
+playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
+
+void
+playlist::SyncWithPlayer(player_control &pc)
+{
+ if (!playing)
+ /* this event has reached us out of sync: we aren't
+ playing anymore; ignore the event */
+ return;
+
+ player_lock(&pc);
+ const enum player_state pc_state = pc_get_state(&pc);
+ const song *pc_next_song = pc.next_song;
+ player_unlock(&pc);
+
+ if (pc_state == PLAYER_STATE_STOP)
+ /* the player thread has stopped: check if playback
+ should be restarted with the next song. That can
+ happen if the playlist isn't filling the queue fast
+ enough */
+ playlist_resume_playback(this, &pc);
+ else {
+ /* check if the player thread has already started
+ playing the queued song */
+ if (pc_next_song == nullptr && queued != -1)
+ playlist_song_started(this, &pc);
+
+ player_lock(&pc);
+ pc_next_song = pc.next_song;
+ player_unlock(&pc);
+
+ /* make sure the queued song is always set (if
+ possible) */
+ if (pc_next_song == nullptr && queued < 0)
+ UpdateQueuedSong(pc, nullptr);
+ }
+}
+
+/**
+ * The player has stopped for some reason. Check the error, and
+ * decide whether to re-start playback
+ */
+static void
+playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
+{
+ enum player_error error;
+
+ assert(playlist->playing);
+ assert(pc_get_state(pc) == PLAYER_STATE_STOP);
+
+ 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_NONE) ||
+ error == PLAYER_ERROR_OUTPUT ||
+ playlist->error_count >= playlist->queue.GetLength())
+ /* too many errors, or critical error: stop
+ playback */
+ playlist->Stop(*pc);
+ else
+ /* continue playback at the next song */
+ playlist->PlayNext(*pc);
+}
+
+void
+playlist::SetRepeat(player_control &pc, bool status)
+{
+ if (status == queue.repeat)
+ return;
+
+ queue.repeat = status;
+
+ pc_set_border_pause(&pc, queue.single && !queue.repeat);
+
+ /* if the last song is currently being played, the "next song"
+ might change when repeat mode is toggled */
+ UpdateQueuedSong(pc, GetQueuedSong());
+
+ idle_add(IDLE_OPTIONS);
+}
+
+static void
+playlist_order(struct playlist *playlist)
+{
+ if (playlist->current >= 0)
+ /* update playlist.current, order==position now */
+ playlist->current = playlist->queue.OrderToPosition(playlist->current);
+
+ playlist->queue.RestoreOrder();
+}
+
+void
+playlist::SetSingle(player_control &pc, bool status)
+{
+ if (status == queue.single)
+ return;
+
+ queue.single = status;
+
+ pc_set_border_pause(&pc, queue.single && !queue.repeat);
+
+ /* if the last song is currently being played, the "next song"
+ might change when single mode is toggled */
+ UpdateQueuedSong(pc, GetQueuedSong());
+
+ idle_add(IDLE_OPTIONS);
+}
+
+void
+playlist::SetConsume(bool status)
+{
+ if (status == queue.consume)
+ return;
+
+ queue.consume = status;
+ idle_add(IDLE_OPTIONS);
+}
+
+void
+playlist::SetRandom(player_control &pc, bool status)
+{
+ if (status == queue.random)
+ return;
+
+ const struct song *const queued_song = GetQueuedSong();
+
+ queue.random = status;
+
+ if (queue.random) {
+ /* shuffle the queue order, but preserve current */
+
+ const int current_position = GetCurrentPosition();
+
+ 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.PositionToOrder(current_position);
+ queue.SwapOrders(0, current_order);
+ current = 0;
+ } else
+ current = -1;
+ } else
+ playlist_order(this);
+
+ UpdateQueuedSong(pc, queued_song);
+
+ idle_add(IDLE_OPTIONS);
+}
+
+int
+playlist::GetCurrentPosition() const
+{
+ return current >= 0
+ ? queue.OrderToPosition(current)
+ : -1;
+}
+
+int
+playlist::GetNextPosition() const
+{
+ if (current < 0)
+ return -1;
+
+ if (queue.single && queue.repeat)
+ return queue.OrderToPosition(current);
+ else if (queue.IsValidOrder(current + 1))
+ return queue.OrderToPosition(current + 1);
+ else if (queue.repeat)
+ return queue.OrderToPosition(0);
+
+ return -1;
+}
diff --git a/src/Playlist.hxx b/src/Playlist.hxx
new file mode 100644
index 000000000..46a9250a7
--- /dev/null
+++ b/src/Playlist.hxx
@@ -0,0 +1,256 @@
+/*
+ * 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_HXX
+#define MPD_PLAYLIST_HXX
+
+#include "Queue.hxx"
+#include "playlist_error.h"
+
+#include <stdbool.h>
+
+struct player_control;
+
+struct playlist {
+ /**
+ * The song queue - it contains the "real" playlist.
+ */
+ struct queue queue;
+
+ /**
+ * This value is true if the player is currently playing (or
+ * should be playing).
+ */
+ bool playing;
+
+ /**
+ * If true, then any error is fatal; if false, MPD will
+ * attempt to play the next song on non-fatal errors. During
+ * seeking, this flag is set.
+ */
+ bool stop_on_error;
+
+ /**
+ * Number of errors since playback was started. If this
+ * number exceeds the length of the playlist, MPD gives up,
+ * because all songs have been tried.
+ */
+ unsigned error_count;
+
+ /**
+ * The "current song pointer". This is the song which is
+ * played when we get the "play" command. It is also the song
+ * which is currently being played.
+ */
+ int current;
+
+ /**
+ * The "next" song to be played, when the current one
+ * finishes. The decoder thread may start decoding and
+ * buffering it, while the "current" song is still playing.
+ *
+ * This variable is only valid if #playing is true.
+ */
+ int queued;
+
+ playlist(unsigned max_length)
+ :queue(max_length), playing(false), current(-1), queued(-1) {
+ }
+
+ ~playlist() {
+ }
+
+ uint32_t GetVersion() const {
+ return queue.version;
+ }
+
+ unsigned GetLength() const {
+ return queue.GetLength();
+ }
+
+ unsigned PositionToId(unsigned position) const {
+ return queue.PositionToId(position);
+ }
+
+ gcc_pure
+ int GetCurrentPosition() const;
+
+ gcc_pure
+ int GetNextPosition() const;
+
+ /**
+ * Returns the song object which is currently queued. Returns
+ * none if there is none (yet?) or if MPD isn't playing.
+ */
+ gcc_pure
+ const struct song *GetQueuedSong() const;
+
+ /**
+ * This is the "PLAYLIST" event handler. It is invoked by the
+ * player thread whenever it requests a new queued song, or
+ * when it exits.
+ */
+ void SyncWithPlayer(player_control &pc);
+
+protected:
+ /**
+ * Called by all editing methods after a modification.
+ * Updates the queue version and emits #IDLE_PLAYLIST.
+ */
+ void OnModified();
+
+ /**
+ * Updates the "queued song". Calculates the next song
+ * according to the current one (if MPD isn't playing, it
+ * takes the first song), and queues this song. Clears the
+ * old queued song if there was one.
+ *
+ * @param prev the song which was previously queued, as
+ * determined by playlist_get_queued_song()
+ */
+ void UpdateQueuedSong(player_control &pc, const song *prev);
+
+public:
+ void Clear(player_control &pc);
+
+ void TagChanged();
+
+ void FullIncrementVersions();
+
+ enum playlist_result AppendSong(player_control &pc,
+ struct song *song,
+ unsigned *added_id=nullptr);
+
+ /**
+ * Appends a local file (outside the music database) to the
+ * playlist.
+ *
+ * Note: the caller is responsible for checking permissions.
+ */
+ enum playlist_result AppendFile(player_control &pc,
+ const char *path_fs,
+ unsigned *added_id=nullptr);
+
+ enum playlist_result AppendURI(player_control &pc,
+ const char *uri_utf8,
+ unsigned *added_id=nullptr);
+
+protected:
+ void DeleteInternal(player_control &pc,
+ unsigned song, const struct song **queued_p);
+
+public:
+ enum playlist_result DeletePosition(player_control &pc,
+ unsigned position);
+
+ enum playlist_result DeleteOrder(player_control &pc,
+ unsigned order) {
+ return DeletePosition(pc, queue.OrderToPosition(order));
+ }
+
+ enum playlist_result DeleteId(player_control &pc, unsigned id);
+
+ /**
+ * Deletes a range of songs from the playlist.
+ *
+ * @param start the position of the first song to delete
+ * @param end the position after the last song to delete
+ */
+ enum playlist_result DeleteRange(player_control &pc,
+ unsigned start, unsigned end);
+
+ void DeleteSong(player_control &pc, const song &song);
+
+ void Shuffle(player_control &pc, unsigned start, unsigned end);
+
+ enum playlist_result MoveRange(player_control &pc,
+ unsigned start, unsigned end, int to);
+
+ enum playlist_result MoveId(player_control &pc, unsigned id, int to);
+
+ enum playlist_result SwapPositions(player_control &pc,
+ unsigned song1, unsigned song2);
+
+ enum playlist_result SwapIds(player_control &pc,
+ unsigned id1, unsigned id2);
+
+ enum playlist_result SetPriorityRange(player_control &pc,
+ unsigned start_position,
+ unsigned end_position,
+ uint8_t priority);
+
+ enum playlist_result SetPriorityId(player_control &pc,
+ unsigned song_id, uint8_t priority);
+
+ void Stop(player_control &pc);
+
+ enum playlist_result PlayPosition(player_control &pc, int position);
+
+ void PlayOrder(player_control &pc, int order);
+
+ enum playlist_result PlayId(player_control &pc, int id);
+
+ void PlayNext(player_control &pc);
+
+ void PlayPrevious(player_control &pc);
+
+ enum playlist_result SeekSongPosition(player_control &pc,
+ unsigned song_position,
+ float seek_time);
+
+ enum playlist_result SeekSongId(player_control &pc,
+ unsigned song_id, float seek_time);
+
+ /**
+ * Seek within the current song. Fails if MPD is not currently
+ * playing.
+ *
+ * @param time the time in seconds
+ * @param relative if true, then the specified time is relative to the
+ * current position
+ */
+ enum playlist_result SeekCurrent(player_control &pc,
+ float seek_time, bool relative);
+
+ bool GetRepeat() const {
+ return queue.repeat;
+ }
+
+ void SetRepeat(player_control &pc, bool new_value);
+
+ bool GetRandom() const {
+ return queue.random;
+ }
+
+ void SetRandom(player_control &pc, bool new_value);
+
+ bool GetSingle() const {
+ return queue.single;
+ }
+
+ void SetSingle(player_control &pc, bool new_value);
+
+ bool GetConsume() const {
+ return queue.consume;
+ }
+
+ void SetConsume(bool new_value);
+};
+
+#endif
diff --git a/src/playlist_any.c b/src/PlaylistAny.cxx
index 450ca5932..f62634c96 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"
+#include "input_stream.h"
+
+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/PlaylistControl.cxx b/src/PlaylistControl.cxx
new file mode 100644
index 000000000..323192242
--- /dev/null
+++ b/src/PlaylistControl.cxx
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions for controlling playback on the playlist level.
+ *
+ */
+
+#include "config.h"
+#include "Playlist.hxx"
+#include "PlayerControl.hxx"
+#include "song.h"
+
+#include <glib.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "playlist"
+
+void
+playlist::Stop(player_control &pc)
+{
+ if (!playing)
+ return;
+
+ assert(current >= 0);
+
+ g_debug("stop");
+ pc_stop(&pc);
+ queued = -1;
+ playing = false;
+
+ if (queue.random) {
+ /* shuffle the playlist, so the next playback will
+ result in a new random order */
+
+ unsigned current_position = queue.OrderToPosition(current);
+
+ queue.ShuffleOrder();
+
+ /* make sure that "current" stays valid, and the next
+ "play" command plays the same song again */
+ current = queue.PositionToOrder(current_position);
+ }
+}
+
+enum playlist_result
+playlist::PlayPosition(player_control &pc, int song)
+{
+ pc_clear_error(&pc);
+
+ unsigned i = song;
+ if (song == -1) {
+ /* play any song ("current" song, or the first song */
+
+ if (queue.IsEmpty())
+ return PLAYLIST_RESULT_SUCCESS;
+
+ if (playing) {
+ /* already playing: unpause playback, just in
+ case it was paused, and return */
+ pc_set_pause(&pc, false);
+ return PLAYLIST_RESULT_SUCCESS;
+ }
+
+ /* select a song: "current" song, or the first one */
+ i = current >= 0
+ ? current
+ : 0;
+ } else if (!queue.IsValidPosition(song))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if (queue.random) {
+ if (song >= 0)
+ /* "i" is currently the song position (which
+ would be equal to the order number in
+ no-random mode); convert it to a order
+ number, because random mode is enabled */
+ i = queue.PositionToOrder(song);
+
+ if (!playing)
+ current = 0;
+
+ /* swap the new song with the previous "current" one,
+ so playback continues as planned */
+ queue.SwapOrders(i, current);
+ i = current;
+ }
+
+ stop_on_error = false;
+ error_count = 0;
+
+ PlayOrder(pc, i);
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::PlayId(player_control &pc, int id)
+{
+ if (id == -1)
+ return PlayPosition(pc, id);
+
+ int song = queue.IdToPosition(id);
+ if (song < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return PlayPosition(pc, song);
+}
+
+void
+playlist::PlayNext(player_control &pc)
+{
+ if (!playing)
+ return;
+
+ assert(!queue.IsEmpty());
+ assert(queue.IsValidOrder(current));
+
+ const int old_current = current;
+ stop_on_error = false;
+
+ /* determine the next song from the queue's order list */
+
+ const int next_order = queue.GetNextOrder(current);
+ if (next_order < 0) {
+ /* no song after this one: stop playback */
+ Stop(pc);
+
+ /* reset "current song" */
+ current = -1;
+ }
+ else
+ {
+ if (next_order == 0 && queue.random) {
+ /* The queue told us that the next song is the first
+ song. This means we are in repeat mode. Shuffle
+ the queue order, so this time, the user hears the
+ songs in a different than before */
+ assert(queue.repeat);
+
+ queue.ShuffleOrder();
+
+ /* note that current and queued are
+ now invalid, but playlist_play_order() will
+ discard them anyway */
+ }
+
+ PlayOrder(pc, next_order);
+ }
+
+ /* Consume mode removes each played songs. */
+ if (queue.consume)
+ DeleteOrder(pc, old_current);
+}
+
+void
+playlist::PlayPrevious(player_control &pc)
+{
+ if (!playing)
+ return;
+
+ assert(!queue.IsEmpty());
+
+ int order;
+ if (current > 0) {
+ /* play the preceding song */
+ order = current - 1;
+ } else if (queue.repeat) {
+ /* play the last song in "repeat" mode */
+ order = queue.GetLength() - 1;
+ } else {
+ /* re-start playing the current song if it's
+ the first one */
+ order = current;
+ }
+
+ PlayOrder(pc, order);
+}
+
+enum playlist_result
+playlist::SeekSongPosition(player_control &pc, unsigned song, float seek_time)
+{
+ if (!queue.IsValidPosition(song))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ const struct song *queued_song = GetQueuedSong();
+
+ unsigned i = queue.random
+ ? queue.PositionToOrder(song)
+ : song;
+
+ pc_clear_error(&pc);
+ stop_on_error = true;
+ error_count = 0;
+
+ if (!playing || (unsigned)current != i) {
+ /* seeking is not within the current song - prepare
+ song change */
+
+ playing = true;
+ current = i;
+
+ queued_song = nullptr;
+ }
+
+ struct song *the_song = song_dup_detached(queue.GetOrder(i));
+ if (!pc_seek(&pc, the_song, seek_time)) {
+ UpdateQueuedSong(pc, queued_song);
+
+ return PLAYLIST_RESULT_NOT_PLAYING;
+ }
+
+ queued = -1;
+ UpdateQueuedSong(pc, NULL);
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::SeekSongId(player_control &pc, unsigned id, float seek_time)
+{
+ int song = queue.IdToPosition(id);
+ if (song < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return SeekSongPosition(pc, song, seek_time);
+}
+
+enum playlist_result
+playlist::SeekCurrent(player_control &pc, float seek_time, bool relative)
+{
+ if (!playing)
+ return PLAYLIST_RESULT_NOT_PLAYING;
+
+ if (relative) {
+ struct player_status status;
+ pc_get_status(&pc, &status);
+
+ if (status.state != PLAYER_STATE_PLAY &&
+ status.state != PLAYER_STATE_PAUSE)
+ return PLAYLIST_RESULT_NOT_PLAYING;
+
+ seek_time += (int)status.elapsed_time;
+ }
+
+ if (seek_time < 0)
+ seek_time = 0;
+
+ return SeekSongPosition(pc, current, seek_time);
+}
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/PlaylistEdit.cxx b/src/PlaylistEdit.cxx
new file mode 100644
index 000000000..cdfb77150
--- /dev/null
+++ b/src/PlaylistEdit.cxx
@@ -0,0 +1,423 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions for editing the playlist (adding, removing, reordering
+ * songs in the queue).
+ *
+ */
+
+#include "config.h"
+#include "Playlist.hxx"
+#include "PlayerControl.hxx"
+
+extern "C" {
+#include "uri.h"
+#include "song.h"
+}
+
+#include "Idle.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+
+#include <stdlib.h>
+
+void
+playlist::OnModified()
+{
+ queue.IncrementVersion();
+
+ idle_add(IDLE_PLAYLIST);
+}
+
+void
+playlist::Clear(player_control &pc)
+{
+ Stop(pc);
+
+ queue.Clear();
+ current = -1;
+
+ OnModified();
+}
+
+enum playlist_result
+playlist::AppendFile(struct player_control &pc,
+ const char *path_fs, unsigned *added_id)
+{
+ struct song *song = song_file_load(path_fs, NULL);
+ if (song == NULL)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return AppendSong(pc, song, added_id);
+}
+
+enum playlist_result
+playlist::AppendSong(struct player_control &pc,
+ struct song *song, unsigned *added_id)
+{
+ unsigned id;
+
+ if (queue.IsFull())
+ return PLAYLIST_RESULT_TOO_LARGE;
+
+ const struct song *const queued_song = GetQueuedSong();
+
+ id = queue.Append(song, 0);
+
+ if (queue.random) {
+ /* shuffle the new song into the list of remaining
+ songs to play */
+
+ unsigned start;
+ if (queued >= 0)
+ start = queued + 1;
+ else
+ start = current + 1;
+ if (start < queue.GetLength())
+ queue.ShuffleOrderLast(start, queue.GetLength());
+ }
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ if (added_id)
+ *added_id = id;
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::AppendURI(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;
+
+ song = db->GetSong(uri, nullptr);
+ if (song == nullptr)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+ }
+
+ enum playlist_result result = AppendSong(pc, song, added_id);
+ if (db != nullptr)
+ db->ReturnSong(song);
+
+ return result;
+}
+
+enum playlist_result
+playlist::SwapPositions(player_control &pc, unsigned song1, unsigned song2)
+{
+ if (!queue.IsValidPosition(song1) || !queue.IsValidPosition(song2))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ const struct song *const queued_song = GetQueuedSong();
+
+ queue.SwapPositions(song1, song2);
+
+ if (queue.random) {
+ /* update the queue order, so that current
+ still points to the current song order */
+
+ queue.SwapOrders(queue.PositionToOrder(song1),
+ queue.PositionToOrder(song2));
+ } else {
+ /* correct the "current" song order */
+
+ if (current == (int)song1)
+ current = song2;
+ else if (current == (int)song2)
+ current = song1;
+ }
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::SwapIds(player_control &pc, unsigned id1, unsigned id2)
+{
+ int song1 = queue.IdToPosition(id1);
+ int song2 = queue.IdToPosition(id2);
+
+ if (song1 < 0 || song2 < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return SwapPositions(pc, song1, song2);
+}
+
+enum playlist_result
+playlist::SetPriorityRange(player_control &pc,
+ unsigned start, unsigned end,
+ uint8_t priority)
+{
+ if (start >= GetLength())
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if (end > GetLength())
+ end = GetLength();
+
+ if (start >= end)
+ return PLAYLIST_RESULT_SUCCESS;
+
+ /* remember "current" and "queued" */
+
+ const int current_position = GetCurrentPosition();
+ const struct song *const queued_song = GetQueuedSong();
+
+ /* apply the priority changes */
+
+ queue.SetPriorityRange(start, end, priority, current);
+
+ /* restore "current" and choose a new "queued" */
+
+ if (current_position >= 0)
+ current = queue.PositionToOrder(current_position);
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::SetPriorityId(struct player_control &pc,
+ unsigned song_id, uint8_t priority)
+{
+ int song_position = queue.IdToPosition(song_id);
+ if (song_position < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return SetPriorityRange(pc, song_position, song_position + 1,
+ priority);
+
+}
+
+void
+playlist::DeleteInternal(player_control &pc,
+ unsigned song, const struct song **queued_p)
+{
+ assert(song < GetLength());
+
+ unsigned songOrder = queue.PositionToOrder(song);
+
+ if (playing && current == (int)songOrder) {
+ bool paused = pc_get_state(&pc) == PLAYER_STATE_PAUSE;
+
+ /* the current song is going to be deleted: stop the player */
+
+ pc_stop(&pc);
+ playing = false;
+
+ /* see which song is going to be played instead */
+
+ current = queue.GetNextOrder(current);
+ if (current == (int)songOrder)
+ current = -1;
+
+ if (current >= 0 && !paused)
+ /* play the song after the deleted one */
+ PlayOrder(pc, current);
+ else
+ /* no songs left to play, stop playback
+ completely */
+ Stop(pc);
+
+ *queued_p = NULL;
+ } else if (current == (int)songOrder)
+ /* there's a "current song" but we're not playing
+ currently - clear "current" */
+ current = -1;
+
+ /* now do it: remove the song */
+
+ queue.DeletePosition(song);
+
+ /* update the "current" and "queued" variables */
+
+ if (current > (int)songOrder)
+ current--;
+}
+
+enum playlist_result
+playlist::DeletePosition(struct player_control &pc, unsigned song)
+{
+ if (song >= queue.GetLength())
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ const struct song *queued_song = GetQueuedSong();
+
+ DeleteInternal(pc, song, &queued_song);
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::DeleteRange(struct player_control &pc, unsigned start, unsigned end)
+{
+ if (start >= queue.GetLength())
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if (end > queue.GetLength())
+ end = queue.GetLength();
+
+ if (start >= end)
+ return PLAYLIST_RESULT_SUCCESS;
+
+ const struct song *queued_song = GetQueuedSong();
+
+ do {
+ DeleteInternal(pc, --end, &queued_song);
+ } while (end != start);
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::DeleteId(struct player_control &pc, unsigned id)
+{
+ int song = queue.IdToPosition(id);
+ if (song < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return DeletePosition(pc, song);
+}
+
+void
+playlist::DeleteSong(struct player_control &pc, const struct song &song)
+{
+ for (int i = queue.GetLength() - 1; i >= 0; --i)
+ // TODO: compare URI instead of pointer
+ if (&song == queue.Get(i))
+ DeletePosition(pc, i);
+}
+
+enum playlist_result
+playlist::MoveRange(player_control &pc, unsigned start, unsigned end, int to)
+{
+ if (!queue.IsValidPosition(start) || !queue.IsValidPosition(end - 1))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if ((to >= 0 && to + end - start - 1 >= GetLength()) ||
+ (to < 0 && unsigned(abs(to)) > GetLength()))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if ((int)start == to)
+ /* nothing happens */
+ return PLAYLIST_RESULT_SUCCESS;
+
+ const struct song *const queued_song = GetQueuedSong();
+
+ /*
+ * (to < 0) => move to offset from current song
+ * (-playlist.length == to) => move to position BEFORE current song
+ */
+ const int currentSong = GetCurrentPosition();
+ if (to < 0 && currentSong >= 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)) % GetLength();
+ if (start < (unsigned)to)
+ to--;
+ }
+
+ queue.MoveRange(start, end, to);
+
+ if (!queue.random) {
+ /* update current/queued */
+ if ((int)start <= current && (unsigned)current < end)
+ current += to - start;
+ else if (current >= (int)end && current <= to)
+ current -= end - start;
+ else if (current >= to && current < (int)start)
+ current += end - start;
+ }
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::MoveId(player_control &pc, unsigned id1, int to)
+{
+ int song = queue.IdToPosition(id1);
+ if (song < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return MoveRange(pc, song, song + 1, to);
+}
+
+void
+playlist::Shuffle(player_control &pc, unsigned start, unsigned end)
+{
+ if (end > GetLength())
+ /* correct the "end" offset */
+ end = GetLength();
+
+ if (start + 1 >= end)
+ /* needs at least two entries. */
+ return;
+
+ const struct song *const queued_song = GetQueuedSong();
+ if (playing && current >= 0) {
+ unsigned current_position = queue.OrderToPosition(current);
+
+ if (current_position >= start && current_position < end) {
+ /* put current playing song first */
+ queue.SwapPositions(start, current_position);
+
+ if (queue.random) {
+ current = queue.PositionToOrder(start);
+ } else
+ current = start;
+
+ /* start shuffle after the current song */
+ start++;
+ }
+ } else {
+ /* no playback currently: reset current */
+
+ current = -1;
+ }
+
+ queue.ShuffleRange(start, end);
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+}
diff --git a/src/stored_playlist.c b/src/PlaylistFile.cxx
index 39ba2bac1..d79bea9c5 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"
+#include "conf.h"
+#include "Idle.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..9dd971d3f 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,27 @@
*/
#include "config.h"
-#include "playlist.h"
-#include "playlist_state.h"
-#include "event_pipe.h"
-#include "main.h"
-
-struct playlist g_playlist;
+#include "PlaylistGlobal.hxx"
+#include "Playlist.hxx"
+#include "Main.hxx"
+#include "Partition.hxx"
+#include "GlobalEvents.hxx"
static void
playlist_tag_event(void)
{
- playlist_tag_changed(&g_playlist);
+ global_partition->playlist.TagChanged();
}
static void
playlist_event(void)
{
- playlist_sync(&g_playlist, global_player_control);
-}
-
-void
-playlist_global_init(void)
-{
- playlist_init(&g_playlist);
-
- event_pipe_register(PIPE_EVENT_TAG, playlist_tag_event);
- event_pipe_register(PIPE_EVENT_PLAYLIST, playlist_event);
+ global_partition->playlist.SyncWithPlayer(global_partition->pc);
}
void
-playlist_global_finish(void)
+playlist_global_init()
{
- playlist_finish(&g_playlist);
+ GlobalEvents::Register(GlobalEvents::TAG, playlist_tag_event);
+ GlobalEvents::Register(GlobalEvents::PLAYLIST, playlist_event);
}
diff --git a/src/PlaylistGlobal.hxx b/src/PlaylistGlobal.hxx
new file mode 100644
index 000000000..4397292db
--- /dev/null
+++ b/src/PlaylistGlobal.hxx
@@ -0,0 +1,26 @@
+/*
+ * 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_GLOBAL_HXX
+#define MPD_PLAYLIST_GLOBAL_HXX
+
+void
+playlist_global_init();
+
+#endif
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_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..e6ece7e6b 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"
+#include "input_stream.h"
+
+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,10 +78,9 @@ 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);
-
+ int current_position = playlist->GetCurrentPosition();
if (current_position < 0)
return false;
@@ -87,21 +90,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)
+playlist_print_find(Client *client, const struct playlist *playlist,
+ const SongFilter &filter)
{
- queue_find(client, &playlist->queue, list);
+ queue_find(client, &playlist->queue, filter);
}
void
-playlist_print_search(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list)
-{
- queue_search(client, &playlist->queue, list);
-}
-
-void
-playlist_print_changes_info(struct client *client,
+playlist_print_changes_info(Client *client,
const struct playlist *playlist,
uint32_t version)
{
@@ -109,46 +105,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 +165,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..1335fa401 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,14 +18,17 @@
*/
#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 "song.h"
+#include "PlaylistAny.hxx"
+#include "PlaylistSong.hxx"
+#include "Playlist.hxx"
#include "input_stream.h"
+extern "C" {
+#include "song.h"
+}
+
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
unsigned start_index, unsigned end_index,
@@ -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;
}
@@ -50,10 +52,9 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
if (song == NULL)
continue;
- result = playlist_append_song(dest, pc, song, NULL);
+ result = dest->AppendSong(*pc, song);
+ 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..745744490 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"
+#include "Idle.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->AppendURI(*pc, uri_utf8.c_str())) != 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->AppendURI(*pc, temp2) != 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..118fb5715 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,20 @@
*/
#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"
+#include "tag.h"
+
+extern "C" {
#include "song.h"
#include "uri.h"
#include "path.h"
-#include "ls.h"
-#include "tag.h"
+}
+
+#include <glib.h>
#include <assert.h>
#include <string.h>
@@ -86,9 +92,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 +108,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..14c6d231e 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,12 +23,11 @@
*/
#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"
#include "conf.h"
#include <string.h>
@@ -72,8 +71,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 +79,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 +99,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 +118,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,31 +140,21 @@ 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)]));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_REPEAT)) {
- if (strcmp
- (&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
- "1") == 0) {
- playlist_set_repeat(playlist, pc, true);
- } else
- playlist_set_repeat(playlist, pc, false);
+ playlist->SetRepeat(*pc,
+ strcmp(&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
+ "1") == 0);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_SINGLE)) {
- if (strcmp
- (&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
- "1") == 0) {
- playlist_set_single(playlist, pc, true);
- } else
- playlist_set_single(playlist, pc, false);
+ playlist->SetSingle(*pc,
+ strcmp(&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
+ "1") == 0);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CONSUME)) {
- if (strcmp
- (&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
- "1") == 0) {
- playlist_set_consume(playlist, true);
- } else
- playlist_set_consume(playlist, false);
+ playlist->SetConsume(strcmp(&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
+ "1") == 0);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CROSSFADE)) {
pc_set_cross_fade(pc,
atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
@@ -187,14 +174,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);
+ playlist->SetRandom(*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 &&
@@ -213,9 +200,9 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
if (state == PLAYER_STATE_STOP /* && config_option */)
playlist->current = current;
else if (seek_time == 0)
- playlist_play(playlist, pc, current);
+ playlist->PlayPosition(*pc, current);
else
- playlist_seek_song(playlist, pc, current, seek_time);
+ playlist->SeekSongPosition(*pc, current, seek_time);
if (state == PLAYER_STATE_PAUSE)
pc_pause(pc);
@@ -237,8 +224,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.cxx b/src/Queue.cxx
new file mode 100644
index 000000000..3fdb9ed1e
--- /dev/null
+++ b/src/Queue.cxx
@@ -0,0 +1,499 @@
+/*
+ * 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 "Queue.hxx"
+#include "song.h"
+
+#include <stdlib.h>
+
+queue::queue(unsigned _max_length)
+ :max_length(_max_length), length(0),
+ version(1),
+ items(new Item[max_length]),
+ order(new unsigned[max_length]),
+ id_table(max_length * HASH_MULT),
+ repeat(false),
+ single(false),
+ consume(false),
+ random(false)
+{
+}
+
+queue::~queue()
+{
+ Clear();
+
+ delete[] items;
+ delete[] order;
+}
+
+int
+queue::GetNextOrder(unsigned _order) const
+{
+ assert(_order < length);
+
+ 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
+ /* end of queue */
+ return -1;
+}
+
+void
+queue::IncrementVersion()
+{
+ static unsigned long max = ((uint32_t) 1 << 31) - 1;
+
+ version++;
+
+ if (version >= max) {
+ for (unsigned i = 0; i < length; i++)
+ items[i].version = 0;
+
+ version = 1;
+ }
+}
+
+void
+queue::ModifyAtOrder(unsigned _order)
+{
+ assert(_order < length);
+
+ unsigned position = order[_order];
+ items[position].version = version;
+
+ IncrementVersion();
+}
+
+void
+queue::ModifyAll()
+{
+ for (unsigned i = 0; i < length; i++)
+ items[i].version = version;
+
+ IncrementVersion();
+}
+
+unsigned
+queue::Append(struct song *song, uint8_t priority)
+{
+ assert(!IsFull());
+
+ const unsigned position = length++;
+ const unsigned id = id_table.Insert(position);
+
+ auto &item = items[position];
+ item.song = song_dup_detached(song);
+ item.id = id;
+ item.version = version;
+ item.priority = priority;
+
+ order[position] = position;
+
+ return id;
+}
+
+void
+queue::SwapPositions(unsigned position1, unsigned position2)
+{
+ unsigned id1 = items[position1].id;
+ unsigned id2 = items[position2].id;
+
+ std::swap(items[position1], items[position2]);
+
+ items[position1].version = version;
+ items[position2].version = version;
+
+ id_table.Move(id1, position2);
+ id_table.Move(id2, position1);
+}
+
+void
+queue::MovePostion(unsigned from, unsigned to)
+{
+ const Item tmp = items[from];
+
+ /* move songs to one less in from->to */
+
+ for (unsigned i = from; i < to; i++)
+ MoveItemTo(i + 1, i);
+
+ /* move songs to one more in to->from */
+
+ for (unsigned i = from; i > to; i--)
+ MoveItemTo(i - 1, i);
+
+ /* put song at _to_ */
+
+ id_table.Move(tmp.id, to);
+ items[to] = tmp;
+ items[to].version = version;
+
+ /* now deal with order */
+
+ 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::MoveRange(unsigned start, unsigned end, unsigned to)
+{
+ Item tmp[end - start];
+ // Copy the original block [start,end-1]
+ for (unsigned i = start; i < end; 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++)
+ MoveItemTo(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 (int i = start - 1; i >= int(to); i--)
+ MoveItemTo(i, i + end - start);
+
+ // Copy the original block back in, starting at to.
+ for (unsigned i = start; i< end; i++)
+ {
+ id_table.Move(tmp[i - start].id, to + i - start);
+ items[to + i - start] = tmp[i-start];
+ items[to + i - start].version = version;
+ }
+
+ 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 < 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;
+ }
+ }
+}
+
+void
+queue::MoveOrder(unsigned from_order, unsigned to_order)
+{
+ assert(from_order < length);
+ assert(to_order <= length);
+
+ const unsigned from_position = OrderToPosition(from_order);
+
+ if (from_order < to_order) {
+ for (unsigned i = from_order; i < to_order; ++i)
+ order[i] = order[i + 1];
+ } else {
+ for (unsigned i = from_order; i > to_order; --i)
+ order[i] = order[i - 1];
+ }
+
+ order[to_order] = from_position;
+}
+
+void
+queue::DeletePosition(unsigned position)
+{
+ assert(position < length);
+
+ struct song *song = Get(position);
+ assert(!song_in_database(song) || song_is_detached(song));
+ song_free(song);
+
+ const unsigned id = PositionToId(position);
+ const unsigned _order = PositionToOrder(position);
+
+ --length;
+
+ /* release the song id */
+
+ id_table.Erase(id);
+
+ /* delete song from songs array */
+
+ for (unsigned i = position; i < length; i++)
+ MoveItemTo(i + 1, i);
+
+ /* delete the entry from the order array */
+
+ for (unsigned i = _order; i < length; i++)
+ order[i] = order[i + 1];
+
+ /* readjust values in the order array */
+
+ for (unsigned i = 0; i < length; i++)
+ if (order[i] > position)
+ --order[i];
+}
+
+void
+queue::Clear()
+{
+ for (unsigned i = 0; i < length; i++) {
+ Item *item = &items[i];
+
+ assert(!song_in_database(item->song) ||
+ song_is_detached(item->song));
+ song_free(item->song);
+
+ id_table.Erase(item->id);
+ }
+
+ length = 0;
+}
+
+static void
+queue_sort_order_by_priority(struct queue *queue, unsigned start, unsigned end)
+{
+ assert(queue != NULL);
+ assert(queue->random);
+ assert(start <= end);
+ assert(end <= queue->length);
+
+ auto cmp = [queue](unsigned a_pos, unsigned b_pos){
+ const queue::Item &a = queue->items[a_pos];
+ const queue::Item &b = queue->items[b_pos];
+
+ return a.priority > b.priority;
+ };
+
+ std::stable_sort(queue->order + start, queue->order + end, cmp);
+}
+
+void
+queue::ShuffleOrderRange(unsigned start, unsigned end)
+{
+ assert(random);
+ assert(start <= end);
+ assert(end <= length);
+
+ rand.AutoCreate();
+ std::shuffle(order + start, order + end, rand);
+}
+
+/**
+ * Sort the "order" of items by priority, and then shuffle each
+ * priority group.
+ */
+void
+queue::ShuffleOrderRangeWithPriority(unsigned start, unsigned end)
+{
+ assert(random);
+ assert(start <= end);
+ assert(end <= length);
+
+ if (start == end)
+ return;
+
+ /* first group the range by priority */
+ queue_sort_order_by_priority(this, start, end);
+
+ /* now shuffle each priority group */
+ unsigned group_start = start;
+ uint8_t group_priority = GetOrderPriority(start);
+
+ for (unsigned i = start + 1; i < end; ++i) {
+ const uint8_t priority = GetOrderPriority(i);
+ assert(priority <= group_priority);
+
+ if (priority != group_priority) {
+ /* start of a new group - shuffle the one that
+ has just ended */
+ ShuffleOrderRange(group_start, i);
+ group_start = i;
+ group_priority = priority;
+ }
+ }
+
+ /* shuffle the last group */
+ ShuffleOrderRange(group_start, end);
+}
+
+void
+queue::ShuffleOrder()
+{
+ ShuffleOrderRangeWithPriority(0, length);
+}
+
+void
+queue::ShuffleOrderFirst(unsigned start, unsigned end)
+{
+ rand.AutoCreate();
+
+ std::uniform_int_distribution<unsigned> distribution(start, end - 1);
+ SwapOrders(start, distribution(rand));
+}
+
+void
+queue::ShuffleOrderLast(unsigned start, unsigned end)
+{
+ rand.AutoCreate();
+
+ std::uniform_int_distribution<unsigned> distribution(start, end - 1);
+ SwapOrders(end - 1, distribution(rand));
+}
+
+void
+queue::ShuffleRange(unsigned start, unsigned end)
+{
+ assert(start <= end);
+ assert(end <= length);
+
+ rand.AutoCreate();
+
+ for (unsigned i = start; i < end; i++) {
+ std::uniform_int_distribution<unsigned> distribution(start,
+ end - 1);
+ unsigned ri = distribution(rand);
+ SwapPositions(i, ri);
+ }
+}
+
+unsigned
+queue::FindPriorityOrder(unsigned start_order, uint8_t priority,
+ unsigned exclude_order) const
+{
+ assert(random);
+ assert(start_order <= length);
+
+ for (unsigned i = start_order; i < length; ++i) {
+ const unsigned position = OrderToPosition(i);
+ const Item *item = &items[position];
+ if (item->priority <= priority && i != exclude_order)
+ return i;
+ }
+
+ return length;
+}
+
+unsigned
+queue::CountSamePriority(unsigned start_order, uint8_t priority) const
+{
+ assert(random);
+ assert(start_order <= length);
+
+ for (unsigned i = start_order; i < length; ++i) {
+ const unsigned position = OrderToPosition(i);
+ const Item *item = &items[position];
+ if (item->priority != priority)
+ return i - start_order;
+ }
+
+ return length - start_order;
+}
+
+bool
+queue::SetPriority(unsigned position, uint8_t priority, int after_order)
+{
+ assert(position < length);
+
+ Item *item = &items[position];
+ uint8_t old_priority = item->priority;
+ if (old_priority == priority)
+ return false;
+
+ item->version = version;
+ item->priority = priority;
+
+ if (!random)
+ /* don't reorder if not in random mode */
+ return true;
+
+ unsigned _order = PositionToOrder(position);
+ if (after_order >= 0) {
+ if (_order == (unsigned)after_order)
+ /* don't reorder the current song */
+ return true;
+
+ 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 =
+ OrderToPosition(after_order);
+ const Item *after_item =
+ &items[after_position];
+ if (old_priority > after_item->priority ||
+ priority <= after_item->priority)
+ /* priority hasn't become bigger */
+ return true;
+ }
+ }
+
+ /* move the item to the beginning of the priority group (or
+ create a new priority group) */
+
+ const unsigned before_order =
+ FindPriorityOrder(after_order + 1, priority, _order);
+ const unsigned new_order = before_order > _order
+ ? before_order - 1
+ : before_order;
+ MoveOrder(_order, new_order);
+
+ /* shuffle the song within that priority group */
+
+ const unsigned priority_count = CountSamePriority(new_order, priority);
+ assert(priority_count >= 1);
+ ShuffleOrderFirst(new_order, new_order + priority_count);
+
+ return true;
+}
+
+bool
+queue::SetPriorityRange(unsigned start_position, unsigned end_position,
+ uint8_t priority, int after_order)
+{
+ assert(start_position <= end_position);
+ assert(end_position <= length);
+
+ bool modified = false;
+ int after_position = after_order >= 0
+ ? (int)OrderToPosition(after_order)
+ : -1;
+ for (unsigned i = start_position; i < end_position; ++i) {
+ after_order = after_position >= 0
+ ? (int)PositionToOrder(after_position)
+ : -1;
+
+ modified |= SetPriority(i, priority, after_order);
+ }
+
+ return modified;
+}
diff --git a/src/Queue.hxx b/src/Queue.hxx
new file mode 100644
index 000000000..6e7786bcd
--- /dev/null
+++ b/src/Queue.hxx
@@ -0,0 +1,368 @@
+/*
+ * 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 "IdTable.hxx"
+#include "util/LazyRandomEngine.hxx"
+
+#include <algorithm>
+
+#include <assert.h>
+#include <stdint.h>
+
+/**
+ * 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 {
+ /**
+ * reserve max_length * HASH_MULT elements in the id
+ * number space
+ */
+ static constexpr unsigned HASH_MULT = 4;
+
+ /**
+ * One element of the queue: basically a song plus some queue specific
+ * information attached.
+ */
+ struct 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;
+ };
+
+ /** 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 */
+ Item *items;
+
+ /** map order numbers to positions */
+ unsigned *order;
+
+ /** map song ids to positions */
+ IdTable id_table;
+
+ /** 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 */
+ LazyRandomEngine 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 {
+ return id_table.IdToPosition(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;
+ }
+ }
+
+ gcc_pure
+ uint8_t GetPriorityAtPosition(unsigned position) const {
+ assert(position < length);
+
+ return items[position].priority;
+ }
+
+ const Item &GetOrderItem(unsigned i) const {
+ assert(IsValidOrder(i));
+
+ return items[OrderToPosition(i)];
+ }
+
+ uint8_t GetOrderPriority(unsigned i) const {
+ return GetOrderItem(i).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, ignoring
+ * their priorities.
+ */
+ void ShuffleOrderRange(unsigned start, unsigned end);
+
+ /**
+ * 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();
+
+ void ShuffleOrderFirst(unsigned start, unsigned end);
+
+ /**
+ * 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);
+
+private:
+ /**
+ * Moves a song to a new position in the "order" list.
+ */
+ void MoveOrder(unsigned from_order, unsigned to_order);
+
+ void MoveItemTo(unsigned from, unsigned to) {
+ unsigned from_id = items[from].id;
+
+ items[to] = items[from];
+ items[to].version = version;
+ id_table.Move(from_id, to);
+ }
+
+ /**
+ * Find the first item that has this specified priority or
+ * higher.
+ */
+ gcc_pure
+ unsigned FindPriorityOrder(unsigned start_order, uint8_t priority,
+ unsigned exclude_order) const;
+
+ gcc_pure
+ unsigned CountSamePriority(unsigned start_order,
+ uint8_t priority) const;
+};
+
+#endif
diff --git a/src/QueueCommands.cxx b/src/QueueCommands.cxx
new file mode 100644
index 000000000..4a3e1312d
--- /dev/null
+++ b/src/QueueCommands.cxx
@@ -0,0 +1,375 @@
+/*
+ * 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 "DatabaseSelection.hxx"
+#include "Playlist.hxx"
+#include "PlaylistPrint.hxx"
+#include "ClientFile.hxx"
+#include "ClientInternal.hxx"
+#include "Partition.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 = client->partition.AppendFile(path);
+ 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 = client->partition.AppendURI(uri);
+ return print_playlist_result(client, result);
+ }
+
+ const DatabaseSelection selection(uri, true);
+ GError *error = NULL;
+ return AddFromDatabase(client->partition, selection, &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 = client->partition.AppendFile(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 = client->partition.AppendURI(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 = client->partition.MoveId(added_id, to);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ enum command_return ret =
+ print_playlist_result(client, result);
+ client->partition.DeleteId(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;
+
+ if (!check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result = client->partition.DeleteRange(start, end);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_deleteid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result = client->partition.DeleteId(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;
+
+ client->partition.Shuffle(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[])
+{
+ client->partition.ClearQueue();
+ 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 =
+ client->partition.SetPriorityRange(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 =
+ client->partition.SetPriorityId(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;
+
+ if (!check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_int(client, &to, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.MoveRange(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;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_int(client, &to, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ enum playlist_result result = client->partition.MoveId(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;
+
+ if (!check_unsigned(client, &song1, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &song2, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SwapPositions(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;
+
+ if (!check_unsigned(client, &id1, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &id2, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result = client->partition.SwapIds(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..d86c70053 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
@@ -19,9 +19,9 @@
#include "config.h"
#include "replay_gain_config.h"
-#include "playlist.h"
+#include "Idle.hxx"
#include "conf.h"
-#include "idle.h"
+#include "Playlist.hxx"
#include "mpd_error.h"
#include <glib.h>
@@ -29,20 +29,14 @@
#include <assert.h>
#include <stdlib.h>
#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;
+static constexpr bool DEFAULT_REPLAYGAIN_LIMIT = true;
float replay_gain_preamp = 1.0;
float replay_gain_missing_preamp = 1.0;
-bool replay_gain_limit;
+bool replay_gain_limit = DEFAULT_REPLAYGAIN_LIMIT;
const char *
replay_gain_get_mode_string(void)
@@ -137,14 +131,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..b9d1b82c6 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
diff --git a/src/server_socket.c b/src/ServerSocket.cxx
index 396399596..7f14168a8 100644
--- a/src/server_socket.c
+++ b/src/ServerSocket.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,17 +23,19 @@
#define _GNU_SOURCE 1
#endif
-#include "server_socket.h"
-#include "socket_util.h"
+#include "ServerSocket.hxx"
+#include "SocketUtil.hxx"
+#include "SocketError.hxx"
+#include "event/SocketMonitor.hxx"
#include "resolver.h"
#include "fd_util.h"
-#include "glib_socket.h"
+
+#include <forward_list>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
-#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
@@ -53,27 +55,72 @@
#define DEFAULT_PORT 6600
-struct one_socket {
- struct one_socket *next;
- struct server_socket *parent;
-
- unsigned serial;
+struct OneServerSocket final : private SocketMonitor {
+ const server_socket &parent;
- int fd;
- guint source_id;
+ const unsigned serial;
char *path;
size_t address_length;
- struct sockaddr address;
+ struct sockaddr *address;
+
+ OneServerSocket(EventLoop &_loop, const server_socket &_parent,
+ unsigned _serial,
+ const struct sockaddr *_address,
+ size_t _address_length)
+ :SocketMonitor(_loop),
+ parent(_parent), serial(_serial),
+ path(nullptr),
+ address_length(_address_length),
+ address((sockaddr *)g_memdup(_address, _address_length))
+ {
+ assert(_address != nullptr);
+ assert(_address_length > 0);
+ }
+
+ OneServerSocket(const OneServerSocket &other) = delete;
+ OneServerSocket &operator=(const OneServerSocket &other) = delete;
+
+ ~OneServerSocket() {
+ g_free(path);
+ g_free(address);
+ }
+
+ bool Open(GError **error_r);
+
+ using SocketMonitor::Close;
+
+ char *ToString() const;
+
+ void SetFD(int _fd) {
+ SocketMonitor::Open(_fd);
+ SocketMonitor::ScheduleRead();
+ }
+
+ void Accept();
+
+private:
+ virtual void OnSocketReady(unsigned flags) override;
};
struct server_socket {
+ EventLoop &loop;
+
server_socket_callback_t callback;
void *callback_ctx;
- struct one_socket *sockets, **sockets_tail_r;
+ std::forward_list<OneServerSocket> sockets;
+
unsigned next_serial;
+
+ server_socket(EventLoop &_loop,
+ server_socket_callback_t _callback, void *_callback_ctx)
+ :loop(_loop),
+ callback(_callback), callback_ctx(_callback_ctx),
+ next_serial(1) {}
+
+ void Close();
};
static GQuark
@@ -83,43 +130,26 @@ server_socket_quark(void)
}
struct server_socket *
-server_socket_new(server_socket_callback_t callback, void *callback_ctx)
+server_socket_new(EventLoop &loop,
+ server_socket_callback_t callback, void *callback_ctx)
{
- struct server_socket *ss = g_new(struct server_socket, 1);
- ss->callback = callback;
- ss->callback_ctx = callback_ctx;
- ss->sockets = NULL;
- ss->sockets_tail_r = &ss->sockets;
- ss->next_serial = 1;
- return ss;
+ return new server_socket(loop, callback, callback_ctx);
}
void
server_socket_free(struct server_socket *ss)
{
- server_socket_close(ss);
-
- while (ss->sockets != NULL) {
- struct one_socket *s = ss->sockets;
- ss->sockets = s->next;
-
- assert(s->fd < 0);
-
- g_free(s->path);
- g_free(s);
- }
-
- g_free(ss);
+ delete ss;
}
/**
* Wraper for sockaddr_to_string() which never fails.
*/
-static char *
-one_socket_to_string(const struct one_socket *s)
+char *
+OneServerSocket::ToString() const
{
- char *p = sockaddr_to_string(&s->address, s->address_length, NULL);
- if (p == NULL)
+ char *p = sockaddr_to_string(address, address_length, nullptr);
+ if (p == nullptr)
p = g_strdup("[unknown]");
return p;
}
@@ -149,72 +179,83 @@ get_remote_uid(int fd)
#endif
}
-static gboolean
-server_socket_in_event(G_GNUC_UNUSED GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition,
- gpointer data)
+inline void
+OneServerSocket::Accept()
{
- struct one_socket *s = data;
+ struct sockaddr_storage peer_address;
+ size_t peer_address_length = sizeof(peer_address);
+ int peer_fd =
+ accept_cloexec_nonblock(Get(), (struct sockaddr*)&peer_address,
+ &peer_address_length);
+ if (peer_fd < 0) {
+ const SocketErrorMessage msg;
+ g_warning("accept() failed: %s", (const char *)msg);
+ return;
+ }
- struct sockaddr_storage address;
- size_t address_length = sizeof(address);
- int fd = accept_cloexec_nonblock(s->fd, (struct sockaddr*)&address,
- &address_length);
- if (fd >= 0) {
- if (socket_keepalive(fd))
- g_warning("Could not set TCP keepalive option: %s",
- g_strerror(errno));
- s->parent->callback(fd, (const struct sockaddr*)&address,
- address_length, get_remote_uid(fd),
- s->parent->callback_ctx);
- } else {
- g_warning("accept() failed: %s", g_strerror(errno));
+ if (socket_keepalive(peer_fd)) {
+ const SocketErrorMessage msg;
+ g_warning("Could not set TCP keepalive option: %s",
+ (const char *)msg);
}
- return true;
+ parent.callback(peer_fd,
+ (const struct sockaddr*)&peer_address,
+ peer_address_length, get_remote_uid(peer_fd),
+ parent.callback_ctx);
}
-static void
-set_fd(struct one_socket *s, int fd)
+void
+OneServerSocket::OnSocketReady(gcc_unused unsigned flags)
{
- assert(s != NULL);
- assert(s->fd < 0);
- assert(fd >= 0);
+ Accept();
+}
+
+inline bool
+OneServerSocket::Open(GError **error_r)
+{
+ assert(!IsDefined());
- s->fd = fd;
+ int _fd = socket_bind_listen(address->sa_family,
+ SOCK_STREAM, 0,
+ address, address_length, 5,
+ error_r);
+ if (_fd < 0)
+ return false;
+
+ /* allow everybody to connect */
+
+ if (path != nullptr)
+ chmod(path, 0666);
+
+ /* register in the GLib main loop */
- GIOChannel *channel = g_io_channel_new_socket(s->fd);
- s->source_id = g_io_add_watch(channel, G_IO_IN,
- server_socket_in_event, s);
- g_io_channel_unref(channel);
+ SetFD(_fd);
+
+ return true;
}
bool
server_socket_open(struct server_socket *ss, GError **error_r)
{
- struct one_socket *good = NULL, *bad = NULL;
- GError *last_error = NULL;
+ struct OneServerSocket *good = nullptr, *bad = nullptr;
+ GError *last_error = nullptr;
- for (struct one_socket *s = ss->sockets; s != NULL; s = s->next) {
- assert(s->serial > 0);
- assert(good == NULL || s->serial >= good->serial);
- assert(s->fd < 0);
+ for (auto &i : ss->sockets) {
+ assert(i.serial > 0);
+ assert(good == nullptr || i.serial <= good->serial);
- if (bad != NULL && s->serial != bad->serial) {
+ if (bad != nullptr && i.serial != bad->serial) {
server_socket_close(ss);
g_propagate_error(error_r, last_error);
return false;
}
- GError *error = NULL;
- int fd = socket_bind_listen(s->address.sa_family,
- SOCK_STREAM, 0,
- &s->address, s->address_length, 5,
- &error);
- if (fd < 0) {
- if (good != NULL && good->serial == s->serial) {
- char *address_string = one_socket_to_string(s);
- char *good_string = one_socket_to_string(good);
+ GError *error = nullptr;
+ if (!i.Open(&error)) {
+ if (good != nullptr && good->serial == i.serial) {
+ char *address_string = i.ToString();
+ char *good_string = good->ToString();
g_warning("bind to '%s' failed: %s "
"(continuing anyway, because "
"binding to '%s' succeeded)",
@@ -223,10 +264,10 @@ server_socket_open(struct server_socket *ss, GError **error_r)
g_free(address_string);
g_free(good_string);
g_error_free(error);
- } else if (bad == NULL) {
- bad = s;
+ } else if (bad == nullptr) {
+ bad = &i;
- char *address_string = one_socket_to_string(s);
+ char *address_string = i.ToString();
g_propagate_prefixed_error(&last_error, error,
"Failed to bind to '%s': ",
address_string);
@@ -236,28 +277,19 @@ server_socket_open(struct server_socket *ss, GError **error_r)
continue;
}
- /* allow everybody to connect */
-
- if (s->path != NULL)
- chmod(s->path, 0666);
-
- /* register in the GLib main loop */
-
- set_fd(s, fd);
-
/* mark this socket as "good", and clear previous
errors */
- good = s;
+ good = &i;
- if (bad != NULL) {
- bad = NULL;
+ if (bad != nullptr) {
+ bad = nullptr;
g_error_free(last_error);
- last_error = NULL;
+ last_error = nullptr;
}
}
- if (bad != NULL) {
+ if (bad != nullptr) {
server_socket_close(ss);
g_propagate_error(error_r, last_error);
return false;
@@ -267,85 +299,54 @@ server_socket_open(struct server_socket *ss, GError **error_r)
}
void
-server_socket_close(struct server_socket *ss)
+server_socket::Close()
{
- for (struct one_socket *s = ss->sockets; s != NULL; s = s->next) {
- if (s->fd < 0)
- continue;
+ for (auto &i : sockets)
+ i.Close();
+}
- g_source_remove(s->source_id);
- close_socket(s->fd);
- s->fd = -1;
- }
+void
+server_socket_close(struct server_socket *ss)
+{
+ ss->Close();
}
-static struct one_socket *
-one_socket_new(unsigned serial, const struct sockaddr *address,
- size_t address_length)
+static OneServerSocket &
+server_socket_add_address(struct server_socket *ss,
+ const struct sockaddr *address,
+ size_t address_length)
{
- assert(address != NULL);
- assert(address_length > 0);
-
- struct one_socket *s = g_malloc(sizeof(*s) - sizeof(s->address) +
- address_length);
- s->next = NULL;
- s->serial = serial;
- s->fd = -1;
- s->path = NULL;
- s->address_length = address_length;
- memcpy(&s->address, address, address_length);
-
- return s;
+ assert(ss != nullptr);
+
+ ss->sockets.emplace_front(ss->loop, *ss, ss->next_serial,
+ address, address_length);
+
+ return ss->sockets.front();
}
bool
server_socket_add_fd(struct server_socket *ss, int fd, GError **error_r)
{
- assert(ss != NULL);
- assert(ss->sockets_tail_r != NULL);
- assert(*ss->sockets_tail_r == NULL);
+ assert(ss != nullptr);
assert(fd >= 0);
struct sockaddr_storage address;
socklen_t address_length;
if (getsockname(fd, (struct sockaddr *)&address,
&address_length) < 0) {
- g_set_error(error_r, server_socket_quark(), errno,
- "Failed to get socket address: %s",
- g_strerror(errno));
+ SetSocketError(error_r);
+ g_prefix_error(error_r, "Failed to get socket address");
return false;
}
- struct one_socket *s = one_socket_new(ss->next_serial,
- (struct sockaddr *)&address,
- address_length);
- s->parent = ss;
- *ss->sockets_tail_r = s;
- ss->sockets_tail_r = &s->next;
-
- set_fd(s, fd);
+ OneServerSocket &s =
+ server_socket_add_address(ss, (struct sockaddr *)&address,
+ address_length);
+ s.SetFD(fd);
return true;
}
-static struct one_socket *
-server_socket_add_address(struct server_socket *ss,
- const struct sockaddr *address,
- size_t address_length)
-{
- assert(ss != NULL);
- assert(ss->sockets_tail_r != NULL);
- assert(*ss->sockets_tail_r == NULL);
-
- struct one_socket *s = one_socket_new(ss->next_serial,
- address, address_length);
- s->parent = ss;
- *ss->sockets_tail_r = s;
- ss->sockets_tail_r = &s->next;
-
- return s;
-}
-
#ifdef HAVE_TCP
/**
@@ -424,10 +425,10 @@ server_socket_add_host(struct server_socket *ss, const char *hostname,
struct addrinfo *ai = resolve_host_port(hostname, port,
AI_PASSIVE, SOCK_STREAM,
error_r);
- if (ai == NULL)
+ if (ai == nullptr)
return false;
- for (const struct addrinfo *i = ai; i != NULL; i = i->ai_next)
+ for (const struct addrinfo *i = ai; i != nullptr; i = i->ai_next)
server_socket_add_address(ss, i->ai_addr, i->ai_addrlen);
freeaddrinfo(ai);
@@ -465,10 +466,10 @@ server_socket_add_path(struct server_socket *ss, const char *path,
s_un.sun_family = AF_UNIX;
memcpy(s_un.sun_path, path, path_length + 1);
- struct one_socket *s =
+ OneServerSocket &s =
server_socket_add_address(ss, (const struct sockaddr *)&s_un,
sizeof(s_un));
- s->path = g_strdup(path);
+ s.path = g_strdup(path);
return true;
#else /* !HAVE_UN */
diff --git a/src/server_socket.h b/src/ServerSocket.hxx
index 7caa4bbf2..eea4b0851 100644
--- a/src/server_socket.h
+++ b/src/ServerSocket.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,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SERVER_SOCKET_H
-#define MPD_SERVER_SOCKET_H
+#ifndef MPD_SERVER_SOCKET_HXX
+#define MPD_SERVER_SOCKET_HXX
-#include <stdbool.h>
+#include "gerror.h"
-#include <glib.h>
+#include <stddef.h>
struct sockaddr;
+class EventLoop;
typedef void (*server_socket_callback_t)(int fd,
const struct sockaddr *address,
@@ -32,7 +33,8 @@ typedef void (*server_socket_callback_t)(int fd,
void *ctx);
struct server_socket *
-server_socket_new(server_socket_callback_t callback, void *callback_ctx);
+server_socket_new(EventLoop &loop,
+ server_socket_callback_t callback, void *callback_ctx);
void
server_socket_free(struct server_socket *ss);
diff --git a/src/sig_handlers.c b/src/SignalHandlers.cxx
index b23f9e778..d438eb703 100644
--- a/src/sig_handlers.c
+++ b/src/SignalHandlers.cxx
@@ -18,13 +18,14 @@
*/
#include "config.h"
-#include "sig_handlers.h"
+#include "SignalHandlers.hxx"
#ifndef WIN32
-#include "log.h"
-#include "main.h"
-#include "event_pipe.h"
+#include "Log.hxx"
+#include "Main.hxx"
+#include "event/Loop.hxx"
+#include "GlobalEvents.hxx"
#include "mpd_error.h"
#include <glib.h>
@@ -35,12 +36,12 @@
static void exit_signal_handler(G_GNUC_UNUSED int signum)
{
- g_main_loop_quit(main_loop);
+ GlobalEvents::Emit(GlobalEvents::SHUTDOWN);
}
static void reload_signal_handler(G_GNUC_UNUSED int signum)
{
- event_pipe_emit_fast(PIPE_EVENT_RELOAD);
+ GlobalEvents::Emit(GlobalEvents::RELOAD);
}
static void
@@ -73,7 +74,7 @@ void initSigHandlers(void)
x_sigaction(SIGINT, &sa);
x_sigaction(SIGTERM, &sa);
- event_pipe_register(PIPE_EVENT_RELOAD, handle_reload_event);
+ GlobalEvents::Register(GlobalEvents::RELOAD, handle_reload_event);
sa.sa_handler = reload_signal_handler;
x_sigaction(SIGHUP, &sa);
#endif
diff --git a/src/SignalHandlers.hxx b/src/SignalHandlers.hxx
new file mode 100644
index 000000000..99f347fb0
--- /dev/null
+++ b/src/SignalHandlers.hxx
@@ -0,0 +1,25 @@
+/*
+ * 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_SIGNAL_HANDLERS_HXX
+#define MPD_SIGNAL_HANDLERS_HXX
+
+void initSigHandlers(void);
+
+#endif
diff --git a/src/SocketError.hxx b/src/SocketError.hxx
new file mode 100644
index 000000000..53a0c1d8f
--- /dev/null
+++ b/src/SocketError.hxx
@@ -0,0 +1,156 @@
+/*
+ * 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_SOCKET_ERROR_HXX
+#define MPD_SOCKET_ERROR_HXX
+
+#include "gcc.h"
+
+#include <glib.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+typedef DWORD socket_error_t;
+#else
+#include <errno.h>
+typedef int socket_error_t;
+#endif
+
+/**
+ * A GQuark for GError for socket I/O errors. The code is an errno
+ * value (or WSAGetLastError() on Windows).
+ */
+gcc_const
+static inline GQuark
+SocketErrorQuark(void)
+{
+ return g_quark_from_static_string("socket");
+}
+
+gcc_pure
+static inline socket_error_t
+GetSocketError()
+{
+#ifdef WIN32
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+gcc_const
+static inline bool
+IsSocketErrorAgain(socket_error_t code)
+{
+#ifdef WIN32
+ return code == WSAEINPROGRESS;
+#else
+ return code == EAGAIN;
+#endif
+}
+
+gcc_const
+static inline bool
+IsSocketErrorInterruped(socket_error_t code)
+{
+#ifdef WIN32
+ return code == WSAEINTR;
+#else
+ return code == EINTR;
+#endif
+}
+
+gcc_const
+static inline bool
+IsSocketErrorClosed(socket_error_t code)
+{
+#ifdef WIN32
+ return code == WSAECONNRESET;
+#else
+ return code == EPIPE || code == ECONNRESET;
+#endif
+}
+
+/**
+ * Helper class that formats a socket error message into a
+ * human-readable string. On Windows, a buffer is necessary for this,
+ * and this class hosts the buffer.
+ */
+class SocketErrorMessage {
+#ifdef WIN32
+ char msg[256];
+#else
+ const char *const msg;
+#endif
+
+public:
+#ifdef WIN32
+ explicit SocketErrorMessage(socket_error_t code=GetSocketError()) {
+ DWORD nbytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL, code, 0,
+ (LPSTR)msg, sizeof(msg), NULL);
+ if (nbytes == 0)
+ strcpy(msg, "Unknown error");
+ }
+#else
+ explicit SocketErrorMessage(socket_error_t code=GetSocketError())
+ :msg(g_strerror(code)) {}
+#endif
+
+ operator const char *() const {
+ return msg;
+ }
+};
+
+static inline void
+SetSocketError(GError **error_r, socket_error_t code)
+{
+#ifdef WIN32
+ if (error_r == NULL)
+ return;
+#endif
+
+ const SocketErrorMessage msg(code);
+ g_set_error_literal(error_r, SocketErrorQuark(), code, msg);
+}
+
+static inline void
+SetSocketError(GError **error_r)
+{
+ SetSocketError(error_r, GetSocketError());
+}
+
+gcc_malloc
+static inline GError *
+NewSocketError(socket_error_t code)
+{
+ const SocketErrorMessage msg(code);
+ return g_error_new_literal(SocketErrorQuark(), code, msg);
+}
+
+gcc_malloc
+static inline GError *
+NewSocketError()
+{
+ return NewSocketError(GetSocketError());
+}
+
+#endif
diff --git a/src/socket_util.c b/src/SocketUtil.cxx
index a06a0cbd5..33ea4e2fe 100644
--- a/src/socket_util.c
+++ b/src/SocketUtil.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 "socket_util.h"
+#include "SocketUtil.hxx"
+#include "SocketError.hxx"
#include "fd_util.h"
-#include <errno.h>
+#include <glib.h>
+
#include <unistd.h>
#ifndef G_OS_WIN32
@@ -35,49 +37,42 @@
#include <string.h>
#endif
-static GQuark
-listen_quark(void)
-{
- return g_quark_from_static_string("listen");
-}
-
int
socket_bind_listen(int domain, int type, int protocol,
const struct sockaddr *address, size_t address_length,
int backlog,
- GError **error)
+ GError **error_r)
{
int fd, ret;
const int reuse = 1;
fd = socket_cloexec_nonblock(domain, type, protocol);
if (fd < 0) {
- g_set_error(error, listen_quark(), errno,
- "Failed to create socket: %s", g_strerror(errno));
+ SetSocketError(error_r);
+ g_prefix_error(error_r, "Failed to create socket");
return -1;
}
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(const char *) &reuse, sizeof(reuse));
if (ret < 0) {
- g_set_error(error, listen_quark(), errno,
- "setsockopt() failed: %s", g_strerror(errno));
+ SetSocketError(error_r);
+ g_prefix_error(error_r, "setsockopt() failed");
close_socket(fd);
return -1;
}
ret = bind(fd, address, address_length);
if (ret < 0) {
- g_set_error(error, listen_quark(), errno,
- "%s", g_strerror(errno));
+ SetSocketError(error_r);
close_socket(fd);
return -1;
}
ret = listen(fd, backlog);
if (ret < 0) {
- g_set_error(error, listen_quark(), errno,
- "listen() failed: %s", g_strerror(errno));
+ SetSocketError(error_r);
+ g_prefix_error(error_r, "listen() failed");
close_socket(fd);
return -1;
}
diff --git a/src/socket_util.h b/src/SocketUtil.hxx
index 93bd27362..3d0be78c4 100644
--- a/src/socket_util.h
+++ b/src/SocketUtil.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,10 +23,12 @@
*
*/
-#ifndef SOCKET_UTIL_H
-#define SOCKET_UTIL_H
+#ifndef MPD_SOCKET_UTIL_HXX
+#define MPD_SOCKET_UTIL_HXX
-#include <glib.h>
+#include "gerror.h"
+
+#include <stddef.h>
struct sockaddr;
diff --git a/src/song.c b/src/Song.cxx
index f5cc7c35a..4c820c3f8 100644
--- a/src/song.c
+++ b/src/Song.cxx
@@ -19,26 +19,28 @@
#include "config.h"
#include "song.h"
-#include "uri.h"
-#include "directory.h"
+#include "Directory.hxx"
#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 +52,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 +75,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 +112,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 +171,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..6d6841675
--- /dev/null
+++ b/src/SongFilter.cxx
@@ -0,0 +1,167 @@
+/*
+ * 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"
+#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..2b74d9354 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"
#include "tag.h"
-#include "text_file.h"
+
+extern "C" {
#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..b96c7c43e 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,17 +18,24 @@
*/
#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"
+#include "tag.h"
+#include "input_stream.h"
+
+extern "C" {
#include "decoder_list.h"
#include "decoder_plugin.h"
#include "tag_ape.h"
#include "tag_id3.h"
-#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/StateFile.cxx b/src/StateFile.cxx
new file mode 100644
index 000000000..b78056520
--- /dev/null
+++ b/src/StateFile.cxx
@@ -0,0 +1,122 @@
+/*
+ * 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 "StateFile.hxx"
+#include "OutputState.hxx"
+#include "PlaylistState.hxx"
+#include "TextFile.hxx"
+#include "Partition.hxx"
+#include "Volume.hxx"
+#include "event/Loop.hxx"
+
+#include <glib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "state_file"
+
+StateFile::StateFile(const char *_path, Partition &_partition, EventLoop &_loop)
+ :TimeoutMonitor(_loop), path(_path), partition(_partition),
+ prev_volume_version(0), prev_output_version(0),
+ prev_playlist_version(0)
+{
+ ScheduleSeconds(5 * 60);
+}
+
+void
+StateFile::Write()
+{
+ g_debug("Saving state file %s", path.c_str());
+
+ FILE *fp = fopen(path.c_str(), "w");
+ if (G_UNLIKELY(!fp)) {
+ g_warning("failed to create %s: %s",
+ path.c_str(), g_strerror(errno));
+ return;
+ }
+
+ save_sw_volume_state(fp);
+ audio_output_state_save(fp);
+ 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(&partition.playlist,
+ &partition.pc);
+}
+
+void
+StateFile::Read()
+{
+ bool success;
+
+ g_debug("Loading state file %s", path.c_str());
+
+ TextFile file(path.c_str());
+ if (file.HasFailed()) {
+ g_warning("failed to open %s: %s",
+ path.c_str(), g_strerror(errno));
+ return;
+ }
+
+ const char *line;
+ while ((line = file.ReadLine()) != NULL) {
+ success = read_sw_volume_state(line) ||
+ audio_output_state_read(line) ||
+ playlist_state_restore(line, file, &partition.playlist,
+ &partition.pc);
+ if (!success)
+ g_warning("Unrecognized line in state file: %s", line);
+ }
+
+ prev_volume_version = sw_volume_state_get_hash();
+ prev_output_version = audio_output_state_get_version();
+ prev_playlist_version = playlist_state_get_hash(&partition.playlist,
+ &partition.pc);
+}
+
+inline void
+StateFile::AutoWrite()
+{
+ if (prev_volume_version == sw_volume_state_get_hash() &&
+ prev_output_version == audio_output_state_get_version() &&
+ 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;
+
+ Write();
+}
+
+/**
+ * This function is called every 5 minutes by the GLib main loop, and
+ * saves the state file.
+ */
+bool
+StateFile::OnTimeout()
+{
+ AutoWrite();
+ return true;
+}
diff --git a/src/StateFile.hxx b/src/StateFile.hxx
new file mode 100644
index 000000000..39c3fcdf6
--- /dev/null
+++ b/src/StateFile.hxx
@@ -0,0 +1,53 @@
+/*
+ * 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_STATE_FILE_HXX
+#define MPD_STATE_FILE_HXX
+
+#include "event/TimeoutMonitor.hxx"
+#include "gcc.h"
+
+#include <string>
+
+struct Partition;
+
+class StateFile final : private TimeoutMonitor {
+ std::string path;
+
+ Partition &partition;
+
+ /**
+ * These version numbers determine whether we need to save the state
+ * file. If nothing has changed, we won't let the hard drive spin up.
+ */
+ unsigned prev_volume_version, prev_output_version,
+ prev_playlist_version;
+
+public:
+ StateFile(const char *path, Partition &partition, EventLoop &loop);
+
+ void Read();
+ void Write();
+ void AutoWrite();
+
+private:
+ virtual bool OnTimeout() override;
+};
+
+#endif /* STATE_FILE_H */
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..2d77e4b63 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,11 @@
*/
#include "config.h"
-#include "sticker.h"
-#include "idle.h"
+#include "StickerDatabase.hxx"
+#include "Idle.hxx"
+
+#include <string>
+#include <map>
#include <glib.h>
#include <sqlite3.h>
@@ -33,7 +36,7 @@
#endif
struct sticker {
- GHashTable *table;
+ std::map<std::string, std::string> table;
};
enum sticker_sql {
@@ -47,19 +50,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 +229,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 +258,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 +523,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);
-}
-
-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;
+ auto i = sticker->table.find(name);
+ if (i == sticker->table.end())
+ return nullptr;
- data->func(key, value, data->user_data);
+ return i->second.c_str();
}
void
@@ -564,33 +545,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.c b/src/Tag.cxx
index c0faa7ab2..afdeb0558 100644
--- a/src/tag.c
+++ b/src/Tag.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
@@ -19,8 +19,8 @@
#include "config.h"
#include "tag.h"
-#include "tag_internal.h"
-#include "tag_pool.h"
+#include "TagInternal.hxx"
+#include "TagPool.hxx"
#include "conf.h"
#include "song.h"
#include "mpd_error.h"
@@ -43,38 +43,15 @@ static struct {
struct tag_item *items[BULK_MAX];
} bulk;
-const char *tag_item_names[TAG_NUM_OF_ITEM_TYPES] = {
- [TAG_ARTIST] = "Artist",
- [TAG_ARTIST_SORT] = "ArtistSort",
- [TAG_ALBUM] = "Album",
- [TAG_ALBUM_ARTIST] = "AlbumArtist",
- [TAG_ALBUM_ARTIST_SORT] = "AlbumArtistSort",
- [TAG_TITLE] = "Title",
- [TAG_TRACK] = "Track",
- [TAG_NAME] = "Name",
- [TAG_GENRE] = "Genre",
- [TAG_DATE] = "Date",
- [TAG_COMPOSER] = "Composer",
- [TAG_PERFORMER] = "Performer",
- [TAG_COMMENT] = "Comment",
- [TAG_DISC] = "Disc",
-
- /* MusicBrainz tags from http://musicbrainz.org/doc/MusicBrainzTag */
- [TAG_MUSICBRAINZ_ARTISTID] = "MUSICBRAINZ_ARTISTID",
- [TAG_MUSICBRAINZ_ALBUMID] = "MUSICBRAINZ_ALBUMID",
- [TAG_MUSICBRAINZ_ALBUMARTISTID] = "MUSICBRAINZ_ALBUMARTISTID",
- [TAG_MUSICBRAINZ_TRACKID] = "MUSICBRAINZ_TRACKID",
-};
-
bool ignore_tag_items[TAG_NUM_OF_ITEM_TYPES];
enum tag_type
tag_name_parse(const char *name)
{
- assert(name != NULL);
+ assert(name != nullptr);
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
- assert(tag_item_names[i] != NULL);
+ assert(tag_item_names[i] != nullptr);
if (strcmp(name, tag_item_names[i]) == 0)
return (enum tag_type)i;
@@ -86,10 +63,10 @@ tag_name_parse(const char *name)
enum tag_type
tag_name_parse_i(const char *name)
{
- assert(name != NULL);
+ assert(name != nullptr);
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
- assert(tag_item_names[i] != NULL);
+ assert(tag_item_names[i] != nullptr);
if (g_ascii_strcasecmp(name, tag_item_names[i]) == 0)
return (enum tag_type)i;
@@ -117,8 +94,8 @@ void tag_lib_init(void)
/* ignore comments by default */
ignore_tag_items[TAG_COMMENT] = true;
- value = config_get_string(CONF_METADATA_TO_USE, NULL);
- if (value == NULL)
+ value = config_get_string(CONF_METADATA_TO_USE, nullptr);
+ if (value == nullptr)
return;
memset(ignore_tag_items, true, TAG_NUM_OF_ITEM_TYPES);
@@ -156,7 +133,7 @@ void tag_lib_init(void)
struct tag *tag_new(void)
{
struct tag *ret = g_new(struct tag, 1);
- ret->items = NULL;
+ ret->items = nullptr;
ret->time = -1;
ret->has_playlist = false;
ret->num_items = 0;
@@ -168,9 +145,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);
+ tag_pool_lock.lock();
tag_pool_put_item(tag->items[idx]);
- g_mutex_unlock(tag_pool_lock);
+ tag_pool_lock.unlock();
if (tag->num_items - idx > 0) {
memmove(tag->items + idx, tag->items + idx + 1,
@@ -178,10 +155,11 @@ static void tag_delete_item(struct tag *tag, unsigned idx)
}
if (tag->num_items > 0) {
- tag->items = g_realloc(tag->items, items_size(tag));
+ tag->items = (struct tag_item **)
+ g_realloc(tag->items, items_size(tag));
} else {
g_free(tag->items);
- tag->items = NULL;
+ tag->items = nullptr;
}
}
@@ -200,12 +178,12 @@ void tag_free(struct tag *tag)
{
int i;
- assert(tag != NULL);
+ assert(tag != nullptr);
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.lock();
for (i = tag->num_items; --i >= 0; )
tag_pool_put_item(tag->items[i]);
- g_mutex_unlock(tag_pool_lock);
+ tag_pool_lock.unlock();
if (tag->items == bulk.items) {
#ifndef NDEBUG
@@ -223,18 +201,20 @@ struct tag *tag_dup(const struct tag *tag)
struct tag *ret;
if (!tag)
- return NULL;
+ return nullptr;
ret = tag_new();
ret->time = tag->time;
ret->has_playlist = tag->has_playlist;
ret->num_items = tag->num_items;
- ret->items = ret->num_items > 0 ? g_malloc(items_size(tag)) : NULL;
+ ret->items = ret->num_items > 0
+ ? (struct tag_item **)g_malloc(items_size(tag))
+ : nullptr;
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.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);
+ tag_pool_lock.unlock();
return ret;
}
@@ -245,17 +225,19 @@ tag_merge(const struct tag *base, const struct tag *add)
struct tag *ret;
unsigned n;
- assert(base != NULL);
- assert(add != NULL);
+ assert(base != nullptr);
+ assert(add != nullptr);
/* allocate new tag object */
ret = tag_new();
ret->time = add->time > 0 ? add->time : base->time;
ret->num_items = base->num_items + add->num_items;
- ret->items = ret->num_items > 0 ? g_malloc(items_size(ret)) : NULL;
+ ret->items = ret->num_items > 0
+ ? (struct tag_item **)g_malloc(items_size(ret))
+ : nullptr;
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.lock();
/* copy all items from "add" */
@@ -270,7 +252,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);
+ tag_pool_lock.unlock();
assert(n <= ret->num_items);
@@ -279,7 +261,8 @@ tag_merge(const struct tag *base, const struct tag *add)
assert(n > 0);
ret->num_items = n;
- ret->items = g_realloc(ret->items, items_size(ret));
+ ret->items = (struct tag_item **)
+ g_realloc(ret->items, items_size(ret));
}
return ret;
@@ -288,10 +271,10 @@ tag_merge(const struct tag *base, const struct tag *add)
struct tag *
tag_merge_replace(struct tag *base, struct tag *add)
{
- if (add == NULL)
+ if (add == nullptr)
return base;
- if (base == NULL)
+ if (base == nullptr)
return add;
struct tag *tag = tag_merge(base, add);
@@ -304,24 +287,24 @@ tag_merge_replace(struct tag *base, struct tag *add)
const char *
tag_get_value(const struct tag *tag, enum tag_type type)
{
- assert(tag != NULL);
+ assert(tag != nullptr);
assert(type < TAG_NUM_OF_ITEM_TYPES);
for (unsigned i = 0; i < tag->num_items; i++)
if (tag->items[i]->type == type)
return tag->items[i]->value;
- return NULL;
+ return nullptr;
}
bool tag_has_type(const struct tag *tag, enum tag_type type)
{
- return tag_get_value(tag, type) != NULL;
+ return tag_get_value(tag, type) != nullptr;
}
bool tag_equal(const struct tag *tag1, const struct tag *tag2)
{
- if (tag1 == NULL && tag2 == NULL)
+ if (tag1 == nullptr && tag2 == nullptr)
return true;
else if (!tag1 || !tag2)
return false;
@@ -367,16 +350,16 @@ fix_utf8(const char *str, size_t length)
char *temp;
gsize written;
- assert(str != NULL);
+ assert(str != nullptr);
/* check if the string is already valid UTF-8 */
if (g_utf8_validate(str, length, &end))
- return NULL;
+ return nullptr;
/* no, it's not - try to import it from ISO-Latin-1 */
temp = g_convert(str, length, "utf-8", "iso-8859-1",
- NULL, &written, NULL);
- if (temp != NULL)
+ nullptr, &written, nullptr);
+ if (temp != nullptr)
/* success! */
return temp;
@@ -388,8 +371,8 @@ fix_utf8(const char *str, size_t length)
void tag_begin_add(struct tag *tag)
{
assert(!bulk.busy);
- assert(tag != NULL);
- assert(tag->items == NULL);
+ assert(tag != nullptr);
+ assert(tag->items == nullptr);
assert(tag->num_items == 0);
#ifndef NDEBUG
@@ -406,10 +389,11 @@ void tag_end_add(struct tag *tag)
if (tag->num_items > 0) {
/* copy the tag items from the bulk list over
to a new list (which fits exactly) */
- tag->items = g_malloc(items_size(tag));
+ tag->items = (struct tag_item **)
+ g_malloc(items_size(tag));
memcpy(tag->items, bulk.items, items_size(tag));
} else
- tag->items = NULL;
+ tag->items = nullptr;
}
#ifndef NDEBUG
@@ -430,12 +414,12 @@ find_non_printable(const char *p, size_t length)
if (char_is_non_printable(p[i]))
return p + i;
- return NULL;
+ return nullptr;
}
/**
* Clears all non-printable characters, convert them to space.
- * Returns NULL if nothing needs to be cleared.
+ * Returns nullptr if nothing needs to be cleared.
*/
static char *
clear_non_printable(const char *p, size_t length)
@@ -443,8 +427,8 @@ clear_non_printable(const char *p, size_t length)
const char *first = find_non_printable(p, length);
char *dest;
- if (first == NULL)
- return NULL;
+ if (first == nullptr)
+ return nullptr;
dest = g_strndup(p, length);
@@ -461,13 +445,13 @@ fix_tag_value(const char *p, size_t length)
char *utf8, *cleared;
utf8 = fix_utf8(p, length);
- if (utf8 != NULL) {
+ if (utf8 != nullptr) {
p = utf8;
length = strlen(p);
}
cleared = clear_non_printable(p, length);
- if (cleared == NULL)
+ if (cleared == nullptr)
cleared = utf8;
else
g_free(utf8);
@@ -483,7 +467,7 @@ tag_add_item_internal(struct tag *tag, enum tag_type type,
char *p;
p = fix_tag_value(value, len);
- if (p != NULL) {
+ if (p != nullptr) {
value = p;
len = strlen(value);
}
@@ -492,19 +476,20 @@ tag_add_item_internal(struct tag *tag, enum tag_type type,
if (tag->items != bulk.items)
/* bulk mode disabled */
- tag->items = g_realloc(tag->items, items_size(tag));
+ tag->items = (struct tag_item **)
+ g_realloc(tag->items, items_size(tag));
else if (tag->num_items >= BULK_MAX) {
/* bulk list already full - switch back to non-bulk */
assert(bulk.busy);
- tag->items = g_malloc(items_size(tag));
+ tag->items = (struct tag_item **)g_malloc(items_size(tag));
memcpy(tag->items, bulk.items,
items_size(tag) - sizeof(struct tag_item *));
}
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.lock();
tag->items[i] = tag_pool_get_item(type, value, len);
- g_mutex_unlock(tag_pool_lock);
+ tag_pool_lock.unlock();
g_free(p);
}
diff --git a/src/tag_internal.h b/src/TagInternal.hxx
index af05cc6b6..afce0427e 100644
--- a/src/tag_internal.h
+++ b/src/TagInternal.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 MPD_TAG_INTERNAL_H
-#define MPD_TAG_INTERNAL_H
+#ifndef MPD_TAG_INTERNAL_HXX
+#define MPD_TAG_INTERNAL_HXX
#include "tag.h"
-#include <stdbool.h>
-
extern bool ignore_tag_items[TAG_NUM_OF_ITEM_TYPES];
#endif
diff --git a/src/TagNames.c b/src/TagNames.c
new file mode 100644
index 000000000..eca320afb
--- /dev/null
+++ b/src/TagNames.c
@@ -0,0 +1,44 @@
+/*
+ * 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 "tag.h"
+
+const char *tag_item_names[TAG_NUM_OF_ITEM_TYPES] = {
+ [TAG_ARTIST] = "Artist",
+ [TAG_ARTIST_SORT] = "ArtistSort",
+ [TAG_ALBUM] = "Album",
+ [TAG_ALBUM_ARTIST] = "AlbumArtist",
+ [TAG_ALBUM_ARTIST_SORT] = "AlbumArtistSort",
+ [TAG_TITLE] = "Title",
+ [TAG_TRACK] = "Track",
+ [TAG_NAME] = "Name",
+ [TAG_GENRE] = "Genre",
+ [TAG_DATE] = "Date",
+ [TAG_COMPOSER] = "Composer",
+ [TAG_PERFORMER] = "Performer",
+ [TAG_COMMENT] = "Comment",
+ [TAG_DISC] = "Disc",
+
+ /* MusicBrainz tags from http://musicbrainz.org/doc/MusicBrainzTag */
+ [TAG_MUSICBRAINZ_ARTISTID] = "MUSICBRAINZ_ARTISTID",
+ [TAG_MUSICBRAINZ_ALBUMID] = "MUSICBRAINZ_ALBUMID",
+ [TAG_MUSICBRAINZ_ALBUMARTISTID] = "MUSICBRAINZ_ALBUMARTISTID",
+ [TAG_MUSICBRAINZ_TRACKID] = "MUSICBRAINZ_TRACKID",
+};
diff --git a/src/tag_pool.c b/src/TagPool.cxx
index eabf3e369..f529cda69 100644
--- a/src/tag_pool.c
+++ b/src/TagPool.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,11 @@
*/
#include "config.h"
-#include "tag_pool.h"
+#include "TagPool.hxx"
#include <assert.h>
-GMutex *tag_pool_lock = NULL;
+Mutex tag_pool_lock;
#define NUM_SLOTS 4096
@@ -39,7 +39,7 @@ calc_hash_n(enum tag_type type, const char *p, size_t length)
{
unsigned hash = 5381;
- assert(p != NULL);
+ assert(p != nullptr);
while (length-- > 0)
hash = (hash << 5) + hash + *p++;
@@ -52,7 +52,7 @@ calc_hash(enum tag_type type, const char *p)
{
unsigned hash = 5381;
- assert(p != NULL);
+ assert(p != nullptr);
while (*p != 0)
hash = (hash << 5) + hash + *p++;
@@ -72,7 +72,8 @@ static struct slot *slot_alloc(struct slot *next,
{
struct slot *slot;
- slot = g_malloc(sizeof(*slot) - sizeof(slot->item.value) + length + 1);
+ slot = (struct slot *)
+ g_malloc(sizeof(*slot) - sizeof(slot->item.value) + length + 1);
slot->next = next;
slot->ref = 1;
slot->item.type = type;
@@ -81,26 +82,13 @@ 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)
{
struct slot **slot_p, *slot;
slot_p = &slots[calc_hash_n(type, value, length) % NUM_SLOTS];
- for (slot = *slot_p; slot != NULL; slot = slot->next) {
+ for (slot = *slot_p; slot != nullptr; slot = slot->next) {
if (slot->item.type == type &&
length == strlen(slot->item.value) &&
memcmp(value, slot->item.value, length) == 0 &&
@@ -153,7 +141,7 @@ void tag_pool_put_item(struct tag_item *item)
for (slot_p = &slots[calc_hash(item->type, item->value) % NUM_SLOTS];
*slot_p != slot;
slot_p = &(*slot_p)->next) {
- assert(*slot_p != NULL);
+ assert(*slot_p != nullptr);
}
*slot_p = slot->next;
diff --git a/src/tag_pool.h b/src/TagPool.hxx
index a96c00d85..5d68a31d7 100644
--- a/src/tag_pool.h
+++ b/src/TagPool.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,21 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TAG_POOL_H
-#define MPD_TAG_POOL_H
+#ifndef MPD_TAG_POOL_HXX
+#define MPD_TAG_POOL_HXX
#include "tag.h"
+#include "thread/Mutex.hxx"
#include <glib.h>
-extern GMutex *tag_pool_lock;
+extern Mutex 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/tag_print.c b/src/TagPrint.cxx
index 9a46b247a..b3ad07df4 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 "TagInternal.hxx"
#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..15da9fc4b 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,9 +18,9 @@
*/
#include "config.h"
-#include "tag_save.h"
+#include "TagSave.hxx"
#include "tag.h"
-#include "tag_internal.h"
+#include "TagInternal.hxx"
#include "song.h"
void tag_save(FILE *file, const struct tag *tag)
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..daa7f1ec4 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,18 @@
*/
#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 "tag.h"
+#include "Mapper.hxx"
+
+extern "C" {
#include "tag_handler.h"
+}
#include <glib.h>
@@ -39,11 +41,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 +59,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 +74,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 +102,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..22a33ade9 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 "event_pipe.h"
-#include "update.h"
-#include "idle.h"
+#include "UpdateGlue.hxx"
+#include "UpdateQueue.hxx"
+#include "UpdateWalk.hxx"
+#include "UpdateRemove.hxx"
+#include "Mapper.hxx"
+#include "DatabaseSimple.hxx"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
+
+extern "C" {
#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);
@@ -90,7 +92,7 @@ static void * update_task(void *_path)
g_free(_path);
progress = UPDATE_PROGRESS_DONE;
- event_pipe_emit(PIPE_EVENT_UPDATE);
+ GlobalEvents::Emit(GlobalEvents::UPDATE);
return NULL;
}
@@ -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);
+ global_partition->playlist.FullIncrementVersions();
idle_add(IDLE_DATABASE);
}
@@ -171,7 +173,7 @@ static void update_finished_event(void)
void update_global_init(void)
{
- event_pipe_register(PIPE_EVENT_UPDATE, update_finished_event);
+ GlobalEvents::Register(GlobalEvents::UPDATE, update_finished_event);
update_remove_global_init();
update_walk_global_init();
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..f7e6c80ec 100644
--- a/src/update_remove.c
+++ b/src/UpdateRemove.cxx
@@ -18,15 +18,17 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_remove.h"
-#include "event_pipe.h"
+#include "UpdateRemove.hxx"
+#include "Playlist.hxx"
+#include "Partition.hxx"
+#include "GlobalEvents.hxx"
+
#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 +61,7 @@ song_remove_event(void)
sticker_song_delete(removed_song);
#endif
- playlist_delete_song(&g_playlist, global_player_control, removed_song);
+ global_partition->DeleteSong(*removed_song);
/* clear "removed_song" and send signal to update thread */
g_mutex_lock(remove_mutex);
@@ -74,7 +76,7 @@ update_remove_global_init(void)
remove_mutex = g_mutex_new();
remove_cond = g_cond_new();
- event_pipe_register(PIPE_EVENT_DELETE, song_remove_event);
+ GlobalEvents::Register(GlobalEvents::DELETE, song_remove_event);
}
void
@@ -91,7 +93,7 @@ update_remove_song(const struct song *song)
removed_song = song;
- event_pipe_emit(PIPE_EVENT_DELETE);
+ GlobalEvents::Emit(GlobalEvents::DELETE);
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..208e6290a 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"
+#include "conf.h"
+
+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/volume.c b/src/Volume.cxx
index d3ce47dd4..116f4aa18 100644
--- a/src/volume.c
+++ b/src/Volume.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,14 @@
*/
#include "config.h"
-#include "volume.h"
-#include "conf.h"
-#include "idle.h"
-#include "pcm_volume.h"
-#include "output_all.h"
-#include "mixer_control.h"
-#include "mixer_all.h"
-#include "mixer_type.h"
-#include "event_pipe.h"
+#include "Volume.hxx"
+#include "MixerAll.hxx"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
#include <glib.h>
#include <assert.h>
-#include <math.h>
-#include <string.h>
#include <stdlib.h>
#undef G_LOG_DOMAIN
@@ -48,7 +41,7 @@ static int last_hardware_volume = -1;
static GTimer *hardware_volume_timer;
/**
- * Handler for #PIPE_EVENT_MIXER.
+ * Handler for #GlobalEvents::MIXER.
*/
static void
mixer_event_callback(void)
@@ -69,7 +62,7 @@ void volume_init(void)
{
hardware_volume_timer = g_timer_new();
- event_pipe_register(PIPE_EVENT_MIXER, mixer_event_callback);
+ GlobalEvents::Register(GlobalEvents::MIXER, mixer_event_callback);
}
int volume_level_get(void)
diff --git a/src/volume.h b/src/Volume.hxx
index b08899a84..024b2840a 100644
--- a/src/volume.h
+++ b/src/Volume.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,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_VOLUME_H
-#define MPD_VOLUME_H
+#ifndef MPD_VOLUME_HXX
+#define MPD_VOLUME_HXX
-#include <stdbool.h>
#include <stdio.h>
void volume_init(void);
diff --git a/src/main_win32.c b/src/Win32Main.cxx
index aac7ad886..0cd5e445b 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"
-#include "event_pipe.h"
+#include "GlobalEvents.hxx"
+
+#include <cstdlib>
+#include <atomic>
#include <glib.h>
@@ -32,7 +35,7 @@
static int service_argc;
static char **service_argv;
static char service_name[] = "";
-static BOOL ignore_console_events;
+static std::atomic_bool running;
static SERVICE_STATUS_HANDLE service_handle;
static void WINAPI
@@ -68,7 +71,7 @@ service_dispatcher(G_GNUC_UNUSED DWORD control, G_GNUC_UNUSED DWORD event_type,
switch (control) {
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
- event_pipe_emit(PIPE_EVENT_SHUTDOWN);
+ GlobalEvents::Emit(GlobalEvents::SHUTDOWN);
return NO_ERROR;
default:
return NO_ERROR;
@@ -103,8 +106,22 @@ console_handler(DWORD event)
switch (event) {
case CTRL_C_EVENT:
case CTRL_CLOSE_EVENT:
- if (!ignore_console_events)
- event_pipe_emit(PIPE_EVENT_SHUTDOWN);
+ if (running.load()) {
+ // Recent msdn docs that process is terminated
+ // if this function returns TRUE.
+ // We initiate correct shutdown sequence (if possible).
+ // Once main() returns CRT will terminate our process
+ // regardless our thread is still active.
+ // If this did not happen within 3 seconds
+ // let's shutdown anyway.
+ GlobalEvents::Emit(GlobalEvents::SHUTDOWN);
+ // Under debugger it's better to wait indefinitely
+ // to allow debugging of shutdown code.
+ Sleep(IsDebuggerPresent() ? INFINITE : 3000);
+ }
+ // If we're not running main loop there is no chance for
+ // clean shutdown.
+ std::exit(EXIT_FAILURE);
return TRUE;
default:
return FALSE;
@@ -125,8 +142,8 @@ int win32_main(int argc, char *argv[])
error_code = GetLastError();
if (error_code == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
/* running as console app */
+ running.store(false);
SetConsoleTitle("Music Player Daemon");
- ignore_console_events = TRUE;
SetConsoleCtrlHandler(console_handler, TRUE);
return mpd_main(argc, argv);
}
@@ -140,7 +157,7 @@ void win32_app_started()
if (service_handle != 0)
service_notify_status(SERVICE_RUNNING);
else
- ignore_console_events = FALSE;
+ running.store(true);
}
void win32_app_stopping()
@@ -148,7 +165,7 @@ void win32_app_stopping()
if (service_handle != 0)
service_notify_status(SERVICE_STOP_PENDING);
else
- ignore_console_events = TRUE;
+ running.store(false);
}
#endif
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_config.c b/src/audio_config.c
index 72869c384..a32f43733 100644
--- a/src/audio_config.c
+++ b/src/audio_config.c
@@ -23,7 +23,6 @@
#include "audio_parser.h"
#include "output_internal.h"
#include "output_plugin.h"
-#include "output_all.h"
#include "conf.h"
#include "mpd_error.h"
diff --git a/src/audio_format.h b/src/audio_format.h
index bf77add3b..f9b176bc1 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 ||
@@ -219,11 +220,15 @@ static inline bool audio_format_equals(const struct audio_format *a,
a->channels == b->channels;
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
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 +259,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 +268,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 +279,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 +292,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,9 +304,13 @@ 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);
+#ifdef __cplusplus
+}
+#endif
+
#endif
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_event.c b/src/client_event.c
deleted file mode 100644
index 4f54ae0a7..000000000
--- a/src/client_event.c
+++ /dev/null
@@ -1,108 +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 "client_internal.h"
-#include "main.h"
-
-#include <assert.h>
-
-static gboolean
-client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
- gpointer data)
-{
- struct client *client = data;
-
- assert(!client_is_expired(client));
-
- if (condition != G_IO_OUT) {
- client_set_expired(client);
- return false;
- }
-
- client_write_deferred(client);
-
- if (client_is_expired(client)) {
- client_close(client);
- return false;
- }
-
- g_timer_start(client->last_activity);
-
- if (g_queue_is_empty(client->deferred_send)) {
- /* 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,
- client_in_event, client);
- return false;
- }
-
- /* write more */
- return true;
-}
-
-gboolean
-client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
- gpointer data)
-{
- struct client *client = data;
- enum command_return ret;
-
- assert(!client_is_expired(client));
-
- if (condition != G_IO_IN) {
- client_set_expired(client);
- return false;
- }
-
- g_timer_start(client->last_activity);
-
- ret = client_read(client);
- switch (ret) {
- case COMMAND_RETURN_OK:
- case COMMAND_RETURN_ERROR:
- break;
-
- case COMMAND_RETURN_KILL:
- client_close(client);
- g_main_loop_quit(main_loop);
- return false;
-
- case COMMAND_RETURN_CLOSE:
- client_close(client);
- return false;
- }
-
- if (client_is_expired(client)) {
- client_close(client);
- return false;
- }
-
- 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,
- client_out_event, client);
- return false;
- }
-
- /* read more */
- return true;
-}
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_read.c b/src/client_read.c
deleted file mode 100644
index 26ade264e..000000000
--- a/src/client_read.c
+++ /dev/null
@@ -1,113 +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 "client_internal.h"
-#include "fifo_buffer.h"
-
-#include <assert.h>
-#include <string.h>
-
-static char *
-client_read_line(struct client *client)
-{
- const char *p, *newline;
- size_t length;
- char *line;
-
- p = fifo_buffer_read(client->input, &length);
- if (p == NULL)
- return NULL;
-
- newline = memchr(p, '\n', length);
- if (newline == NULL)
- return NULL;
-
- 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)
-{
- char *line;
-
- fifo_buffer_append(client->input, bytesRead);
-
- /* process all lines */
-
- while ((line = client_read_line(client)) != NULL) {
- enum command_return ret = client_process_line(client, line);
- g_free(line);
-
- if (ret == COMMAND_RETURN_KILL ||
- ret == COMMAND_RETURN_CLOSE)
- return ret;
- if (client_is_expired(client))
- return COMMAND_RETURN_CLOSE;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-enum command_return
-client_read(struct client *client)
-{
- char *p;
- size_t max_length;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_read;
-
- assert(client != NULL);
- assert(client->channel != NULL);
-
- p = fifo_buffer_write(client->input, &max_length);
- if (p == NULL) {
- g_warning("[%u] buffer overflow", client->num);
- return COMMAND_RETURN_CLOSE;
- }
-
- status = g_io_channel_read_chars(client->channel, p, max_length,
- &bytes_read, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- return client_input_received(client, bytes_read);
-
- case G_IO_STATUS_AGAIN:
- /* try again later, after select() */
- return COMMAND_RETURN_OK;
-
- case G_IO_STATUS_EOF:
- /* peer disconnected */
- return COMMAND_RETURN_CLOSE;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
- g_warning("failed to read from client %d: %s",
- client->num, error->message);
- g_error_free(error);
- return COMMAND_RETURN_CLOSE;
- }
-
- /* unreachable */
- return COMMAND_RETURN_CLOSE;
-}
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/client_write.c b/src/client_write.c
deleted file mode 100644
index 78cfca8a1..000000000
--- a/src/client_write.c
+++ /dev/null
@@ -1,284 +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 "client_internal.h"
-
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-
-static size_t
-client_write_deferred_buffer(struct client *client,
- const struct deferred_buffer *buffer)
-{
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
-
- assert(client != NULL);
- assert(client->channel != NULL);
- assert(buffer != NULL);
-
- status = g_io_channel_write_chars
- (client->channel, buffer->data, buffer->size,
- &bytes_written, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- return bytes_written;
-
- case G_IO_STATUS_AGAIN:
- return 0;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- client_set_expired(client);
- return 0;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- client_set_expired(client);
- g_warning("failed to flush buffer for %i: %s",
- client->num, error->message);
- g_error_free(error);
- return 0;
- }
-
- /* unreachable */
- return 0;
-}
-
-void
-client_write_deferred(struct client *client)
-{
- size_t ret;
-
- while (!g_queue_is_empty(client->deferred_send)) {
- struct deferred_buffer *buf =
- g_queue_peek_head(client->deferred_send);
-
- assert(buf->size > 0);
- assert(buf->size <= client->deferred_bytes);
-
- ret = client_write_deferred_buffer(client, buf);
- if (ret == 0)
- break;
-
- if (ret < buf->size) {
- assert(client->deferred_bytes >= (size_t)ret);
- client->deferred_bytes -= ret;
- buf->size -= ret;
- memmove(buf->data, buf->data + ret, buf->size);
- break;
- } else {
- size_t decr = sizeof(*buf) -
- sizeof(buf->data) + buf->size;
-
- assert(client->deferred_bytes >= decr);
- client->deferred_bytes -= decr;
- g_free(buf);
- g_queue_pop_head(client->deferred_send);
- }
-
- g_timer_start(client->last_activity);
- }
-
- if (g_queue_is_empty(client->deferred_send)) {
- g_debug("[%u] buffer empty %lu", client->num,
- (unsigned long)client->deferred_bytes);
- assert(client->deferred_bytes == 0);
- }
-}
-
-static void client_defer_output(struct client *client,
- const void *data, size_t length)
-{
- size_t alloc;
- struct deferred_buffer *buf;
-
- assert(length > 0);
-
- alloc = sizeof(*buf) - sizeof(buf->data) + length;
- client->deferred_bytes += alloc;
- if (client->deferred_bytes > client_max_output_buffer_size) {
- g_warning("[%u] output buffer size (%lu) is "
- "larger than the max (%lu)",
- client->num,
- (unsigned long)client->deferred_bytes,
- (unsigned long)client_max_output_buffer_size);
- /* cause client to close */
- client_set_expired(client);
- return;
- }
-
- buf = 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)
-{
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
-
- assert(client != NULL);
- assert(client->channel != NULL);
- assert(data != NULL);
- assert(length > 0);
- assert(g_queue_is_empty(client->deferred_send));
-
- status = g_io_channel_write_chars(client->channel, data, length,
- &bytes_written, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- case G_IO_STATUS_AGAIN:
- break;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- client_set_expired(client);
- return;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- client_set_expired(client);
- g_warning("failed to write to %i: %s",
- client->num, error->message);
- g_error_free(error);
- return;
- }
-
- if (bytes_written < length)
- client_defer_output(client, data + bytes_written,
- length - bytes_written);
-
- if (!g_queue_is_empty(client->deferred_send))
- g_debug("[%u] buffer created", client->num);
-}
-
-void
-client_write_output(struct client *client)
-{
- if (client_is_expired(client) || !client->send_buf_used)
- return;
-
- if (!g_queue_is_empty(client->deferred_send)) {
- client_defer_output(client, client->send_buf,
- client->send_buf_used);
-
- if (client_is_expired(client))
- return;
-
- /* try to flush the deferred buffers now; the current
- server command may take too long to finish, and
- meanwhile try to feed output to the client,
- otherwise it will time out. One reason why
- deferring is slow might be that currently each
- client_write() allocates a new deferred buffer.
- This should be optimized after MPD 0.14. */
- client_write_deferred(client);
- } else
- client_write_direct(client, client->send_buf,
- client->send_buf_used);
-
- client->send_buf_used = 0;
-}
-
-/**
- * Write a block of data to the client.
- */
-static void client_write(struct client *client, const char *buffer, size_t buflen)
-{
- /* if the client is going to be closed, do nothing */
- if (client_is_expired(client))
- return;
-
- while (buflen > 0 && !client_is_expired(client)) {
- size_t copylen;
-
- assert(client->send_buf_used < sizeof(client->send_buf));
-
- copylen = sizeof(client->send_buf) - client->send_buf_used;
- if (copylen > buflen)
- copylen = buflen;
-
- memcpy(client->send_buf + client->send_buf_used, buffer,
- copylen);
- buflen -= copylen;
- client->send_buf_used += copylen;
- buffer += copylen;
- if (client->send_buf_used >= sizeof(client->send_buf))
- client_write_output(client);
- }
-}
-
-void client_puts(struct client *client, const char *s)
-{
- client_write(client, s, strlen(s));
-}
-
-void client_vprintf(struct 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);
- va_end(tmp);
-
- if (length <= 0)
- /* wtf.. */
- return;
-
- buffer = g_malloc(length + 1);
- vsnprintf(buffer, length + 1, fmt, args);
- client_write(client, buffer, length);
- g_free(buffer);
-#else
- /* On mingw32, snprintf() expects a 64 bit integer instead of
- a "long int" for "%li". This is not consistent with our
- expectation, so we're using plain sprintf() here, hoping
- the static buffer is large enough. Sorry for this hack,
- but WIN32 development is so painful, I'm not in the mood to
- do it properly now. */
-
- static char buffer[4096];
- vsprintf(buffer, fmt, args);
- client_write(client, buffer, strlen(buffer));
-#endif
-}
-
-G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- client_vprintf(client, fmt, args);
- va_end(args);
-}
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.h b/src/conf.h
index 815c739b1..d20dc78bc 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -118,6 +118,8 @@ config_quark(void)
return g_quark_from_static_string("config");
}
+G_BEGIN_DECLS
+
void config_global_init(void);
void config_global_finish(void);
@@ -224,4 +226,6 @@ void
config_add_block_param(struct config_param * param, const char *name,
const char *value, int line);
+G_END_DECLS
+
#endif
diff --git a/src/cue/cue_parser.c b/src/cue/cue_parser.c
index 9ccc3bcdd..bee757c9c 100644
--- a/src/cue/cue_parser.c
+++ b/src/cue/cue_parser.c
@@ -23,6 +23,8 @@
#include "song.h"
#include "tag.h"
+#include <glib.h>
+
#include <assert.h>
#include <stdlib.h>
diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx
new file mode 100644
index 000000000..fcdbae9f0
--- /dev/null
+++ b/src/db/ProxyDatabasePlugin.cxx
@@ -0,0 +1,475 @@
+/*
+ * 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"
+#include "conf.h"
+
+extern "C" {
+#include "db_error.h"
+#include "song.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..8eea81e30
--- /dev/null
+++ b/src/db/SimpleDatabasePlugin.cxx
@@ -0,0 +1,344 @@
+/*
+ * 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"
+#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..6d08fab56
--- /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"
+#include "decoder_api.h"
+
+extern "C" {
+#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..b80372bbf 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"
#include "decoder_api.h"
-#include "pcm_buffer.h"
-#include <glib.h>
+extern "C" {
+#include "pcm_buffer.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..43b604097 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,10 @@
*/
#include "config.h" /* must be first for large file support */
-#include "_flac_common.h"
-#include "flac_compat.h"
-#include "flac_metadata.h"
-
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-#include "_ogg_common.h"
-#endif
+#include "FLACDecoderPlugin.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
+#include "OggCodec.hxx"
#include <glib.h>
@@ -34,114 +31,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 +55,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 +87,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 +120,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 +172,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 +213,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 +254,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 +278,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 +288,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..0c2c24770
--- /dev/null
+++ b/src/decoder/FLACIOHandle.hxx
@@ -0,0 +1,45 @@
+/*
+ * 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"
+#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..99f321cdd
--- /dev/null
+++ b/src/decoder/FLACInput.cxx
@@ -0,0 +1,149 @@
+/*
+ * 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 "decoder_api.h"
+#include "gcc.h"
+#include "input_stream.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..8273a230b 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 "replay_gain_info.h"
+#include "FLACMetaData.hxx"
+
+extern "C" {
+#include "XiphTags.h"
+}
+
#include "tag.h"
#include "tag_handler.h"
#include "tag_table.h"
+#include "replay_gain_info.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/_ogg_common.c b/src/decoder/OggCodec.cxx
index 09d2712da..5ad9c69d6 100644
--- a/src/decoder/_ogg_common.c
+++ b/src/decoder/OggCodec.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,25 +22,27 @@
*/
#include "config.h"
-#include "_ogg_common.h"
+#include "OggCodec.hxx"
-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/OggCodec.hxx
index 85e4ebba6..e241560fb 100644
--- a/src/decoder/_ogg_common.h
+++ b/src/decoder/OggCodec.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,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_HXX
+#define MPD_OGG_CODEC_HXX
#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/OggFind.cxx b/src/decoder/OggFind.cxx
new file mode 100644
index 000000000..9df4c6455
--- /dev/null
+++ b/src/decoder/OggFind.cxx
@@ -0,0 +1,37 @@
+/*
+ * 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 "OggFind.hxx"
+#include "OggSyncState.hxx"
+
+bool
+OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet)
+{
+ while (true) {
+ int r = ogg_stream_packetout(&os, &packet);
+ if (r == 0) {
+ if (!oy.ExpectPageIn(os))
+ return false;
+
+ continue;
+ } else if (r > 0 && packet.e_o_s)
+ return true;
+ }
+}
diff --git a/src/decoder/OggFind.hxx b/src/decoder/OggFind.hxx
new file mode 100644
index 000000000..7d18d2067
--- /dev/null
+++ b/src/decoder/OggFind.hxx
@@ -0,0 +1,38 @@
+/*
+ * 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_OGG_FIND_HXX
+#define MPD_OGG_FIND_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+class OggSyncState;
+
+/**
+ * Skip all pages/packets until an end-of-stream (EOS) packet for the
+ * specified stream is found.
+ *
+ * @return true if the EOS packet was found
+ */
+bool
+OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet);
+
+#endif
diff --git a/src/decoder/OggSyncState.hxx b/src/decoder/OggSyncState.hxx
new file mode 100644
index 000000000..eaeb9bd8c
--- /dev/null
+++ b/src/decoder/OggSyncState.hxx
@@ -0,0 +1,78 @@
+/*
+ * 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_OGG_SYNC_STATE_HXX
+#define MPD_OGG_SYNC_STATE_HXX
+
+#include "check.h"
+#include "OggUtil.hxx"
+
+#include <ogg/ogg.h>
+
+#include <stddef.h>
+
+/**
+ * Wrapper for an ogg_sync_state.
+ */
+class OggSyncState {
+ ogg_sync_state oy;
+
+ input_stream &is;
+ struct decoder *const decoder;
+
+public:
+ OggSyncState(input_stream &_is, struct decoder *const _decoder=nullptr)
+ :is(_is), decoder(_decoder) {
+ ogg_sync_init(&oy);
+ }
+
+ ~OggSyncState() {
+ ogg_sync_clear(&oy);
+ }
+
+ void Reset() {
+ ogg_sync_reset(&oy);
+ }
+
+ bool Feed(size_t size) {
+ return OggFeed(oy, decoder, &is, size);
+ }
+
+ bool ExpectPage(ogg_page &page) {
+ return OggExpectPage(oy, page, decoder, &is);
+ }
+
+ bool ExpectFirstPage(ogg_stream_state &os) {
+ return OggExpectFirstPage(oy, os, decoder, &is);
+ }
+
+ bool ExpectPageIn(ogg_stream_state &os) {
+ return OggExpectPageIn(oy, os, decoder, &is);
+ }
+
+ bool ExpectPageSeek(ogg_page &page) {
+ return OggExpectPageSeek(oy, page, decoder, &is);
+ }
+
+ bool ExpectPageSeekIn(ogg_stream_state &os) {
+ return OggExpectPageSeekIn(oy, os, decoder, &is);
+ }
+};
+
+#endif
diff --git a/src/decoder/OggUtil.cxx b/src/decoder/OggUtil.cxx
new file mode 100644
index 000000000..a1125a2c6
--- /dev/null
+++ b/src/decoder/OggUtil.cxx
@@ -0,0 +1,118 @@
+/*
+ * 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"
+#include "decoder_api.h"
+
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+ 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,
+ decoder *decoder, 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;
+ }
+}
+
+bool
+OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPage(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_init(&os, ogg_page_serialno(&page));
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+bool
+OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPage(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+bool
+OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream)
+{
+ size_t remaining_skipped = 16384;
+
+ while (true) {
+ int r = ogg_sync_pageseek(&oy, &page);
+ if (r > 0)
+ return true;
+
+ if (r < 0) {
+ /* skipped -r bytes */
+ size_t nbytes = -r;
+ if (nbytes > remaining_skipped)
+ /* still no ogg page - we lost our
+ patience, abort */
+ return false;
+
+ remaining_skipped -= nbytes;
+ continue;
+ }
+
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ return false;
+ }
+}
+
+bool
+OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPageSeek(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
diff --git a/src/decoder/OggUtil.hxx b/src/decoder/OggUtil.hxx
new file mode 100644
index 000000000..324797815
--- /dev/null
+++ b/src/decoder/OggUtil.hxx
@@ -0,0 +1,87 @@
+/*
+ * 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>
+
+struct input_stream;
+struct decoder;
+
+/**
+ * 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, input_stream *is,
+ 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,
+ decoder *decoder, input_stream *input_stream);
+
+/**
+ * Combines OggExpectPage(), ogg_stream_init() and
+ * ogg_stream_pagein().
+ *
+ * @return true if the stream was initialized and the first page was
+ * delivered to it
+ */
+bool
+OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+/**
+ * Combines OggExpectPage() and ogg_stream_pagein().
+ *
+ * @return true if a page was delivered to the stream
+ */
+bool
+OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+/**
+ * Like OggExpectPage(), but allow skipping garbage (after seeking).
+ */
+bool
+OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream);
+
+/**
+ * Combines OggExpectPageSeek() and ogg_stream_pagein().
+ *
+ * @return true if a page was delivered to the stream
+ */
+bool
+OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+#endif
diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/OpusDecoderPlugin.cxx
new file mode 100644
index 000000000..f0c0a442d
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.cxx
@@ -0,0 +1,396 @@
+/*
+ * 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"
+#include "OggFind.hxx"
+#include "OggSyncState.hxx"
+#include "decoder_api.h"
+#include "OggCodec.hxx"
+#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();
+
+ bool ReadFirstPage(OggSyncState &oy);
+ bool ReadNextPage(OggSyncState &oy);
+
+ enum decoder_command HandlePackets();
+ 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);
+}
+
+inline bool
+MPDOpusDecoder::ReadFirstPage(OggSyncState &oy)
+{
+ assert(!os_initialized);
+
+ if (!oy.ExpectFirstPage(os))
+ return false;
+
+ os_initialized = true;
+ return true;
+}
+
+inline bool
+MPDOpusDecoder::ReadNextPage(OggSyncState &oy)
+{
+ assert(os_initialized);
+
+ ogg_page page;
+ if (!oy.ExpectPage(page))
+ return false;
+
+ const auto page_serialno = ogg_page_serialno(&page);
+ if (page_serialno != os.serialno)
+ ogg_stream_reset_serialno(&os, page_serialno);
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandlePackets()
+{
+ 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;
+}
+
+inline 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);
+}
+
+inline 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);
+}
+
+inline 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;
+}
+
+inline 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);
+ OggSyncState oy(*input_stream, decoder);
+
+ if (!d.ReadFirstPage(oy))
+ return;
+
+ while (true) {
+ enum decoder_command cmd = d.HandlePackets();
+ if (cmd != DECODE_COMMAND_NONE)
+ break;
+
+ if (!d.ReadNextPage(oy))
+ break;
+
+ }
+}
+
+static bool
+SeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet,
+ input_stream *is)
+{
+ if (is->size > 0 && is->size - is->offset < 65536)
+ return OggFindEOS(oy, os, packet);
+
+ if (!input_stream_cheap_seeking(is))
+ return false;
+
+ oy.Reset();
+
+ return input_stream_lock_seek(is, -65536, SEEK_END, nullptr) &&
+ oy.ExpectPageSeekIn(os) &&
+ OggFindEOS(oy, os, packet);
+}
+
+static bool
+mpd_opus_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ OggSyncState oy(*is);
+
+ ogg_stream_state os;
+ if (!oy.ExpectFirstPage(os))
+ return false;
+
+ /* read at most two more pages */
+ unsigned remaining_pages = 2;
+
+ bool result = false;
+
+ 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 (!oy.ExpectPageIn(os)) {
+ result = false;
+ break;
+ }
+
+ 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;
+ }
+ }
+
+ if (packet.e_o_s || SeekFindEOS(oy, os, packet, is))
+ tag_handler_invoke_duration(handler, handler_ctx,
+ packet.granulepos / opus_sample_rate);
+
+ ogg_stream_clear(&os);
+
+ 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..1fd07b55c
--- /dev/null
+++ b/src/decoder/OpusReader.hxx
@@ -0,0 +1,98 @@
+/*
+ * 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 "string_util.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/vorbis_comments.c b/src/decoder/VorbisComments.cxx
index 6c2d57b72..10fe22369 100644
--- a/src/decoder/vorbis_comments.c
+++ b/src/decoder/VorbisComments.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,7 +18,8 @@
*/
#include "config.h"
-#include "vorbis_comments.h"
+#include "VorbisComments.hxx"
+#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,14 +113,14 @@ 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;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (vorbis_copy_comment(comment,
- tag_item_names[i], i,
+ tag_item_names[i], tag_type(i),
handler, handler_ctx))
return;
}
diff --git a/src/decoder/vorbis_comments.h b/src/decoder/VorbisComments.hxx
index c15096930..8212cac47 100644
--- a/src/decoder/vorbis_comments.h
+++ b/src/decoder/VorbisComments.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_VORBIS_COMMENTS_H
-#define MPD_VORBIS_COMMENTS_H
+#ifndef MPD_VORBIS_COMMENTS_HXX
+#define MPD_VORBIS_COMMENTS_HXX
#include "check.h"
diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/VorbisDecoderPlugin.cxx
index 15cdc0ca9..01a558def 100644
--- a/src/decoder/vorbis_decoder_plugin.c
+++ b/src/decoder/VorbisDecoderPlugin.cxx
@@ -18,10 +18,16 @@
*/
#include "config.h"
-#include "vorbis_comments.h"
-#include "_ogg_common.h"
+#include "VorbisDecoderPlugin.h"
+#include "VorbisComments.hxx"
+#include "decoder_api.h"
+#include "OggCodec.hxx"
+
+extern "C" {
#include "audio_check.h"
#include "uri.h"
+}
+
#include "tag_handler.h"
#ifndef HAVE_TREMOR
@@ -48,12 +54,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 {
@@ -65,10 +70,9 @@ 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);
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
+ size_t ret = decoder_read(vis->decoder, vis->input_stream,
+ ptr, size * nmemb);
errno = 0;
@@ -77,7 +81,7 @@ static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
static int ogg_seek_cb(void *data, ogg_int64_t offset, int whence)
{
- struct vorbis_input_stream *vis = data;
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
return vis->seekable &&
(!vis->decoder || decoder_get_command(vis->decoder) != DECODE_COMMAND_STOP) &&
@@ -93,16 +97,16 @@ static int ogg_close_cb(G_GNUC_UNUSED void *data)
static long ogg_tell_cb(void *data)
{
- const struct vorbis_input_stream *vis = data;
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
return (long)vis->input_stream->offset;
}
static const ov_callbacks vorbis_is_callbacks = {
- .read_func = ogg_read_cb,
- .seek_func = ogg_seek_cb,
- .close_func = ogg_close_cb,
- .tell_func = ogg_tell_cb,
+ ogg_read_cb,
+ ogg_seek_cb,
+ ogg_close_cb,
+ ogg_tell_cb,
};
static const char *
@@ -135,9 +139,7 @@ vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf,
{
vis->decoder = decoder;
vis->input_stream = input_stream;
- vis->seekable = input_stream->seekable &&
- (input_stream->uri == NULL ||
- !uri_has_scheme(input_stream->uri));
+ vis->seekable = input_stream_cheap_seeking(input_stream);
int ret = ov_open_callbacks(vis, vf, NULL, 0, vorbis_is_callbacks);
if (ret < 0) {
@@ -155,9 +157,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 +165,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 +247,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 +288,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 +298,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);
@@ -306,9 +347,14 @@ static const char *const vorbis_mime_types[] = {
};
const struct decoder_plugin vorbis_decoder_plugin = {
- .name = "vorbis",
- .stream_decode = vorbis_stream_decode,
- .scan_stream = vorbis_scan_stream,
- .suffixes = vorbis_suffixes,
- .mime_types = vorbis_mime_types
+ "vorbis",
+ nullptr,
+ nullptr,
+ vorbis_stream_decode,
+ nullptr,
+ nullptr,
+ vorbis_scan_stream,
+ nullptr,
+ vorbis_suffixes,
+ vorbis_mime_types
};
diff --git a/src/decoder/VorbisDecoderPlugin.h b/src/decoder/VorbisDecoderPlugin.h
new file mode 100644
index 000000000..618c9ffde
--- /dev/null
+++ b/src/decoder/VorbisDecoderPlugin.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_VORBIS_H
+#define MPD_DECODER_VORBIS_H
+
+extern const struct decoder_plugin vorbis_decoder_plugin;
+
+#endif
diff --git a/src/decoder/wavpack_decoder_plugin.c b/src/decoder/WavpackDecoderPlugin.cxx
index 9ebd0fccc..fdd910c84 100644
--- a/src/decoder/wavpack_decoder_plugin.c
+++ b/src/decoder/WavpackDecoderPlugin.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 "WavpackDecoderPlugin.hxx"
#include "decoder_api.h"
+
+extern "C" {
#include "audio_check.h"
-#include "path.h"
-#include "utils.h"
-#include "tag_table.h"
+}
+
#include "tag_handler.h"
#include "tag_ape.h"
@@ -30,7 +32,6 @@
#include <glib.h>
#include <assert.h>
-#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -53,18 +54,18 @@ typedef void (*format_samples_t)(
static void
format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
{
- int32_t *src = buffer;
+ int32_t *src = (int32_t *)buffer;
switch (bytes_per_sample) {
case 1: {
- int8_t *dst = buffer;
+ int8_t *dst = (int8_t *)buffer;
/*
* The asserts like the following one are because we do the
* formatting of samples within a single buffer. The size
* of the output samples never can be greater than the size
* of the input ones. Otherwise we would have an overflow.
*/
- assert_static(sizeof(*dst) <= sizeof(*src));
+ static_assert(sizeof(*dst) <= sizeof(*src), "Wrong size");
/* pass through and align 8-bit samples */
while (count--) {
@@ -73,8 +74,8 @@ format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
break;
}
case 2: {
- uint16_t *dst = buffer;
- assert_static(sizeof(*dst) <= sizeof(*src));
+ uint16_t *dst = (uint16_t *)buffer;
+ static_assert(sizeof(*dst) <= sizeof(*src), "Wrong size");
/* pass through and align 16-bit samples */
while (count--) {
@@ -97,7 +98,7 @@ static void
format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
uint32_t count)
{
- float *p = buffer;
+ float *p = (float *)buffer;
while (count--) {
*p /= (1 << 23);
@@ -359,7 +360,7 @@ static struct wavpack_input *
wpin(void *id)
{
assert(id);
- return id;
+ return (struct wavpack_input *)id;
}
static int32_t
@@ -441,14 +442,14 @@ wavpack_input_can_seek(void *id)
}
static WavpackStreamReader mpd_is_reader = {
- .read_bytes = wavpack_input_read_bytes,
- .get_pos = wavpack_input_get_pos,
- .set_pos_abs = wavpack_input_set_pos_abs,
- .set_pos_rel = wavpack_input_set_pos_rel,
- .push_back_byte = wavpack_input_push_back_byte,
- .get_length = wavpack_input_get_length,
- .can_seek = wavpack_input_can_seek,
- .write_bytes = NULL /* no need to write edited tags */
+ wavpack_input_read_bytes,
+ wavpack_input_get_pos,
+ wavpack_input_set_pos_abs,
+ wavpack_input_set_pos_rel,
+ wavpack_input_push_back_byte,
+ wavpack_input_get_length,
+ wavpack_input_can_seek,
+ nullptr /* no need to write edited tags */
};
static void
@@ -475,7 +476,7 @@ wavpack_open_wvc(struct decoder *decoder, const char *uri,
* single files. utf8url is not absolute file path :/
*/
if (uri == NULL)
- return false;
+ return nullptr;
wvc_url = g_strconcat(uri, "c", NULL);
is_wvc = input_stream_open(wvc_url, mutex, cond, NULL);
@@ -587,10 +588,14 @@ static char const *const wavpack_mime_types[] = {
};
const struct decoder_plugin wavpack_decoder_plugin = {
- .name = "wavpack",
- .stream_decode = wavpack_streamdecode,
- .file_decode = wavpack_filedecode,
- .scan_file = wavpack_scan_file,
- .suffixes = wavpack_suffixes,
- .mime_types = wavpack_mime_types
+ "wavpack",
+ nullptr,
+ nullptr,
+ wavpack_streamdecode,
+ wavpack_filedecode,
+ wavpack_scan_file,
+ nullptr,
+ nullptr,
+ wavpack_suffixes,
+ wavpack_mime_types
};
diff --git a/src/decoder/WavpackDecoderPlugin.hxx b/src/decoder/WavpackDecoderPlugin.hxx
new file mode 100644
index 000000000..9ebe6354f
--- /dev/null
+++ b/src/decoder/WavpackDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * 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_WAVPACK_HXX
+#define MPD_DECODER_WAVPACK_HXX
+
+extern const struct decoder_plugin wavpack_decoder_plugin;
+
+#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/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx
index 5d162f179..175d2ee7c 100644
--- a/src/decoder/sidplay_decoder_plugin.cxx
+++ b/src/decoder/sidplay_decoder_plugin.cxx
@@ -18,9 +18,9 @@
*/
#include "config.h"
+#include "../decoder_api.h"
extern "C" {
-#include "../decoder_api.h"
#include "tag_handler.h"
}
@@ -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_api.h b/src/decoder_api.h
index 6e011c395..3f84ca8bc 100644
--- a/src/decoder_api.h
+++ b/src/decoder_api.h
@@ -38,6 +38,10 @@
#include <stdbool.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Notify the player thread that it has finished initialization and
* that it has read the song's meta data.
@@ -152,9 +156,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 +165,15 @@ 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);
+#ifdef __cplusplus
+}
+#endif
+
#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..d6818d109 100644
--- a/src/decoder_list.c
+++ b/src/decoder_list.c
@@ -20,12 +20,16 @@
#include "config.h"
#include "decoder_list.h"
#include "decoder_plugin.h"
-#include "utils.h"
#include "conf.h"
#include "mpd_error.h"
#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/VorbisDecoderPlugin.h"
+#include "decoder/AdPlugDecoderPlugin.h"
+#include "decoder/WavpackDecoderPlugin.hxx"
#include <glib.h>
@@ -33,15 +37,11 @@
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;
extern const struct decoder_plugin faad_decoder_plugin;
extern const struct decoder_plugin mpcdec_decoder_plugin;
-extern const struct decoder_plugin wavpack_decoder_plugin;
extern const struct decoder_plugin modplug_decoder_plugin;
extern const struct decoder_plugin mikmod_decoder_plugin;
extern const struct decoder_plugin sidplay_decoder_plugin;
@@ -66,6 +66,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 +104,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..53d16a8e4
--- /dev/null
+++ b/src/encoder/OpusEncoderPlugin.cxx
@@ -0,0 +1,433 @@
+/*
+ * 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;
+
+ opus_encoder() {
+ encoder_struct_init(&encoder, &opus_encoder_plugin);
+ }
+};
+
+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)
+{
+ opus_encoder *encoder = new opus_encoder();
+
+ /* load configuration from "param" */
+ if (!opus_encoder_configure(encoder, param, error)) {
+ /* configuration has failed, roll back and return error */
+ delete 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 */
+ delete 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..74048e0cd 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,16 +50,16 @@ 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;
+ vorbis_encoder() {
+ encoder_struct_init(&encoder, &vorbis_encoder_plugin);
+ }
+};
static inline GQuark
vorbis_encoder_quark(void)
@@ -65,8 +71,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 +87,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 +97,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",
@@ -118,14 +124,13 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
static struct encoder *
vorbis_encoder_init(const struct config_param *param, GError **error)
{
- struct vorbis_encoder *encoder = g_new(struct vorbis_encoder, 1);
- encoder_struct_init(&encoder->encoder, &vorbis_encoder_plugin);
+ vorbis_encoder *encoder = new vorbis_encoder();
/* load configuration from "param" */
if (!vorbis_encoder_configure(encoder, param, error)) {
/* configuration has failed, roll back and return error */
- g_free(encoder);
- return NULL;
+ delete encoder;
+ return nullptr;
}
return &encoder->encoder;
@@ -138,7 +143,7 @@ vorbis_encoder_finish(struct encoder *_encoder)
/* the real libvorbis/libogg cleanup was already performed by
vorbis_encoder_close(), so no real work here */
- g_free(encoder);
+ delete encoder;
}
static bool
@@ -174,7 +179,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 +192,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 +214,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 +223,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 +247,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 +261,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 +280,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 +309,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 +340,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 +352,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 +366,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..db6503fb0 100644
--- a/src/encoder/flac_encoder.c
+++ b/src/encoder/flac_encoder.c
@@ -22,14 +22,18 @@
#include "encoder_plugin.h"
#include "audio_format.h"
#include "pcm_buffer.h"
-#include "fifo_buffer.h"
-#include "growing_fifo.h"
+#include "util/fifo_buffer.h"
+#include "util/growing_fifo.h"
#include <assert.h>
#include <string.h>
#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/null_encoder.c b/src/encoder/null_encoder.c
index 48cdf139b..fa1a0a034 100644
--- a/src/encoder/null_encoder.c
+++ b/src/encoder/null_encoder.c
@@ -20,8 +20,8 @@
#include "config.h"
#include "encoder_api.h"
#include "encoder_plugin.h"
-#include "fifo_buffer.h"
-#include "growing_fifo.h"
+#include "util/fifo_buffer.h"
+#include "util/growing_fifo.h"
#include <assert.h>
#include <string.h>
diff --git a/src/encoder/wave_encoder.c b/src/encoder/wave_encoder.c
index 9eeb4d513..a6ee512d4 100644
--- a/src/encoder/wave_encoder.c
+++ b/src/encoder/wave_encoder.c
@@ -20,8 +20,8 @@
#include "config.h"
#include "encoder_api.h"
#include "encoder_plugin.h"
-#include "fifo_buffer.h"
-#include "growing_fifo.h"
+#include "util/fifo_buffer.h"
+#include "util/growing_fifo.h"
#include <assert.h>
#include <string.h>
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_list.h b/src/encoder_list.h
index fb1c9bf9c..31663c751 100644
--- a/src/encoder_list.h
+++ b/src/encoder_list.h
@@ -30,6 +30,10 @@ extern const struct encoder_plugin *const encoder_plugins[];
(plugin = *encoder_plugin_iterator) != NULL; \
++encoder_plugin_iterator)
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Looks up an encoder plugin by its name.
*
@@ -40,4 +44,8 @@ extern const struct encoder_plugin *const encoder_plugins[];
const struct encoder_plugin *
encoder_plugin_get(const char *name);
+#ifdef __cplusplus
+}
+#endif
+
#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/BufferedSocket.cxx b/src/event/BufferedSocket.cxx
new file mode 100644
index 000000000..f84fe808b
--- /dev/null
+++ b/src/event/BufferedSocket.cxx
@@ -0,0 +1,256 @@
+/*
+ * 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 "BufferedSocket.hxx"
+#include "SocketError.hxx"
+#include "util/fifo_buffer.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+BufferedSocket::~BufferedSocket()
+{
+ if (input != nullptr)
+ fifo_buffer_free(input);
+}
+
+BufferedSocket::ssize_t
+BufferedSocket::DirectWrite(const void *data, size_t length)
+{
+ int flags = 0;
+#ifdef MSG_NOSIGNAL
+ flags |= MSG_NOSIGNAL;
+#endif
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+#endif
+
+ const auto nbytes = send(Get(), (const char *)data, length, flags);
+ if (gcc_unlikely(nbytes < 0)) {
+ const auto code = GetSocketError();
+ if (IsSocketErrorAgain(code))
+ return 0;
+
+ Cancel();
+
+ if (IsSocketErrorClosed(code))
+ OnSocketClosed();
+ else
+ OnSocketError(NewSocketError(code));
+ }
+
+ return nbytes;
+}
+
+ssize_t
+BufferedSocket::DirectRead(void *data, size_t length)
+{
+ int flags = 0;
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+#endif
+
+ const auto nbytes = recv(Get(), (char *)data, length, flags);
+ if (gcc_likely(nbytes > 0))
+ return nbytes;
+
+ if (nbytes == 0) {
+ OnSocketClosed();
+ return -1;
+ }
+
+ const auto code = GetSocketError();
+ if (IsSocketErrorAgain(code))
+ return 0;
+
+ if (IsSocketErrorClosed(code))
+ OnSocketClosed();
+ else
+ OnSocketError(NewSocketError(code));
+ return -1;
+}
+
+bool
+BufferedSocket::WriteFromBuffer()
+{
+ assert(IsDefined());
+
+ size_t length;
+ const void *data = output.Read(&length);
+ if (data == nullptr) {
+ CancelWrite();
+ return true;
+ }
+
+ auto nbytes = DirectWrite(data, length);
+ if (gcc_unlikely(nbytes <= 0))
+ return nbytes == 0;
+
+ output.Consume(nbytes);
+
+ if (output.IsEmpty())
+ CancelWrite();
+
+ return true;
+}
+
+bool
+BufferedSocket::ReadToBuffer()
+{
+ assert(IsDefined());
+
+ if (input == nullptr)
+ input = fifo_buffer_new(8192);
+
+ size_t length;
+ void *buffer = fifo_buffer_write(input, &length);
+ assert(buffer != nullptr);
+
+ const auto nbytes = DirectRead(buffer, length);
+ if (nbytes > 0)
+ fifo_buffer_append(input, nbytes);
+
+ return nbytes >= 0;
+}
+
+bool
+BufferedSocket::Write(const void *data, size_t length)
+{
+ assert(IsDefined());
+
+#if 0
+ /* TODO: disabled because this would add overhead on some callers (the ones that often), but it may be useful */
+
+ if (output.IsEmpty()) {
+ /* try to write it directly first */
+ const auto nbytes = DirectWrite(data, length);
+ if (gcc_likely(nbytes > 0)) {
+ data = (const uint8_t *)data + nbytes;
+ length -= nbytes;
+ if (length == 0)
+ return true;
+ } else if (nbytes < 0)
+ return false;
+ }
+#endif
+
+ if (!output.Append(data, length)) {
+ // TODO
+ OnSocketError(g_error_new_literal(g_quark_from_static_string("buffered_socket"),
+ 0, "Output buffer is full"));
+ return false;
+ }
+
+ ScheduleWrite();
+ return true;
+}
+
+bool
+BufferedSocket::ResumeInput()
+{
+ assert(IsDefined());
+
+ if (input == nullptr) {
+ ScheduleRead();
+ return true;
+ }
+
+ while (true) {
+ size_t length;
+ const void *data = fifo_buffer_read(input, &length);
+ if (data == nullptr) {
+ ScheduleRead();
+ return true;
+ }
+
+ const auto result = OnSocketInput(data, length);
+ switch (result) {
+ case InputResult::MORE:
+ if (fifo_buffer_is_full(input)) {
+ // TODO
+ OnSocketError(g_error_new_literal(g_quark_from_static_string("buffered_socket"),
+ 0, "Input buffer is full"));
+ return false;
+ }
+
+ ScheduleRead();
+ return true;
+
+ case InputResult::PAUSE:
+ CancelRead();
+ return true;
+
+ case InputResult::AGAIN:
+ continue;
+
+ case InputResult::CLOSED:
+ return false;
+ }
+ }
+}
+
+void
+BufferedSocket::ConsumeInput(size_t nbytes)
+{
+ assert(IsDefined());
+
+ fifo_buffer_consume(input, nbytes);
+}
+
+void
+BufferedSocket::OnSocketReady(unsigned flags)
+{
+ assert(IsDefined());
+
+ if (gcc_unlikely(flags & (ERROR|HANGUP))) {
+ OnSocketClosed();
+ return;
+ }
+
+ if (flags & READ) {
+ assert(input == nullptr || !fifo_buffer_is_full(input));
+
+ if (!ReadToBuffer() || !ResumeInput())
+ return;
+
+ if (input == nullptr || !fifo_buffer_is_full(input))
+ ScheduleRead();
+
+ /* just in case the OnSocketInput() method has added
+ data to the output buffer: try to send it now
+ instead of waiting for the next event loop
+ iteration */
+ if (!output.IsEmpty())
+ flags |= WRITE;
+ }
+
+ if (flags & WRITE) {
+ assert(!output.IsEmpty());
+
+ if (!WriteFromBuffer())
+ return;
+ }
+}
diff --git a/src/event/BufferedSocket.hxx b/src/event/BufferedSocket.hxx
new file mode 100644
index 000000000..49b17c86f
--- /dev/null
+++ b/src/event/BufferedSocket.hxx
@@ -0,0 +1,125 @@
+/*
+ * 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_BUFFERED_SOCKET_HXX
+#define MPD_BUFFERED_SOCKET_HXX
+
+#include "check.h"
+#include "SocketMonitor.hxx"
+#include "util/PeakBuffer.hxx"
+#include "gcc.h"
+
+#include <type_traits>
+
+#include <stddef.h>
+
+struct fifo_buffer;
+class EventLoop;
+
+class BufferedSocket : private SocketMonitor {
+ typedef std::make_signed<size_t>::type ssize_t;
+
+ fifo_buffer *input;
+ PeakBuffer output;
+
+public:
+ BufferedSocket(int _fd, EventLoop &_loop,
+ size_t normal_size, size_t peak_size=0)
+ :SocketMonitor(_fd, _loop), input(nullptr),
+ output(normal_size, peak_size) {
+ ScheduleRead();
+ }
+
+ ~BufferedSocket();
+
+ using SocketMonitor::IsDefined;
+ using SocketMonitor::Close;
+
+private:
+ ssize_t DirectWrite(const void *data, size_t length);
+ ssize_t DirectRead(void *data, size_t length);
+
+ /**
+ * Send data from the output buffer to the socket.
+ *
+ * @return false if the socket has been closed
+ */
+ bool WriteFromBuffer();
+
+ /**
+ * Receive data from the socket to the input buffer.
+ *
+ * @return false if the socket has been closed
+ */
+ bool ReadToBuffer();
+
+protected:
+ /**
+ * @return false if the socket has been closed
+ */
+ bool Write(const void *data, size_t length);
+
+ /**
+ * @return false if the socket has been closed
+ */
+ bool ResumeInput();
+
+ /**
+ * Mark a portion of the input buffer "consumed". Only
+ * allowed to be called from OnSocketInput(). This method
+ * does not invalidate the pointer passed to OnSocketInput()
+ * yet.
+ */
+ void ConsumeInput(size_t nbytes);
+
+ enum class InputResult {
+ /**
+ * The method was successful, and it is ready to
+ * read more data.
+ */
+ MORE,
+
+ /**
+ * The method does not want to get more data for now.
+ * It will call ResumeInput() when it's ready for
+ * more.
+ */
+ PAUSE,
+
+ /**
+ * The method wants to be called again immediately, if
+ * there's more data in the buffer.
+ */
+ AGAIN,
+
+ /**
+ * The method has closed the socket.
+ */
+ CLOSED,
+ };
+
+ virtual InputResult OnSocketInput(const void *data, size_t length) = 0;
+ virtual void OnSocketError(GError *error) = 0;
+ virtual void OnSocketClosed() = 0;
+
+private:
+ virtual void OnSocketReady(unsigned flags) override;
+};
+
+#endif
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx
new file mode 100644
index 000000000..31ef1613c
--- /dev/null
+++ b/src/event/Loop.hxx
@@ -0,0 +1,84 @@
+/*
+ * 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_EVENT_LOOP_HXX
+#define MPD_EVENT_LOOP_HXX
+
+#include "check.h"
+#include "gcc.h"
+
+#include <glib.h>
+
+class EventLoop {
+ GMainContext *context;
+ GMainLoop *loop;
+
+public:
+ EventLoop()
+ :context(g_main_context_new()),
+ loop(g_main_loop_new(context, false)) {}
+
+ struct Default {};
+ EventLoop(gcc_unused Default _dummy)
+ :context(g_main_context_ref(g_main_context_default())),
+ loop(g_main_loop_new(context, false)) {}
+
+ ~EventLoop() {
+ g_main_loop_unref(loop);
+ g_main_context_unref(context);
+ }
+
+ GMainContext *GetContext() {
+ return context;
+ }
+
+ void Break() {
+ g_main_loop_quit(loop);
+ }
+
+ void Run() {
+ g_main_loop_run(loop);
+ }
+
+ guint AddIdle(GSourceFunc function, gpointer data) {
+ GSource *source = g_idle_source_new();
+ g_source_set_callback(source, function, data, NULL);
+ guint id = g_source_attach(source, GetContext());
+ g_source_unref(source);
+ return id;
+ }
+
+ GSource *AddTimeout(guint interval_ms,
+ GSourceFunc function, gpointer data) {
+ GSource *source = g_timeout_source_new(interval_ms);
+ g_source_set_callback(source, function, data, nullptr);
+ g_source_attach(source, GetContext());
+ return source;
+ }
+
+ GSource *AddTimeoutSeconds(guint interval_s,
+ GSourceFunc function, gpointer data) {
+ GSource *source = g_timeout_source_new_seconds(interval_s);
+ g_source_set_callback(source, function, data, nullptr);
+ g_source_attach(source, GetContext());
+ return source;
+ }
+};
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx
new file mode 100644
index 000000000..6f20b907c
--- /dev/null
+++ b/src/event/MultiSocketMonitor.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 "MultiSocketMonitor.hxx"
+#include "Loop.hxx"
+#include "fd_util.h"
+#include "gcc.h"
+
+#include <assert.h>
+
+/**
+ * The vtable for our GSource implementation. Unfortunately, we
+ * cannot declare it "const", because g_source_new() takes a non-const
+ * pointer, for whatever reason.
+ */
+static GSourceFuncs multi_socket_monitor_source_funcs = {
+ MultiSocketMonitor::Prepare,
+ MultiSocketMonitor::Check,
+ MultiSocketMonitor::Dispatch,
+ nullptr,
+ nullptr,
+ nullptr,
+};
+
+MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
+ :loop(_loop),
+ source((Source *)g_source_new(&multi_socket_monitor_source_funcs,
+ sizeof(*source))) {
+ source->monitor = this;
+
+ g_source_attach(&source->base, loop.GetContext());
+}
+
+MultiSocketMonitor::~MultiSocketMonitor()
+{
+ g_source_destroy(&source->base);
+ g_source_unref(&source->base);
+ source = nullptr;
+}
+
+bool
+MultiSocketMonitor::Check() const
+{
+ if (CheckSockets())
+ return true;
+
+ for (const auto &i : fds)
+ if (i.revents != 0)
+ return true;
+
+ return false;
+}
+
+/*
+ * GSource methods
+ *
+ */
+
+gboolean
+MultiSocketMonitor::Prepare(GSource *_source, gint *timeout_r)
+{
+ Source &source = *(Source *)_source;
+ MultiSocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ return monitor.Prepare(timeout_r);
+}
+
+gboolean
+MultiSocketMonitor::Check(GSource *_source)
+{
+ const Source &source = *(const Source *)_source;
+ const MultiSocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ return monitor.Check();
+}
+
+gboolean
+MultiSocketMonitor::Dispatch(GSource *_source,
+ gcc_unused GSourceFunc callback,
+ gcc_unused gpointer user_data)
+{
+ Source &source = *(Source *)_source;
+ MultiSocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ monitor.Dispatch();
+ return true;
+}
diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx
new file mode 100644
index 000000000..9d0e1502b
--- /dev/null
+++ b/src/event/MultiSocketMonitor.hxx
@@ -0,0 +1,124 @@
+/*
+ * 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_MULTI_SOCKET_MONITOR_HXX
+#define MPD_MULTI_SOCKET_MONITOR_HXX
+
+#include "check.h"
+#include "gcc.h"
+
+#include <glib.h>
+
+#include <forward_list>
+
+#include <assert.h>
+
+#ifdef WIN32
+/* ERRORis a WIN32 macro that poisons our namespace; this is a
+ kludge to allow us to use it anyway */
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+class EventLoop;
+
+/**
+ * Monitor multiple sockets.
+ */
+class MultiSocketMonitor {
+ struct Source {
+ GSource base;
+
+ MultiSocketMonitor *monitor;
+ };
+
+ EventLoop &loop;
+ Source *source;
+ std::forward_list<GPollFD> fds;
+
+public:
+ static constexpr unsigned READ = G_IO_IN;
+ static constexpr unsigned WRITE = G_IO_OUT;
+ static constexpr unsigned ERROR = G_IO_ERR;
+ static constexpr unsigned HANGUP = G_IO_HUP;
+
+ MultiSocketMonitor(EventLoop &_loop);
+ ~MultiSocketMonitor();
+
+public:
+ gcc_pure
+ gint64 GetTime() const {
+ return g_source_get_time(&source->base);
+ }
+
+ void InvalidateSockets() {
+ /* no-op because GLib always calls the GSource's
+ "prepare" method before each poll() anyway */
+ }
+
+ void AddSocket(int fd, unsigned events) {
+ fds.push_front({fd, gushort(events), 0});
+ g_source_add_poll(&source->base, &fds.front());
+ }
+
+ template<typename E>
+ void UpdateSocketList(E &&e) {
+ for (auto prev = fds.before_begin(), end = fds.end(),
+ i = std::next(prev);
+ i != end; i = std::next(prev)) {
+ assert(i->events != 0);
+
+ unsigned events = e(i->fd);
+ if (events != 0) {
+ i->events = events;
+ prev = i;
+ } else {
+ g_source_remove_poll(&source->base, &*i);
+ fds.erase_after(prev);
+ }
+ }
+ }
+
+protected:
+ virtual void PrepareSockets(gcc_unused gint *timeout_r) {}
+ virtual bool CheckSockets() const { return false; }
+ virtual void DispatchSockets() = 0;
+
+public:
+ /* GSource callbacks */
+ static gboolean Prepare(GSource *source, gint *timeout_r);
+ static gboolean Check(GSource *source);
+ static gboolean Dispatch(GSource *source, GSourceFunc callback,
+ gpointer user_data);
+
+private:
+ bool Prepare(gint *timeout_r) {
+ PrepareSockets(timeout_r);
+ return false;
+ }
+
+ bool Check() const;
+
+ void Dispatch() {
+ DispatchSockets();
+ }
+};
+
+#endif
diff --git a/src/event/SocketMonitor.cxx b/src/event/SocketMonitor.cxx
new file mode 100644
index 000000000..b75dc72a4
--- /dev/null
+++ b/src/event/SocketMonitor.cxx
@@ -0,0 +1,121 @@
+/*
+ * 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 "SocketMonitor.hxx"
+#include "Loop.hxx"
+#include "fd_util.h"
+#include "gcc.h"
+
+#include <assert.h>
+
+/*
+ * GSource methods
+ *
+ */
+
+gboolean
+SocketMonitor::Prepare(gcc_unused GSource *source, gcc_unused gint *timeout_r)
+{
+ return false;
+}
+
+gboolean
+SocketMonitor::Check(GSource *_source)
+{
+ const Source &source = *(const Source *)_source;
+ const SocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ return monitor.Check();
+}
+
+gboolean
+SocketMonitor::Dispatch(GSource *_source,
+ gcc_unused GSourceFunc callback,
+ gcc_unused gpointer user_data)
+{
+ Source &source = *(Source *)_source;
+ SocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ monitor.Dispatch();
+ return true;
+}
+
+/**
+ * The vtable for our GSource implementation. Unfortunately, we
+ * cannot declare it "const", because g_source_new() takes a non-const
+ * pointer, for whatever reason.
+ */
+static GSourceFuncs socket_monitor_source_funcs = {
+ SocketMonitor::Prepare,
+ SocketMonitor::Check,
+ SocketMonitor::Dispatch,
+ nullptr,
+ nullptr,
+ nullptr,
+};
+
+SocketMonitor::SocketMonitor(int _fd, EventLoop &_loop)
+ :fd(-1), loop(_loop),
+ source(nullptr) {
+ assert(_fd >= 0);
+
+ Open(_fd);
+}
+
+SocketMonitor::~SocketMonitor()
+{
+ if (IsDefined())
+ Close();
+}
+
+void
+SocketMonitor::Open(int _fd)
+{
+ assert(fd < 0);
+ assert(source == nullptr);
+ assert(_fd >= 0);
+
+ fd = _fd;
+ poll = {fd, 0, 0};
+
+ source = (Source *)g_source_new(&socket_monitor_source_funcs,
+ sizeof(*source));
+ source->monitor = this;
+
+ g_source_attach(&source->base, loop.GetContext());
+ g_source_add_poll(&source->base, &poll);
+}
+
+void
+SocketMonitor::Close()
+{
+ assert(IsDefined());
+
+ Cancel();
+
+ close_socket(fd);
+ fd = -1;
+
+ g_source_destroy(&source->base);
+ g_source_unref(&source->base);
+ source = nullptr;
+}
diff --git a/src/event/SocketMonitor.hxx b/src/event/SocketMonitor.hxx
new file mode 100644
index 000000000..236e5fbda
--- /dev/null
+++ b/src/event/SocketMonitor.hxx
@@ -0,0 +1,123 @@
+/*
+ * 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_SOCKET_MONITOR_HXX
+#define MPD_SOCKET_MONITOR_HXX
+
+#include "check.h"
+
+#include <glib.h>
+
+#include <assert.h>
+
+#ifdef WIN32
+/* ERRORis a WIN32 macro that poisons our namespace; this is a
+ kludge to allow us to use it anyway */
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+class EventLoop;
+
+class SocketMonitor {
+ struct Source {
+ GSource base;
+
+ SocketMonitor *monitor;
+ };
+
+ int fd;
+ EventLoop &loop;
+ Source *source;
+ GPollFD poll;
+
+public:
+ static constexpr unsigned READ = G_IO_IN;
+ static constexpr unsigned WRITE = G_IO_OUT;
+ static constexpr unsigned ERROR = G_IO_ERR;
+ static constexpr unsigned HANGUP = G_IO_HUP;
+
+ SocketMonitor(EventLoop &_loop)
+ :fd(-1), loop(_loop), source(nullptr) {}
+
+ SocketMonitor(int _fd, EventLoop &_loop);
+
+ ~SocketMonitor();
+
+ bool IsDefined() const {
+ return fd >= 0;
+ }
+
+ int Get() const {
+ assert(IsDefined());
+
+ return fd;
+ }
+
+ void Open(int _fd);
+
+ void Close();
+
+ void Schedule(unsigned flags) {
+ poll.events = flags;
+ poll.revents &= flags;
+ }
+
+ void Cancel() {
+ poll.events = 0;
+ }
+
+ void ScheduleRead() {
+ poll.events |= READ|HANGUP|ERROR;
+ }
+
+ void ScheduleWrite() {
+ poll.events |= WRITE;
+ }
+
+ void CancelRead() {
+ poll.events &= ~(READ|HANGUP|ERROR);
+ }
+
+ void CancelWrite() {
+ poll.events &= ~WRITE;
+ }
+
+protected:
+ virtual void OnSocketReady(unsigned flags) = 0;
+
+public:
+ /* GSource callbacks */
+ static gboolean Prepare(GSource *source, gint *timeout_r);
+ static gboolean Check(GSource *source);
+ static gboolean Dispatch(GSource *source, GSourceFunc callback,
+ gpointer user_data);
+
+private:
+ bool Check() const {
+ return (poll.revents & poll.events) != 0;
+ }
+
+ void Dispatch() {
+ OnSocketReady(poll.revents & poll.events);
+ }
+};
+
+#endif
diff --git a/src/event/TimeoutMonitor.cxx b/src/event/TimeoutMonitor.cxx
new file mode 100644
index 000000000..e0bf997a0
--- /dev/null
+++ b/src/event/TimeoutMonitor.cxx
@@ -0,0 +1,65 @@
+/*
+ * 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 "TimeoutMonitor.hxx"
+#include "Loop.hxx"
+
+void
+TimeoutMonitor::Cancel()
+{
+ if (source != nullptr) {
+ g_source_destroy(source);
+ g_source_unref(source);
+ source = nullptr;
+ }
+}
+
+void
+TimeoutMonitor::Schedule(unsigned ms)
+{
+ Cancel();
+ source = loop.AddTimeout(ms, Callback, this);
+}
+
+void
+TimeoutMonitor::ScheduleSeconds(unsigned s)
+{
+ Cancel();
+ source = loop.AddTimeoutSeconds(s, Callback, this);
+}
+
+bool
+TimeoutMonitor::Run()
+{
+ bool result = OnTimeout();
+ if (!result && source != nullptr) {
+ g_source_unref(source);
+ source = nullptr;
+ }
+
+ return result;
+}
+
+gboolean
+TimeoutMonitor::Callback(gpointer data)
+{
+ TimeoutMonitor &monitor = *(TimeoutMonitor *)data;
+ return monitor.Run();
+}
diff --git a/src/event/TimeoutMonitor.hxx b/src/event/TimeoutMonitor.hxx
new file mode 100644
index 000000000..6914bcb61
--- /dev/null
+++ b/src/event/TimeoutMonitor.hxx
@@ -0,0 +1,60 @@
+/*
+ * 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_SOCKET_TIMEOUT_MONITOR_HXX
+#define MPD_SOCKET_TIMEOUT_MONITOR_HXX
+
+#include "check.h"
+
+#include <glib.h>
+
+class EventLoop;
+
+class TimeoutMonitor {
+ EventLoop &loop;
+ GSource *source;
+
+public:
+ TimeoutMonitor(EventLoop &_loop)
+ :loop(_loop), source(nullptr) {}
+
+ ~TimeoutMonitor() {
+ Cancel();
+ }
+
+ bool IsActive() const {
+ return source != nullptr;
+ }
+
+ void Schedule(unsigned ms);
+ void ScheduleSeconds(unsigned s);
+ void Cancel();
+
+protected:
+ /**
+ * @return true reschedules the timeout again
+ */
+ virtual bool OnTimeout() = 0;
+
+private:
+ bool Run();
+ static gboolean Callback(gpointer data);
+};
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/WakeFD.cxx b/src/event/WakeFD.cxx
new file mode 100644
index 000000000..1a84f5645
--- /dev/null
+++ b/src/event/WakeFD.cxx
@@ -0,0 +1,225 @@
+/*
+ * 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 "WakeFD.hxx"
+#include "fd_util.h"
+#include "gcc.h"
+
+#include <unistd.h>
+
+#ifdef WIN32
+#include <ws2tcpip.h>
+#include <winsock2.h>
+#include <cstring> /* for memset() */
+#endif
+
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
+#ifdef WIN32
+static bool PoorSocketPair(int fd[2]);
+#endif
+
+bool
+WakeFD::Create()
+{
+ assert(fds[0] == -1);
+ assert(fds[1] == -1);
+
+#ifdef WIN32
+ return PoorSocketPair(fds);
+#else
+#ifdef HAVE_EVENTFD
+ fds[0] = eventfd_cloexec_nonblock(0, 0);
+ if (fds[0] >= 0) {
+ fds[1] = -2;
+ return true;
+ }
+#endif
+ return pipe_cloexec_nonblock(fds) >= 0;
+#endif
+}
+
+void
+WakeFD::Destroy()
+{
+#ifdef WIN32
+ closesocket(fds[0]);
+ closesocket(fds[1]);
+#else
+ close(fds[0]);
+#ifdef HAVE_EVENTFD
+ if (!IsEventFD())
+#endif
+ close(fds[1]);
+#endif
+
+#ifndef NDEBUG
+ fds[0] = -1;
+ fds[1] = -1;
+#endif
+}
+
+bool
+WakeFD::Read()
+{
+ assert(fds[0] >= 0);
+
+#ifdef WIN32
+ assert(fds[1] >= 0);
+ char buffer[256];
+ return recv(fds[0], buffer, sizeof(buffer), 0) > 0;
+#else
+
+#ifdef HAVE_EVENTFD
+ if (IsEventFD()) {
+ eventfd_t value;
+ return read(fds[0], &value,
+ sizeof(value)) == (ssize_t)sizeof(value);
+ }
+#endif
+
+ assert(fds[1] >= 0);
+
+ char buffer[256];
+ return read(fds[0], buffer, sizeof(buffer)) > 0;
+#endif
+}
+
+void
+WakeFD::Write()
+{
+ assert(fds[0] >= 0);
+
+#ifdef WIN32
+ assert(fds[1] >= 0);
+
+ send(fds[1], "", 1, 0);
+#else
+
+#ifdef HAVE_EVENTFD
+ if (IsEventFD()) {
+ static constexpr eventfd_t value = 1;
+ gcc_unused ssize_t nbytes =
+ write(fds[0], &value, sizeof(value));
+ return;
+ }
+#endif
+
+ assert(fds[1] >= 0);
+
+ gcc_unused ssize_t nbytes = write(fds[1], "", 1);
+#endif
+}
+
+#ifdef WIN32
+
+static void SafeCloseSocket(SOCKET s)
+{
+ int error = WSAGetLastError();
+ closesocket(s);
+ WSASetLastError(error);
+}
+
+/* Our poor man's socketpair() implementation
+ * Due to limited protocol/address family support and primitive error handling
+ * it's better to keep this as a private implementation detail of WakeFD
+ * rather than wide-available API.
+ */
+static bool PoorSocketPair(int fd[2])
+{
+ assert (fd != nullptr);
+
+ SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (listen_socket == INVALID_SOCKET)
+ return false;
+
+ sockaddr_in address;
+ std::memset(&address, 0, sizeof(address));
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ int ret = bind(listen_socket,
+ reinterpret_cast<sockaddr*>(&address),
+ sizeof(address));
+
+ if (ret < 0) {
+ SafeCloseSocket(listen_socket);
+ return false;
+ }
+
+ ret = listen(listen_socket, 1);
+
+ if (ret < 0) {
+ SafeCloseSocket(listen_socket);
+ return false;
+ }
+
+ int address_len = sizeof(address);
+ ret = getsockname(listen_socket,
+ reinterpret_cast<sockaddr*>(&address),
+ &address_len);
+
+ if (ret < 0) {
+ SafeCloseSocket(listen_socket);
+ return false;
+ }
+
+ SOCKET socket0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (socket0 == INVALID_SOCKET) {
+ SafeCloseSocket(listen_socket);
+ return false;
+ }
+
+ ret = connect(socket0,
+ reinterpret_cast<sockaddr*>(&address),
+ sizeof(address));
+
+ if (ret < 0) {
+ SafeCloseSocket(listen_socket);
+ SafeCloseSocket(socket0);
+ return false;
+ }
+
+ SOCKET socket1 = accept(listen_socket, nullptr, nullptr);
+ if (socket1 == INVALID_SOCKET) {
+ SafeCloseSocket(listen_socket);
+ SafeCloseSocket(socket0);
+ return false;
+ }
+
+ SafeCloseSocket(listen_socket);
+
+ u_long non_block = 1;
+ if (ioctlsocket(socket0, FIONBIO, &non_block) < 0
+ || ioctlsocket(socket1, FIONBIO, &non_block) < 0) {
+ SafeCloseSocket(socket0);
+ SafeCloseSocket(socket1);
+ return false;
+ }
+
+ fd[0] = static_cast<int>(socket0);
+ fd[1] = static_cast<int>(socket1);
+
+ return true;
+}
+
+#endif
diff --git a/src/event/WakeFD.hxx b/src/event/WakeFD.hxx
new file mode 100644
index 000000000..15b66b4cf
--- /dev/null
+++ b/src/event/WakeFD.hxx
@@ -0,0 +1,80 @@
+/*
+ * 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_WAKE_FD_HXX
+#define MPD_WAKE_FD_HXX
+
+#include "check.h"
+
+#include <assert.h>
+
+/**
+ * This class can be used to wake up an I/O event loop.
+ *
+ * For optimization purposes, this class does not have a constructor
+ * or a destructor.
+ */
+class WakeFD {
+ int fds[2];
+
+public:
+#ifdef NDEBUG
+ WakeFD() = default;
+#else
+ WakeFD():fds{-1, -1} {};
+#endif
+
+ WakeFD(const WakeFD &other) = delete;
+ WakeFD &operator=(const WakeFD &other) = delete;
+
+ bool Create();
+ void Destroy();
+
+ int Get() const {
+ assert(fds[0] >= 0);
+#ifndef HAVE_EVENTFD
+ assert(fds[1] >= 0);
+#endif
+
+ return fds[0];
+ }
+
+ /**
+ * Checks if Write() was called at least once since the last
+ * Read() call.
+ */
+ bool Read();
+
+ /**
+ * Wakes up the reader. Multiple calls to this function will
+ * be combined to one wakeup.
+ */
+ void Write();
+
+private:
+#ifdef HAVE_EVENTFD
+ bool IsEventFD() {
+ assert(fds[0] >= 0);
+
+ return fds[1] == -2;
+ }
+#endif
+};
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event_pipe.c b/src/event_pipe.c
deleted file mode 100644
index d5c3b9564..000000000
--- a/src/event_pipe.c
+++ /dev/null
@@ -1,164 +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 "event_pipe.h"
-#include "fd_util.h"
-#include "mpd_error.h"
-
-#include <stdbool.h>
-#include <assert.h>
-#include <glib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifdef WIN32
-/* for _O_BINARY */
-#include <fcntl.h>
-#endif
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "event_pipe"
-
-static int event_pipe[2];
-static GIOChannel *event_channel;
-static guint event_pipe_source_id;
-static GMutex *event_pipe_mutex;
-static bool pipe_events[PIPE_EVENT_MAX];
-static event_pipe_callback_t event_pipe_callbacks[PIPE_EVENT_MAX];
-
-/**
- * Invoke the callback for a certain event.
- */
-static void
-event_pipe_invoke(enum pipe_event event)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
- assert(event_pipe_callbacks[event] != NULL);
-
- event_pipe_callbacks[event]();
-}
-
-static gboolean
-main_notify_event(G_GNUC_UNUSED GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition,
- G_GNUC_UNUSED gpointer data)
-{
- char buffer[256];
- gsize bytes_read;
- GError *error = NULL;
- GIOStatus status = g_io_channel_read_chars(event_channel,
- buffer, sizeof(buffer),
- &bytes_read, &error);
- if (status == G_IO_STATUS_ERROR)
- MPD_ERROR("error reading from pipe: %s", error->message);
-
- bool events[PIPE_EVENT_MAX];
- g_mutex_lock(event_pipe_mutex);
- memcpy(events, pipe_events, sizeof(events));
- memset(pipe_events, 0, sizeof(pipe_events));
- g_mutex_unlock(event_pipe_mutex);
-
- for (unsigned i = 0; i < PIPE_EVENT_MAX; ++i)
- if (events[i])
- /* invoke the event handler */
- event_pipe_invoke(i);
-
- return true;
-}
-
-void event_pipe_init(void)
-{
- GIOChannel *channel;
- int ret;
-
- ret = pipe_cloexec_nonblock(event_pipe);
- if (ret < 0)
- MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
-
-#ifndef G_OS_WIN32
- channel = g_io_channel_unix_new(event_pipe[0]);
-#else
- channel = g_io_channel_win32_new_fd(event_pipe[0]);
-#endif
- g_io_channel_set_encoding(channel, NULL, NULL);
- g_io_channel_set_buffered(channel, false);
-
- event_pipe_source_id = g_io_add_watch(channel, G_IO_IN,
- main_notify_event, NULL);
-
- event_channel = channel;
-
- event_pipe_mutex = g_mutex_new();
-}
-
-void event_pipe_deinit(void)
-{
- g_mutex_free(event_pipe_mutex);
-
- g_source_remove(event_pipe_source_id);
- g_io_channel_unref(event_channel);
-
-#ifndef WIN32
- /* By some strange reason this call hangs on Win32 */
- close(event_pipe[0]);
-#endif
- close(event_pipe[1]);
-}
-
-void
-event_pipe_register(enum pipe_event event, event_pipe_callback_t callback)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
- assert(event_pipe_callbacks[event] == NULL);
-
- event_pipe_callbacks[event] = callback;
-}
-
-void event_pipe_emit(enum pipe_event event)
-{
- ssize_t w;
-
- assert((unsigned)event < PIPE_EVENT_MAX);
-
- g_mutex_lock(event_pipe_mutex);
- if (pipe_events[event]) {
- /* already set: don't write */
- g_mutex_unlock(event_pipe_mutex);
- return;
- }
-
- pipe_events[event] = true;
- g_mutex_unlock(event_pipe_mutex);
-
- w = write(event_pipe[1], "", 1);
- if (w < 0 && errno != EAGAIN && errno != EINTR)
- MPD_ERROR("error writing to pipe: %s", strerror(errno));
-}
-
-void event_pipe_emit_fast(enum pipe_event event)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
-
- pipe_events[event] = true;
-
- G_GNUC_UNUSED ssize_t nbytes = write(event_pipe[1], "", 1);
-}
diff --git a/src/event_pipe.h b/src/event_pipe.h
deleted file mode 100644
index 3734bb86c..000000000
--- a/src/event_pipe.h
+++ /dev/null
@@ -1,71 +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 EVENT_PIPE_H
-#define EVENT_PIPE_H
-
-#include <glib.h>
-
-enum pipe_event {
- /** database update was finished */
- PIPE_EVENT_UPDATE,
-
- /** during database update, a song was deleted */
- PIPE_EVENT_DELETE,
-
- /** an idle event was emitted */
- PIPE_EVENT_IDLE,
-
- /** must call playlist_sync() */
- PIPE_EVENT_PLAYLIST,
-
- /** the current song's tag has changed */
- PIPE_EVENT_TAG,
-
- /** SIGHUP received: reload configuration, roll log file */
- PIPE_EVENT_RELOAD,
-
- /** a hardware mixer plugin has detected a change */
- PIPE_EVENT_MIXER,
-
- /** shutdown requested */
- PIPE_EVENT_SHUTDOWN,
-
- PIPE_EVENT_MAX
-};
-
-typedef void (*event_pipe_callback_t)(void);
-
-void event_pipe_init(void);
-
-void event_pipe_deinit(void);
-
-void
-event_pipe_register(enum pipe_event event, event_pipe_callback_t callback);
-
-void event_pipe_emit(enum pipe_event event);
-
-/**
- * Similar to event_pipe_emit(), but aimed for use in signal handlers:
- * it doesn't lock the mutex, and doesn't log on error. That makes it
- * potentially lossy, but for its intended use, that does not matter.
- */
-void event_pipe_emit_fast(enum pipe_event event);
-
-#endif /* MAIN_NOTIFY_H */
diff --git a/src/fd_util.c b/src/fd_util.c
index 882b4c7d5..ea29d6eaa 100644
--- a/src/fd_util.c
+++ b/src/fd_util.c
@@ -49,6 +49,10 @@
#include <sys/inotify.h>
#endif
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
#ifndef WIN32
static int
@@ -328,6 +332,16 @@ inotify_init_cloexec(void)
#endif
+#ifdef HAVE_EVENTFD
+
+int
+eventfd_cloexec_nonblock(unsigned initval, int flags)
+{
+ return eventfd(initval, flags | EFD_CLOEXEC | EFD_NONBLOCK);
+}
+
+#endif
+
int
close_socket(int fd)
{
diff --git a/src/fd_util.h b/src/fd_util.h
index dd4df7a13..e65c6a69b 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.
@@ -140,10 +144,25 @@ inotify_init_cloexec(void);
#endif
+#ifdef HAVE_EVENTFD
+
+/**
+ * Wrapper for eventfd() which sets the flags CLOEXEC and NONBLOCK
+ * flag (atomically if supported by the OS).
+ */
+int
+eventfd_cloexec_nonblock(unsigned initval, int flags);
+
+#endif
+
/**
* Portable wrapper for close(); use closesocket() on WIN32/WinSock.
*/
int
close_socket(int fd);
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
#endif
diff --git a/src/filter/replay_gain_filter_plugin.c b/src/filter/ReplayGainFilterPlugin.cxx
index 583a09f90..1c2f40260 100644
--- a/src/filter/replay_gain_filter_plugin.c
+++ b/src/filter/ReplayGainFilterPlugin.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,19 @@
*/
#include "config.h"
-#include "filter/replay_gain_filter_plugin.h"
+#include "ReplayGainFilterPlugin.hxx"
#include "filter_plugin.h"
#include "filter_internal.h"
#include "filter_registry.h"
#include "audio_format.h"
-#include "pcm_buffer.h"
-#include "pcm_volume.h"
#include "replay_gain_info.h"
#include "replay_gain_config.h"
+
+extern "C" {
+#include "pcm_buffer.h"
+#include "pcm_volume.h"
#include "mixer_control.h"
+}
#include <assert.h>
#include <string.h>
@@ -119,7 +122,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 +167,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;
@@ -193,7 +186,8 @@ replay_gain_filter_filter(struct filter *_filter,
memcpy(dest, src, src_size);
- success = pcm_volume(dest, src_size, filter->audio_format.format,
+ success = pcm_volume(dest, src_size,
+ sample_format(filter->audio_format.format),
filter->volume);
if (!success) {
g_set_error(error_r, replay_gain_quark(), 0,
@@ -205,12 +199,12 @@ replay_gain_filter_filter(struct filter *_filter,
}
const struct filter_plugin replay_gain_filter_plugin = {
- .name = "replay_gain",
- .init = replay_gain_filter_init,
- .finish = replay_gain_filter_finish,
- .open = replay_gain_filter_open,
- .close = replay_gain_filter_close,
- .filter = replay_gain_filter_filter,
+ "replay_gain",
+ replay_gain_filter_init,
+ replay_gain_filter_finish,
+ replay_gain_filter_open,
+ replay_gain_filter_close,
+ replay_gain_filter_filter,
};
void
@@ -243,3 +237,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/ReplayGainFilterPlugin.hxx
index 45b738e40..9b4ffc522 100644
--- a/src/filter/replay_gain_filter_plugin.h
+++ b/src/filter/ReplayGainFilterPlugin.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 REPLAY_GAIN_FILTER_PLUGIN_H
-#define REPLAY_GAIN_FILTER_PLUGIN_H
+#ifndef MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
+#define MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
#include "replay_gain_info.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/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_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..884e62be6 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,30 @@
# 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
+#define final
+#endif
+
#endif
#endif /* MPD_GCC_H */
diff --git a/src/sig_handlers.h b/src/gerror.h
index 32e9bad95..fe4c54da9 100644
--- a/src/sig_handlers.h
+++ b/src/gerror.h
@@ -17,9 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SIG_HANDLERS_H
-#define MPD_SIG_HANDLERS_H
+#ifndef MPD_GERROR_H
+#define MPD_GERROR_H
-void initSigHandlers(void);
+typedef struct _GError GError;
#endif
diff --git a/src/icy_metadata.h b/src/icy_metadata.h
deleted file mode 100644
index 9797122ca..000000000
--- a/src/icy_metadata.h
+++ /dev/null
@@ -1,99 +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 ICY_METADATA_H
-#define ICY_METADATA_H
-
-#include <stdbool.h>
-#include <stddef.h>
-
-struct icy_metadata {
- size_t data_size, data_rest;
-
- size_t meta_size, meta_position;
- char *meta_data;
-
- struct tag *tag;
-};
-
-/**
- * Initialize a disabled icy_metadata object.
- */
-static inline void
-icy_clear(struct icy_metadata *im)
-{
- im->data_size = 0;
-}
-
-/**
- * Initialize an enabled icy_metadata object with the specified
- * data_size (from the icy-metaint HTTP response header).
- */
-static inline void
-icy_start(struct icy_metadata *im, size_t data_size)
-{
- im->data_size = im->data_rest = data_size;
- im->meta_size = 0;
- im->tag = NULL;
-}
-
-/**
- * Resets the icy_metadata. Call this after rewinding the stream.
- */
-void
-icy_reset(struct icy_metadata *im);
-
-void
-icy_deinit(struct icy_metadata *im);
-
-/**
- * Checks whether the icy_metadata object is enabled.
- */
-static inline bool
-icy_defined(const struct icy_metadata *im)
-{
- return im->data_size > 0;
-}
-
-/**
- * Evaluates data. Returns the number of bytes of normal data which
- * can be read by the caller, but not more than "length". If the
- * return value is smaller than "length", the caller should invoke
- * icy_meta().
- */
-size_t
-icy_data(struct icy_metadata *im, size_t length);
-
-/**
- * Reads metadata from the stream. Returns the number of bytes
- * consumed. If the return value is smaller than "length", the caller
- * should invoke icy_data().
- */
-size_t
-icy_meta(struct icy_metadata *im, const void *data, size_t length);
-
-static inline struct tag *
-icy_tag(struct icy_metadata *im)
-{
- struct tag *tag = im->tag;
- im->tag = NULL;
- return tag;
-}
-
-#endif
diff --git a/src/icy_server.h b/src/icy_server.h
index 04f21d2ad..17fb391ba 100644
--- a/src/icy_server.h
+++ b/src/icy_server.h
@@ -25,6 +25,10 @@
#include <stdarg.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
char*
icy_server_metadata_header(const char *name,
const char *genre, const char *url,
@@ -33,4 +37,8 @@ icy_server_metadata_header(const char *name,
struct page*
icy_server_metadata_page(const struct tag *tag, ...);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/inotify_source.h b/src/inotify_source.h
deleted file mode 100644
index f92e18e39..000000000
--- a/src/inotify_source.h
+++ /dev/null
@@ -1,61 +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_INOTIFY_SOURCE_H
-#define MPD_INOTIFY_SOURCE_H
-
-#include <glib.h>
-
-typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask,
- const char *name, void *ctx);
-
-struct mpd_inotify_source;
-
-/**
- * Creates a new inotify source and registers it in the GLib main
- * loop.
- *
- * @param a callback invoked for events received from the kernel
- */
-struct mpd_inotify_source *
-mpd_inotify_source_new(mpd_inotify_callback_t callback, void *callback_ctx,
- GError **error_r);
-
-void
-mpd_inotify_source_free(struct mpd_inotify_source *source);
-
-/**
- * Adds a path to the notify list.
- *
- * @return a watch descriptor or -1 on error
- */
-int
-mpd_inotify_source_add(struct mpd_inotify_source *source,
- const char *path_fs, unsigned mask,
- GError **error_r);
-
-/**
- * Removes a path from the notify list.
- *
- * @param wd the watch descriptor returned by mpd_inotify_source_add()
- */
-void
-mpd_inotify_source_rm(struct mpd_inotify_source *source, unsigned wd);
-
-#endif
diff --git a/src/input/curl_input_plugin.c b/src/input/CurlInputPlugin.cxx
index 3f191141e..29d8266ce 100644
--- a/src/input/curl_input_plugin.c
+++ b/src/input/CurlInputPlugin.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,19 @@
*/
#include "config.h"
-#include "input/curl_input_plugin.h"
-#include "input_internal.h"
+#include "CurlInputPlugin.hxx"
#include "input_plugin.h"
#include "conf.h"
#include "tag.h"
-#include "icy_metadata.h"
-#include "io_thread.h"
+#include "IcyMetaDataParser.hxx"
+#include "event/MultiSocketMonitor.hxx"
+
+extern "C" {
+#include "input_internal.h"
+}
+
+#include "event/Loop.hxx"
+#include "IOThread.hxx"
#include "glib_compat.h"
#include <assert.h>
@@ -38,9 +44,16 @@
#include <string.h>
#include <errno.h>
+#include <list>
+#include <forward_list>
+
#include <curl/curl.h>
#include <glib.h>
+#if LIBCURL_VERSION_NUM < 0x071200
+#error libcurl is too old
+#endif
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_curl"
@@ -59,7 +72,7 @@ static const size_t CURL_RESUME_AT = 384 * 1024;
/**
* Buffers created by input_curl_writefunction().
*/
-struct buffer {
+class CurlInputBuffer {
/** size of the payload */
size_t size;
@@ -67,7 +80,55 @@ struct buffer {
size_t consumed;
/** the payload */
- unsigned char data[sizeof(long)];
+ uint8_t *data;
+
+public:
+ CurlInputBuffer(const void *_data, size_t _size)
+ :size(_size), consumed(0), data(new uint8_t[size]) {
+ memcpy(data, _data, size);
+ }
+
+ ~CurlInputBuffer() {
+ delete[] data;
+ }
+
+ CurlInputBuffer(const CurlInputBuffer &) = delete;
+ CurlInputBuffer &operator=(const CurlInputBuffer &) = delete;
+
+ const void *Begin() const {
+ return data + consumed;
+ }
+
+ size_t TotalSize() const {
+ return size;
+ }
+
+ size_t Available() const {
+ return size - consumed;
+ }
+
+ /**
+ * Mark a part of the buffer as consumed.
+ *
+ * @return false if the buffer is now empty
+ */
+ bool Consume(size_t length) {
+ assert(consumed < size);
+
+ consumed += length;
+ if (consumed < size)
+ return true;
+
+ assert(consumed == size);
+ return false;
+ }
+
+ bool Read(void *dest, size_t length) {
+ assert(consumed + length <= size);
+
+ memcpy(dest, data + consumed, length);
+ return Consume(length);
+ }
};
struct input_curl {
@@ -75,40 +136,28 @@ struct input_curl {
/* some buffers which were passed to libcurl, which we have
too free */
- char *url, *range;
+ char *range;
struct curl_slist *request_headers;
/** the curl handles */
CURL *easy;
- /** the GMainLoop source used to poll all CURL file
- descriptors */
- GSource *source;
-
- /** the source id of #source */
- guint source_id;
-
- /** a linked list of all registered GPollFD objects */
- GSList *fds;
-
/** list of buffers, where input_curl_writefunction() appends
to, and input_curl_read() reads from them */
- GQueue *buffers;
+ std::list<CurlInputBuffer> buffers;
-#if LIBCURL_VERSION_NUM >= 0x071200
/**
* Is the connection currently paused? That happens when the
* buffer was getting too large. It will be unpaused when the
* buffer is below the threshold again.
*/
bool paused;
-#endif
/** error message provided by libcurl */
char error[CURL_ERROR_SIZE];
/** parser for icy-metadata */
- struct icy_metadata icy_metadata;
+ IcyMetaDataParser icy;
/** the stream name from the icy-name response header */
char *meta_name;
@@ -118,6 +167,50 @@ struct input_curl {
struct tag *tag;
GError *postponed_error;
+
+ input_curl(const char *url, GMutex *mutex, GCond *cond)
+ :range(nullptr), request_headers(nullptr),
+ paused(false),
+ meta_name(nullptr),
+ tag(nullptr),
+ postponed_error(nullptr) {
+ input_stream_init(&base, &input_plugin_curl, url, mutex, cond);
+ }
+
+ ~input_curl();
+
+ input_curl(const input_curl &) = delete;
+ input_curl &operator=(const input_curl &) = delete;
+};
+
+/**
+ * This class monitors all CURL file descriptors.
+ */
+class CurlSockets final : private MultiSocketMonitor {
+ /**
+ * Did CURL give us a timeout? If yes, then we need to call
+ * curl_multi_perform(), even if there was no event on any
+ * file descriptor.
+ */
+ bool have_timeout;
+
+ /**
+ * The absolute time stamp when the timeout expires.
+ */
+ gint64 absolute_timeout;
+
+public:
+ CurlSockets(EventLoop &_loop)
+ :MultiSocketMonitor(_loop) {}
+
+ using MultiSocketMonitor::InvalidateSockets;
+
+private:
+ void UpdateSockets();
+
+ virtual void PrepareSockets(gcc_unused gint *timeout_r) override;
+ virtual bool CheckSockets() const override;
+ virtual void DispatchSockets() override;
};
/** libcurl should accept "ICY 200 OK" */
@@ -134,35 +227,9 @@ static struct {
* A linked list of all active HTTP requests. An active
* request is one that doesn't have the "eof" flag set.
*/
- GSList *requests;
-
- /**
- * The GMainLoop source used to poll all CURL file
- * descriptors.
- */
- GSource *source;
-
- /**
- * The source id of #source.
- */
- guint source_id;
-
- GSList *fds;
-
-#if LIBCURL_VERSION_NUM >= 0x070f04
- /**
- * Did CURL give us a timeout? If yes, then we need to call
- * curl_multi_perform(), even if there was no event on any
- * file descriptor.
- */
- bool timeout;
+ std::forward_list<input_curl *> requests;
- /**
- * The absolute time stamp when the timeout expires. This is
- * used in the GSource method check().
- */
- gint64 absolute_timeout;
-#endif
+ CurlSockets *sockets;
} curl;
static inline GQuark
@@ -181,23 +248,19 @@ input_curl_find_request(CURL *easy)
{
assert(io_thread_inside());
- for (GSList *i = curl.requests; i != NULL; i = g_slist_next(i)) {
- struct input_curl *c = i->data;
+ for (auto c : curl.requests)
if (c->easy == easy)
return c;
- }
return NULL;
}
-#if LIBCURL_VERSION_NUM >= 0x071200
-
static gpointer
input_curl_resume(gpointer data)
{
assert(io_thread_inside());
- struct input_curl *c = data;
+ struct input_curl *c = (struct input_curl *)data;
if (c->paused) {
c->paused = false;
@@ -207,13 +270,11 @@ input_curl_resume(gpointer data)
return NULL;
}
-#endif
-
/**
* Calculates the GLib event bit mask for one file descriptor,
* obtained from three #fd_set objects filled by curl_multi_fdset().
*/
-static gushort
+static unsigned
input_curl_fd_events(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds)
{
gushort events = 0;
@@ -242,8 +303,8 @@ input_curl_fd_events(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds)
*
* Runs in the I/O thread. No lock needed.
*/
-static void
-curl_update_fds(void)
+void
+CurlSockets::UpdateSockets()
{
assert(io_thread_inside());
@@ -262,42 +323,15 @@ curl_update_fds(void)
return;
}
- GSList *fds = curl.fds;
- curl.fds = NULL;
-
- while (fds != NULL) {
- GPollFD *poll_fd = fds->data;
- gushort events = input_curl_fd_events(poll_fd->fd, &rfds,
- &wfds, &efds);
-
- assert(poll_fd->events != 0);
-
- fds = g_slist_remove(fds, poll_fd);
-
- if (events != poll_fd->events)
- g_source_remove_poll(curl.source, poll_fd);
-
- if (events != 0) {
- if (events != poll_fd->events) {
- poll_fd->events = events;
- g_source_add_poll(curl.source, poll_fd);
- }
-
- curl.fds = g_slist_prepend(curl.fds, poll_fd);
- } else {
- g_free(poll_fd);
- }
- }
+ UpdateSocketList([&rfds, &wfds, &efds](int fd){
+ return input_curl_fd_events(fd, &rfds,
+ &wfds, &efds);
+ });
for (int fd = 0; fd <= max_fd; ++fd) {
- gushort events = input_curl_fd_events(fd, &rfds, &wfds, &efds);
- if (events != 0) {
- GPollFD *poll_fd = g_new(GPollFD, 1);
- poll_fd->fd = fd;
- poll_fd->events = events;
- g_source_add_poll(curl.source, poll_fd);
- curl.fds = g_slist_prepend(curl.fds, poll_fd);
- }
+ unsigned events = input_curl_fd_events(fd, &rfds, &wfds, &efds);
+ if (events != 0)
+ AddSocket(fd, events);
}
}
@@ -312,7 +346,7 @@ input_curl_easy_add(struct input_curl *c, GError **error_r)
assert(c->easy != NULL);
assert(input_curl_find_request(c->easy) == NULL);
- curl.requests = g_slist_prepend(curl.requests, c);
+ curl.requests.push_front(c);
CURLMcode mcode = curl_multi_add_handle(curl.multi, c->easy);
if (mcode != CURLM_OK) {
@@ -322,7 +356,7 @@ input_curl_easy_add(struct input_curl *c, GError **error_r)
return false;
}
- curl_update_fds();
+ curl.sockets->InvalidateSockets();
return true;
}
@@ -335,7 +369,8 @@ struct easy_add_params {
static gpointer
input_curl_easy_add_callback(gpointer data)
{
- const struct easy_add_params *params = data;
+ const struct easy_add_params *params =
+ (const struct easy_add_params *)data;
bool success = input_curl_easy_add(params->c, params->error_r);
return GUINT_TO_POINTER(success);
@@ -352,8 +387,8 @@ input_curl_easy_add_indirect(struct input_curl *c, GError **error_r)
assert(c->easy != NULL);
struct easy_add_params params = {
- .c = c,
- .error_r = error_r,
+ c,
+ error_r,
};
gpointer result =
@@ -376,7 +411,7 @@ input_curl_easy_free(struct input_curl *c)
if (c->easy == NULL)
return;
- curl.requests = g_slist_remove(curl.requests, c);
+ curl.requests.remove(c);
curl_multi_remove_handle(curl.multi, c->easy);
curl_easy_cleanup(c->easy);
@@ -392,10 +427,10 @@ input_curl_easy_free(struct input_curl *c)
static gpointer
input_curl_easy_free_callback(gpointer data)
{
- struct input_curl *c = data;
+ struct input_curl *c = (struct input_curl *)data;
input_curl_easy_free(c);
- curl_update_fds();
+ curl.sockets->InvalidateSockets();
return NULL;
}
@@ -424,8 +459,8 @@ input_curl_abort_all_requests(GError *error)
assert(io_thread_inside());
assert(error != NULL);
- while (curl.requests != NULL) {
- struct input_curl *c = curl.requests->data;
+ while (!curl.requests.empty()) {
+ struct input_curl *c = curl.requests.front();
assert(c->postponed_error == NULL);
input_curl_easy_free(c);
@@ -532,28 +567,18 @@ input_curl_perform(void)
return true;
}
-/*
- * GSource methods
- *
- */
-
-/**
- * The GSource prepare() method implementation.
- */
-static gboolean
-input_curl_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout_r)
+void
+CurlSockets::PrepareSockets(gint *timeout_r)
{
- curl_update_fds();
+ UpdateSockets();
-#if LIBCURL_VERSION_NUM >= 0x070f04
- curl.timeout = false;
+ have_timeout = false;
long timeout2;
CURLMcode mcode = curl_multi_timeout(curl.multi, &timeout2);
if (mcode == CURLM_OK) {
if (timeout2 >= 0)
- curl.absolute_timeout = g_source_get_time(source)
- + timeout2 * 1000;
+ absolute_timeout = GetTime() + timeout2 * 1000;
if (timeout2 >= 0 && timeout2 < 10)
/* CURL 7.21.1 likes to report "timeout=0",
@@ -564,69 +589,28 @@ input_curl_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout_r)
*timeout_r = timeout2;
- curl.timeout = timeout2 >= 0;
+ have_timeout = timeout2 >= 0;
} else
g_warning("curl_multi_timeout() failed: %s\n",
curl_multi_strerror(mcode));
-#else
- (void)timeout_r;
-#endif
-
- return false;
}
-/**
- * The GSource check() method implementation.
- */
-static gboolean
-input_curl_source_check(G_GNUC_UNUSED GSource *source)
+bool
+CurlSockets::CheckSockets() const
{
-#if LIBCURL_VERSION_NUM >= 0x070f04
- if (curl.timeout) {
- /* when a timeout has expired, we need to call
- curl_multi_perform(), even if there was no file
- descriptor event */
-
- if (g_source_get_time(source) >= curl.absolute_timeout)
- return true;
- }
-#endif
-
- for (GSList *i = curl.fds; i != NULL; i = i->next) {
- GPollFD *poll_fd = i->data;
- if (poll_fd->revents != 0)
- return true;
- }
-
- return false;
+ /* when a timeout has expired, we need to call
+ curl_multi_perform(), even if there was no file descriptor
+ event */
+ return have_timeout && GetTime() >= absolute_timeout;
}
-/**
- * The GSource dispatch() method implementation. The callback isn't
- * used, because we're handling all events directly.
- */
-static gboolean
-input_curl_source_dispatch(G_GNUC_UNUSED GSource *source,
- G_GNUC_UNUSED GSourceFunc callback,
- G_GNUC_UNUSED gpointer user_data)
+void
+CurlSockets::DispatchSockets()
{
if (input_curl_perform())
input_curl_info_read();
-
- return true;
}
-/**
- * The vtable for our GSource implementation. Unfortunately, we
- * cannot declare it "const", because g_source_new() takes a non-const
- * pointer, for whatever reason.
- */
-static GSourceFuncs curl_source_funcs = {
- .prepare = input_curl_source_prepare,
- .check = input_curl_source_check,
- .dispatch = input_curl_source_dispatch,
-};
-
/*
* input_plugin methods
*
@@ -668,8 +652,7 @@ input_curl_init(const struct config_param *param,
return false;
}
- curl.source = g_source_new(&curl_source_funcs, sizeof(*curl.source));
- curl.source_id = g_source_attach(curl.source, io_thread_context());
+ curl.sockets = new CurlSockets(io_thread_get());
return true;
}
@@ -677,7 +660,7 @@ input_curl_init(const struct config_param *param,
static gpointer
curl_destroy_sources(G_GNUC_UNUSED gpointer data)
{
- g_source_destroy(curl.source);
+ delete curl.sockets;
return NULL;
}
@@ -685,7 +668,7 @@ curl_destroy_sources(G_GNUC_UNUSED gpointer data)
static void
input_curl_finish(void)
{
- assert(curl.requests == NULL);
+ assert(curl.requests.empty());
io_thread_call(curl_destroy_sources, NULL);
@@ -696,8 +679,6 @@ input_curl_finish(void)
curl_global_cleanup();
}
-#if LIBCURL_VERSION_NUM >= 0x071200
-
/**
* Determine the total sizes of all buffers, including portions that
* have already been consumed.
@@ -710,55 +691,24 @@ curl_total_buffer_size(const struct input_curl *c)
{
size_t total = 0;
- for (GList *i = g_queue_peek_head_link(c->buffers);
- i != NULL; i = g_list_next(i)) {
- struct buffer *buffer = i->data;
- total += buffer->size;
- }
+ for (const auto &i : c->buffers)
+ total += i.TotalSize();
return total;
}
-#endif
-
-static void
-buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
+input_curl::~input_curl()
{
- struct buffer *buffer = data;
+ if (tag != NULL)
+ tag_free(tag);
+ g_free(meta_name);
- assert(buffer->consumed <= buffer->size);
+ input_curl_easy_free_indirect(this);
- g_free(buffer);
-}
+ if (postponed_error != NULL)
+ g_error_free(postponed_error);
-static void
-input_curl_flush_buffers(struct input_curl *c)
-{
- g_queue_foreach(c->buffers, buffer_free_callback, NULL);
- g_queue_clear(c->buffers);
-}
-
-/**
- * Frees this stream, including the input_stream struct.
- */
-static void
-input_curl_free(struct input_curl *c)
-{
- if (c->tag != NULL)
- tag_free(c->tag);
- g_free(c->meta_name);
-
- input_curl_easy_free_indirect(c);
- input_curl_flush_buffers(c);
-
- g_queue_free(c->buffers);
-
- if (c->postponed_error != NULL)
- g_error_free(c->postponed_error);
-
- g_free(c->url);
- input_stream_deinit(&c->base);
- g_free(c);
+ input_stream_deinit(&base);
}
static bool
@@ -788,7 +738,7 @@ input_curl_tag(struct input_stream *is)
static bool
fill_buffer(struct input_curl *c, GError **error_r)
{
- while (c->easy != NULL && g_queue_is_empty(c->buffers))
+ while (c->easy != NULL && c->buffers.empty())
g_cond_wait(c->base.cond, c->base.mutex);
if (c->postponed_error != NULL) {
@@ -797,86 +747,63 @@ fill_buffer(struct input_curl *c, GError **error_r)
return false;
}
- return !g_queue_is_empty(c->buffers);
-}
-
-/**
- * Mark a part of the buffer object as consumed.
- */
-static struct buffer *
-consume_buffer(struct buffer *buffer, size_t length)
-{
- assert(buffer != NULL);
- assert(buffer->consumed < buffer->size);
-
- buffer->consumed += length;
- if (buffer->consumed < buffer->size)
- return buffer;
-
- assert(buffer->consumed == buffer->size);
-
- g_free(buffer);
-
- return NULL;
+ return !c->buffers.empty();
}
static size_t
-read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers,
+read_from_buffer(IcyMetaDataParser &icy, std::list<CurlInputBuffer> &buffers,
void *dest0, size_t length)
{
- struct buffer *buffer = g_queue_pop_head(buffers);
- uint8_t *dest = dest0;
+ auto &buffer = buffers.front();
+ uint8_t *dest = (uint8_t *)dest0;
size_t nbytes = 0;
- assert(buffer->size > 0);
- assert(buffer->consumed < buffer->size);
-
- if (length > buffer->size - buffer->consumed)
- length = buffer->size - buffer->consumed;
+ if (length > buffer.Available())
+ length = buffer.Available();
while (true) {
size_t chunk;
- chunk = icy_data(icy_metadata, length);
+ chunk = icy.Data(length);
if (chunk > 0) {
- memcpy(dest, buffer->data + buffer->consumed,
- chunk);
- buffer = consume_buffer(buffer, chunk);
+ const bool empty = !buffer.Read(dest, chunk);
nbytes += chunk;
dest += chunk;
length -= chunk;
- if (length == 0)
+ if (empty) {
+ buffers.pop_front();
break;
+ }
- assert(buffer != NULL);
+ if (length == 0)
+ break;
}
- chunk = icy_meta(icy_metadata, buffer->data + buffer->consumed,
- length);
+ chunk = icy.Meta(buffer.Begin(), length);
if (chunk > 0) {
- buffer = consume_buffer(buffer, chunk);
+ const bool empty = !buffer.Consume(chunk);
length -= chunk;
- if (length == 0)
+ if (empty) {
+ buffers.pop_front();
break;
+ }
- assert(buffer != NULL);
+ if (length == 0)
+ break;
}
}
- if (buffer != NULL)
- g_queue_push_head(buffers, buffer);
-
return nbytes;
}
static void
copy_icy_tag(struct input_curl *c)
{
- struct tag *tag = icy_tag(&c->icy_metadata);
+ struct tag *tag = c->icy.ReadTag();
if (tag == NULL)
return;
@@ -896,7 +823,7 @@ input_curl_available(struct input_stream *is)
struct input_curl *c = (struct input_curl *)is;
return c->postponed_error != NULL || c->easy == NULL ||
- !g_queue_is_empty(c->buffers);
+ !c->buffers.empty();
}
static size_t
@@ -906,7 +833,7 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
struct input_curl *c = (struct input_curl *)is;
bool success;
size_t nbytes = 0;
- char *dest = ptr;
+ char *dest = (char *)ptr;
do {
/* fill the buffer */
@@ -917,8 +844,8 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
/* send buffer contents */
- while (size > 0 && !g_queue_is_empty(c->buffers)) {
- size_t copy = read_from_buffer(&c->icy_metadata, c->buffers,
+ while (size > 0 && !c->buffers.empty()) {
+ size_t copy = read_from_buffer(c->icy, c->buffers,
dest + nbytes, size);
nbytes += copy;
@@ -926,18 +853,16 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
}
} while (nbytes == 0);
- if (icy_defined(&c->icy_metadata))
+ if (c->icy.IsDefined())
copy_icy_tag(c);
is->offset += (goffset)nbytes;
-#if LIBCURL_VERSION_NUM >= 0x071200
if (c->paused && curl_total_buffer_size(c) < CURL_RESUME_AT) {
g_mutex_unlock(c->base.mutex);
io_thread_call(input_curl_resume, c);
g_mutex_lock(c->base.mutex);
}
-#endif
return nbytes;
}
@@ -947,7 +872,7 @@ input_curl_close(struct input_stream *is)
{
struct input_curl *c = (struct input_curl *)is;
- input_curl_free(c);
+ delete c;
}
static bool
@@ -955,7 +880,7 @@ input_curl_eof(G_GNUC_UNUSED struct input_stream *is)
{
struct input_curl *c = (struct input_curl *)is;
- return c->easy == NULL && g_queue_is_empty(c->buffers);
+ return c->easy == NULL && c->buffers.empty();
}
/** called by curl when new data is available */
@@ -963,13 +888,14 @@ static size_t
input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
{
struct input_curl *c = (struct input_curl *)stream;
- const char *header = ptr, *end, *value;
char name[64];
size *= nmemb;
- end = header + size;
- value = memchr(header, ':', size);
+ const char *header = (const char *)ptr;
+ const char *end = header + size;
+
+ const char *value = (const char *)memchr(header, ':', size);
if (value == NULL || (size_t)(value - header) >= sizeof(name))
return size;
@@ -990,7 +916,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
if (g_ascii_strcasecmp(name, "accept-ranges") == 0) {
/* a stream with icy-metadata is not seekable */
- if (!icy_defined(&c->icy_metadata))
+ if (!c->icy.IsDefined())
c->base.seekable = true;
} else if (g_ascii_strcasecmp(name, "content-length") == 0) {
char buffer[64];
@@ -1021,7 +947,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
size_t icy_metaint;
if ((size_t)(end - header) >= sizeof(buffer) ||
- icy_defined(&c->icy_metadata))
+ c->icy.IsDefined())
return size;
memcpy(buffer, value, end - value);
@@ -1031,7 +957,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
g_debug("icy-metaint=%zu", icy_metaint);
if (icy_metaint > 0) {
- icy_start(&c->icy_metadata, icy_metaint);
+ c->icy.Start(icy_metaint);
/* a stream with icy-metadata is not
seekable */
@@ -1047,7 +973,6 @@ static size_t
input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
{
struct input_curl *c = (struct input_curl *)stream;
- struct buffer *buffer;
size *= nmemb;
if (size == 0)
@@ -1055,20 +980,13 @@ input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
g_mutex_lock(c->base.mutex);
-#if LIBCURL_VERSION_NUM >= 0x071200
if (curl_total_buffer_size(c) + size >= CURL_MAX_BUFFERED) {
c->paused = true;
g_mutex_unlock(c->base.mutex);
return CURL_WRITEFUNC_PAUSE;
}
-#endif
-
- buffer = g_malloc(sizeof(*buffer) - sizeof(buffer->data) + size);
- buffer->size = size;
- buffer->consumed = 0;
- memcpy(buffer->data, ptr, size);
- g_queue_push_tail(c->buffers, buffer);
+ c->buffers.emplace_back(ptr, size);
c->base.ready = true;
g_cond_broadcast(c->base.cond);
@@ -1120,7 +1038,7 @@ input_curl_easy_init(struct input_curl *c, GError **error_r)
g_free(proxy_auth_str);
}
- code = curl_easy_setopt(c->easy, CURLOPT_URL, c->url);
+ code = curl_easy_setopt(c->easy, CURLOPT_URL, c->base.uri);
if (code != CURLE_OK) {
g_set_error(error_r, curl_quark(), code,
"curl_easy_setopt() failed: %s",
@@ -1179,19 +1097,15 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
/* check if we can fast-forward the buffer */
- while (offset > is->offset && !g_queue_is_empty(c->buffers)) {
- struct buffer *buffer;
- size_t length;
-
- buffer = (struct buffer *)g_queue_pop_head(c->buffers);
-
- length = buffer->size - buffer->consumed;
+ while (offset > is->offset && !c->buffers.empty()) {
+ auto &buffer = c->buffers.front();
+ size_t length = buffer.Available();
if (offset - is->offset < (goffset)length)
length = offset - is->offset;
- buffer = consume_buffer(buffer, length);
- if (buffer != NULL)
- g_queue_push_head(c->buffers, buffer);
+ const bool empty = !buffer.Consume(length);
+ if (empty)
+ c->buffers.pop_front();
is->offset += length;
}
@@ -1204,7 +1118,7 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
g_mutex_unlock(c->base.mutex);
input_curl_easy_free_indirect(c);
- input_curl_flush_buffers(c);
+ c->buffers.clear();
is->offset = offset;
if (is->offset == is->size) {
@@ -1251,34 +1165,18 @@ input_curl_open(const char *url, GMutex *mutex, GCond *cond,
assert(mutex != NULL);
assert(cond != NULL);
- struct input_curl *c;
-
if (strncmp(url, "http://", 7) != 0)
return NULL;
- c = g_new0(struct input_curl, 1);
- input_stream_init(&c->base, &input_plugin_curl, url,
- mutex, cond);
-
- c->url = g_strdup(url);
- c->buffers = g_queue_new();
-
- icy_clear(&c->icy_metadata);
- c->tag = NULL;
-
- c->postponed_error = NULL;
-
-#if LIBCURL_VERSION_NUM >= 0x071200
- c->paused = false;
-#endif
+ struct input_curl *c = new input_curl(url, mutex, cond);
if (!input_curl_easy_init(c, error_r)) {
- input_curl_free(c);
+ delete c;
return NULL;
}
if (!input_curl_easy_add_indirect(c, error_r)) {
- input_curl_free(c);
+ delete c;
return NULL;
}
@@ -1286,16 +1184,16 @@ input_curl_open(const char *url, GMutex *mutex, GCond *cond,
}
const struct input_plugin input_plugin_curl = {
- .name = "curl",
- .init = input_curl_init,
- .finish = input_curl_finish,
-
- .open = input_curl_open,
- .close = input_curl_close,
- .check = input_curl_check,
- .tag = input_curl_tag,
- .available = input_curl_available,
- .read = input_curl_read,
- .eof = input_curl_eof,
- .seek = input_curl_seek,
+ "curl",
+ input_curl_init,
+ input_curl_finish,
+ input_curl_open,
+ input_curl_close,
+ input_curl_check,
+ nullptr,
+ input_curl_tag,
+ input_curl_available,
+ input_curl_read,
+ input_curl_eof,
+ input_curl_seek,
};
diff --git a/src/input/curl_input_plugin.h b/src/input/CurlInputPlugin.hxx
index c6e71bf40..20d1309d8 100644
--- a/src/input/curl_input_plugin.h
+++ b/src/input/CurlInputPlugin.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_INPUT_CURL_H
-#define MPD_INPUT_CURL_H
+#ifndef MPD_INPUT_CURL_HXX
+#define MPD_INPUT_CURL_HXX
struct input_stream;
diff --git a/src/input/soup_input_plugin.c b/src/input/SoupInputPlugin.cxx
index fc903b48c..5a60fa725 100644
--- a/src/input/soup_input_plugin.c
+++ b/src/input/SoupInputPlugin.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,21 @@
*/
#include "config.h"
-#include "input/soup_input_plugin.h"
-#include "input_internal.h"
+#include "SoupInputPlugin.hxx"
#include "input_plugin.h"
-#include "io_thread.h"
+
+extern "C" {
+#include "input_internal.h"
+}
+
+#include "IOThread.hxx"
+#include "event/Loop.hxx"
#include "conf.h"
+extern "C" {
#include <libsoup/soup-uri.h>
#include <libsoup/soup-session-async.h>
+}
#include <assert.h>
#include <string.h>
@@ -99,7 +106,7 @@ input_soup_init(const struct config_param *param, GError **error_r)
soup_session_async_new_with_options(SOUP_SESSION_PROXY_URI,
soup_proxy,
SOUP_SESSION_ASYNC_CONTEXT,
- io_thread_context(),
+ io_thread_get().GetContext(),
NULL);
return true;
@@ -156,7 +163,7 @@ static void
input_soup_session_callback(G_GNUC_UNUSED SoupSession *session,
SoupMessage *msg, gpointer user_data)
{
- struct input_soup *s = user_data;
+ struct input_soup *s = (struct input_soup *)user_data;
assert(msg == s->msg);
assert(!s->completed);
@@ -177,7 +184,7 @@ input_soup_session_callback(G_GNUC_UNUSED SoupSession *session,
static void
input_soup_got_headers(SoupMessage *msg, gpointer user_data)
{
- struct input_soup *s = user_data;
+ struct input_soup *s = (struct input_soup *)user_data;
g_mutex_lock(s->base.mutex);
@@ -199,7 +206,7 @@ input_soup_got_headers(SoupMessage *msg, gpointer user_data)
static void
input_soup_got_chunk(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
{
- struct input_soup *s = user_data;
+ struct input_soup *s = (struct input_soup *)user_data;
assert(msg == s->msg);
@@ -220,7 +227,7 @@ input_soup_got_chunk(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
static void
input_soup_got_body(G_GNUC_UNUSED SoupMessage *msg, gpointer user_data)
{
- struct input_soup *s = user_data;
+ struct input_soup *s = (struct input_soup *)user_data;
assert(msg == s->msg);
@@ -256,7 +263,7 @@ input_soup_wait_data(struct input_soup *s)
static gpointer
input_soup_queue(gpointer data)
{
- struct input_soup *s = data;
+ struct input_soup *s = (struct input_soup *)data;
soup_session_queue_message(soup_session, s->msg,
input_soup_session_callback, s);
@@ -320,7 +327,7 @@ input_soup_open(const char *uri,
static gpointer
input_soup_cancel(gpointer data)
{
- struct input_soup *s = data;
+ struct input_soup *s = (struct input_soup *)data;
if (!s->completed)
soup_session_cancel_message(soup_session, s->msg,
@@ -352,7 +359,7 @@ input_soup_close(struct input_stream *is)
g_mutex_unlock(s->base.mutex);
SoupBuffer *buffer;
- while ((buffer = g_queue_pop_head(s->buffers)) != NULL)
+ while ((buffer = (SoupBuffer *)g_queue_pop_head(s->buffers)) != NULL)
soup_buffer_free(buffer);
g_queue_free(s->buffers);
@@ -403,10 +410,11 @@ input_soup_read(struct input_stream *is, void *ptr, size_t size,
return 0;
}
- char *p0 = ptr, *p = p0, *p_end = p0 + size;
+ char *p0 = (char *)ptr, *p = p0, *p_end = p0 + size;
while (p < p_end) {
- SoupBuffer *buffer = g_queue_pop_head(s->buffers);
+ SoupBuffer *buffer = (SoupBuffer *)
+ g_queue_pop_head(s->buffers);
if (buffer == NULL) {
assert(s->current_consumed == 0);
break;
@@ -460,14 +468,16 @@ input_soup_eof(G_GNUC_UNUSED struct input_stream *is)
}
const struct input_plugin input_plugin_soup = {
- .name = "soup",
- .init = input_soup_init,
- .finish = input_soup_finish,
-
- .open = input_soup_open,
- .close = input_soup_close,
- .check = input_soup_check,
- .available = input_soup_available,
- .read = input_soup_read,
- .eof = input_soup_eof,
+ "soup",
+ input_soup_init,
+ input_soup_finish,
+ input_soup_open,
+ input_soup_close,
+ input_soup_check,
+ nullptr,
+ nullptr,
+ input_soup_available,
+ input_soup_read,
+ input_soup_eof,
+ nullptr,
};
diff --git a/src/input/soup_input_plugin.h b/src/input/SoupInputPlugin.hxx
index 689b2d971..4c089b39b 100644
--- a/src/input/soup_input_plugin.h
+++ b/src/input/SoupInputPlugin.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_INPUT_SOUP_H
-#define MPD_INPUT_SOUP_H
+#ifndef MPD_INPUT_SOUP_HXX
+#define MPD_INPUT_SOUP_HXX
extern const struct input_plugin input_plugin_soup;
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_stream.h b/src/input_stream.h
index 10ad97161..d5b1dae2e 100644
--- a/src/input_stream.h
+++ b/src/input_stream.h
@@ -88,6 +88,10 @@ struct input_stream {
char *mime;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Opens a new input stream. You may not access it until the "ready"
* flag is set.
@@ -164,6 +168,13 @@ void
input_stream_lock_wait_ready(struct input_stream *is);
/**
+ * Determines whether seeking is cheap. This is true for local files.
+ */
+gcc_pure gcc_nonnull(1)
+bool
+input_stream_cheap_seeking(const struct input_stream *is);
+
+/**
* Seeks to the specified position in the stream. This will most
* likely fail if the "seekable" flag is false.
*
@@ -264,4 +275,8 @@ size_t
input_stream_lock_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/io_error.h b/src/io_error.h
new file mode 100644
index 000000000..930ced108
--- /dev/null
+++ b/src/io_error.h
@@ -0,0 +1,51 @@
+/*
+ * 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_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));
+}
+
+static inline GError *
+new_error_errno(void)
+{
+ return g_error_new_literal(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/alsa_mixer_plugin.c b/src/mixer/AlsaMixerPlugin.cxx
index 22e4e22bd..17f8b9a6f 100644
--- a/src/mixer/alsa_mixer_plugin.c
+++ b/src/mixer/AlsaMixerPlugin.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
@@ -20,7 +20,11 @@
#include "config.h"
#include "mixer_api.h"
#include "output_api.h"
-#include "event_pipe.h"
+#include "GlobalEvents.hxx"
+#include "Main.hxx"
+#include "event/MultiSocketMonitor.hxx"
+
+#include <algorithm>
#include <glib.h>
#include <alsa/asoundlib.h>
@@ -29,13 +33,16 @@
#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM"
#define VOLUME_MIXER_ALSA_INDEX_DEFAULT 0
-struct alsa_mixer_source {
- GSource source;
+class AlsaMixerMonitor final : private MultiSocketMonitor {
+ snd_mixer_t *const mixer;
- snd_mixer_t *mixer;
+public:
+ AlsaMixerMonitor(EventLoop &_loop, snd_mixer_t *_mixer)
+ :MultiSocketMonitor(_loop), mixer(_mixer) {}
- /** a linked list of all registered GPollFD objects */
- GSList *fds;
+private:
+ virtual void PrepareSockets(gcc_unused gint *timeout_r) override;
+ virtual void DispatchSockets() override;
};
struct alsa_mixer {
@@ -52,7 +59,7 @@ struct alsa_mixer {
long volume_max;
int volume_set;
- struct alsa_mixer_source *source;
+ AlsaMixerMonitor *monitor;
};
/**
@@ -64,142 +71,45 @@ alsa_mixer_quark(void)
return g_quark_from_static_string("alsa_mixer");
}
-/*
- * GSource helper functions
- *
- */
-
-static GSList **
-find_fd(GSList **list_r, int fd)
-{
- while (true) {
- GSList *list = *list_r;
- if (list == NULL)
- return NULL;
-
- GPollFD *p = list->data;
- if (p->fd == fd)
- return list_r;
-
- list_r = &list->next;
- }
-}
-
-static void
-alsa_mixer_update_fd(struct alsa_mixer_source *source, const struct pollfd *p,
- GSList **old_r)
-{
- GSList **found_r = find_fd(old_r, p->fd);
- if (found_r == NULL) {
- /* new fd */
- GPollFD *q = g_new(GPollFD, 1);
- q->fd = p->fd;
- q->events = p->events;
- g_source_add_poll(&source->source, q);
- source->fds = g_slist_prepend(source->fds, q);
- return;
- }
-
- GSList *found = *found_r;
- *found_r = found->next;
-
- GPollFD *q = found->data;
- if (q->events != p->events) {
- /* refresh events */
- g_source_remove_poll(&source->source, q);
- q->events = p->events;
- g_source_add_poll(&source->source, q);
- }
-
- found->next = source->fds;
- source->fds = found;
-}
-
-static void
-alsa_mixer_update_fds(struct alsa_mixer_source *source)
+void
+AlsaMixerMonitor::PrepareSockets(gcc_unused gint *timeout_r)
{
- int count = snd_mixer_poll_descriptors_count(source->mixer);
+ int count = snd_mixer_poll_descriptors_count(mixer);
if (count < 0)
count = 0;
struct pollfd *pfds = g_new(struct pollfd, count);
- count = snd_mixer_poll_descriptors(source->mixer, pfds, count);
+ count = snd_mixer_poll_descriptors(mixer, pfds, count);
if (count < 0)
count = 0;
- GSList *old = source->fds;
- source->fds = NULL;
+ struct pollfd *end = pfds + count;
- for (int i = 0; i < count; ++i)
- alsa_mixer_update_fd(source, &pfds[i], &old);
- g_free(pfds);
+ UpdateSocketList([pfds, end](int fd) -> unsigned {
+ auto i = std::find_if(pfds, end, [fd](const struct pollfd &pfd){
+ return pfd.fd == fd;
+ });
+ if (i == end)
+ return 0;
- for (; old != NULL; old = old->next) {
- GPollFD *q = old->data;
- g_source_remove_poll(&source->source, q);
- g_free(q);
- }
+ auto events = i->events;
+ i->events = 0;
+ return events;
+ });
- g_slist_free(old);
-}
+ for (auto i = pfds; i != end; ++i)
+ if (i->events != 0)
+ AddSocket(i->fd, i->events);
-/*
- * GSource methods
- *
- */
-
-static gboolean
-alsa_mixer_source_prepare(GSource *_source, G_GNUC_UNUSED gint *timeout_r)
-{
- struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
- alsa_mixer_update_fds(source);
-
- return false;
-}
-
-static gboolean
-alsa_mixer_source_check(GSource *_source)
-{
- struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
-
- for (const GSList *i = source->fds; i != NULL; i = i->next) {
- const GPollFD *poll_fd = i->data;
- if (poll_fd->revents != 0)
- return true;
- }
-
- return false;
+ g_free(pfds);
}
-static gboolean
-alsa_mixer_source_dispatch(GSource *_source,
- G_GNUC_UNUSED GSourceFunc callback,
- G_GNUC_UNUSED gpointer user_data)
+void
+AlsaMixerMonitor::DispatchSockets()
{
- struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
-
- snd_mixer_handle_events(source->mixer);
- return true;
+ snd_mixer_handle_events(mixer);
}
-static void
-alsa_mixer_source_finalize(GSource *_source)
-{
- struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
-
- for (GSList *i = source->fds; i != NULL; i = i->next)
- g_free(i->data);
-
- g_slist_free(source->fds);
-}
-
-static GSourceFuncs alsa_mixer_source_funcs = {
- .prepare = alsa_mixer_source_prepare,
- .check = alsa_mixer_source_check,
- .dispatch = alsa_mixer_source_dispatch,
- .finalize = alsa_mixer_source_finalize,
-};
-
/*
* libasound callbacks
*
@@ -209,7 +119,7 @@ static int
alsa_mixer_elem_callback(G_GNUC_UNUSED snd_mixer_elem_t *elem, unsigned mask)
{
if (mask & SND_CTL_EVENT_MASK_VALUE)
- event_pipe_emit(PIPE_EVENT_MIXER);
+ GlobalEvents::Emit(GlobalEvents::MIXER);
return 0;
}
@@ -304,11 +214,7 @@ alsa_mixer_setup(struct alsa_mixer *am, GError **error_r)
snd_mixer_elem_set_callback(am->elem, alsa_mixer_elem_callback);
- am->source = (struct alsa_mixer_source *)
- g_source_new(&alsa_mixer_source_funcs, sizeof(*am->source));
- am->source->mixer = am->handle;
- am->source->fds = NULL;
- g_source_attach(&am->source->source, g_main_context_default());
+ am->monitor = new AlsaMixerMonitor(*main_loop, am->handle);
return true;
}
@@ -343,8 +249,7 @@ alsa_mixer_close(struct mixer *data)
assert(am->handle != NULL);
- g_source_destroy(&am->source->source);
- g_source_unref(&am->source->source);
+ delete am->monitor;
snd_mixer_elem_set_callback(am->elem, NULL);
snd_mixer_close(am->handle);
@@ -421,11 +326,11 @@ alsa_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
}
const struct mixer_plugin alsa_mixer_plugin = {
- .init = alsa_mixer_init,
- .finish = alsa_mixer_finish,
- .open = alsa_mixer_open,
- .close = alsa_mixer_close,
- .get_volume = alsa_mixer_get_volume,
- .set_volume = alsa_mixer_set_volume,
- .global = true,
+ alsa_mixer_init,
+ alsa_mixer_finish,
+ alsa_mixer_open,
+ alsa_mixer_close,
+ alsa_mixer_get_volume,
+ alsa_mixer_set_volume,
+ true,
};
diff --git a/src/mixer/pulse_mixer_plugin.c b/src/mixer/PulseMixerPlugin.cxx
index a82c032b3..65dbc01fe 100644
--- a/src/mixer/pulse_mixer_plugin.c
+++ b/src/mixer/PulseMixerPlugin.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,11 @@
*/
#include "config.h"
-#include "pulse_mixer_plugin.h"
+#include "PulseMixerPlugin.h"
#include "mixer_api.h"
#include "output/pulse_output_plugin.h"
#include "conf.h"
-#include "event_pipe.h"
+#include "GlobalEvents.hxx"
#include <glib.h>
@@ -66,7 +66,7 @@ pulse_mixer_offline(struct pulse_mixer *pm)
pm->online = false;
- event_pipe_emit(PIPE_EVENT_MIXER);
+ GlobalEvents::Emit(GlobalEvents::MIXER);
}
/**
@@ -77,7 +77,7 @@ static void
pulse_mixer_volume_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
int eol, void *userdata)
{
- struct pulse_mixer *pm = userdata;
+ struct pulse_mixer *pm = (struct pulse_mixer *)userdata;
if (eol)
return;
@@ -90,7 +90,7 @@ pulse_mixer_volume_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_inf
pm->online = true;
pm->volume = i->volume;
- event_pipe_emit(PIPE_EVENT_MIXER);
+ GlobalEvents::Emit(GlobalEvents::MIXER);
}
static void
@@ -153,16 +153,15 @@ static struct mixer *
pulse_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param,
GError **error_r)
{
- struct pulse_mixer *pm;
- struct pulse_output *po = ao;
+ struct pulse_output *po = (struct pulse_output *)ao;
if (ao == NULL) {
g_set_error(error_r, pulse_mixer_quark(), 0,
"The pulse mixer cannot work without the audio output");
- return false;
+ return nullptr;
}
- pm = g_new(struct pulse_mixer,1);
+ struct pulse_mixer *pm = g_new(struct pulse_mixer,1);
mixer_init(&pm->base, &pulse_mixer_plugin);
pm->online = false;
@@ -229,8 +228,11 @@ pulse_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
}
const struct mixer_plugin pulse_mixer_plugin = {
- .init = pulse_mixer_init,
- .finish = pulse_mixer_finish,
- .get_volume = pulse_mixer_get_volume,
- .set_volume = pulse_mixer_set_volume,
+ pulse_mixer_init,
+ pulse_mixer_finish,
+ nullptr,
+ nullptr,
+ pulse_mixer_get_volume,
+ pulse_mixer_set_volume,
+ false,
};
diff --git a/src/mixer/pulse_mixer_plugin.h b/src/mixer/PulseMixerPlugin.h
index 461633d37..f432c44a0 100644
--- a/src/mixer/pulse_mixer_plugin.h
+++ b/src/mixer/PulseMixerPlugin.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
@@ -26,6 +26,10 @@ struct pulse_mixer;
struct pa_context;
struct pa_stream;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void
pulse_mixer_on_connect(struct pulse_mixer *pm, struct pa_context *context);
@@ -36,4 +40,8 @@ void
pulse_mixer_on_change(struct pulse_mixer *pm,
struct pa_context *context, struct pa_stream *stream);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/mixer/winmm_mixer_plugin.c b/src/mixer/winmm_mixer_plugin.c
index ceddf6afd..99da60cce 100644
--- a/src/mixer/winmm_mixer_plugin.c
+++ b/src/mixer/winmm_mixer_plugin.c
@@ -22,6 +22,8 @@
#include "output_api.h"
#include "output/winmm_output_plugin.h"
+#include <mmsystem.h>
+
#include <assert.h>
#include <math.h>
#include <windows.h>
diff --git a/src/mixer_api.h b/src/mixer_api.h
index 29c1e00ca..f0c9a0937 100644
--- a/src/mixer_api.h
+++ b/src/mixer_api.h
@@ -46,7 +46,15 @@ struct mixer {
bool failed;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void
mixer_init(struct mixer *mixer, const struct mixer_plugin *plugin);
+#ifdef __cplusplus
+}
+#endif
+
#endif
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/notify.c b/src/notify.c
deleted file mode 100644
index 3c0112c91..000000000
--- a/src/notify.c
+++ /dev/null
@@ -1,58 +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 "notify.h"
-
-void notify_init(struct notify *notify)
-{
- notify->mutex = g_mutex_new();
- notify->cond = g_cond_new();
- notify->pending = false;
-}
-
-void notify_deinit(struct notify *notify)
-{
- g_mutex_free(notify->mutex);
- g_cond_free(notify->cond);
-}
-
-void notify_wait(struct notify *notify)
-{
- g_mutex_lock(notify->mutex);
- while (!notify->pending)
- g_cond_wait(notify->cond, notify->mutex);
- notify->pending = false;
- g_mutex_unlock(notify->mutex);
-}
-
-void notify_signal(struct notify *notify)
-{
- g_mutex_lock(notify->mutex);
- notify->pending = true;
- g_cond_signal(notify->cond);
- g_mutex_unlock(notify->mutex);
-}
-
-void notify_clear(struct notify *notify)
-{
- g_mutex_lock(notify->mutex);
- notify->pending = false;
- g_mutex_unlock(notify->mutex);
-}
diff --git a/src/state_file.h b/src/notify.cxx
index 4c4f881cc..64018968c 100644
--- a/src/state_file.h
+++ b/src/notify.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,17 +17,29 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_STATE_FILE_H
-#define MPD_STATE_FILE_H
-
-struct player_control;
+#include "config.h"
+#include "notify.hxx"
void
-state_file_init(const char *path, struct player_control *pc);
+notify::Wait()
+{
+ const ScopeLock protect(mutex);
+ while (!pending)
+ cond.wait(mutex);
+ pending = false;
+}
void
-state_file_finish(struct player_control *pc);
-
-void write_state_file(void);
+notify::Signal()
+{
+ const ScopeLock protect(mutex);
+ pending = true;
+ cond.signal();
+}
-#endif /* STATE_FILE_H */
+void
+notify::Clear()
+{
+ const ScopeLock protect(mutex);
+ pending = false;
+}
diff --git a/src/notify.hxx b/src/notify.hxx
new file mode 100644
index 000000000..6b9e95368
--- /dev/null
+++ b/src/notify.hxx
@@ -0,0 +1,53 @@
+/*
+ * 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_NOTIFY_HXX
+#define MPD_NOTIFY_HXX
+
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+
+struct notify {
+ Mutex mutex;
+ Cond cond;
+ bool pending;
+
+#ifndef WIN32
+ constexpr
+#endif
+ notify():pending(false) {}
+
+ /**
+ * Wait for a notification. Return immediately if we have already
+ * been notified since we last returned from notify_wait().
+ */
+ void Wait();
+
+ /**
+ * Notify the thread. This function never blocks.
+ */
+ void Signal();
+
+ /**
+ * Clears a pending notification.
+ */
+ void Clear();
+};
+
+#endif
diff --git a/src/output/HttpdClient.cxx b/src/output/HttpdClient.cxx
new file mode 100644
index 000000000..33aad1919
--- /dev/null
+++ b/src/output/HttpdClient.cxx
@@ -0,0 +1,592 @@
+/*
+ * 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 "HttpdClient.hxx"
+#include "HttpdInternal.hxx"
+#include "util/fifo_buffer.h"
+#include "page.h"
+#include "icy_server.h"
+#include "glib_socket.h"
+
+#include <assert.h>
+#include <string.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "httpd_output"
+
+HttpdClient::~HttpdClient()
+{
+ if (state == RESPONSE) {
+ if (write_source_id != 0)
+ g_source_remove(write_source_id);
+
+ if (current_page != nullptr)
+ page_unref(current_page);
+
+ for (auto page : pages)
+ page_unref(page);
+ } else
+ fifo_buffer_free(input);
+
+ if (metadata)
+ page_unref(metadata);
+
+ g_source_remove(read_source_id);
+ g_io_channel_unref(channel);
+}
+
+void
+HttpdClient::Close()
+{
+ httpd_output_remove_client(httpd, this);
+}
+
+void
+HttpdClient::LockClose()
+{
+ const ScopeLock protect(httpd->mutex);
+ Close();
+}
+
+void
+HttpdClient::BeginResponse()
+{
+ assert(state != RESPONSE);
+
+ state = RESPONSE;
+ write_source_id = 0;
+ current_page = nullptr;
+
+ httpd_output_send_header(httpd, this);
+}
+
+/**
+ * Handle a line of the HTTP request.
+ */
+bool
+HttpdClient::HandleLine(const char *line)
+{
+ assert(state != RESPONSE);
+
+ if (state == REQUEST) {
+ if (strncmp(line, "GET /", 5) != 0) {
+ /* only GET is supported */
+ g_warning("malformed request line from client");
+ return false;
+ }
+
+ line = strchr(line + 5, ' ');
+ if (line == nullptr || strncmp(line + 1, "HTTP/", 5) != 0) {
+ /* HTTP/0.9 without request headers */
+ BeginResponse();
+ return true;
+ }
+
+ /* after the request line, request headers follow */
+ state = HEADERS;
+ return true;
+ } else {
+ if (*line == 0) {
+ /* empty line: request is finished */
+ BeginResponse();
+ return true;
+ }
+
+ if (g_ascii_strncasecmp(line, "Icy-MetaData: 1", 15) == 0) {
+ /* Send icy metadata */
+ metadata_requested = metadata_supported;
+ return true;
+ }
+
+ if (g_ascii_strncasecmp(line, "transferMode.dlna.org: Streaming", 32) == 0) {
+ /* Send as dlna */
+ dlna_streaming_requested = true;
+ /* metadata is not supported by dlna streaming, so disable it */
+ metadata_supported = false;
+ metadata_requested = false;
+ return true;
+ }
+
+ /* expect more request headers */
+ return true;
+ }
+}
+
+char *
+HttpdClient::ReadLine()
+{
+ assert(state != RESPONSE);
+
+ const ScopeLock protect(httpd->mutex);
+
+ size_t length;
+ const char *p = (const char *)fifo_buffer_read(input, &length);
+ if (p == nullptr)
+ /* empty input buffer */
+ return nullptr;
+
+ const char *newline = (const char *)memchr(p, '\n', length);
+ if (newline == nullptr)
+ /* incomplete line */
+ return nullptr;
+
+ char *line = g_strndup(p, newline - p);
+ fifo_buffer_consume(input, newline - p + 1);
+
+ /* remove trailing whitespace (e.g. '\r') */
+ return g_strchomp(line);
+}
+
+/**
+ * Sends the status line and response headers to the client.
+ */
+bool
+HttpdClient::SendResponse()
+{
+ char buffer[1024];
+ GError *error = nullptr;
+ GIOStatus status;
+ gsize bytes_written;
+
+ assert(state == RESPONSE);
+
+ if (dlna_streaming_requested) {
+ g_snprintf(buffer, sizeof(buffer),
+ "HTTP/1.1 206 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: 10000\r\n"
+ "Content-RangeX: 0-1000000/1000000\r\n"
+ "transferMode.dlna.org: Streaming\r\n"
+ "Accept-Ranges: bytes\r\n"
+ "Connection: close\r\n"
+ "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
+ "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
+ "\r\n",
+ httpd->content_type);
+
+ } else if (metadata_requested) {
+ gchar *metadata_header;
+
+ metadata_header =
+ icy_server_metadata_header(httpd->name, httpd->genre,
+ httpd->website,
+ httpd->content_type,
+ metaint);
+
+ g_strlcpy(buffer, metadata_header, sizeof(buffer));
+
+ g_free(metadata_header);
+
+ } else { /* revert to a normal HTTP request */
+ g_snprintf(buffer, sizeof(buffer),
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Connection: close\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache, no-store\r\n"
+ "\r\n",
+ httpd->content_type);
+ }
+
+ status = g_io_channel_write_chars(channel,
+ buffer, strlen(buffer),
+ &bytes_written, &error);
+
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ case G_IO_STATUS_AGAIN:
+ return true;
+
+ case G_IO_STATUS_EOF:
+ /* client has disconnected */
+
+ Close();
+ return false;
+
+ case G_IO_STATUS_ERROR:
+ /* I/O error */
+
+ g_warning("failed to write to client: %s", error->message);
+ g_error_free(error);
+
+ Close();
+ return false;
+ }
+
+ /* unreachable */
+ Close();
+ return false;
+}
+
+bool
+HttpdClient::Received()
+{
+ assert(state != RESPONSE);
+
+ char *line;
+ bool success;
+
+ while ((line = ReadLine()) != nullptr) {
+ success = HandleLine(line);
+ g_free(line);
+ if (!success) {
+ assert(state != RESPONSE);
+ return false;
+ }
+
+ if (state == RESPONSE) {
+ if (!fifo_buffer_is_empty(input)) {
+ g_warning("unexpected input from client");
+ return false;
+ }
+
+ fifo_buffer_free(input);
+
+ return SendResponse();
+ }
+ }
+
+ return true;
+}
+
+bool
+HttpdClient::Read()
+{
+ size_t max_length;
+ GError *error = nullptr;
+ GIOStatus status;
+ gsize bytes_read;
+
+ if (state == RESPONSE) {
+ /* the client has already sent the request, and he
+ must not send more */
+ char buffer[1];
+
+ status = g_io_channel_read_chars(channel, buffer,
+ sizeof(buffer), &bytes_read,
+ nullptr);
+ if (status == G_IO_STATUS_NORMAL)
+ g_warning("unexpected input from client");
+
+ return false;
+ }
+
+ char *p = (char *)fifo_buffer_write(input, &max_length);
+ if (p == nullptr) {
+ g_warning("buffer overflow");
+ return false;
+ }
+
+ status = g_io_channel_read_chars(channel, p, max_length,
+ &bytes_read, &error);
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ fifo_buffer_append(input, bytes_read);
+ return Received();
+
+ case G_IO_STATUS_AGAIN:
+ /* try again later, after select() */
+ return true;
+
+ case G_IO_STATUS_EOF:
+ /* peer disconnected */
+ return false;
+
+ case G_IO_STATUS_ERROR:
+ /* I/O error */
+ g_warning("failed to read from client: %s",
+ error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ /* unreachable */
+ return false;
+}
+
+static gboolean
+httpd_client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ HttpdClient *client = (HttpdClient *)data;
+
+ if (condition == G_IO_IN && client->Read()) {
+ return true;
+ } else {
+ client->LockClose();
+ return false;
+ }
+}
+
+HttpdClient::HttpdClient(httpd_output *_httpd, int _fd,
+ bool _metadata_supported)
+ :httpd(_httpd),
+ channel(g_io_channel_new_socket(_fd)),
+ input(fifo_buffer_new(4096)),
+ state(REQUEST),
+ dlna_streaming_requested(false),
+ metadata_supported(_metadata_supported),
+ metadata_requested(false), metadata_sent(true),
+ metaint(8192), /*TODO: just a std value */
+ metadata(nullptr),
+ metadata_current_position(0), metadata_fill(0)
+{
+ /* 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 */
+ g_io_channel_set_encoding(channel, nullptr, nullptr);
+ /* we prefer to do buffering */
+ g_io_channel_set_buffered(channel, false);
+
+ read_source_id = g_io_add_watch(channel,
+ GIOCondition(G_IO_IN|G_IO_ERR|G_IO_HUP),
+ httpd_client_in_event, this);
+}
+
+size_t
+HttpdClient::GetQueueSize() const
+{
+ if (state != RESPONSE)
+ return 0;
+
+ size_t size = 0;
+ for (auto page : pages)
+ size += page->size;
+ return size;
+}
+
+void
+HttpdClient::CancelQueue()
+{
+ if (state != RESPONSE)
+ return;
+
+ for (auto page : pages)
+ page_unref(page);
+ pages.clear();
+
+ if (write_source_id != 0 && current_page == nullptr) {
+ g_source_remove(write_source_id);
+ write_source_id = 0;
+ }
+}
+
+static GIOStatus
+write_page_to_channel(GIOChannel *channel,
+ const struct page *page, size_t position,
+ gsize *bytes_written_r, GError **error)
+{
+ assert(channel != nullptr);
+ assert(page != nullptr);
+ assert(position < page->size);
+
+ return g_io_channel_write_chars(channel,
+ (const gchar*)page->data + position,
+ page->size - position,
+ bytes_written_r, error);
+}
+
+static GIOStatus
+write_n_bytes_to_channel(GIOChannel *channel, const struct page *page,
+ size_t position, gint n,
+ gsize *bytes_written_r, GError **error)
+{
+ GIOStatus status;
+
+ assert(channel != nullptr);
+ assert(page != nullptr);
+ assert(position < page->size);
+
+ if (n == -1) {
+ status = write_page_to_channel (channel, page, position,
+ bytes_written_r, error);
+ } else {
+ status = g_io_channel_write_chars(channel,
+ (const gchar*)page->data + position,
+ n, bytes_written_r, error);
+ }
+
+ return status;
+}
+
+int
+HttpdClient::GetBytesTillMetaData() const
+{
+ if (metadata_requested &&
+ current_page->size - current_position > metaint - metadata_fill)
+ return metaint - metadata_fill;
+
+ return -1;
+}
+
+inline bool
+HttpdClient::Write()
+{
+ GError *error = nullptr;
+ GIOStatus status;
+ gsize bytes_written;
+
+ const ScopeLock protect(httpd->mutex);
+
+ assert(state == RESPONSE);
+
+ if (write_source_id == 0)
+ /* another thread has removed the event source while
+ this thread was waiting for httpd->mutex */
+ return false;
+
+ if (current_page == nullptr) {
+ current_page = pages.front();
+ pages.pop_front();
+ current_position = 0;
+ }
+
+ const gint bytes_to_write = GetBytesTillMetaData();
+ if (bytes_to_write == 0) {
+ gint metadata_to_write;
+
+ metadata_to_write = metadata_current_position;
+
+ if (!metadata_sent) {
+ status = write_page_to_channel(channel,
+ metadata,
+ metadata_to_write,
+ &bytes_written, &error);
+
+ metadata_current_position += bytes_written;
+
+ if (metadata->size - metadata_current_position == 0) {
+ metadata_fill = 0;
+ metadata_current_position = 0;
+ metadata_sent = true;
+ }
+ } else {
+ struct page *empty_meta;
+ guchar empty_data = 0;
+
+ empty_meta = page_new_copy(&empty_data, 1);
+
+ status = write_page_to_channel(channel,
+ empty_meta,
+ metadata_to_write,
+ &bytes_written, &error);
+
+ metadata_current_position += bytes_written;
+
+ if (empty_meta->size - metadata_current_position == 0) {
+ metadata_fill = 0;
+ metadata_current_position = 0;
+ }
+ }
+
+ bytes_written = 0;
+ } else {
+ status = write_n_bytes_to_channel(channel, current_page,
+ current_position, bytes_to_write,
+ &bytes_written, &error);
+ }
+
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ current_position += bytes_written;
+ assert(current_position <= current_page->size);
+
+ if (metadata_requested)
+ metadata_fill += bytes_written;
+
+ if (current_position >= current_page->size) {
+ page_unref(current_page);
+ current_page = nullptr;
+
+ if (pages.empty()) {
+ /* all pages are sent: remove the
+ event source */
+ write_source_id = 0;
+
+ return false;
+ }
+ }
+
+ return true;
+
+ case G_IO_STATUS_AGAIN:
+ return true;
+
+ case G_IO_STATUS_EOF:
+ /* client has disconnected */
+
+ Close();
+ return false;
+
+ case G_IO_STATUS_ERROR:
+ /* I/O error */
+
+ g_warning("failed to write to client: %s", error->message);
+ g_error_free(error);
+
+ Close();
+ return false;
+ }
+
+ /* unreachable */
+ Close();
+ return false;
+}
+
+static gboolean
+httpd_client_out_event(gcc_unused GIOChannel *source,
+ gcc_unused GIOCondition condition, gpointer data)
+{
+ assert(condition == G_IO_OUT);
+
+ HttpdClient *client = (HttpdClient *)data;
+ return client->Write();
+}
+
+void
+HttpdClient::PushPage(struct page *page)
+{
+ if (state != RESPONSE)
+ /* the client is still writing the HTTP request */
+ return;
+
+ page_ref(page);
+ pages.push_back(page);
+
+ if (write_source_id == 0)
+ write_source_id = g_io_add_watch(channel, G_IO_OUT,
+ httpd_client_out_event,
+ this);
+}
+
+void
+HttpdClient::PushMetaData(struct page *page)
+{
+ if (metadata) {
+ page_unref(metadata);
+ metadata = nullptr;
+ }
+
+ g_return_if_fail (page);
+
+ page_ref(page);
+ metadata = page;
+ metadata_sent = false;
+}
diff --git a/src/output/HttpdClient.hxx b/src/output/HttpdClient.hxx
new file mode 100644
index 000000000..d3ed2746d
--- /dev/null
+++ b/src/output/HttpdClient.hxx
@@ -0,0 +1,215 @@
+/*
+ * 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_HTTPD_CLIENT_HXX
+#define MPD_OUTPUT_HTTPD_CLIENT_HXX
+
+#include "gcc.h"
+
+#include <glib.h>
+
+#include <list>
+
+#include <stddef.h>
+
+struct httpd_output;
+struct page;
+
+class HttpdClient final {
+ /**
+ * The httpd output object this client is connected to.
+ */
+ httpd_output *const httpd;
+
+ /**
+ * The TCP socket.
+ */
+ GIOChannel *channel;
+
+ /**
+ * The GLib main loop source id for reading from the socket,
+ * and to detect errors.
+ */
+ guint read_source_id;
+
+ /**
+ * The GLib main loop source id for writing to the socket. If
+ * 0, then there is no event source currently (because there
+ * are no queued pages).
+ */
+ guint write_source_id;
+
+ /**
+ * For buffered reading. This pointer is only valid while the
+ * HTTP request is read.
+ */
+ struct fifo_buffer *input;
+
+ /**
+ * The current state of the client.
+ */
+ enum {
+ /** reading the request line */
+ REQUEST,
+
+ /** reading the request headers */
+ HEADERS,
+
+ /** sending the HTTP response */
+ RESPONSE,
+ } state;
+
+ /**
+ * A queue of #page objects to be sent to the client.
+ */
+ std::list<page *> pages;
+
+ /**
+ * The #page which is currently being sent to the client.
+ */
+ page *current_page;
+
+ /**
+ * The amount of bytes which were already sent from
+ * #current_page.
+ */
+ size_t current_position;
+
+ /**
+ * If DLNA streaming was an option.
+ */
+ bool dlna_streaming_requested;
+
+ /* ICY */
+
+ /**
+ * Do we support sending Icy-Metadata to the client? This is
+ * disabled if the httpd audio output uses encoder tags.
+ */
+ bool metadata_supported;
+
+ /**
+ * If we should sent icy metadata.
+ */
+ bool metadata_requested;
+
+ /**
+ * If the current metadata was already sent to the client.
+ */
+ bool metadata_sent;
+
+ /**
+ * The amount of streaming data between each metadata block
+ */
+ guint metaint;
+
+ /**
+ * The metadata as #page which is currently being sent to the client.
+ */
+ page *metadata;
+
+ /*
+ * The amount of bytes which were already sent from the metadata.
+ */
+ size_t metadata_current_position;
+
+ /**
+ * The amount of streaming data sent to the client
+ * since the last icy information was sent.
+ */
+ guint metadata_fill;
+
+public:
+ /**
+ * @param httpd the HTTP output device
+ * @param fd the socket file descriptor
+ */
+ HttpdClient(httpd_output *httpd, int _fd, bool _metadata_supported);
+
+ /**
+ * Note: this does not remove the client from the
+ * #httpd_output object.
+ */
+ ~HttpdClient();
+
+ /**
+ * Frees the client and removes it from the server's client list.
+ */
+ void Close();
+
+ void LockClose();
+
+ /**
+ * Returns the total size of this client's page queue.
+ */
+ gcc_pure
+ size_t GetQueueSize() const;
+
+ /**
+ * Clears the page queue.
+ */
+ void CancelQueue();
+
+ bool Read();
+
+ /**
+ * Data has been received from the client and it is appended
+ * to the input buffer.
+ */
+ bool Received();
+
+ /**
+ * Check if a complete line of input is present in the input
+ * buffer, and duplicates it. It is removed from the input
+ * buffer. The return value has to be freed with g_free().
+ */
+ char *ReadLine();
+
+ /**
+ * Handle a line of the HTTP request.
+ */
+ bool HandleLine(const char *line);
+
+ /**
+ * Switch the client to the "RESPONSE" state.
+ */
+ void BeginResponse();
+
+ /**
+ * Sends the status line and response headers to the client.
+ */
+ bool SendResponse();
+
+ gcc_pure
+ int GetBytesTillMetaData() const;
+
+ bool Write();
+
+ /**
+ * Appends a page to the client's queue.
+ */
+ void PushPage(page *page);
+
+ /**
+ * Sends the passed metadata.
+ */
+ void PushMetaData(page *page);
+};
+
+#endif
diff --git a/src/output/httpd_internal.h b/src/output/HttpdInternal.hxx
index 5dcb8ab9b..601c43162 100644
--- a/src/output/httpd_internal.h
+++ b/src/output/HttpdInternal.hxx
@@ -25,14 +25,18 @@
#ifndef MPD_OUTPUT_HTTPD_INTERNAL_H
#define MPD_OUTPUT_HTTPD_INTERNAL_H
+#include "HttpdClient.hxx"
#include "output_internal.h"
#include "timer.h"
+#include "thread/Mutex.hxx"
#include <glib.h>
+#include <forward_list>
+
#include <stdbool.h>
-struct httpd_client;
+class HttpdClient;
struct httpd_output {
struct audio_output base;
@@ -65,7 +69,7 @@ struct httpd_output {
* This mutex protects the listener socket and the client
* list.
*/
- GMutex *mutex;
+ mutable Mutex mutex;
/**
* A #timer object to synchronize this output with the
@@ -105,7 +109,7 @@ struct httpd_output {
* A linked list containing all clients which are currently
* connected.
*/
- GList *clients;
+ std::forward_list<HttpdClient> clients;
/**
* A temporary buffer for the httpd_output_read_page()
@@ -125,7 +129,7 @@ struct httpd_output {
*/
void
httpd_output_remove_client(struct httpd_output *httpd,
- struct httpd_client *client);
+ HttpdClient *client);
/**
* Sends the encoder header to the client. This is called right after
@@ -133,6 +137,6 @@ httpd_output_remove_client(struct httpd_output *httpd,
*/
void
httpd_output_send_header(struct httpd_output *httpd,
- struct httpd_client *client);
+ HttpdClient *client);
#endif
diff --git a/src/output/httpd_output_plugin.c b/src/output/HttpdOutputPlugin.cxx
index 1d730df7f..d1a1a36df 100644
--- a/src/output/httpd_output_plugin.c
+++ b/src/output/HttpdOutputPlugin.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,9 @@
*/
#include "config.h"
-#include "httpd_output_plugin.h"
-#include "httpd_internal.h"
-#include "httpd_client.h"
+#include "HttpdOutputPlugin.hxx"
+#include "HttpdInternal.hxx"
+#include "HttpdClient.hxx"
#include "output_api.h"
#include "encoder_plugin.h"
#include "encoder_list.h"
@@ -28,7 +28,8 @@
#include "page.h"
#include "icy_server.h"
#include "fd_util.h"
-#include "server_socket.h"
+#include "ServerSocket.hxx"
+#include "Main.hxx"
#include <assert.h>
@@ -60,9 +61,9 @@ httpd_output_quark(void)
*/
G_GNUC_PURE
static bool
-httpd_output_has_clients(const struct httpd_output *httpd)
+httpd_output_has_clients(const httpd_output *httpd)
{
- return httpd->clients != NULL;
+ return !httpd->clients.empty();
}
/**
@@ -70,12 +71,10 @@ httpd_output_has_clients(const struct httpd_output *httpd)
*/
G_GNUC_PURE
static bool
-httpd_output_lock_has_clients(const struct httpd_output *httpd)
+httpd_output_lock_has_clients(const httpd_output *httpd)
{
- g_mutex_lock(httpd->mutex);
- bool result = httpd_output_has_clients(httpd);
- g_mutex_unlock(httpd->mutex);
- return result;
+ const ScopeLock protect(httpd->mutex);
+ return httpd_output_has_clients(httpd);
}
static void
@@ -83,32 +82,28 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
size_t address_length, int uid, void *ctx);
static bool
-httpd_output_bind(struct httpd_output *httpd, GError **error_r)
+httpd_output_bind(httpd_output *httpd, GError **error_r)
{
httpd->open = false;
- g_mutex_lock(httpd->mutex);
- bool success = server_socket_open(httpd->server_socket, error_r);
- g_mutex_unlock(httpd->mutex);
-
- return success;
+ const ScopeLock protect(httpd->mutex);
+ return server_socket_open(httpd->server_socket, error_r);
}
static void
-httpd_output_unbind(struct httpd_output *httpd)
+httpd_output_unbind(httpd_output *httpd)
{
assert(!httpd->open);
- g_mutex_lock(httpd->mutex);
+ const ScopeLock protect(httpd->mutex);
server_socket_close(httpd->server_socket);
- g_mutex_unlock(httpd->mutex);
}
static struct audio_output *
httpd_output_init(const struct config_param *param,
GError **error)
{
- struct httpd_output *httpd = g_new(struct httpd_output, 1);
+ httpd_output *httpd = new httpd_output();
if (!ao_base_init(&httpd->base, &httpd_output_plugin, param, error)) {
g_free(httpd);
return NULL;
@@ -140,7 +135,8 @@ httpd_output_init(const struct config_param *param,
/* set up bind_to_address */
- httpd->server_socket = server_socket_new(httpd_listen_in_event, httpd);
+ httpd->server_socket = server_socket_new(*main_loop,
+ httpd_listen_in_event, httpd);
const char *bind_to_address =
config_get_block_string(param, "bind_to_address", NULL);
@@ -174,50 +170,44 @@ httpd_output_init(const struct config_param *param,
httpd->content_type = "application/octet-stream";
}
- httpd->mutex = g_mutex_new();
-
return &httpd->base;
}
static void
httpd_output_finish(struct audio_output *ao)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
if (httpd->metadata)
page_unref(httpd->metadata);
encoder_finish(httpd->encoder);
server_socket_free(httpd->server_socket);
- g_mutex_free(httpd->mutex);
ao_base_finish(&httpd->base);
- g_free(httpd);
+ delete httpd;
}
/**
- * Creates a new #httpd_client object and adds it into the
+ * Creates a new #HttpdClient object and adds it into the
* httpd_output.clients linked list.
*/
static void
-httpd_client_add(struct httpd_output *httpd, int fd)
+httpd_client_add(httpd_output *httpd, int fd)
{
- struct httpd_client *client =
- httpd_client_new(httpd, fd,
- httpd->encoder->plugin->tag == NULL);
-
- httpd->clients = g_list_prepend(httpd->clients, client);
+ httpd->clients.emplace_front(httpd, fd,
+ httpd->encoder->plugin->tag == NULL);
httpd->clients_cnt++;
/* pass metadata to client */
if (httpd->metadata)
- httpd_client_send_metadata(client, httpd->metadata);
+ httpd->clients.front().PushMetaData(httpd->metadata);
}
static void
httpd_listen_in_event(int fd, const struct sockaddr *address,
size_t address_length, G_GNUC_UNUSED int uid, void *ctx)
{
- struct httpd_output *httpd = ctx;
+ httpd_output *httpd = (httpd_output *)ctx;
/* the listener socket has become readable - a client has
connected */
@@ -238,7 +228,6 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
progname, hostaddr);
g_free(hostaddr);
close_socket(fd);
- g_mutex_unlock(httpd->mutex);
return;
}
@@ -249,7 +238,7 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
(void)address_length;
#endif /* HAVE_WRAP */
- g_mutex_lock(httpd->mutex);
+ const ScopeLock protect(httpd->mutex);
if (fd >= 0) {
/* can we allow additional client */
@@ -262,8 +251,6 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
} else if (fd < 0 && errno != EINTR) {
g_warning("accept() failed: %s", g_strerror(errno));
}
-
- g_mutex_unlock(httpd->mutex);
}
/**
@@ -271,7 +258,7 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
* as a new #page object.
*/
static struct page *
-httpd_output_read_page(struct httpd_output *httpd)
+httpd_output_read_page(httpd_output *httpd)
{
if (httpd->unflushed_input >= 65536) {
/* we have fed a lot of input into the encoder, but it
@@ -301,7 +288,7 @@ httpd_output_read_page(struct httpd_output *httpd)
}
static bool
-httpd_output_encoder_open(struct httpd_output *httpd,
+httpd_output_encoder_open(httpd_output *httpd,
struct audio_format *audio_format,
GError **error)
{
@@ -321,7 +308,7 @@ httpd_output_encoder_open(struct httpd_output *httpd,
static bool
httpd_output_enable(struct audio_output *ao, GError **error_r)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
return httpd_output_bind(httpd, error_r);
}
@@ -329,7 +316,7 @@ httpd_output_enable(struct audio_output *ao, GError **error_r)
static void
httpd_output_disable(struct audio_output *ao)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
httpd_output_unbind(httpd);
}
@@ -338,82 +325,75 @@ static bool
httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
GError **error)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
+
+ assert(httpd->clients.empty());
- g_mutex_lock(httpd->mutex);
+ const ScopeLock protect(httpd->mutex);
/* open the encoder */
- if (!httpd_output_encoder_open(httpd, audio_format, error)) {
- g_mutex_unlock(httpd->mutex);
+ if (!httpd_output_encoder_open(httpd, audio_format, error))
return false;
- }
/* initialize other attributes */
- httpd->clients = NULL;
httpd->clients_cnt = 0;
httpd->timer = timer_new(audio_format);
httpd->open = true;
- g_mutex_unlock(httpd->mutex);
return true;
}
static void
-httpd_client_delete(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- httpd_client_free(client);
-}
-
-static void
httpd_output_close(struct audio_output *ao)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
- g_mutex_lock(httpd->mutex);
+ const ScopeLock protect(httpd->mutex);
httpd->open = false;
timer_free(httpd->timer);
- g_list_foreach(httpd->clients, httpd_client_delete, NULL);
- g_list_free(httpd->clients);
+ httpd->clients.clear();
if (httpd->header != NULL)
page_unref(httpd->header);
encoder_close(httpd->encoder);
-
- g_mutex_unlock(httpd->mutex);
}
void
-httpd_output_remove_client(struct httpd_output *httpd,
- struct httpd_client *client)
+httpd_output_remove_client(httpd_output *httpd, HttpdClient *client)
{
assert(httpd != NULL);
+ assert(httpd->clients_cnt > 0);
assert(client != NULL);
- httpd->clients = g_list_remove(httpd->clients, client);
- httpd->clients_cnt--;
+ for (auto prev = httpd->clients.before_begin(), i = std::next(prev);;
+ prev = i, i = std::next(prev)) {
+ assert(i != httpd->clients.end());
+ if (&*i == client) {
+ httpd->clients.erase_after(prev);
+ httpd->clients_cnt--;
+ break;
+ }
+ }
}
void
-httpd_output_send_header(struct httpd_output *httpd,
- struct httpd_client *client)
+httpd_output_send_header(httpd_output *httpd, HttpdClient *client)
{
if (httpd->header != NULL)
- httpd_client_send(client, httpd->header);
+ client->PushPage(httpd->header);
}
static unsigned
httpd_output_delay(struct audio_output *ao)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
if (!httpd_output_lock_has_clients(httpd) && httpd->base.pause) {
/* if there's no client and this output is paused,
@@ -433,51 +413,35 @@ httpd_output_delay(struct audio_output *ao)
: 0;
}
-static void
-httpd_client_check_queue(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- if (httpd_client_queue_size(client) > 256 * 1024) {
- g_debug("client is too slow, flushing its queue");
- httpd_client_cancel(client);
- }
-}
-
-static void
-httpd_client_send_page(gpointer data, gpointer user_data)
-{
- struct httpd_client *client = data;
- struct page *page = user_data;
-
- httpd_client_send(client, page);
-}
-
/**
* Broadcasts a page struct to all clients.
*/
static void
-httpd_output_broadcast_page(struct httpd_output *httpd, struct page *page)
+httpd_output_broadcast_page(httpd_output *httpd, struct page *page)
{
assert(page != NULL);
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_send_page, page);
- g_mutex_unlock(httpd->mutex);
+ const ScopeLock protect(httpd->mutex);
+ for (auto &client : httpd->clients)
+ client.PushPage(page);
}
/**
* Broadcasts data from the encoder to all clients.
*/
static void
-httpd_output_encoder_to_clients(struct httpd_output *httpd)
+httpd_output_encoder_to_clients(httpd_output *httpd)
{
- struct page *page;
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_check_queue, NULL);
- g_mutex_unlock(httpd->mutex);
+ httpd->mutex.lock();
+ for (auto &client : httpd->clients) {
+ if (client.GetQueueSize() > 256 * 1024) {
+ g_debug("client is too slow, flushing its queue");
+ client.CancelQueue();
+ }
+ }
+ httpd->mutex.unlock();
+ struct page *page;
while ((page = httpd_output_read_page(httpd)) != NULL) {
httpd_output_broadcast_page(httpd, page);
page_unref(page);
@@ -485,7 +449,7 @@ httpd_output_encoder_to_clients(struct httpd_output *httpd)
}
static bool
-httpd_output_encode_and_play(struct httpd_output *httpd,
+httpd_output_encode_and_play(httpd_output *httpd,
const void *chunk, size_t size, GError **error)
{
if (!encoder_write(httpd->encoder, chunk, size, error))
@@ -502,7 +466,7 @@ static size_t
httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
GError **error_r)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
if (httpd_output_lock_has_clients(httpd)) {
if (!httpd_output_encode_and_play(httpd, chunk, size, error_r))
@@ -519,10 +483,10 @@ httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
static bool
httpd_output_pause(struct audio_output *ao)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
if (httpd_output_lock_has_clients(httpd)) {
- static const char silence[1020];
+ static const char silence[1020] = { 0 };
return httpd_output_play(ao, silence, sizeof(silence),
NULL) > 0;
} else {
@@ -531,18 +495,9 @@ httpd_output_pause(struct audio_output *ao)
}
static void
-httpd_send_metadata(gpointer data, gpointer user_data)
-{
- struct httpd_client *client = data;
- struct page *icy_metadata = user_data;
-
- httpd_client_send_metadata(client, icy_metadata);
-}
-
-static void
httpd_output_tag(struct audio_output *ao, const struct tag *tag)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
assert(tag != NULL);
@@ -581,43 +536,37 @@ httpd_output_tag(struct audio_output *ao, const struct tag *tag)
TAG_ARTIST, TAG_TITLE,
TAG_NUM_OF_ITEM_TYPES);
if (httpd->metadata != NULL) {
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients,
- httpd_send_metadata, httpd->metadata);
- g_mutex_unlock(httpd->mutex);
+ const ScopeLock protect(httpd->mutex);
+ for (auto &client : httpd->clients)
+ client.PushMetaData(httpd->metadata);
}
}
}
static void
-httpd_client_cancel_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- httpd_client_cancel(client);
-}
-
-static void
httpd_output_cancel(struct audio_output *ao)
{
- struct httpd_output *httpd = (struct httpd_output *)ao;
+ httpd_output *httpd = (httpd_output *)ao;
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_cancel_callback, NULL);
- g_mutex_unlock(httpd->mutex);
+ const ScopeLock protect(httpd->mutex);
+ for (auto &client : httpd->clients)
+ client.CancelQueue();
}
const struct audio_output_plugin httpd_output_plugin = {
- .name = "httpd",
- .init = httpd_output_init,
- .finish = httpd_output_finish,
- .enable = httpd_output_enable,
- .disable = httpd_output_disable,
- .open = httpd_output_open,
- .close = httpd_output_close,
- .delay = httpd_output_delay,
- .send_tag = httpd_output_tag,
- .play = httpd_output_play,
- .pause = httpd_output_pause,
- .cancel = httpd_output_cancel,
+ "httpd",
+ nullptr,
+ httpd_output_init,
+ httpd_output_finish,
+ httpd_output_enable,
+ httpd_output_disable,
+ httpd_output_open,
+ httpd_output_close,
+ httpd_output_delay,
+ httpd_output_tag,
+ httpd_output_play,
+ nullptr,
+ httpd_output_cancel,
+ httpd_output_pause,
+ nullptr,
};
diff --git a/src/output/httpd_output_plugin.h b/src/output/HttpdOutputPlugin.hxx
index d0eb1533f..c74d2bd4a 100644
--- a/src/output/httpd_output_plugin.h
+++ b/src/output/HttpdOutputPlugin.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_HTTPD_OUTPUT_PLUGIN_H
-#define MPD_HTTPD_OUTPUT_PLUGIN_H
+#ifndef MPD_HTTPD_OUTPUT_PLUGIN_HXX
+#define MPD_HTTPD_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin httpd_output_plugin;
diff --git a/src/output/fifo_output_plugin.c b/src/output/fifo_output_plugin.c
index 022be0b4a..3d6171ae2 100644
--- a/src/output/fifo_output_plugin.c
+++ b/src/output/fifo_output_plugin.c
@@ -20,7 +20,6 @@
#include "config.h"
#include "fifo_output_plugin.h"
#include "output_api.h"
-#include "utils.h"
#include "timer.h"
#include "fd_util.h"
#include "open.h"
diff --git a/src/output/httpd_client.c b/src/output/httpd_client.c
deleted file mode 100644
index 72de90457..000000000
--- a/src/output/httpd_client.c
+++ /dev/null
@@ -1,764 +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 "httpd_client.h"
-#include "httpd_internal.h"
-#include "fifo_buffer.h"
-#include "page.h"
-#include "icy_server.h"
-#include "glib_socket.h"
-
-#include <stdbool.h>
-#include <assert.h>
-#include <string.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "httpd_output"
-
-struct httpd_client {
- /**
- * The httpd output object this client is connected to.
- */
- struct httpd_output *httpd;
-
- /**
- * The TCP socket.
- */
- GIOChannel *channel;
-
- /**
- * The GLib main loop source id for reading from the socket,
- * and to detect errors.
- */
- guint read_source_id;
-
- /**
- * The GLib main loop source id for writing to the socket. If
- * 0, then there is no event source currently (because there
- * are no queued pages).
- */
- guint write_source_id;
-
- /**
- * For buffered reading. This pointer is only valid while the
- * HTTP request is read.
- */
- struct fifo_buffer *input;
-
- /**
- * The current state of the client.
- */
- enum {
- /** reading the request line */
- REQUEST,
-
- /** reading the request headers */
- HEADERS,
-
- /** sending the HTTP response */
- RESPONSE,
- } state;
-
- /**
- * A queue of #page objects to be sent to the client.
- */
- GQueue *pages;
-
- /**
- * The #page which is currently being sent to the client.
- */
- struct page *current_page;
-
- /**
- * The amount of bytes which were already sent from
- * #current_page.
- */
- size_t current_position;
-
- /**
- * If DLNA streaming was an option.
- */
- bool dlna_streaming_requested;
-
- /* ICY */
-
- /**
- * Do we support sending Icy-Metadata to the client? This is
- * disabled if the httpd audio output uses encoder tags.
- */
- bool metadata_supported;
-
- /**
- * If we should sent icy metadata.
- */
- bool metadata_requested;
-
- /**
- * If the current metadata was already sent to the client.
- */
- bool metadata_sent;
-
- /**
- * The amount of streaming data between each metadata block
- */
- guint metaint;
-
- /**
- * The metadata as #page which is currently being sent to the client.
- */
- struct page *metadata;
-
- /*
- * The amount of bytes which were already sent from the metadata.
- */
- size_t metadata_current_position;
-
- /**
- * The amount of streaming data sent to the client
- * since the last icy information was sent.
- */
- guint metadata_fill;
-};
-
-static void
-httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct page *page = data;
-
- page_unref(page);
-}
-
-void
-httpd_client_free(struct httpd_client *client)
-{
- assert(client != NULL);
-
- if (client->state == RESPONSE) {
- if (client->write_source_id != 0)
- g_source_remove(client->write_source_id);
-
- if (client->current_page != NULL)
- page_unref(client->current_page);
-
- g_queue_foreach(client->pages, httpd_client_unref_page, NULL);
- g_queue_free(client->pages);
- } else
- fifo_buffer_free(client->input);
-
- if (client->metadata)
- page_unref (client->metadata);
-
- g_source_remove(client->read_source_id);
- g_io_channel_unref(client->channel);
- g_free(client);
-}
-
-/**
- * Frees the client and removes it from the server's client list.
- */
-static void
-httpd_client_close(struct httpd_client *client)
-{
- assert(client != NULL);
-
- httpd_output_remove_client(client->httpd, client);
- httpd_client_free(client);
-}
-
-/**
- * Switch the client to the "RESPONSE" state.
- */
-static void
-httpd_client_begin_response(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- client->state = RESPONSE;
- client->write_source_id = 0;
- client->pages = g_queue_new();
- client->current_page = NULL;
-
- httpd_output_send_header(client->httpd, client);
-}
-
-/**
- * Handle a line of the HTTP request.
- */
-static bool
-httpd_client_handle_line(struct httpd_client *client, const char *line)
-{
- assert(client->state != RESPONSE);
-
- if (client->state == REQUEST) {
- if (strncmp(line, "GET /", 5) != 0) {
- /* only GET is supported */
- g_warning("malformed request line from client");
- return false;
- }
-
- line = strchr(line + 5, ' ');
- if (line == NULL || strncmp(line + 1, "HTTP/", 5) != 0) {
- /* HTTP/0.9 without request headers */
- httpd_client_begin_response(client);
- return true;
- }
-
- /* after the request line, request headers follow */
- client->state = HEADERS;
- return true;
- } else {
- if (*line == 0) {
- /* empty line: request is finished */
- httpd_client_begin_response(client);
- return true;
- }
-
- if (g_ascii_strncasecmp(line, "Icy-MetaData: 1", 15) == 0) {
- /* Send icy metadata */
- client->metadata_requested =
- client->metadata_supported;
- return true;
- }
-
- if (g_ascii_strncasecmp(line, "transferMode.dlna.org: Streaming", 32) == 0) {
- /* Send as dlna */
- client->dlna_streaming_requested = true;
- /* metadata is not supported by dlna streaming, so disable it */
- client->metadata_supported = false;
- client->metadata_requested = false;
- return true;
- }
-
- /* expect more request headers */
- return true;
- }
-}
-
-/**
- * Check if a complete line of input is present in the input buffer,
- * and duplicates it. It is removed from the input buffer. The
- * return value has to be freed with g_free().
- */
-static char *
-httpd_client_read_line(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- const char *p, *newline;
- size_t length;
- char *line;
-
- p = fifo_buffer_read(client->input, &length);
- if (p == NULL)
- /* empty input buffer */
- return NULL;
-
- newline = memchr(p, '\n', length);
- if (newline == NULL)
- /* incomplete line */
- return NULL;
-
- line = g_strndup(p, newline - p);
- fifo_buffer_consume(client->input, newline - p + 1);
-
- /* remove trailing whitespace (e.g. '\r') */
- return g_strchomp(line);
-}
-
-/**
- * Sends the status line and response headers to the client.
- */
-static bool
-httpd_client_send_response(struct httpd_client *client)
-{
- char buffer[1024];
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
-
- assert(client != NULL);
- assert(client->state == RESPONSE);
-
- if (client->dlna_streaming_requested) {
- g_snprintf(buffer, sizeof(buffer),
- "HTTP/1.1 206 OK\r\n"
- "Content-Type: %s\r\n"
- "Content-Length: 10000\r\n"
- "Content-RangeX: 0-1000000/1000000\r\n"
- "transferMode.dlna.org: Streaming\r\n"
- "Accept-Ranges: bytes\r\n"
- "Connection: close\r\n"
- "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
- "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
- "\r\n",
- client->httpd->content_type);
-
- } else if (client->metadata_requested) {
- gchar *metadata_header;
-
- metadata_header = icy_server_metadata_header(
- client->httpd->name,
- client->httpd->genre,
- client->httpd->website,
- client->httpd->content_type,
- client->metaint);
-
- g_strlcpy(buffer, metadata_header, sizeof(buffer));
-
- g_free(metadata_header);
-
- } else { /* revert to a normal HTTP request */
- g_snprintf(buffer, sizeof(buffer),
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: %s\r\n"
- "Connection: close\r\n"
- "Pragma: no-cache\r\n"
- "Cache-Control: no-cache, no-store\r\n"
- "\r\n",
- client->httpd->content_type);
- }
-
- status = g_io_channel_write_chars(client->channel,
- buffer, strlen(buffer),
- &bytes_written, &error);
-
- switch (status) {
- case G_IO_STATUS_NORMAL:
- case G_IO_STATUS_AGAIN:
- return true;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- httpd_client_close(client);
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- g_warning("failed to write to client: %s", error->message);
- g_error_free(error);
-
- httpd_client_close(client);
- return false;
- }
-
- /* unreachable */
- httpd_client_close(client);
- return false;
-}
-
-/**
- * Data has been received from the client and it is appended to the
- * input buffer.
- */
-static bool
-httpd_client_received(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- char *line;
- bool success;
-
- while ((line = httpd_client_read_line(client)) != NULL) {
- success = httpd_client_handle_line(client, line);
- g_free(line);
- if (!success) {
- assert(client->state != RESPONSE);
- return false;
- }
-
- if (client->state == RESPONSE) {
- if (!fifo_buffer_is_empty(client->input)) {
- g_warning("unexpected input from client");
- return false;
- }
-
- fifo_buffer_free(client->input);
-
- return httpd_client_send_response(client);
- }
- }
-
- return true;
-}
-
-static bool
-httpd_client_read(struct httpd_client *client)
-{
- char *p;
- size_t max_length;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_read;
-
- if (client->state == RESPONSE) {
- /* the client has already sent the request, and he
- must not send more */
- char buffer[1];
-
- status = g_io_channel_read_chars(client->channel, buffer,
- sizeof(buffer), &bytes_read,
- NULL);
- if (status == G_IO_STATUS_NORMAL)
- g_warning("unexpected input from client");
-
- return false;
- }
-
- p = fifo_buffer_write(client->input, &max_length);
- if (p == NULL) {
- g_warning("buffer overflow");
- return false;
- }
-
- status = g_io_channel_read_chars(client->channel, p, max_length,
- &bytes_read, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- fifo_buffer_append(client->input, bytes_read);
- return httpd_client_received(client);
-
- case G_IO_STATUS_AGAIN:
- /* try again later, after select() */
- return true;
-
- case G_IO_STATUS_EOF:
- /* peer disconnected */
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
- g_warning("failed to read from client: %s",
- error->message);
- g_error_free(error);
- return false;
- }
-
- /* unreachable */
- return false;
-}
-
-static gboolean
-httpd_client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
- gpointer data)
-{
- struct httpd_client *client = data;
- struct httpd_output *httpd = client->httpd;
- bool ret;
-
- g_mutex_lock(httpd->mutex);
-
- if (condition == G_IO_IN && httpd_client_read(client)) {
- ret = true;
- } else {
- httpd_client_close(client);
- ret = false;
- }
-
- g_mutex_unlock(httpd->mutex);
-
- return ret;
-}
-
-struct httpd_client *
-httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported)
-{
- struct httpd_client *client = g_new(struct httpd_client, 1);
-
- client->httpd = httpd;
-
- 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 */
- g_io_channel_set_encoding(client->channel, NULL, NULL);
- /* we prefer to do buffering */
- g_io_channel_set_buffered(client->channel, false);
-
- client->read_source_id = g_io_add_watch(client->channel,
- G_IO_IN|G_IO_ERR|G_IO_HUP,
- httpd_client_in_event, client);
-
- client->input = fifo_buffer_new(4096);
- client->state = REQUEST;
-
- client->dlna_streaming_requested = false;
- client->metadata_supported = metadata_supported;
- client->metadata_requested = false;
- client->metadata_sent = true;
- client->metaint = 8192; /*TODO: just a std value */
- client->metadata = NULL;
- client->metadata_current_position = 0;
- client->metadata_fill = 0;
-
- return client;
-}
-
-static void
-httpd_client_add_page_size(gpointer data, gpointer user_data)
-{
- struct page *page = data;
- size_t *size = user_data;
-
- *size += page->size;
-}
-
-size_t
-httpd_client_queue_size(const struct httpd_client *client)
-{
- size_t size = 0;
-
- if (client->state != RESPONSE)
- return 0;
-
- g_queue_foreach(client->pages, httpd_client_add_page_size, &size);
- return size;
-}
-
-void
-httpd_client_cancel(struct httpd_client *client)
-{
- if (client->state != RESPONSE)
- return;
-
- g_queue_foreach(client->pages, httpd_client_unref_page, NULL);
- g_queue_clear(client->pages);
-
- if (client->write_source_id != 0 && client->current_page == NULL) {
- g_source_remove(client->write_source_id);
- client->write_source_id = 0;
- }
-}
-
-static GIOStatus
-write_page_to_channel(GIOChannel *channel,
- const struct page *page, size_t position,
- gsize *bytes_written_r, GError **error)
-{
- assert(channel != NULL);
- assert(page != NULL);
- assert(position < page->size);
-
- return g_io_channel_write_chars(channel,
- (const gchar*)page->data + position,
- page->size - position,
- bytes_written_r, error);
-}
-
-static GIOStatus
-write_n_bytes_to_channel(GIOChannel *channel, const struct page *page,
- size_t position, gint n,
- gsize *bytes_written_r, GError **error)
-{
- GIOStatus status;
-
- assert(channel != NULL);
- assert(page != NULL);
- assert(position < page->size);
-
- if (n == -1) {
- status = write_page_to_channel (channel, page, position,
- bytes_written_r, error);
- } else {
- status = g_io_channel_write_chars(channel,
- (const gchar*)page->data + position,
- n, bytes_written_r, error);
- }
-
- return status;
-}
-
-static gint
-bytes_left_till_metadata (struct httpd_client *client)
-{
- assert(client != NULL);
-
- if (client->metadata_requested &&
- client->current_page->size - client->current_position
- > client->metaint - client->metadata_fill)
- return client->metaint - client->metadata_fill;
-
- return -1;
-}
-
-static gboolean
-httpd_client_out_event(GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition, gpointer data)
-{
- struct httpd_client *client = data;
- struct httpd_output *httpd = client->httpd;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
- gint bytes_to_write;
-
- g_mutex_lock(httpd->mutex);
-
- assert(condition == G_IO_OUT);
- assert(client->state == RESPONSE);
-
- if (client->write_source_id == 0) {
- /* another thread has removed the event source while
- this thread was waiting for httpd->mutex */
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- if (client->current_page == NULL) {
- client->current_page = g_queue_pop_head(client->pages);
- client->current_position = 0;
- }
-
- bytes_to_write = bytes_left_till_metadata(client);
-
- if (bytes_to_write == 0) {
- gint metadata_to_write;
-
- metadata_to_write = client->metadata_current_position;
-
- if (!client->metadata_sent) {
- status = write_page_to_channel(source,
- client->metadata,
- metadata_to_write,
- &bytes_written, &error);
-
- client->metadata_current_position += bytes_written;
-
- if (client->metadata->size
- - client->metadata_current_position == 0) {
- client->metadata_fill = 0;
- client->metadata_current_position = 0;
- client->metadata_sent = true;
- }
- } else {
- struct page *empty_meta;
- guchar empty_data = 0;
-
- empty_meta = page_new_copy(&empty_data, 1);
-
- status = write_page_to_channel(source,
- empty_meta,
- metadata_to_write,
- &bytes_written, &error);
-
- client->metadata_current_position += bytes_written;
-
- if (empty_meta->size
- - client->metadata_current_position == 0) {
- client->metadata_fill = 0;
- client->metadata_current_position = 0;
- }
- }
-
- bytes_written = 0;
- } else {
- status = write_n_bytes_to_channel(source, client->current_page,
- client->current_position, bytes_to_write,
- &bytes_written, &error);
- }
-
- switch (status) {
- case G_IO_STATUS_NORMAL:
- client->current_position += bytes_written;
- assert(client->current_position <= client->current_page->size);
-
- if (client->metadata_requested)
- client->metadata_fill += bytes_written;
-
- if (client->current_position >= client->current_page->size) {
- page_unref(client->current_page);
- client->current_page = NULL;
-
- if (g_queue_is_empty(client->pages)) {
- /* all pages are sent: remove the
- event source */
- client->write_source_id = 0;
-
- g_mutex_unlock(httpd->mutex);
- return false;
- }
- }
-
- g_mutex_unlock(httpd->mutex);
- return true;
-
- case G_IO_STATUS_AGAIN:
- g_mutex_unlock(httpd->mutex);
- return true;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- g_warning("failed to write to client: %s", error->message);
- g_error_free(error);
-
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- /* unreachable */
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
-}
-
-void
-httpd_client_send(struct httpd_client *client, struct page *page)
-{
- if (client->state != RESPONSE)
- /* the client is still writing the HTTP request */
- return;
-
- page_ref(page);
- g_queue_push_tail(client->pages, page);
-
- if (client->write_source_id == 0)
- client->write_source_id =
- g_io_add_watch(client->channel, G_IO_OUT,
- httpd_client_out_event, client);
-}
-
-void
-httpd_client_send_metadata(struct httpd_client *client, struct page *page)
-{
- if (client->metadata) {
- page_unref(client->metadata);
- client->metadata = NULL;
- }
-
- g_return_if_fail (page);
-
- page_ref(page);
- client->metadata = page;
- client->metadata_sent = false;
-}
diff --git a/src/output/httpd_client.h b/src/output/httpd_client.h
deleted file mode 100644
index 739163f42..000000000
--- a/src/output/httpd_client.h
+++ /dev/null
@@ -1,71 +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_OUTPUT_HTTPD_CLIENT_H
-#define MPD_OUTPUT_HTTPD_CLIENT_H
-
-#include <glib.h>
-
-#include <stdbool.h>
-
-struct httpd_client;
-struct httpd_output;
-struct page;
-
-/**
- * Creates a new #httpd_client object
- *
- * @param httpd the HTTP output device
- * @param fd the socket file descriptor
- */
-struct httpd_client *
-httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported);
-
-/**
- * Frees memory and resources allocated by the #httpd_client object.
- * This does not remove it from the #httpd_output object.
- */
-void
-httpd_client_free(struct httpd_client *client);
-
-/**
- * Returns the total size of this client's page queue.
- */
-size_t
-httpd_client_queue_size(const struct httpd_client *client);
-
-/**
- * Clears the page queue.
- */
-void
-httpd_client_cancel(struct httpd_client *client);
-
-/**
- * Appends a page to the client's queue.
- */
-void
-httpd_client_send(struct httpd_client *client, struct page *page);
-
-/**
- * Sends the passed metadata.
- */
-void
-httpd_client_send_metadata(struct httpd_client *client, struct page *page);
-
-#endif
diff --git a/src/output/osx_output_plugin.c b/src/output/osx_output_plugin.c
index cd51fca20..7b42589f7 100644
--- a/src/output/osx_output_plugin.c
+++ b/src/output/osx_output_plugin.c
@@ -20,7 +20,7 @@
#include "config.h"
#include "osx_output_plugin.h"
#include "output_api.h"
-#include "fifo_buffer.h"
+#include "util/fifo_buffer.h"
#include <glib.h>
#include <CoreAudio/AudioHardware.h>
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index e267427df..457fa9f04 100644
--- a/src/output/pulse_output_plugin.c
+++ b/src/output/pulse_output_plugin.c
@@ -21,7 +21,7 @@
#include "pulse_output_plugin.h"
#include "output_api.h"
#include "mixer_list.h"
-#include "mixer/pulse_mixer_plugin.h"
+#include "mixer/PulseMixerPlugin.h"
#include <glib.h>
diff --git a/src/output/pulse_output_plugin.h b/src/output/pulse_output_plugin.h
index 02a51f27b..bcc8004a7 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;
@@ -30,6 +30,10 @@ struct pa_cvolume;
extern const struct audio_output_plugin pulse_output_plugin;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void
pulse_output_lock(struct pulse_output *po);
@@ -46,4 +50,8 @@ bool
pulse_output_set_volume(struct pulse_output *po,
const struct pa_cvolume *volume, GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/output/shout_output_plugin.c b/src/output/shout_output_plugin.c
index 56456a0ea..56d7a88b1 100644
--- a/src/output/shout_output_plugin.c
+++ b/src/output/shout_output_plugin.c
@@ -97,23 +97,22 @@ static void free_shout_data(struct shout_data *sd)
g_free(sd);
}
-#define check_block_param(name) { \
- block_param = config_get_block_param(param, name); \
- if (!block_param) { \
- MPD_ERROR("no \"%s\" defined for shout device defined at line " \
- "%i\n", name, param->line); \
- } \
- }
+gcc_pure
+static const char *
+require_block_string(const struct config_param *param, const char *name)
+{
+ const char *value = config_get_block_string(param, name, NULL);
+ if (value == NULL)
+ MPD_ERROR("no \"%s\" defined for shout device defined at line " \
+ "%i\n", name, param->line); \
-static struct audio_output *
-my_shout_init_driver(const struct config_param *param,
- GError **error)
+ return value;
+}
+
+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,30 +124,18 @@ 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;
-
- check_block_param("mount");
- char *mount = block_param->value;
+ const char *host = require_block_string(param, "host");
+ const char *mount = require_block_string(param, "mount");
unsigned port = config_get_block_unsigned(param, "port", 0);
if (port == 0) {
g_set_error(error, shout_output_quark(), 0,
"shout port must be configured");
- goto failure;
+ return false;
}
- check_block_param("password");
- const char *passwd = block_param->value;
-
- check_block_param("name");
- const char *name = block_param->value;
+ const char *passwd = require_block_string(param, "password");
+ const char *name = require_block_string(param, "name");
bool public = config_get_block_bool(param, "public", false);
@@ -164,21 +151,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 +174,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 +186,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 +207,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 +219,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 +238,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 +249,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 +288,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/winmm_output_plugin.c b/src/output/winmm_output_plugin.c
index 4d95834b9..c1b3af126 100644
--- a/src/output/winmm_output_plugin.c
+++ b/src/output/winmm_output_plugin.c
@@ -26,7 +26,6 @@
#include <stdlib.h>
#include <string.h>
-#include <windows.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "winmm_output"
diff --git a/src/output/winmm_output_plugin.h b/src/output/winmm_output_plugin.h
index 0605530e1..364356483 100644
--- a/src/output/winmm_output_plugin.h
+++ b/src/output/winmm_output_plugin.h
@@ -25,6 +25,7 @@
#ifdef ENABLE_WINMM_OUTPUT
#include <windows.h>
+#include <mmsystem.h>
struct winmm_output;
diff --git a/src/output_internal.h b/src/output_internal.h
index 9d975d789..692233f3c 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?
*/
@@ -250,6 +257,10 @@ audio_output_command_is_finished(const struct audio_output *ao)
return ao->command == AO_COMMAND_NONE;
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct audio_output *
audio_output_new(const struct config_param *param,
struct player_control *pc,
@@ -266,4 +277,8 @@ ao_base_finish(struct audio_output *ao);
void
audio_output_free(struct audio_output *ao);
+#ifdef __cplusplus
+}
+#endif
+
#endif
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/page.h b/src/page.h
index 8a3aaf396..8629298cd 100644
--- a/src/page.h
+++ b/src/page.h
@@ -53,6 +53,10 @@ struct page {
unsigned char data[sizeof(long)];
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Creates a new #page object, and copies data from the specified
* buffer. It is initialized with a reference count of 1.
@@ -91,4 +95,8 @@ page_ref(struct page *page);
bool
page_unref(struct page *page);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/path.c b/src/path.c
index 59a91a0f7..21a4dd41e 100644
--- a/src/path.c
+++ b/src/path.c
@@ -21,6 +21,7 @@
#include "path.h"
#include "conf.h"
#include "mpd_error.h"
+#include "gcc.h"
#include <glib.h>
@@ -60,18 +61,26 @@ utf8_to_fs_charset(const char *path_utf8)
return p;
}
+gcc_pure
+static bool
+IsSupportedCharset(const char *charset)
+{
+ /* convert a space to check if the charset is valid */
+ char *test = g_convert(" ", 1, charset, "UTF-8", NULL, NULL, NULL);
+ if (test == NULL)
+ return false;
+
+ g_free(test);
+ return true;
+}
+
static void
path_set_fs_charset(const char *charset)
{
- char *test;
-
assert(charset != NULL);
- /* convert a space to ensure that the charset is valid */
- test = g_convert(" ", 1, charset, "UTF-8", NULL, NULL, NULL);
- if (test == NULL)
+ if (!IsSupportedCharset(charset))
MPD_ERROR("invalid filesystem charset: %s", charset);
- g_free(test);
g_free(fs_charset);
fs_charset = g_strdup(charset);
@@ -102,7 +111,7 @@ void path_global_init(void)
* However this is true only if <gstdio.h> helpers are used.
* MPD uses regular <stdio.h> functions.
* Those functions use encoding determined by GetACP(). */
- char win_charset[13];
+ static char win_charset[13];
sprintf(win_charset, "cp%u", GetACP());
charset = win_charset;
#endif
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.c b/src/playlist.c
deleted file mode 100644
index dc6d8c340..000000000
--- a/src/playlist.c
+++ /dev/null
@@ -1,452 +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_internal.h"
-#include "playlist_save.h"
-#include "player_control.h"
-#include "command.h"
-#include "tag.h"
-#include "song.h"
-#include "conf.h"
-#include "stored_playlist.h"
-#include "idle.h"
-
-#include <glib.h>
-
-#include <assert.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "playlist"
-
-void
-playlist_increment_version_all(struct playlist *playlist)
-{
- queue_modify_all(&playlist->queue);
- idle_add(IDLE_PLAYLIST);
-}
-
-void
-playlist_tag_changed(struct playlist *playlist)
-{
- if (!playlist->playing)
- return;
-
- assert(playlist->current >= 0);
-
- queue_modify(&playlist->queue, 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.
- */
-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));
-
- playlist->queued = order;
-
- song = queue_get_order(&playlist->queue, order);
- uri = song_get_uri(song);
- g_debug("queue song %i:\"%s\"", playlist->queued, uri);
- g_free(uri);
-
- pc_enqueue_song(pc, song);
-}
-
-/**
- * Called if the player thread has started playing the "queued" song.
- */
-static void
-playlist_song_started(struct playlist *playlist, struct player_control *pc)
-{
- assert(pc->next_song == NULL);
- assert(playlist->queued >= -1);
-
- /* queued song has started: copy queued to current,
- and notify the clients */
-
- int current = playlist->current;
- playlist->current = playlist->queued;
- playlist->queued = -1;
-
- if(playlist->queue.consume)
- playlist_delete(playlist, pc,
- queue_order_to_position(&playlist->queue,
- current));
-
- idle_add(IDLE_PLAYER);
-}
-
-const struct song *
-playlist_get_queued_song(struct playlist *playlist)
-{
- if (!playlist->playing || playlist->queued < 0)
- return NULL;
-
- return queue_get_order(&playlist->queue, playlist->queued);
-}
-
-void
-playlist_update_queued_song(struct playlist *playlist,
- struct player_control *pc,
- const struct song *prev)
-{
- int next_order;
- const struct song *next_song;
-
- if (!playlist->playing)
- return;
-
- assert(!queue_is_empty(&playlist->queue));
- assert((playlist->queued < 0) == (prev == NULL));
-
- next_order = playlist->current >= 0
- ? queue_next_order(&playlist->queue, playlist->current)
- : 0;
-
- if (next_order == 0 && playlist->queue.random &&
- !playlist->queue.single) {
- /* shuffle the song order again, so we get a different
- order each time the playlist is played
- completely */
- unsigned current_position =
- queue_order_to_position(&playlist->queue,
- playlist->current);
-
- queue_shuffle_order(&playlist->queue);
-
- /* 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);
- }
-
- if (next_order >= 0)
- next_song = queue_get_order(&playlist->queue, next_order);
- else
- next_song = NULL;
-
- if (prev != NULL && next_song != prev) {
- /* clear the currently queued song */
- pc_cancel(pc);
- playlist->queued = -1;
- }
-
- if (next_order >= 0) {
- if (next_song != prev)
- playlist_queue_song_order(playlist, pc, next_order);
- else
- playlist->queued = next_order;
- }
-}
-
-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);
-
- uri = song_get_uri(song);
- g_debug("play %i:\"%s\"", orderNum, uri);
- g_free(uri);
-
- pc_play(pc, song);
- playlist->current = orderNum;
-}
-
-static void
-playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
-
-/**
- * This is the "PLAYLIST" event handler. It is invoked by the player
- * thread whenever it requests a new queued song, or when it exits.
- */
-void
-playlist_sync(struct playlist *playlist, struct player_control *pc)
-{
- if (!playlist->playing)
- /* this event has reached us out of sync: we aren't
- playing anymore; ignore the event */
- return;
-
- player_lock(pc);
- enum player_state pc_state = pc_get_state(pc);
- const struct song *pc_next_song = pc->next_song;
- player_unlock(pc);
-
- if (pc_state == PLAYER_STATE_STOP)
- /* the player thread has stopped: check if playback
- should be restarted with the next song. That can
- happen if the playlist isn't filling the queue fast
- enough */
- playlist_resume_playback(playlist, pc);
- else {
- /* check if the player thread has already started
- playing the queued song */
- if (pc_next_song == NULL && playlist->queued != -1)
- playlist_song_started(playlist, pc);
-
- player_lock(pc);
- pc_next_song = pc->next_song;
- player_unlock(pc);
-
- /* make sure the queued song is always set (if
- possible) */
- if (pc_next_song == NULL && playlist->queued < 0)
- playlist_update_queued_song(playlist, pc, NULL);
- }
-}
-
-/**
- * The player has stopped for some reason. Check the error, and
- * decide whether to re-start playback
- */
-static void
-playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
-{
- enum player_error error;
-
- assert(playlist->playing);
- assert(pc_get_state(pc) == PLAYER_STATE_STOP);
-
- error = pc_get_error(pc);
- if (error == PLAYER_ERROR_NOERROR)
- 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))
- /* too many errors, or critical error: stop
- playback */
- playlist_stop(playlist, pc);
- else
- /* continue playback at the next song */
- playlist_next(playlist, pc);
-}
-
-bool
-playlist_get_repeat(const struct playlist *playlist)
-{
- return playlist->queue.repeat;
-}
-
-bool
-playlist_get_random(const struct playlist *playlist)
-{
- return playlist->queue.random;
-}
-
-bool
-playlist_get_single(const struct playlist *playlist)
-{
- return playlist->queue.single;
-}
-
-bool
-playlist_get_consume(const struct playlist *playlist)
-{
- return playlist->queue.consume;
-}
-
-void
-playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
- bool status)
-{
- if (status == playlist->queue.repeat)
- return;
-
- struct queue *queue = &playlist->queue;
-
- queue->repeat = status;
-
- pc_set_border_pause(pc, queue->single && !queue->repeat);
-
- /* if the last song is currently being played, the "next song"
- might change when repeat mode is toggled */
- playlist_update_queued_song(playlist, pc,
- playlist_get_queued_song(playlist));
-
- idle_add(IDLE_OPTIONS);
-}
-
-static void
-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);
-
- queue_restore_order(&playlist->queue);
-}
-
-void
-playlist_set_single(struct playlist *playlist, struct player_control *pc,
- bool status)
-{
- if (status == playlist->queue.single)
- return;
-
- struct queue *queue = &playlist->queue;
-
- queue->single = status;
-
- pc_set_border_pause(pc, queue->single && !queue->repeat);
-
- /* if the last song is currently being played, the "next song"
- might change when single mode is toggled */
- playlist_update_queued_song(playlist, pc,
- playlist_get_queued_song(playlist));
-
- idle_add(IDLE_OPTIONS);
-}
-
-void
-playlist_set_consume(struct playlist *playlist, bool status)
-{
- if (status == playlist->queue.consume)
- return;
-
- playlist->queue.consume = status;
- idle_add(IDLE_OPTIONS);
-}
-
-void
-playlist_set_random(struct playlist *playlist, struct player_control *pc,
- bool status)
-{
- const struct song *queued;
-
- if (status == playlist->queue.random)
- return;
-
- queued = playlist_get_queued_song(playlist);
-
- playlist->queue.random = status;
-
- if (playlist->queue.random) {
- /* shuffle the queue order, but preserve
- playlist->current */
-
- int current_position =
- playlist->playing && playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- playlist->current)
- : -1;
-
- queue_shuffle_order(&playlist->queue);
-
- 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->current = 0;
- } else
- playlist->current = -1;
- } else
- playlist_order(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- idle_add(IDLE_OPTIONS);
-}
-
-int
-playlist_get_current_song(const struct playlist *playlist)
-{
- if (playlist->current >= 0)
- return queue_order_to_position(&playlist->queue,
- playlist->current);
-
- return -1;
-}
-
-int
-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);
- else if (playlist->queue.repeat == 1)
- return queue_order_to_position(&playlist->queue, 0);
- }
-
- return -1;
-}
-
-unsigned long
-playlist_get_version(const struct playlist *playlist)
-{
- return playlist->queue.version;
-}
-
-int
-playlist_get_length(const struct playlist *playlist)
-{
- return queue_length(&playlist->queue);
-}
-
-unsigned
-playlist_get_song_id(const struct playlist *playlist, unsigned song)
-{
- return queue_position_to_id(&playlist->queue, song);
-}
diff --git a/src/playlist.h b/src/playlist.h
deleted file mode 100644
index a21bdf24a..000000000
--- a/src/playlist.h
+++ /dev/null
@@ -1,256 +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_H
-#define MPD_PLAYLIST_H
-
-#include "queue.h"
-#include "playlist_error.h"
-
-#include <stdbool.h>
-
-struct player_control;
-
-struct playlist {
- /**
- * The song queue - it contains the "real" playlist.
- */
- struct queue queue;
-
- /**
- * This value is true if the player is currently playing (or
- * should be playing).
- */
- bool playing;
-
- /**
- * If true, then any error is fatal; if false, MPD will
- * attempt to play the next song on non-fatal errors. During
- * seeking, this flag is set.
- */
- bool stop_on_error;
-
- /**
- * Number of errors since playback was started. If this
- * number exceeds the length of the playlist, MPD gives up,
- * because all songs have been tried.
- */
- unsigned error_count;
-
- /**
- * The "current song pointer". This is the song which is
- * played when we get the "play" command. It is also the song
- * which is currently being played.
- */
- int current;
-
- /**
- * The "next" song to be played, when the current one
- * finishes. The decoder thread may start decoding and
- * buffering it, while the "current" song is still playing.
- *
- * 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);
-
-void
-playlist_global_finish(void);
-
-void
-playlist_init(struct playlist *playlist);
-
-void
-playlist_finish(struct playlist *playlist);
-
-void
-playlist_tag_changed(struct playlist *playlist);
-
-/**
- * Returns the "queue" object of the global playlist instance.
- */
-static inline const struct queue *
-playlist_get_queue(const struct playlist *playlist)
-{
- return &playlist->queue;
-}
-
-void
-playlist_clear(struct playlist *playlist, struct player_control *pc);
-
-/**
- * Appends a local file (outside the music database) to the playlist.
- *
- * Note: the caller is responsible for checking permissions.
- */
-enum playlist_result
-playlist_append_file(struct playlist *playlist, struct player_control *pc,
- const char *path_fs, unsigned *added_id);
-
-enum playlist_result
-playlist_append_uri(struct playlist *playlist, struct player_control *pc,
- const char *file, unsigned *added_id);
-
-enum playlist_result
-playlist_append_song(struct playlist *playlist, struct player_control *pc,
- struct song *song, unsigned *added_id);
-
-enum playlist_result
-playlist_delete(struct playlist *playlist, struct player_control *pc,
- unsigned song);
-
-/**
- * Deletes a range of songs from the playlist.
- *
- * @param start the position of the first song to delete
- * @param end the position after the last song to delete
- */
-enum playlist_result
-playlist_delete_range(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end);
-
-enum playlist_result
-playlist_delete_id(struct playlist *playlist, struct player_control *pc,
- unsigned song);
-
-void
-playlist_stop(struct playlist *playlist, struct player_control *pc);
-
-enum playlist_result
-playlist_play(struct playlist *playlist, struct player_control *pc,
- int song);
-
-enum playlist_result
-playlist_play_id(struct playlist *playlist, struct player_control *pc,
- int song);
-
-void
-playlist_next(struct playlist *playlist, struct player_control *pc);
-
-void
-playlist_sync(struct playlist *playlist, struct player_control *pc);
-
-void
-playlist_previous(struct playlist *playlist, struct player_control *pc);
-
-void
-playlist_shuffle(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end);
-
-void
-playlist_delete_song(struct playlist *playlist, struct player_control *pc,
- const struct song *song);
-
-enum playlist_result
-playlist_move_range(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end, int to);
-
-enum playlist_result
-playlist_move_id(struct playlist *playlist, struct player_control *pc,
- unsigned id, int to);
-
-enum playlist_result
-playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
- unsigned song1, unsigned song2);
-
-enum playlist_result
-playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
- unsigned id1, unsigned id2);
-
-enum playlist_result
-playlist_set_priority(struct playlist *playlist, struct player_control *pc,
- unsigned start_position, unsigned end_position,
- uint8_t priority);
-
-enum playlist_result
-playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
- unsigned song_id, uint8_t priority);
-
-bool
-playlist_get_repeat(const struct playlist *playlist);
-
-void
-playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
- bool status);
-
-bool
-playlist_get_random(const struct playlist *playlist);
-
-void
-playlist_set_random(struct playlist *playlist, struct player_control *pc,
- bool status);
-
-bool
-playlist_get_single(const struct playlist *playlist);
-
-void
-playlist_set_single(struct playlist *playlist, struct player_control *pc,
- bool status);
-
-bool
-playlist_get_consume(const struct playlist *playlist);
-
-void
-playlist_set_consume(struct playlist *playlist, bool status);
-
-int
-playlist_get_current_song(const struct playlist *playlist);
-
-int
-playlist_get_next_song(const struct playlist *playlist);
-
-unsigned
-playlist_get_song_id(const struct playlist *playlist, unsigned song);
-
-int
-playlist_get_length(const struct playlist *playlist);
-
-unsigned long
-playlist_get_version(const struct playlist *playlist);
-
-enum playlist_result
-playlist_seek_song(struct playlist *playlist, struct player_control *pc,
- unsigned song, float seek_time);
-
-enum playlist_result
-playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
- unsigned id, float seek_time);
-
-/**
- * Seek within the current song. Fails if MPD is not currently
- * playing.
- *
- * @param time the time in seconds
- * @param relative if true, then the specified time is relative to the
- * current position
- */
-enum playlist_result
-playlist_seek_current(struct playlist *playlist, struct player_control *pc,
- float seek_time, bool relative);
-
-void
-playlist_increment_version_all(struct playlist *playlist);
-
-#endif
diff --git a/src/playlist_control.c b/src/playlist_control.c
deleted file mode 100644
index 0dea7676a..000000000
--- a/src/playlist_control.c
+++ /dev/null
@@ -1,288 +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.
- */
-
-/*
- * Functions for controlling playback on the playlist level.
- *
- */
-
-#include "config.h"
-#include "playlist_internal.h"
-#include "player_control.h"
-#include "idle.h"
-
-#include <glib.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "playlist"
-
-void
-playlist_stop(struct playlist *playlist, struct player_control *pc)
-{
- if (!playlist->playing)
- return;
-
- assert(playlist->current >= 0);
-
- g_debug("stop");
- pc_stop(pc);
- playlist->queued = -1;
- playlist->playing = false;
-
- if (playlist->queue.random) {
- /* shuffle the playlist, so the next playback will
- result in a new random order */
-
- unsigned current_position =
- queue_order_to_position(&playlist->queue,
- playlist->current);
-
- queue_shuffle_order(&playlist->queue);
-
- /* 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);
- }
-}
-
-enum playlist_result
-playlist_play(struct playlist *playlist, struct player_control *pc,
- int song)
-{
- unsigned i = song;
-
- pc_clear_error(pc);
-
- if (song == -1) {
- /* play any song ("current" song, or the first song */
-
- if (queue_is_empty(&playlist->queue))
- return PLAYLIST_RESULT_SUCCESS;
-
- if (playlist->playing) {
- /* already playing: unpause playback, just in
- case it was paused, and return */
- pc_set_pause(pc, false);
- return PLAYLIST_RESULT_SUCCESS;
- }
-
- /* select a song: "current" song, or the first one */
- i = playlist->current >= 0
- ? playlist->current
- : 0;
- } else if (!queue_valid_position(&playlist->queue, song))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if (playlist->queue.random) {
- if (song >= 0)
- /* "i" is currently the song position (which
- 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);
-
- 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);
- i = playlist->current;
- }
-
- playlist->stop_on_error = false;
- playlist->error_count = 0;
-
- playlist_play_order(playlist, pc, i);
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_play_id(struct playlist *playlist, struct player_control *pc,
- int id)
-{
- int song;
-
- if (id == -1) {
- return playlist_play(playlist, pc, id);
- }
-
- song = queue_id_to_position(&playlist->queue, id);
- if (song < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_play(playlist, pc, song);
-}
-
-void
-playlist_next(struct playlist *playlist, struct player_control *pc)
-{
- int next_order;
- int current;
-
- if (!playlist->playing)
- return;
-
- assert(!queue_is_empty(&playlist->queue));
- assert(queue_valid_order(&playlist->queue, 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);
- if (next_order < 0) {
- /* no song after this one: stop playback */
- playlist_stop(playlist, pc);
-
- /* reset "current song" */
- playlist->current = -1;
- }
- else
- {
- if (next_order == 0 && playlist->queue.random) {
- /* The queue told us that the next song is the first
- song. This means we are in repeat mode. Shuffle
- the queue order, so this time, the user hears the
- songs in a different than before */
- assert(playlist->queue.repeat);
-
- queue_shuffle_order(&playlist->queue);
-
- /* note that playlist->current and playlist->queued are
- now invalid, but playlist_play_order() will
- discard them anyway */
- }
-
- playlist_play_order(playlist, pc, next_order);
- }
-
- /* Consume mode removes each played songs. */
- if(playlist->queue.consume)
- playlist_delete(playlist, pc,
- queue_order_to_position(&playlist->queue,
- current));
-}
-
-void
-playlist_previous(struct playlist *playlist, struct player_control *pc)
-{
- if (!playlist->playing)
- return;
-
- assert(queue_length(&playlist->queue) > 0);
-
- if (playlist->current > 0) {
- /* play the preceding song */
- playlist_play_order(playlist, pc,
- playlist->current - 1);
- } else if (playlist->queue.repeat) {
- /* play the last song in "repeat" mode */
- playlist_play_order(playlist, pc,
- queue_length(&playlist->queue) - 1);
- } else {
- /* re-start playing the current song if it's
- the first one */
- playlist_play_order(playlist, pc, playlist->current);
- }
-}
-
-enum playlist_result
-playlist_seek_song(struct playlist *playlist, struct player_control *pc,
- unsigned song, float seek_time)
-{
- const struct song *queued;
- unsigned i;
- bool success;
-
- if (!queue_valid_position(&playlist->queue, song))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- queued = playlist_get_queued_song(playlist);
-
- if (playlist->queue.random)
- i = queue_position_to_order(&playlist->queue, song);
- else
- i = song;
-
- pc_clear_error(pc);
- playlist->stop_on_error = true;
- playlist->error_count = 0;
-
- if (!playlist->playing || (unsigned)playlist->current != i) {
- /* seeking is not within the current song - prepare
- song change */
-
- playlist->playing = true;
- playlist->current = i;
-
- queued = NULL;
- }
-
- success = pc_seek(pc, queue_get_order(&playlist->queue, i), seek_time);
- if (!success) {
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_NOT_PLAYING;
- }
-
- playlist->queued = -1;
- playlist_update_queued_song(playlist, pc, NULL);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-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);
- if (song < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_seek_song(playlist, pc, song, seek_time);
-}
-
-enum playlist_result
-playlist_seek_current(struct playlist *playlist, struct player_control *pc,
- float seek_time, bool relative)
-{
- if (!playlist->playing)
- return PLAYLIST_RESULT_NOT_PLAYING;
-
- if (relative) {
- struct player_status status;
- pc_get_status(pc, &status);
-
- if (status.state != PLAYER_STATE_PLAY &&
- status.state != PLAYER_STATE_PAUSE)
- return PLAYLIST_RESULT_NOT_PLAYING;
-
- seek_time += (int)status.elapsed_time;
- }
-
- if (seek_time < 0)
- seek_time = 0;
-
- return playlist_seek_song(playlist, pc, playlist->current, seek_time);
-}
diff --git a/src/playlist_edit.c b/src/playlist_edit.c
deleted file mode 100644
index d10f49451..000000000
--- a/src/playlist_edit.c
+++ /dev/null
@@ -1,487 +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.
- */
-
-/*
- * Functions for editing the playlist (adding, removing, reordering
- * songs in the queue).
- *
- */
-
-#include "config.h"
-#include "playlist_internal.h"
-#include "player_control.h"
-#include "database.h"
-#include "uri.h"
-#include "song.h"
-#include "idle.h"
-
-#include <stdlib.h>
-
-static void playlist_increment_version(struct playlist *playlist)
-{
- queue_increment_version(&playlist->queue);
-
- idle_add(IDLE_PLAYLIST);
-}
-
-void
-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->current = -1;
-
- playlist_increment_version(playlist);
-}
-
-enum playlist_result
-playlist_append_file(struct playlist *playlist, struct player_control *pc,
- const char *path_fs, unsigned *added_id)
-{
- struct song *song = song_file_load(path_fs, NULL);
- if (song == NULL)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_append_song(playlist, pc, song, added_id);
-}
-
-enum playlist_result
-playlist_append_song(struct playlist *playlist, struct player_control *pc,
- struct song *song, unsigned *added_id)
-{
- const struct song *queued;
- unsigned id;
-
- if (queue_is_full(&playlist->queue))
- return PLAYLIST_RESULT_TOO_LARGE;
-
- queued = playlist_get_queued_song(playlist);
-
- id = queue_append(&playlist->queue, song, 0);
-
- if (playlist->queue.random) {
- /* shuffle the new song into the list of remaining
- songs to play */
-
- unsigned start;
- if (playlist->queued >= 0)
- 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));
- }
-
- playlist_increment_version(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- if (added_id)
- *added_id = id;
-
- 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)
-{
- struct song *song;
-
- g_debug("add to playlist: %s", uri);
-
- song = song_by_uri(uri);
- if (song == NULL)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_append_song(playlist, pc, song, added_id);
-}
-
-enum playlist_result
-playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
- unsigned song1, unsigned song2)
-{
- const struct song *queued;
-
- if (!queue_valid_position(&playlist->queue, song1) ||
- !queue_valid_position(&playlist->queue, song2))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- queued = playlist_get_queued_song(playlist);
-
- queue_swap(&playlist->queue, 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));
- } else {
- /* correct the "current" song order */
-
- if (playlist->current == (int)song1)
- playlist->current = song2;
- else if (playlist->current == (int)song2)
- playlist->current = song1;
- }
-
- playlist_increment_version(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-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);
-
- if (song1 < 0 || song2 < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_swap_songs(playlist, pc, song1, song2);
-}
-
-enum playlist_result
-playlist_set_priority(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end,
- uint8_t priority)
-{
- if (start >= queue_length(&playlist->queue))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if (end > queue_length(&playlist->queue))
- end = queue_length(&playlist->queue);
-
- if (start >= end)
- return PLAYLIST_RESULT_SUCCESS;
-
- /* remember "current" and "queued" */
-
- int current_position = playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- 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_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_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-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);
- if (song_position < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_set_priority(playlist, pc,
- song_position, song_position + 1,
- priority);
-
-}
-
-static void
-playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
- unsigned song, const struct song **queued_p)
-{
- unsigned songOrder;
-
- assert(song < queue_length(&playlist->queue));
-
- songOrder = queue_position_to_order(&playlist->queue, song);
-
- if (playlist->playing && playlist->current == (int)songOrder) {
- bool paused = pc_get_state(pc) == PLAYER_STATE_PAUSE;
-
- /* the current song is going to be deleted: stop the player */
-
- pc_stop(pc);
- playlist->playing = false;
-
- /* see which song is going to be played instead */
-
- playlist->current = queue_next_order(&playlist->queue,
- playlist->current);
- if (playlist->current == (int)songOrder)
- playlist->current = -1;
-
- if (playlist->current >= 0 && !paused)
- /* play the song after the deleted one */
- playlist_play_order(playlist, pc, playlist->current);
- else
- /* no songs left to play, stop playback
- completely */
- playlist_stop(playlist, pc);
-
- *queued_p = NULL;
- } else if (playlist->current == (int)songOrder)
- /* there's a "current song" but we're not playing
- currently - clear "current" */
- playlist->current = -1;
-
- /* 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);
-
- /* update the "current" and "queued" variables */
-
- if (playlist->current > (int)songOrder) {
- playlist->current--;
- }
-}
-
-enum playlist_result
-playlist_delete(struct playlist *playlist, struct player_control *pc,
- unsigned song)
-{
- const struct song *queued;
-
- if (song >= queue_length(&playlist->queue))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- queued = playlist_get_queued_song(playlist);
-
- playlist_delete_internal(playlist, pc, song, &queued);
-
- playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_delete_range(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end)
-{
- const struct song *queued;
-
- if (start >= queue_length(&playlist->queue))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if (end > queue_length(&playlist->queue))
- end = queue_length(&playlist->queue);
-
- if (start >= end)
- return PLAYLIST_RESULT_SUCCESS;
-
- queued = playlist_get_queued_song(playlist);
-
- do {
- playlist_delete_internal(playlist, pc, --end, &queued);
- } while (end != start);
-
- playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_delete_id(struct playlist *playlist, struct player_control *pc,
- unsigned id)
-{
- int song = queue_id_to_position(&playlist->queue, id);
- if (song < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_delete(playlist, pc, song);
-}
-
-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))
- playlist_delete(playlist, pc, i);
-
- pc_song_deleted(pc, song);
-}
-
-enum playlist_result
-playlist_move_range(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end, int to)
-{
- const struct song *queued;
- int currentSong;
-
- if (!queue_valid_position(&playlist->queue, start) ||
- !queue_valid_position(&playlist->queue, 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)))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if ((int)start == to)
- /* nothing happens */
- return PLAYLIST_RESULT_SUCCESS;
-
- queued = playlist_get_queued_song(playlist);
-
- /*
- * (to < 0) => move to offset from current song
- * (-playlist.length == to) => move to position BEFORE current song
- */
- currentSong = playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- 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);
- if (start < (unsigned)to)
- to--;
- }
-
- queue_move_range(&playlist->queue, start, end, to);
-
- if (!playlist->queue.random) {
- /* update current/queued */
- if ((int)start <= playlist->current &&
- (unsigned)playlist->current < end)
- playlist->current += to - start;
- else if (playlist->current >= (int)end &&
- playlist->current <= to) {
- playlist->current -= end - start;
- } else if (playlist->current >= to &&
- playlist->current < (int)start) {
- playlist->current += end - start;
- }
- }
-
- playlist_increment_version(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-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);
- if (song < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_move_range(playlist, pc, song, song+1, to);
-}
-
-void
-playlist_shuffle(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end)
-{
- const struct song *queued;
-
- if (end > queue_length(&playlist->queue))
- /* correct the "end" offset */
- end = queue_length(&playlist->queue);
-
- if ((start+1) >= end)
- /* needs at least two entries. */
- return;
-
- 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);
-
- if (current_position >= start && current_position < end)
- {
- /* put current playing song first */
- queue_swap(&playlist->queue, start, current_position);
-
- if (playlist->queue.random) {
- playlist->current =
- queue_position_to_order(&playlist->queue, start);
- } else
- playlist->current = start;
-
- /* start shuffle after the current song */
- start++;
- }
- } else {
- /* no playback currently: reset playlist->current */
-
- playlist->current = -1;
- }
-
- queue_shuffle_range(&playlist->queue, start, end);
-
- playlist_increment_version(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-}
diff --git a/src/playlist_internal.h b/src/playlist_internal.h
deleted file mode 100644
index 81b175176..000000000
--- a/src/playlist_internal.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.
- */
-
-/*
- * Internal header for the components of the playlist code.
- *
- */
-
-#ifndef PLAYLIST_INTERNAL_H
-#define PLAYLIST_INTERNAL_H
-
-#include "playlist.h"
-
-struct player_control;
-
-/**
- * Returns the song object which is currently queued. Returns none if
- * there is none (yet?) or if MPD isn't playing.
- */
-const struct song *
-playlist_get_queued_song(struct playlist *playlist);
-
-/**
- * Updates the "queued song". Calculates the next song according to
- * the current one (if MPD isn't playing, it takes the first song),
- * and queues this song. Clears the old queued song if there was one.
- *
- * @param prev the song which was previously queued, as determined by
- * playlist_get_queued_song()
- */
-void
-playlist_update_queued_song(struct playlist *playlist,
- struct player_control *pc,
- const struct song *prev);
-
-void
-playlist_play_order(struct playlist *playlist, struct player_control *pc,
- int orderNum);
-
-#endif
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.c b/src/queue.c
deleted file mode 100644
index 4fe564a35..000000000
--- a/src/queue.c
+++ /dev/null
@@ -1,603 +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.h"
-#include "song.h"
-
-#include <stdlib.h>
-
-/**
- * Generate a non-existing id number.
- */
-static unsigned
-queue_generate_id(const struct queue *queue)
-{
- static unsigned cur = (unsigned)-1;
-
- do {
- cur++;
-
- if (cur >= queue->max_length * QUEUE_HASH_MULT)
- cur = 0;
- } while (queue->id_to_position[cur] != -1);
-
- return cur;
-}
-
-int
-queue_next_order(const struct queue *queue, unsigned order)
-{
- assert(order < queue->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))
- /* restart at first song */
- return 0;
- else
- /* end of queue */
- return -1;
-}
-
-void
-queue_increment_version(struct queue *queue)
-{
- static unsigned long max = ((uint32_t) 1 << 31) - 1;
-
- queue->version++;
-
- if (queue->version >= max) {
- for (unsigned i = 0; i < queue->length; i++)
- queue->items[i].version = 0;
-
- queue->version = 1;
- }
-}
-
-void
-queue_modify(struct queue *queue, unsigned order)
-{
- unsigned position;
-
- assert(order < queue->length);
-
- position = queue->order[order];
- queue->items[position].version = queue->version;
-
- queue_increment_version(queue);
-}
-
-void
-queue_modify_all(struct queue *queue)
-{
- for (unsigned i = 0; i < queue->length; i++)
- queue->items[i].version = queue->version;
-
- queue_increment_version(queue);
-}
-
-unsigned
-queue_append(struct queue *queue, struct song *song, uint8_t priority)
-{
- unsigned id = queue_generate_id(queue);
-
- assert(!queue_is_full(queue));
-
- queue->items[queue->length] = (struct queue_item){
- .song = song,
- .id = id,
- .version = queue->version,
- .priority = priority,
- };
-
- queue->order[queue->length] = queue->length;
- queue->id_to_position[id] = queue->length;
-
- ++queue->length;
-
- return id;
-}
-
-void
-queue_swap(struct queue *queue, unsigned position1, unsigned position2)
-{
- struct queue_item tmp;
- unsigned id1 = queue->items[position1].id;
- unsigned id2 = queue->items[position2].id;
-
- tmp = queue->items[position1];
- queue->items[position1] = queue->items[position2];
- queue->items[position2] = tmp;
-
- queue->items[position1].version = queue->version;
- queue->items[position2].version = queue->version;
-
- queue->id_to_position[id1] = position2;
- queue->id_to_position[id2] = position1;
-}
-
-static void
-queue_move_song_to(struct queue *queue, unsigned from, unsigned to)
-{
- unsigned from_id = queue->items[from].id;
-
- queue->items[to] = queue->items[from];
- queue->items[to].version = queue->version;
- queue->id_to_position[from_id] = to;
-}
-
-void
-queue_move(struct queue *queue, unsigned from, unsigned to)
-{
- struct queue_item item = queue->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);
-
- /* move songs to one more in to->from */
-
- for (unsigned i = from; i > to; i--)
- queue_move_song_to(queue, i - 1, i);
-
- /* put song at _to_ */
-
- queue->id_to_position[item.id] = to;
- queue->items[to] = item;
- queue->items[to].version = queue->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;
- }
- }
-}
-
-void
-queue_move_range(struct queue *queue, unsigned start, unsigned end, unsigned to)
-{
- struct queue_item items[end - start];
- // Copy the original block [start,end-1]
- for (unsigned i = start; i < end; i++)
- items[i - start] = queue->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);
-
- // 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);
-
- // 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;
- }
-
- if (queue->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;
- }
- }
-}
-
-/**
- * Moves a song to a new position in the "order" list.
- */
-static void
-queue_move_order(struct queue *queue, unsigned from_order, unsigned to_order)
-{
- assert(queue != NULL);
- assert(from_order < queue->length);
- assert(to_order <= queue->length);
-
- const unsigned from_position =
- queue_order_to_position(queue, from_order);
-
- if (from_order < to_order) {
- for (unsigned i = from_order; i < to_order; ++i)
- queue->order[i] = queue->order[i + 1];
- } else {
- for (unsigned i = from_order; i > to_order; --i)
- queue->order[i] = queue->order[i - 1];
- }
-
- queue->order[to_order] = from_position;
-}
-
-void
-queue_delete(struct queue *queue, unsigned position)
-{
- struct song *song;
- unsigned id, order;
-
- assert(position < queue->length);
-
- song = queue_get(queue, position);
- if (!song_in_database(song))
- song_free(song);
-
- id = queue_position_to_id(queue, position);
- order = queue_position_to_order(queue, position);
-
- --queue->length;
-
- /* release the song id */
-
- queue->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);
-
- /* delete the entry from the order array */
-
- for (unsigned i = order; i < queue->length; i++)
- queue->order[i] = queue->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];
-}
-
-void
-queue_clear(struct queue *queue)
-{
- for (unsigned i = 0; i < queue->length; i++) {
- struct queue_item *item = &queue->items[i];
-
- if (!song_in_database(item->song))
- song_free(item->song);
-
- queue->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);
-}
-
-static const struct queue_item *
-queue_get_order_item_const(const struct queue *queue, unsigned order)
-{
- assert(queue != NULL);
- assert(order < queue->length);
-
- return &queue->items[queue->order[order]];
-}
-
-static uint8_t
-queue_get_order_priority(const struct queue *queue, unsigned order)
-{
- return queue_get_order_item_const(queue, order)->priority;
-}
-
-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;
- 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;
- uint8_t b = queue->items[*bp].priority;
-
- if (G_LIKELY(a == b))
- return 0;
- else if (a > b)
- return -1;
- else
- return 1;
-}
-
-static void
-queue_sort_order_by_priority(struct queue *queue, unsigned start, unsigned end)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start <= end);
- assert(end <= queue->length);
-
- g_qsort_with_data(&queue->order[start], end - start,
- sizeof(queue->order[0]),
- queue_item_compare_order_priority,
- queue);
-}
-
-/**
- * Shuffle the order of items in the specified range, ignoring their
- * priorities.
- */
-static void
-queue_shuffle_order_range(struct queue *queue, unsigned start, unsigned end)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start <= 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));
-}
-
-/**
- * Sort the "order" of items by priority, and then shuffle each
- * priority group.
- */
-void
-queue_shuffle_order_range_with_priority(struct queue *queue,
- unsigned start, unsigned end)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start <= end);
- assert(end <= queue->length);
-
- if (start == end)
- return;
-
- /* first group the range by priority */
- queue_sort_order_by_priority(queue, start, end);
-
- /* now shuffle each priority group */
- unsigned group_start = start;
- uint8_t group_priority = queue_get_order_priority(queue, start);
-
- for (unsigned i = start + 1; i < end; ++i) {
- uint8_t priority = queue_get_order_priority(queue, 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);
- group_start = i;
- group_priority = priority;
- }
- }
-
- /* shuffle the last group */
- queue_shuffle_order_range(queue, group_start, end);
-}
-
-void
-queue_shuffle_order(struct queue *queue)
-{
- queue_shuffle_order_range_with_priority(queue, 0, queue->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));
-}
-
-void
-queue_shuffle_order_last(struct queue *queue, unsigned start, unsigned end)
-{
- queue_swap_order(queue, end - 1,
- g_rand_int_range(queue->rand, start, end));
-}
-
-void
-queue_shuffle_range(struct queue *queue, unsigned start, unsigned end)
-{
- assert(start <= end);
- assert(end <= queue->length);
-
- for (unsigned i = start; i < end; i++) {
- unsigned ri = g_rand_int_range(queue->rand, i, end);
- queue_swap(queue, i, ri);
- }
-}
-
-/**
- * Find the first item that has this specified priority or higher.
- */
-G_GNUC_PURE
-static unsigned
-queue_find_priority_order(const struct queue *queue, unsigned start_order,
- uint8_t priority, unsigned exclude_order)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start_order <= queue->length);
-
- for (unsigned order = start_order; order < queue->length; ++order) {
- const unsigned position = queue_order_to_position(queue, order);
- const struct queue_item *item = &queue->items[position];
- if (item->priority <= priority && order != exclude_order)
- return order;
- }
-
- return queue->length;
-}
-
-G_GNUC_PURE
-static unsigned
-queue_count_same_priority(const struct queue *queue, unsigned start_order,
- uint8_t priority)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start_order <= queue->length);
-
- for (unsigned order = start_order; order < queue->length; ++order) {
- const unsigned position = queue_order_to_position(queue, order);
- const struct queue_item *item = &queue->items[position];
- if (item->priority != priority)
- return order - start_order;
- }
-
- return queue->length - start_order;
-}
-
-bool
-queue_set_priority(struct queue *queue, unsigned position, uint8_t priority,
- int after_order)
-{
- assert(queue != NULL);
- assert(position < queue->length);
-
- struct queue_item *item = &queue->items[position];
- uint8_t old_priority = item->priority;
- if (old_priority == priority)
- return false;
-
- item->version = queue->version;
- item->priority = priority;
-
- if (!queue->random)
- /* don't reorder if not in random mode */
- return true;
-
- unsigned order = queue_position_to_order(queue, position);
- if (after_order >= 0) {
- if (order == (unsigned)after_order)
- /* don't reorder the current song */
- return true;
-
- 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);
- const struct queue_item *after_item =
- &queue->items[after_position];
- if (old_priority > after_item->priority ||
- priority <= after_item->priority)
- /* priority hasn't become bigger */
- return true;
- }
- }
-
- /* move the item to the beginning of the priority group (or
- 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
- ? before_order - 1
- : before_order;
- queue_move_order(queue, order, new_order);
-
- /* shuffle the song within that priority group */
-
- const unsigned priority_count =
- queue_count_same_priority(queue, new_order, priority);
- assert(priority_count >= 1);
- queue_shuffle_order_first(queue, 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)
-{
- assert(queue != NULL);
- assert(start_position <= end_position);
- assert(end_position <= queue->length);
-
- bool modified = false;
- int after_position = after_order >= 0
- ? (int)queue_order_to_position(queue, 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)
- : -1;
-
- modified |= queue_set_priority(queue, i, priority,
- after_order);
- }
-
- return modified;
-}
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..4b59334c0 100644
--- a/src/replay_gain_config.h
+++ b/src/replay_gain_config.h
@@ -30,6 +30,10 @@ extern float replay_gain_preamp;
extern float replay_gain_missing_preamp;
extern bool replay_gain_limit;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void replay_gain_global_init(void);
/**
@@ -50,6 +54,10 @@ 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);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/replay_gain_info.h b/src/replay_gain_info.h
index 9097c3e02..b06dc6cf0 100644
--- a/src/replay_gain_info.h
+++ b/src/replay_gain_info.h
@@ -22,8 +22,12 @@
#include "check.h"
+#ifdef __cplusplus
+#include <cmath>
+#else
#include <stdbool.h>
#include <math.h>
+#endif
enum replay_gain_mode {
REPLAY_GAIN_AUTO = -2,
@@ -58,9 +62,17 @@ replay_gain_info_init(struct replay_gain_info *info)
static inline bool
replay_gain_tuple_defined(const struct replay_gain_tuple *tuple)
{
+#ifdef __cplusplus
+ return !std::isinf(tuple->gain);
+#else
return !isinf(tuple->gain);
+#endif
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
float
replay_gain_tuple_scale(const struct replay_gain_tuple *tuple, float preamp, float missing_preamp, bool peak_limit);
@@ -71,4 +83,8 @@ replay_gain_tuple_scale(const struct replay_gain_tuple *tuple, float preamp, flo
void
replay_gain_info_complete(struct replay_gain_info *info);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/resolver.h b/src/resolver.h
index e5ad06754..af14f5f23 100644
--- a/src/resolver.h
+++ b/src/resolver.h
@@ -20,18 +20,24 @@
#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)
{
return g_quark_from_static_string("resolver");
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Converts the specified socket address into a string in the form
* "IP:PORT". The return value must be freed with g_free() when you
@@ -42,7 +48,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);
@@ -61,4 +67,8 @@ resolve_host_port(const char *host_port, unsigned default_port,
int flags, int socktype,
GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/song.h b/src/song.h
index 8b97d45d0..db582beea 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,23 @@ struct song {
char uri[sizeof(int)];
};
+/**
+ * A dummy #directory instance that is used for "detached" song
+ * copies.
+ */
+extern struct Directory detached_root;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/** 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 +84,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 +95,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 +160,8 @@ 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] == '/';
+#ifdef __cplusplus
}
+#endif
#endif
diff --git a/src/state_file.c b/src/state_file.c
deleted file mode 100644
index de0e70538..000000000
--- a/src/state_file.c
+++ /dev/null
@@ -1,162 +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 "state_file.h"
-#include "output_state.h"
-#include "playlist.h"
-#include "playlist_state.h"
-#include "volume.h"
-#include "text_file.h"
-
-#include <glib.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "state_file"
-
-static char *state_file_path;
-
-/** the GLib source id for the save timer */
-static guint save_state_source_id;
-
-/**
- * These version numbers determine whether we need to save the state
- * file. If nothing has changed, we won't let the hard drive spin up.
- */
-static unsigned prev_volume_version, prev_output_version,
- prev_playlist_version;
-
-static void
-state_file_write(struct player_control *pc)
-{
- FILE *fp;
-
- assert(state_file_path != NULL);
-
- g_debug("Saving state file %s", state_file_path);
-
- fp = fopen(state_file_path, "w");
- if (G_UNLIKELY(!fp)) {
- g_warning("failed to create %s: %s",
- state_file_path, g_strerror(errno));
- return;
- }
-
- save_sw_volume_state(fp);
- audio_output_state_save(fp);
- playlist_state_save(fp, &g_playlist, 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);
-}
-
-static void
-state_file_read(struct player_control *pc)
-{
- 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)) {
- 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) {
- success = read_sw_volume_state(line) ||
- audio_output_state_read(line) ||
- playlist_state_restore(line, fp, buffer,
- &g_playlist, 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);
-}
-
-/**
- * This function is called every 5 minutes by the GLib main loop, and
- * saves the state file.
- */
-static gboolean
-timer_save_state_file(gpointer data)
-{
- struct player_control *pc = 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))
- /* nothing has changed - don't save the state file,
- don't spin up the hard disk */
- return true;
-
- state_file_write(pc);
- return true;
-}
-
-void
-state_file_init(const char *path, struct player_control *pc)
-{
- assert(state_file_path == NULL);
-
- if (path == NULL)
- return;
-
- state_file_path = g_strdup(path);
- state_file_read(pc);
-
- save_state_source_id = g_timeout_add_seconds(5 * 60,
- timer_save_state_file,
- pc);
-}
-
-void
-state_file_finish(struct player_control *pc)
-{
- if (state_file_path == NULL)
- /* no state file configured, no cleanup required */
- return;
-
- if (save_state_source_id != 0)
- g_source_remove(save_state_source_id);
-
- state_file_write(pc);
-
- g_free(state_file_path);
-}
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.c b/src/string_util.c
index 6e5429076..b76b257ba 100644
--- a/src/string_util.c
+++ b/src/string_util.c
@@ -20,6 +20,8 @@
#include "config.h"
#include "string_util.h"
+#include <stdlib.h> /* for malloc() */
+#include <string.h> /* for strnlen() */
#include <glib.h>
#include <assert.h>
@@ -45,3 +47,22 @@ string_array_contains(const char *const* haystack, const char *needle)
return false;
}
+
+#if !defined(HAVE_STRNDUP)
+
+char *
+strndup(const char *str, size_t n)
+{
+ assert(str != NULL);
+
+ size_t len = strnlen(str, n);
+ char* ret = (char *) malloc(len + 1);
+ if (ret == NULL)
+ return NULL;
+
+ memcpy(ret, str, len);
+ ret[len] = '\0';
+ return ret;
+}
+
+#endif
diff --git a/src/string_util.h b/src/string_util.h
index dc80a46ef..374fd0f91 100644
--- a/src/string_util.h
+++ b/src/string_util.h
@@ -20,18 +20,26 @@
#ifndef MPD_STRING_UTIL_H
#define MPD_STRING_UTIL_H
-#include <glib.h>
+#include "gcc.h"
#include <stdbool.h>
+#include <stdlib.h> /* for size_t */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
/**
* 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 +48,7 @@ deconst_string(const char *p)
};
return u.out;
+#endif
}
/**
@@ -49,14 +58,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)
{
@@ -74,4 +83,25 @@ strchug_fast(char *p)
bool
string_array_contains(const char *const* haystack, const char *needle);
+#if !defined(HAVE_STRNDUP)
+
+/**
+ * Duplicates the string to a newly allocated buffer
+ * copying at most n characters.
+ *
+ * @param str a string to duplicate
+ * @param n maximal number of characters to copy
+ * @return a pointer to the duplicated string,
+ * or NULL if memory allocation failed.
+ */
+gcc_malloc
+char *
+strndup(const char *str, size_t n);
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
#endif
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.h b/src/tag.h
index 2556cf3a7..3b47148a4 100644
--- a/src/tag.h
+++ b/src/tag.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
@@ -101,6 +101,10 @@ struct tag {
unsigned num_items;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Parse the string, and convert it into a #tag_type. Returns
* #TAG_NUM_OF_ITEM_TYPES if the string could not be recognized.
@@ -236,4 +240,8 @@ bool tag_has_type(const struct tag *tag, enum tag_type type);
*/
bool tag_equal(const struct tag *tag1, const struct tag *tag2);
+#ifdef __cplusplus
+}
+#endif
+
#endif
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/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/text_input_stream.c b/src/text_input_stream.c
index 4a858fc85..4a2eeb817 100644
--- a/src/text_input_stream.c
+++ b/src/text_input_stream.c
@@ -20,7 +20,7 @@
#include "config.h"
#include "text_input_stream.h"
#include "input_stream.h"
-#include "fifo_buffer.h"
+#include "util/fifo_buffer.h"
#include <glib.h>
diff --git a/src/thread/Cond.hxx b/src/thread/Cond.hxx
new file mode 100644
index 000000000..bbaabddc2
--- /dev/null
+++ b/src/thread/Cond.hxx
@@ -0,0 +1,37 @@
+/*
+ * 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_COND_HXX
+#define MPD_THREAD_COND_HXX
+
+#ifdef WIN32
+
+/* mingw-w64 4.6.3 lacks a std::cond implementation */
+
+#include "WindowsCond.hxx"
+typedef WindowsCond Cond;
+
+#else
+
+#include "PosixCond.hxx"
+typedef PosixCond Cond;
+
+#endif
+
+#endif
diff --git a/src/thread/CriticalSection.hxx b/src/thread/CriticalSection.hxx
new file mode 100644
index 000000000..8bc05b8f5
--- /dev/null
+++ b/src/thread/CriticalSection.hxx
@@ -0,0 +1,68 @@
+/*
+ * 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>
+
+/**
+ * Wrapper for a CRITICAL_SECTION, backend for the Mutex class.
+ */
+class CriticalSection {
+ friend class WindowsCond;
+
+ 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/GLibCond.hxx b/src/thread/GLibCond.hxx
new file mode 100644
index 000000000..9ab08e9fd
--- /dev/null
+++ b/src/thread/GLibCond.hxx
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 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_GLIB_COND_HXX
+#define MPD_THREAD_GLIB_COND_HXX
+
+#include "GLibMutex.hxx"
+
+/**
+ * A wrapper for GCond.
+ */
+class GLibCond {
+#if GLIB_CHECK_VERSION(2,32,0)
+ GCond cond;
+#else
+ GCond *cond;
+#endif
+
+public:
+ GLibCond() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ g_cond_init(&cond);
+#else
+ cond = g_cond_new();
+#endif
+ }
+
+ ~GLibCond() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ g_cond_clear(&cond);
+#else
+ g_cond_free(cond);
+#endif
+ }
+
+ GLibCond(const GLibCond &other) = delete;
+ GLibCond &operator=(const GLibCond &other) = delete;
+
+private:
+ GCond *GetNative() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ return &cond;
+#else
+ return cond;
+#endif
+ }
+
+public:
+ void signal() {
+ g_cond_signal(GetNative());
+ }
+
+ void broadcast() {
+ g_cond_broadcast(GetNative());
+ }
+
+ void wait(GLibMutex &mutex) {
+ g_cond_wait(GetNative(), mutex.GetNative());
+ }
+};
+
+#endif
diff --git a/src/thread/GLibMutex.hxx b/src/thread/GLibMutex.hxx
new file mode 100644
index 000000000..2c666c1ea
--- /dev/null
+++ b/src/thread/GLibMutex.hxx
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 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_GLIB_MUTEX_HXX
+#define MPD_THREAD_GLIB_MUTEX_HXX
+
+#include <glib.h>
+
+/**
+ * A wrapper for GMutex.
+ */
+class GLibMutex {
+ friend class GLibCond;
+
+#if GLIB_CHECK_VERSION(2,32,0)
+ GMutex mutex;
+#else
+ GMutex *mutex;
+#endif
+
+public:
+ GLibMutex() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ g_mutex_init(&mutex);
+#else
+ mutex = g_mutex_new();
+#endif
+ }
+
+ ~GLibMutex() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ g_mutex_clear(&mutex);
+#else
+ g_mutex_free(mutex);
+#endif
+ }
+
+ GLibMutex(const GLibMutex &other) = delete;
+ GLibMutex &operator=(const GLibMutex &other) = delete;
+
+private:
+ GMutex *GetNative() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ return &mutex;
+#else
+ return mutex;
+#endif
+ }
+
+public:
+ void lock() {
+ g_mutex_lock(GetNative());
+ }
+
+ bool try_lock() {
+ return g_mutex_trylock(GetNative());
+ }
+
+ void unlock() {
+ g_mutex_lock(GetNative());
+ }
+};
+
+#endif
diff --git a/src/thread/Mutex.hxx b/src/thread/Mutex.hxx
new file mode 100644
index 000000000..675af74b1
--- /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;
+
+#else
+
+#include "PosixMutex.hxx"
+
+typedef PosixMutex Mutex;
+
+#endif
+
+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;
+};
+
+#endif
diff --git a/src/thread/PosixCond.hxx b/src/thread/PosixCond.hxx
new file mode 100644
index 000000000..acdc05edc
--- /dev/null
+++ b/src/thread/PosixCond.hxx
@@ -0,0 +1,60 @@
+/*
+ * 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_POSIX_COND_HXX
+#define MPD_THREAD_POSIX_COND_HXX
+
+#include "PosixMutex.hxx"
+
+/**
+ * Low-level wrapper for a pthread_cond_t.
+ */
+class PosixCond {
+ pthread_cond_t cond;
+
+public:
+ constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {}
+
+ PosixCond(const PosixCond &other) = delete;
+ PosixCond &operator=(const PosixCond &other) = delete;
+
+ void signal() {
+ pthread_cond_signal(&cond);
+ }
+
+ void broadcast() {
+ pthread_cond_broadcast(&cond);
+ }
+
+ void wait(PosixMutex &mutex) {
+ pthread_cond_wait(&cond, &mutex.mutex);
+ }
+};
+
+#endif
diff --git a/src/thread/PosixMutex.hxx b/src/thread/PosixMutex.hxx
new file mode 100644
index 000000000..d50764af4
--- /dev/null
+++ b/src/thread/PosixMutex.hxx
@@ -0,0 +1,62 @@
+/*
+ * 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_POSIX_MUTEX_HXX
+#define MPD_THREAD_POSIX_MUTEX_HXX
+
+#include <pthread.h>
+
+/**
+ * Low-level wrapper for a pthread_mutex_t.
+ */
+class PosixMutex {
+ friend class PosixCond;
+
+ pthread_mutex_t mutex;
+
+public:
+ constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {}
+
+ PosixMutex(const PosixMutex &other) = delete;
+ PosixMutex &operator=(const PosixMutex &other) = delete;
+
+ void lock() {
+ pthread_mutex_lock(&mutex);
+ }
+
+ bool try_lock() {
+ return pthread_mutex_trylock(&mutex) == 0;
+ }
+
+ void unlock() {
+ pthread_mutex_unlock(&mutex);
+ }
+};
+
+#endif
diff --git a/src/thread/WindowsCond.hxx b/src/thread/WindowsCond.hxx
new file mode 100644
index 000000000..f4e909c72
--- /dev/null
+++ b/src/thread/WindowsCond.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_WINDOWS_COND_HXX
+#define MPD_THREAD_WINDOWS_COND_HXX
+
+#include "CriticalSection.hxx"
+
+/**
+ * Wrapper for a CONDITION_VARIABLE, backend for the Cond class.
+ */
+class WindowsCond {
+ CONDITION_VARIABLE cond;
+
+public:
+ WindowsCond() {
+ InitializeConditionVariable(&cond);
+ }
+
+ WindowsCond(const WindowsCond &other) = delete;
+ WindowsCond &operator=(const WindowsCond &other) = delete;
+
+ void signal() {
+ WakeConditionVariable(&cond);
+ }
+
+ void broadcast() {
+ WakeAllConditionVariable(&cond);
+ }
+
+ void wait(CriticalSection &mutex) {
+ SleepConditionVariableCS(&cond, &mutex.critical_section,
+ INFINITE);
+ }
+};
+
+#endif
diff --git a/src/timer.h b/src/timer.h
index 184881249..1506c9173 100644
--- a/src/timer.h
+++ b/src/timer.h
@@ -30,6 +30,10 @@ struct timer {
int rate;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct timer *timer_new(const struct audio_format *af);
void timer_free(struct timer *timer);
@@ -48,4 +52,8 @@ timer_delay(const struct timer *timer);
void timer_sync(struct timer *timer);
+#ifdef __cplusplus
+}
+#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/inotify_queue.h b/src/util/LazyRandomEngine.cxx
index cfc28ebfe..0f90ebb2e 100644
--- a/src/inotify_queue.h
+++ b/src/util/LazyRandomEngine.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,16 +17,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INOTIFY_QUEUE_H
-#define MPD_INOTIFY_QUEUE_H
+#include "config.h"
+#include "LazyRandomEngine.hxx"
void
-mpd_inotify_queue_init(void);
+LazyRandomEngine::AutoCreate()
+{
+ if (engine != nullptr)
+ return;
-void
-mpd_inotify_queue_finish(void);
-
-void
-mpd_inotify_enqueue(char *uri_utf8);
-
-#endif
+ std::random_device rd;
+ engine = new std::mt19937(rd());
+}
diff --git a/src/util/LazyRandomEngine.hxx b/src/util/LazyRandomEngine.hxx
new file mode 100644
index 000000000..8afe1d1c0
--- /dev/null
+++ b/src/util/LazyRandomEngine.hxx
@@ -0,0 +1,67 @@
+/*
+ * 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_LAZY_RANDOM_ENGINE_HXX
+#define MPD_LAZY_RANDOM_ENGINE_HXX
+
+#include "check.h"
+
+#include <random>
+
+#include <assert.h>
+
+/**
+ * A random engine that will be created and seeded on demand.
+ */
+class LazyRandomEngine {
+ std::mt19937 *engine;
+
+public:
+ typedef std::mt19937::result_type result_type;
+
+ LazyRandomEngine():engine(nullptr) {}
+ ~LazyRandomEngine() {
+ delete engine;
+ }
+
+ LazyRandomEngine(const LazyRandomEngine &other) = delete;
+ LazyRandomEngine &operator=(const LazyRandomEngine &other) = delete;
+
+ /**
+ * Create and seed the real engine. Call this before any
+ * other method.
+ */
+ void AutoCreate();
+
+ result_type min() const {
+ return engine->min();
+ }
+
+ result_type max() const {
+ return engine->max();
+ }
+
+ result_type operator()() {
+ assert(engine != nullptr);
+
+ return engine->operator()();
+ }
+};
+
+#endif
diff --git a/src/util/PeakBuffer.cxx b/src/util/PeakBuffer.cxx
new file mode 100644
index 000000000..a3659b8f4
--- /dev/null
+++ b/src/util/PeakBuffer.cxx
@@ -0,0 +1,143 @@
+/*
+ * 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 "PeakBuffer.hxx"
+#include "HugeAllocator.hxx"
+#include "fifo_buffer.h"
+
+#include <algorithm>
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+PeakBuffer::~PeakBuffer()
+{
+ if (normal_buffer != nullptr)
+ fifo_buffer_free(normal_buffer);
+
+ if (peak_buffer != nullptr)
+ HugeFree(peak_buffer, peak_size);
+}
+
+bool
+PeakBuffer::IsEmpty() const
+{
+ return (normal_buffer == nullptr ||
+ fifo_buffer_is_empty(normal_buffer)) &&
+ (peak_buffer == nullptr ||
+ fifo_buffer_is_empty(peak_buffer));
+}
+
+const void *
+PeakBuffer::Read(size_t *length_r) const
+{
+ if (normal_buffer != nullptr) {
+ const void *p = fifo_buffer_read(normal_buffer, length_r);
+ if (p != nullptr)
+ return p;
+ }
+
+ if (peak_buffer != nullptr) {
+ const void *p = fifo_buffer_read(peak_buffer, length_r);
+ if (p != nullptr)
+ return p;
+ }
+
+ return nullptr;
+}
+
+void
+PeakBuffer::Consume(size_t length)
+{
+ if (normal_buffer != nullptr && !fifo_buffer_is_empty(normal_buffer)) {
+ fifo_buffer_consume(normal_buffer, length);
+ return;
+ }
+
+ if (peak_buffer != nullptr && !fifo_buffer_is_empty(peak_buffer)) {
+ fifo_buffer_consume(peak_buffer, length);
+ if (fifo_buffer_is_empty(peak_buffer)) {
+ HugeFree(peak_buffer, peak_size);
+ peak_buffer = nullptr;
+ }
+
+ return;
+ }
+}
+
+static size_t
+AppendTo(fifo_buffer *buffer, const void *data, size_t length)
+{
+ assert(data != nullptr);
+ assert(length > 0);
+
+ size_t total = 0;
+
+ do {
+ size_t max_length;
+ void *p = fifo_buffer_write(buffer, &max_length);
+ if (p == nullptr)
+ break;
+
+ const size_t nbytes = std::min(length, max_length);
+ memcpy(p, data, nbytes);
+ fifo_buffer_append(buffer, nbytes);
+
+ data = (const uint8_t *)data + nbytes;
+ length -= nbytes;
+ total += nbytes;
+ } while (length > 0);
+
+ return total;
+}
+
+bool
+PeakBuffer::Append(const void *data, size_t length)
+{
+ if (length == 0)
+ return true;
+
+ if (peak_buffer != nullptr && !fifo_buffer_is_empty(peak_buffer)) {
+ size_t nbytes = AppendTo(peak_buffer, data, length);
+ return nbytes == length;
+ }
+
+ if (normal_buffer == nullptr)
+ normal_buffer = fifo_buffer_new(normal_size);
+
+ size_t nbytes = AppendTo(normal_buffer, data, length);
+ if (nbytes > 0) {
+ data = (const uint8_t *)data + nbytes;
+ length -= nbytes;
+ if (length == 0)
+ return true;
+ }
+
+ if (peak_buffer == nullptr && peak_size > 0) {
+ peak_buffer = (fifo_buffer *)HugeAllocate(peak_size);
+ if (peak_buffer == nullptr)
+ return false;
+
+ fifo_buffer_init(peak_buffer, peak_size);
+ }
+
+ nbytes = AppendTo(peak_buffer, data, length);
+ return nbytes == length;
+}
diff --git a/src/util/PeakBuffer.hxx b/src/util/PeakBuffer.hxx
new file mode 100644
index 000000000..0fbba8d77
--- /dev/null
+++ b/src/util/PeakBuffer.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_PEAK_BUFFER_HXX
+#define MPD_PEAK_BUFFER_HXX
+
+#include "gcc.h"
+
+#include <stddef.h>
+
+struct fifo_buffer;
+
+/**
+ * A FIFO-like buffer that will allocate more memory on demand to
+ * allow large peaks. This second buffer will be given back to the
+ * kernel when it has been consumed.
+ */
+class PeakBuffer {
+ size_t normal_size, peak_size;
+
+ fifo_buffer *normal_buffer, *peak_buffer;
+
+public:
+ PeakBuffer(size_t _normal_size, size_t _peak_size)
+ :normal_size(_normal_size), peak_size(_peak_size),
+ normal_buffer(nullptr), peak_buffer(nullptr) {}
+
+ PeakBuffer(PeakBuffer &&other)
+ :normal_size(other.normal_size), peak_size(other.peak_size),
+ normal_buffer(other.normal_buffer),
+ peak_buffer(other.peak_buffer) {
+ other.normal_buffer = nullptr;
+ other.peak_buffer = nullptr;
+ }
+
+ ~PeakBuffer();
+
+ PeakBuffer(const PeakBuffer &) = delete;
+ PeakBuffer &operator=(const PeakBuffer &) = delete;
+
+ gcc_pure
+ bool IsEmpty() const;
+
+ const void *Read(size_t *length_r) const;
+ void Consume(size_t length);
+
+ bool Append(const void *data, size_t length);
+};
+
+#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/fifo_buffer.c b/src/util/fifo_buffer.c
index 915fb0579..162ddf946 100644
--- a/src/fifo_buffer.c
+++ b/src/util/fifo_buffer.c
@@ -58,6 +58,14 @@ fifo_buffer_new(size_t size)
return buffer;
}
+void
+fifo_buffer_init(struct fifo_buffer *buffer, size_t size)
+{
+ buffer->size = size - (sizeof(*buffer) - sizeof(buffer->buffer));
+ buffer->start = 0;
+ buffer->end = 0;
+}
+
static void
fifo_buffer_move(struct fifo_buffer *buffer);
diff --git a/src/fifo_buffer.h b/src/util/fifo_buffer.h
index 3bdb23938..ccea97d86 100644
--- a/src/fifo_buffer.h
+++ b/src/util/fifo_buffer.h
@@ -46,6 +46,10 @@
struct fifo_buffer;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Creates a new #fifo_buffer object. Free this object with
* fifo_buffer_free().
@@ -56,6 +60,9 @@ struct fifo_buffer;
struct fifo_buffer *
fifo_buffer_new(size_t size);
+void
+fifo_buffer_init(struct fifo_buffer *buffer, size_t size);
+
/**
* Change the capacity of the #fifo_buffer, while preserving existing
* data.
@@ -150,4 +157,8 @@ fifo_buffer_is_empty(struct fifo_buffer *buffer);
bool
fifo_buffer_is_full(struct fifo_buffer *buffer);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/growing_fifo.c b/src/util/growing_fifo.c
index 88431f60e..88431f60e 100644
--- a/src/growing_fifo.c
+++ b/src/util/growing_fifo.c
diff --git a/src/growing_fifo.h b/src/util/growing_fifo.h
index 723c3b3ff..723c3b3ff 100644
--- a/src/growing_fifo.h
+++ b/src/util/growing_fifo.h
diff --git a/src/util/list.h b/src/util/list.h
index fdab47675..73d99befa 100644
--- a/src/util/list.h
+++ b/src/util/list.h
@@ -25,8 +25,6 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
-#include <glib.h>
-
#ifdef __clang__
/* allow typeof() */
#pragma GCC diagnostic ignored "-Wlanguage-extension-token"
@@ -40,15 +38,15 @@
*
*/
#define container_of(ptr, type, member) \
- (&G_STRUCT_MEMBER(type, ptr, -G_STRUCT_OFFSET(type, member)))
+ ((type *)((uint8_t *)ptr - offsetof(type, member)))
/*
* These are non-NULL pointers that will result in page faults
* 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 +80,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 +162,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..059d44fa3 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -20,17 +20,7 @@
#ifndef MPD_UTILS_H
#define MPD_UTILS_H
-#include <glib.h>
-#include <stdbool.h>
-
-#ifndef assert_static
-/* Compile time assertion developed by Ralf Holly */
-/* http://pera-software.com/articles/compile-time-assertions.pdf */
-#define assert_static(e) \
- do { \
- enum { assert_static__ = 1/(e) }; \
- } while (0)
-#endif /* !assert_static */
+#include "gerror.h"
char *
parsePath(const char *path, GError **error_r);
diff --git a/src/zeroconf-avahi.c b/src/zeroconf-avahi.c
index f2cc5359b..d2c6c084c 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>
@@ -234,13 +234,8 @@ void init_avahi(const char *serviceName)
if (!avahiClient) {
g_warning("Failed to create client: %s",
avahi_strerror(error));
- goto fail;
+ avahi_finish();
}
-
- return;
-
-fail:
- avahi_finish();
}
void avahi_finish(void)
diff --git a/src/zeroconf-bonjour.c b/src/zeroconf-bonjour.c
index 0f216aade..f3776ca29 100644
--- a/src/zeroconf-bonjour.c
+++ b/src/zeroconf-bonjour.c
@@ -19,7 +19,7 @@
#include "config.h"
#include "zeroconf-internal.h"
-#include "listen.h"
+#include "Listen.hxx"
#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>