aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bs/config.mk.default3
-rw-r--r--configure.ac2
-rw-r--r--src/Makefile.am10
-rw-r--r--src/audio.c19
-rw-r--r--src/audio.h6
-rw-r--r--src/audioOutput.c3
-rw-r--r--src/audioOutput.h5
-rw-r--r--src/audioOutputs/audioOutput_alsa.c254
-rw-r--r--src/audioOutputs/audioOutput_ao.c21
-rw-r--r--src/audioOutputs/audioOutput_jack.c17
-rw-r--r--src/audioOutputs/audioOutput_shout.c32
-rw-r--r--src/audio_format.h6
-rw-r--r--src/charConv.c23
-rw-r--r--src/command.c379
-rw-r--r--src/dbUtils.c159
-rw-r--r--src/dbUtils.h18
-rw-r--r--src/directory.c49
-rw-r--r--src/directory.h8
-rw-r--r--src/inputPlugin.h2
-rw-r--r--src/inputPlugins/_flac_common.c12
-rw-r--r--src/inputPlugins/_flac_common.h6
-rw-r--r--src/inputPlugins/aac_plugin.c8
-rw-r--r--src/inputPlugins/audiofile_plugin.c6
-rw-r--r--src/inputPlugins/flac_plugin.c20
-rw-r--r--src/inputPlugins/mod_plugin.c25
-rw-r--r--src/inputPlugins/mp3_plugin.c51
-rw-r--r--src/inputPlugins/mp4_plugin.c30
-rw-r--r--src/inputPlugins/mpc_plugin.c10
-rw-r--r--src/inputPlugins/oggflac_plugin.c6
-rw-r--r--src/inputPlugins/oggvorbis_plugin.c24
-rw-r--r--src/inputPlugins/wavpack_plugin.c25
-rw-r--r--src/list.c9
-rw-r--r--src/locate.c8
-rw-r--r--src/main.c2
-rw-r--r--src/metadata_pipe.c38
-rw-r--r--src/metadata_pipe.h14
-rw-r--r--src/mpd_types.h5
-rw-r--r--src/outputBuffer.c8
-rw-r--r--src/outputBuffer.h3
-rw-r--r--src/outputBuffer_ob_send.h2
-rw-r--r--src/path.c2
-rw-r--r--src/pcm_utils.c19
-rw-r--r--src/playlist.c336
-rw-r--r--src/playlist.h66
-rw-r--r--src/song.c43
-rw-r--r--src/song.h4
-rw-r--r--src/stats.c65
-rw-r--r--src/storedPlaylist.c167
-rw-r--r--src/storedPlaylist.h19
-rw-r--r--src/strset.c144
-rw-r--r--src/strset.h49
-rw-r--r--src/tag.c533
-rw-r--r--src/tag.h64
-rw-r--r--src/tagTracker.c155
-rw-r--r--src/tag_id3.c367
-rw-r--r--src/tag_id3.h (renamed from src/tagTracker.h)21
-rw-r--r--src/tag_pool.c141
-rw-r--r--src/tag_pool.h36
-rw-r--r--src/tree.c699
-rw-r--r--src/tree.h63
-rw-r--r--src/utf8.c18
-rw-r--r--src/utf8.h4
-rw-r--r--src/utils.h11
-rw-r--r--src/volume.c26
-rw-r--r--src/volume.h2
65 files changed, 2073 insertions, 2309 deletions
diff --git a/bs/config.mk.default b/bs/config.mk.default
index 765cda236..52c5829d6 100644
--- a/bs/config.mk.default
+++ b/bs/config.mk.default
@@ -10,7 +10,8 @@ CC = gcc
CFLAGS = -O2 -Wall \
-Wmissing-prototypes -Wextra -Wno-unused-parameter \
-Wno-deprecated-declarations -Wmissing-prototypes \
- -Wdeclaration-after-statement -Wshadow
+ -Wdeclaration-after-statement -Wshadow -Wpointer-arith \
+ -Wstrict-prototypes -Wcast-qual -Wwrite-strings
LDFLAGS = -Wl,-O1
diff --git a/configure.ac b/configure.ac
index 5630fe650..c3db4cbc1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,6 +32,8 @@ then
MPD_CHECK_FLAG([-Wshadow])
MPD_CHECK_FLAG([-Wpointer-arith])
MPD_CHECK_FLAG([-Wstrict-prototypes])
+ MPD_CHECK_FLAG([-Wcast-qual])
+ MPD_CHECK_FLAG([-Wwrite-strings])
fi
if test -z "$prefix" || test "x$prefix" = xNONE; then
diff --git a/src/Makefile.am b/src/Makefile.am
index 66eea5fff..15efaf78e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -82,9 +82,10 @@ mpd_headers = \
state_file.h \
stats.h \
tag.h \
- tagTracker.h \
- tree.h \
+ tag_pool.h \
+ tag_id3.h \
utf8.h \
+ strset.h \
utils.h \
volume.h \
ioops.h \
@@ -139,8 +140,9 @@ mpd_SOURCES = \
state_file.c \
stats.c \
tag.c \
- tagTracker.c \
- tree.c \
+ tag_pool.c \
+ tag_id3.c \
+ strset.c \
utils.c \
volume.c \
utf8.c \
diff --git a/src/audio.c b/src/audio.c
index 409761177..d4447bc7e 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -19,9 +19,7 @@
#include "audio.h"
#include "audioOutput.h"
#include "log.h"
-#include "command.h"
#include "path.h"
-#include "ack.h"
#include "myfprintf.h"
#include "os_compat.h"
@@ -419,7 +417,7 @@ void closeAudioDevice(void)
audioOpened = 0;
}
-void sendMetadataToAudioDevice(const MpdTag * tag)
+void sendMetadataToAudioDevice(const struct mpd_tag *tag)
{
unsigned int i;
@@ -428,13 +426,10 @@ void sendMetadataToAudioDevice(const MpdTag * tag)
}
}
-int enableAudioDevice(int fd, unsigned int device)
+int enableAudioDevice(unsigned int device)
{
- if (device >= audioOutputArraySize) {
- commandError(fd, ACK_ERROR_ARG, "audio output device id %i "
- "doesn't exist\n", device);
+ if (device >= audioOutputArraySize)
return -1;
- }
if (!(audioDeviceStates[device] & 0x01))
audioDeviceStates[device] = DEVICE_ENABLE;
@@ -442,13 +437,11 @@ int enableAudioDevice(int fd, unsigned int device)
return 0;
}
-int disableAudioDevice(int fd, unsigned int device)
+int disableAudioDevice(unsigned int device)
{
- if (device >= audioOutputArraySize) {
- commandError(fd, ACK_ERROR_ARG, "audio output device id %i "
- "doesn't exist\n", device);
+ if (device >= audioOutputArraySize)
return -1;
- }
+
if (audioDeviceStates[device] & 0x01)
audioDeviceStates[device] = DEVICE_DISABLE;
diff --git a/src/audio.h b/src/audio.h
index 0032cff22..7ff0d5d4a 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -55,13 +55,13 @@ int isAudioDeviceOpen(void);
int isCurrentAudioFormat(const AudioFormat * audioFormat);
-void sendMetadataToAudioDevice(const MpdTag * tag);
+void sendMetadataToAudioDevice(const struct mpd_tag *tag);
/* these functions are called in the main parent process while the child
process is busy playing to the audio */
-int enableAudioDevice(int fd, unsigned int device);
+int enableAudioDevice(unsigned int device);
-int disableAudioDevice(int fd, unsigned int device);
+int disableAudioDevice(unsigned int device);
void printAudioDevices(int fd);
diff --git a/src/audioOutput.c b/src/audioOutput.c
index f165979d0..d5b37f0fe 100644
--- a/src/audioOutput.c
+++ b/src/audioOutput.c
@@ -244,7 +244,8 @@ void finishAudioOutput(AudioOutput * audioOutput)
free(audioOutput->convBuffer);
}
-void sendMetadataToAudioOutput(AudioOutput * audioOutput, const MpdTag * tag)
+void sendMetadataToAudioOutput(AudioOutput * audioOutput,
+ const struct mpd_tag *tag)
{
if (!audioOutput->sendMetdataFunc)
return;
diff --git a/src/audioOutput.h b/src/audioOutput.h
index f82eedfba..4ce67a5de 100644
--- a/src/audioOutput.h
+++ b/src/audioOutput.h
@@ -50,7 +50,7 @@ typedef void (*AudioOutputDropBufferedAudioFunc) (AudioOutput * audioOutput);
typedef void (*AudioOutputCloseDeviceFunc) (AudioOutput * audioOutput);
typedef void (*AudioOutputSendMetadataFunc) (AudioOutput * audioOutput,
- const MpdTag * tag);
+ const struct mpd_tag *tag);
struct _AudioOutput {
int open;
@@ -104,7 +104,8 @@ void dropBufferedAudioOutput(AudioOutput * audioOutput);
void closeAudioOutput(AudioOutput * audioOutput);
void finishAudioOutput(AudioOutput * audioOutput);
int keepAudioOutputAlive(AudioOutput * audioOutput, int ms);
-void sendMetadataToAudioOutput(AudioOutput * audioOutput, const MpdTag * tag);
+void sendMetadataToAudioOutput(AudioOutput * audioOutput,
+ const struct mpd_tag *tag);
void printAllOutputPluginTypes(FILE * fp);
diff --git a/src/audioOutputs/audioOutput_alsa.c b/src/audioOutputs/audioOutput_alsa.c
index f0aa32713..b7b0a1151 100644
--- a/src/audioOutputs/audioOutput_alsa.c
+++ b/src/audioOutputs/audioOutput_alsa.c
@@ -23,6 +23,8 @@
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
+static const char default_device[] = "default";
+
#define MPD_ALSA_BUFFER_TIME_US 500000
/* the default period time of xmms is 50 ms, so let's use that as well.
* a user can tweak this parameter via the "period_time" config parameter.
@@ -36,26 +38,42 @@
#include <alsa/asoundlib.h>
+/* #define MPD_SND_PCM_NONBLOCK SND_PCM_NONBLOCK */
+#define MPD_SND_PCM_NONBLOCK 0
+
+/*
+ * This macro will evaluate both statements, but only returns the result
+ * of the second statement to the reader. Thus it'll stringify the
+ * command name and assign it to the scoped cmd variable.
+ * Note that ALSA is strictly for Linux , and anybody compiling
+ * on Linux will have gcc or a gcc-compatible compiler anyways.
+ */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+# define E(command , arg1 , ...) \
+ (err_cmd = #command, command( arg1 , __VA_ARGS__ ))
+#else /* ! C99, this works for gcc 2.95 at least: */
+# define E(command , arg1 , args...) \
+ (err_cmd = #command, command( arg1 , ##args ))
+#endif /* ! C99 */
+
typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
snd_pcm_uframes_t size);
typedef struct _AlsaData {
- char *device;
+ const char *device;
snd_pcm_t *pcmHandle;
alsa_writei_t *writei;
unsigned int buffer_time;
unsigned int period_time;
int sampleSize;
int useMmap;
- int canPause;
- int canResume;
} AlsaData;
static AlsaData *newAlsaData(void)
{
AlsaData *ret = xmalloc(sizeof(AlsaData));
- ret->device = NULL;
+ ret->device = default_device;
ret->pcmHandle = NULL;
ret->writei = snd_pcm_writei;
ret->useMmap = 0;
@@ -67,20 +85,27 @@ static AlsaData *newAlsaData(void)
static void freeAlsaData(AlsaData * ad)
{
- if (ad->device)
- free(ad->device);
-
+ if (ad->device && ad->device != default_device)
+ free(deconst_ptr(ad->device));
free(ad);
}
static int alsa_initDriver(AudioOutput * audioOutput, ConfigParam * param)
{
+ /* no need for pthread_once thread-safety when reading config */
+ static int free_global_registered;
AlsaData *ad = newAlsaData();
+ if (!free_global_registered) {
+ atexit((void(*)(void))snd_config_update_free_global);
+ free_global_registered = 1;
+ }
+
if (param) {
- BlockParam *bp = getBlockParam(param, "device");
- ad->device = bp ? xstrdup(bp->value) : xstrdup("default");
+ BlockParam *bp;
+ if ((bp = getBlockParam(param, "device")))
+ ad->device = xstrdup(bp->value);
ad->useMmap = getBoolBlockParam(param, "use_mmap", 1);
if (ad->useMmap == CONF_BOOL_UNSET)
ad->useMmap = 0;
@@ -88,8 +113,7 @@ static int alsa_initDriver(AudioOutput * audioOutput, ConfigParam * param)
ad->buffer_time = atoi(bp->value);
if ((bp = getBlockParam(param, "period_time")))
ad->period_time = atoi(bp->value);
- } else
- ad->device = xstrdup("default");
+ }
audioOutput->data = ad;
return 0;
@@ -106,12 +130,10 @@ static int alsa_testDefault(void)
{
snd_pcm_t *handle;
- int ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK,
- SND_PCM_NONBLOCK);
- snd_config_update_free_global();
-
+ int ret = snd_pcm_open(&handle, default_device,
+ SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (ret) {
- WARNING("Error opening default alsa device: %s\n",
+ WARNING("Error opening default ALSA device: %s\n",
snd_strerror(-ret));
return -1;
} else
@@ -120,6 +142,17 @@ static int alsa_testDefault(void)
return 0;
}
+static snd_pcm_format_t get_bitformat(const AudioFormat * af)
+{
+ switch (af->bits) {
+ case 8: return SND_PCM_FORMAT_S8;
+ case 16: return SND_PCM_FORMAT_S16;
+ case 24: return SND_PCM_FORMAT_S24;
+ case 32: return SND_PCM_FORMAT_S32;
+ }
+ return SND_PCM_FORMAT_UNKNOWN;
+}
+
static int alsa_openDevice(AudioOutput * audioOutput)
{
AlsaData *ad = audioOutput->data;
@@ -129,84 +162,57 @@ static int alsa_openDevice(AudioOutput * audioOutput)
snd_pcm_sw_params_t *swparams;
unsigned int sampleRate = audioFormat->sampleRate;
unsigned int channels = audioFormat->channels;
- snd_pcm_uframes_t alsa_buffer_size;
- snd_pcm_uframes_t alsa_period_size;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t period_size;
int err;
- const char *cmd = NULL;
+ const char *err_cmd = NULL;
int retry = MPD_ALSA_RETRY_NR;
unsigned int period_time, period_time_ro;
unsigned int buffer_time;
- switch (audioFormat->bits) {
- case 8:
- bitformat = SND_PCM_FORMAT_S8;
- break;
- case 16:
- bitformat = SND_PCM_FORMAT_S16;
- break;
- case 24:
- bitformat = SND_PCM_FORMAT_S24;
- break;
- case 32:
- bitformat = SND_PCM_FORMAT_S32;
- break;
- default:
+ if ((bitformat = get_bitformat(audioFormat)) == SND_PCM_FORMAT_UNKNOWN)
ERROR("ALSA device \"%s\" doesn't support %i bit audio\n",
ad->device, audioFormat->bits);
- return -1;
- }
- err = snd_pcm_open(&ad->pcmHandle, ad->device,
- SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
- snd_config_update_free_global();
+ err = E(snd_pcm_open, &ad->pcmHandle, ad->device,
+ SND_PCM_STREAM_PLAYBACK, MPD_SND_PCM_NONBLOCK);
if (err < 0) {
ad->pcmHandle = NULL;
goto error;
}
- cmd = "snd_pcm_nonblock";
- err = snd_pcm_nonblock(ad->pcmHandle, 0);
- if (err < 0)
+#if MPD_SND_PCM_NONBLOCK == SND_PCM_NONBLOCK
+ if ((err = E(snd_pcm_nonblock, ad->pcmHandle, 0)) < 0)
goto error;
+#endif /* MPD_SND_PCM_NONBLOCK == SND_PCM_NONBLOCK */
period_time_ro = period_time = ad->period_time;
configure_hw:
/* configure HW params */
snd_pcm_hw_params_alloca(&hwparams);
-
- cmd = "snd_pcm_hw_params_any";
- err = snd_pcm_hw_params_any(ad->pcmHandle, hwparams);
- if (err < 0)
+ if ((err = E(snd_pcm_hw_params_any, ad->pcmHandle, hwparams)) < 0)
goto error;
if (ad->useMmap) {
- err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
- SND_PCM_ACCESS_MMAP_INTERLEAVED);
- if (err < 0) {
- ERROR("Cannot set mmap'ed mode on alsa device \"%s\": "
- " %s\n", ad->device, snd_strerror(-err));
- ERROR("Falling back to direct write mode\n");
- ad->useMmap = 0;
- } else
+ if (!(err = snd_pcm_hw_params_set_access(ad->pcmHandle,
+ hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED))) {
ad->writei = snd_pcm_mmap_writei;
- }
-
- if (!ad->useMmap) {
- cmd = "snd_pcm_hw_params_set_access";
- err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
- SND_PCM_ACCESS_RW_INTERLEAVED);
- if (err < 0)
- goto error;
- ad->writei = snd_pcm_writei;
- }
+ } else {
+ ERROR("ALSA cannot enable mmap on device \"%s\": %s. "
+ "Falling back to direct write mode\n",
+ ad->device, snd_strerror(-err));
+ ad->useMmap = 0;
+ }
+ } else if ((err = E(snd_pcm_hw_params_set_access, ad->pcmHandle,
+ hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ goto error;
err = snd_pcm_hw_params_set_format(ad->pcmHandle, hwparams, bitformat);
if (err < 0) {
- ERROR("ALSA device \"%s\" does not support %i bit audio: "
- "%s\n", ad->device, audioFormat->bits, snd_strerror(-err));
+ ERROR("ALSA device \"%s\" does not support %i bit audio:%s\n",
+ ad->device, audioFormat->bits, snd_strerror(-err));
goto fail;
}
-
err = snd_pcm_hw_params_set_channels_near(ad->pcmHandle, hwparams,
&channels);
if (err < 0) {
@@ -227,92 +233,62 @@ configure_hw:
audioFormat->sampleRate = sampleRate;
buffer_time = ad->buffer_time;
- cmd = "snd_pcm_hw_params_set_buffer_time_near";
- err = snd_pcm_hw_params_set_buffer_time_near(ad->pcmHandle, hwparams,
- &buffer_time, NULL);
- if (err < 0)
+ if ((err = E(snd_pcm_hw_params_set_buffer_time_near, ad->pcmHandle,
+ hwparams, &buffer_time, NULL)) < 0)
goto error;
period_time = period_time_ro;
- cmd = "snd_pcm_hw_params_set_period_time_near";
- err = snd_pcm_hw_params_set_period_time_near(ad->pcmHandle, hwparams,
- &period_time, NULL);
- if (err < 0)
+
+ if ((err = E(snd_pcm_hw_params_set_period_time_near,
+ ad->pcmHandle, hwparams, &period_time, NULL)) < 0)
goto error;
- cmd = "snd_pcm_hw_params";
- err = snd_pcm_hw_params(ad->pcmHandle, hwparams);
+ err = E(snd_pcm_hw_params, ad->pcmHandle, hwparams);
if (err == -EPIPE && --retry > 0) {
period_time_ro = period_time_ro >> 1;
goto configure_hw;
} else if (err < 0)
goto error;
- if (retry != MPD_ALSA_RETRY_NR)
- DEBUG("ALSA period_time set to %d\n", period_time);
- cmd = "snd_pcm_hw_params_get_buffer_size";
- err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
- if (err < 0)
+ DEBUG("ALSA(%s) period_time: %u, buffer_time: %u\n",
+ ad->device, period_time, buffer_time);
+ if ((err = E(snd_pcm_hw_params_get_buffer_size, hwparams,
+ &buffer_size)) < 0)
goto error;
- cmd = "snd_pcm_hw_params_get_period_size";
- err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
- NULL);
- if (err < 0)
+ if ((err = E(snd_pcm_hw_params_get_period_size, hwparams,
+ &period_size, NULL)) < 0)
goto error;
-
- ad->canPause = snd_pcm_hw_params_can_pause(hwparams);
- ad->canResume = snd_pcm_hw_params_can_resume(hwparams);
+ DEBUG("ALSA(%s) period_size: %lu buffer_size: %lu\n",
+ ad->device, period_size, buffer_size);
/* configure SW params */
snd_pcm_sw_params_alloca(&swparams);
- cmd = "snd_pcm_sw_params_current";
- err = snd_pcm_sw_params_current(ad->pcmHandle, swparams);
- if (err < 0)
- goto error;
-
- cmd = "snd_pcm_sw_params_set_start_threshold";
- err = snd_pcm_sw_params_set_start_threshold(ad->pcmHandle, swparams,
- alsa_buffer_size -
- alsa_period_size);
- if (err < 0)
+ if ((err = E(snd_pcm_sw_params_current, ad->pcmHandle, swparams)) < 0)
goto error;
-
- cmd = "snd_pcm_sw_params_set_avail_min";
- err = snd_pcm_sw_params_set_avail_min(ad->pcmHandle, swparams,
- alsa_period_size);
- if (err < 0)
+ if ((err = E(snd_pcm_sw_params_set_start_threshold, ad->pcmHandle,
+ swparams, buffer_size - period_size)) < 0)
goto error;
-
- cmd = "snd_pcm_sw_params_set_xfer_align";
- err = snd_pcm_sw_params_set_xfer_align(ad->pcmHandle, swparams, 1);
- if (err < 0)
+ if ((err = E(snd_pcm_sw_params_set_avail_min, ad->pcmHandle,
+ swparams, period_size)) < 0)
goto error;
-
- cmd = "snd_pcm_sw_params";
- err = snd_pcm_sw_params(ad->pcmHandle, swparams);
- if (err < 0)
+ if ((err = E(snd_pcm_sw_params, ad->pcmHandle, swparams)) < 0)
goto error;
ad->sampleSize = (audioFormat->bits / 8) * audioFormat->channels;
audioOutput->open = 1;
- DEBUG("alsa device \"%s\" will be playing %i bit, %i channel audio at "
+ DEBUG("ALSA device \"%s\" will be playing %i bit, %i channel audio at "
"%i Hz\n", ad->device, (int)audioFormat->bits,
channels, sampleRate);
return 0;
error:
- if (cmd) {
- ERROR("Error opening alsa device \"%s\" (%s): %s\n",
- ad->device, cmd, snd_strerror(-err));
- } else {
- ERROR("Error opening alsa device \"%s\": %s\n", ad->device,
- snd_strerror(-err));
- }
+ ERROR("Error opening ALSA device \"%s\" (%s): %s\n",
+ ad->device, (err_cmd ? err_cmd : ""), snd_strerror(-err));
fail:
if (ad->pcmHandle)
snd_pcm_close(ad->pcmHandle);
@@ -323,24 +299,25 @@ fail:
static int alsa_errorRecovery(AlsaData * ad, int err)
{
- if (err == -EPIPE) {
- DEBUG("Underrun on alsa device \"%s\"\n", ad->device);
- } else if (err == -ESTRPIPE) {
- DEBUG("alsa device \"%s\" was suspended\n", ad->device);
- }
+ snd_pcm_state_t state = snd_pcm_state(ad->pcmHandle);
+ const char *err_cmd = NULL;
+
+ if (err == -EPIPE)
+ DEBUG("Underrun on ALSA device \"%s\"\n", ad->device);
+ else if (err == -ESTRPIPE)
+ DEBUG("ALSA device \"%s\" was suspended\n", ad->device);
- switch (snd_pcm_state(ad->pcmHandle)) {
+ switch (state) {
case SND_PCM_STATE_PAUSED:
- err = snd_pcm_pause(ad->pcmHandle, /* disable */ 0);
+ err = E(snd_pcm_pause, ad->pcmHandle, /* disable */ 0);
break;
case SND_PCM_STATE_SUSPENDED:
- err = ad->canResume ?
- snd_pcm_resume(ad->pcmHandle) :
- snd_pcm_prepare(ad->pcmHandle);
- break;
+ if ((err = E(snd_pcm_resume, ad->pcmHandle)) == -EAGAIN)
+ return 0;
+ /* fall-through to snd_pcm_prepare: */
case SND_PCM_STATE_SETUP:
case SND_PCM_STATE_XRUN:
- err = snd_pcm_prepare(ad->pcmHandle);
+ err = E(snd_pcm_prepare, ad->pcmHandle);
break;
case SND_PCM_STATE_DISCONNECTED:
/* so alsa_closeDevice won't try to drain: */
@@ -349,13 +326,20 @@ static int alsa_errorRecovery(AlsaData * ad, int err)
break;
/* this is no error, so just keep running */
case SND_PCM_STATE_RUNNING:
- err = 0;
+ if (mpd_unlikely(err)) {
+ DEBUG("ALSA(%s) ignoring possible error: %s\n",
+ ad->device, snd_strerror(-err));
+ err = 0;
+ }
break;
default:
- /* unknown state, do nothing */
+ DEBUG("ALSA device \"%s\" in unknown state: %s\n",
+ ad->device, snd_pcm_state_name(state));
break;
}
-
+ if (err && err_cmd)
+ ERROR("ALSA error on device \"%s\" (%s): %s\n",
+ ad->device, err_cmd, snd_strerror(-err));
return err;
}
@@ -397,7 +381,7 @@ static int alsa_playAudio(AudioOutput * audioOutput,
if (ret < 0) {
if (alsa_errorRecovery(ad, ret) < 0) {
- ERROR("closing alsa device \"%s\" due to write "
+ ERROR("closing ALSA device \"%s\" due to write "
"error: %s\n", ad->device,
snd_strerror(-errno));
alsa_closeDevice(audioOutput);
diff --git a/src/audioOutputs/audioOutput_ao.c b/src/audioOutputs/audioOutput_ao.c
index e7e201add..ed8eaa796 100644
--- a/src/audioOutputs/audioOutput_ao.c
+++ b/src/audioOutputs/audioOutput_ao.c
@@ -199,23 +199,6 @@ static int audioOutputAo_openDevice(AudioOutput * audioOutput)
return 0;
}
-/**
- * For whatever reason, libao wants a non-const pointer. Let's hope
- * it does not write to the buffer, and use the union deconst hack to
- * work around this API misdesign.
- */
-static int ao_play_deconst(ao_device *device, const void *output_samples,
- uint_32 num_bytes)
-{
- union {
- const void *in;
- void *out;
- } u;
-
- u.in = output_samples;
- return ao_play(device, u.out, num_bytes);
-}
-
static int audioOutputAo_play(AudioOutput * audioOutput,
const char *playChunk, size_t size)
{
@@ -229,7 +212,9 @@ static int audioOutputAo_play(AudioOutput * audioOutput,
chunk_size = (size_t)ad->writeSize > size
? size : (size_t)ad->writeSize;
- if (ao_play_deconst(ad->device, playChunk, chunk_size) == 0) {
+ if (!ao_play(ad->device,
+ (char *)deconst_ptr(playChunk),
+ (uint_32)chunk_size)) {
audioOutputAo_error();
ERROR("closing audio device due to write error\n");
audioOutputAo_closeDevice(audioOutput);
diff --git a/src/audioOutputs/audioOutput_jack.c b/src/audioOutputs/audioOutput_jack.c
index 8818bb739..77ee077a4 100644
--- a/src/audioOutputs/audioOutput_jack.c
+++ b/src/audioOutputs/audioOutput_jack.c
@@ -31,8 +31,8 @@ static const size_t sample_size = sizeof(jack_default_audio_sample_t);
typedef struct _JackData {
/* configuration */
- char *name;
- char *output_ports[2];
+ const char *name;
+ const char *output_ports[2];
int ringbuf_sz;
/* locks */
@@ -52,6 +52,7 @@ typedef struct _JackData {
static JackData *newJackData(void)
{
JackData *ret;
+
ret = xcalloc(sizeof(JackData), 1);
ret->name = "mpd";
@@ -97,12 +98,12 @@ static void freeJackData(AudioOutput *audioOutput)
freeJackClient(jd);
if (strcmp(jd->name, "mpd") != 0)
- free(jd->name);
+ free(deconst_ptr(jd->name));
for ( i = ARRAY_SIZE(jd->output_ports); --i >= 0; ) {
if (!jd->output_ports[i])
continue;
- free(jd->output_ports[i]);
+ free(deconst_ptr(jd->output_ports[i]));
}
free(jd);
@@ -267,7 +268,7 @@ static int jack_testDefault(void)
static int connect_jack(AudioOutput *audioOutput)
{
JackData *jd = audioOutput->data;
- char **jports;
+ const char **jports;
char *port_name;
if ( (jd->client = jack_client_new(jd->name)) == NULL ) {
@@ -304,9 +305,9 @@ static int connect_jack(AudioOutput *audioOutput)
/* hay que buscar que hay */
if ( !jd->output_ports[1]
- && (jports = (char **)jack_get_ports(jd->client, NULL, NULL,
- JackPortIsPhysical|
- JackPortIsInput)) ) {
+ && (jports = jack_get_ports(jd->client, NULL, NULL,
+ JackPortIsPhysical|
+ JackPortIsInput)) ) {
jd->output_ports[0] = jports[0];
jd->output_ports[1] = jports[1] ? jports[1] : jports[0];
DEBUG("output_ports: %s %s\n",
diff --git a/src/audioOutputs/audioOutput_shout.c b/src/audioOutputs/audioOutput_shout.c
index 49d69eebd..2ee942809 100644
--- a/src/audioOutputs/audioOutput_shout.c
+++ b/src/audioOutputs/audioOutput_shout.c
@@ -56,7 +56,7 @@ typedef struct _ShoutData {
int opened;
- MpdTag *tag;
+ struct mpd_tag *tag;
int tagToSend;
int timeout;
@@ -93,7 +93,7 @@ static void freeShoutData(ShoutData * sd)
if (sd->shoutConn)
shout_free(sd->shoutConn);
if (sd->tag)
- freeMpdTag(sd->tag);
+ tag_free(sd->tag);
if (sd->timer)
timer_free(sd->timer);
@@ -390,11 +390,10 @@ static void myShout_closeDevice(AudioOutput * audioOutput)
static void addTag(ShoutData *sd, const char *name, char *value)
{
- if (value) {
- union const_hack u;
- u.in = name;
- vorbis_comment_add_tag(&(sd->vc), u.out, value);
- }
+ if (value)
+ vorbis_comment_add_tag(&(sd->vc),
+ (char *)deconst_ptr(name),
+ value);
}
static void copyTagToVorbisComment(ShoutData * sd)
@@ -403,15 +402,15 @@ static void copyTagToVorbisComment(ShoutData * sd)
int i;
for (i = 0; i < sd->tag->numOfItems; i++) {
- switch (sd->tag->items[i].type) {
+ switch (sd->tag->items[i]->type) {
case TAG_ITEM_ARTIST:
- addTag(sd, "ARTIST", sd->tag->items[i].value);
+ addTag(sd, "ARTIST", sd->tag->items[i]->value);
break;
case TAG_ITEM_ALBUM:
- addTag(sd, "ALBUM", sd->tag->items[i].value);
+ addTag(sd, "ALBUM", sd->tag->items[i]->value);
break;
case TAG_ITEM_TITLE:
- addTag(sd, "TITLE", sd->tag->items[i].value);
+ addTag(sd, "TITLE", sd->tag->items[i]->value);
break;
default:
break;
@@ -594,7 +593,7 @@ static void myShout_sendMetadata(ShoutData * sd)
}
}
- /*if(sd->tag) freeMpdTag(sd->tag);
+ /*if(sd->tag) tag_free(sd->tag);
sd->tag = NULL; */
sd->tagToSend = 0;
}
@@ -637,7 +636,8 @@ static int myShout_play(AudioOutput * audioOutput,
for (i = 0; i < samples; i++) {
for (j = 0; j < sd->audioFormat->channels; j++) {
- vorbbuf[j][i] = (*((mpd_sint16 *) playChunk)) / 32768.0;
+ vorbbuf[j][i] = (*((const mpd_sint16 *) playChunk))
+ / 32768.0;
playChunk += bytes;
}
}
@@ -663,19 +663,19 @@ static int myShout_play(AudioOutput * audioOutput,
return 0;
}
-static void myShout_setTag(AudioOutput * audioOutput, MpdTag * tag)
+static void myShout_setTag(AudioOutput * audioOutput, const struct mpd_tag *tag)
{
ShoutData *sd = (ShoutData *) audioOutput->data;
if (sd->tag)
- freeMpdTag(sd->tag);
+ tag_free(sd->tag);
sd->tag = NULL;
sd->tagToSend = 0;
if (!tag)
return;
- sd->tag = mpdTagDup(tag);
+ sd->tag = tag_dup(tag);
sd->tagToSend = 1;
}
diff --git a/src/audio_format.h b/src/audio_format.h
index a6e97e046..ade9d8810 100644
--- a/src/audio_format.h
+++ b/src/audio_format.h
@@ -22,9 +22,9 @@
#include "mpd_types.h"
typedef struct _AudioFormat {
- volatile mpd_sint8 channels;
- volatile mpd_uint32 sampleRate;
- volatile mpd_sint8 bits;
+ mpd_sint8 channels;
+ mpd_uint32 sampleRate;
+ mpd_sint8 bits;
} AudioFormat;
static inline double audio_format_time_to_size(const AudioFormat * af)
diff --git a/src/charConv.c b/src/charConv.c
index c205e93e4..60bcc655a 100644
--- a/src/charConv.c
+++ b/src/charConv.c
@@ -95,22 +95,6 @@ int setCharSetConversion(const char *to, const char *from)
#endif
}
-#ifdef HAVE_ICONV
-static inline size_t deconst_iconv(iconv_t cd,
- const char **inbuf, size_t *inbytesleft,
- char **outbuf, size_t *outbytesleft)
-{
- union {
- const char **a;
- char **b;
- } deconst;
-
- deconst.a = inbuf;
-
- return iconv(cd, deconst.b, inbytesleft, outbuf, outbytesleft);
-}
-#endif
-
char *char_conv_str(char *dest, const char *string)
{
if (!char_conv_to)
@@ -132,11 +116,12 @@ char *char_conv_str(char *dest, const char *string)
dest[0] = '\0';
while (inleft) {
+ char *inbuf = deconst_ptr(string);
+
bufferPtr = buffer;
outleft = BUFFER_SIZE;
- err =
- deconst_iconv(char_conv_iconv, &string, &inleft,
- &bufferPtr, &outleft);
+ err = iconv(char_conv_iconv, &inbuf,
+ &inleft, &bufferPtr, &outleft);
if (outleft == BUFFER_SIZE
|| (err == (size_t)-1L && errno != E2BIG)) {
return NULL;
diff --git a/src/command.c b/src/command.c
index 805addde1..ebca41bde 100644
--- a/src/command.c
+++ b/src/command.c
@@ -218,6 +218,54 @@ static int mpd_fprintf__ check_int(int fd, int *dst,
return 0;
}
+static int print_playlist_result(int fd, enum playlist_result result)
+{
+ switch (result) {
+ case PLAYLIST_RESULT_SUCCESS:
+ return 0;
+
+ case PLAYLIST_RESULT_ERRNO:
+ commandError(fd, ACK_ERROR_SYSTEM, strerror(errno));
+ return -1;
+
+ case PLAYLIST_RESULT_NO_SUCH_SONG:
+ commandError(fd, ACK_ERROR_NO_EXIST, "No such song");
+ return -1;
+
+ case PLAYLIST_RESULT_NO_SUCH_LIST:
+ commandError(fd, ACK_ERROR_NO_EXIST, "No such playlist");
+ return -1;
+
+ case PLAYLIST_RESULT_LIST_EXISTS:
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "Playlist already exists");
+ return -1;
+
+ case PLAYLIST_RESULT_BAD_NAME:
+ commandError(fd, ACK_ERROR_ARG,
+ "playlist name is invalid: "
+ "playlist names may not contain slashes,"
+ " newlines or carriage returns");
+ return -1;
+
+ case PLAYLIST_RESULT_BAD_RANGE:
+ commandError(fd, ACK_ERROR_ARG, "Bad song index");
+ return -1;
+
+ case PLAYLIST_RESULT_NOT_PLAYING:
+ commandError(fd, ACK_ERROR_PLAYER_SYNC, "Not playing");
+ return -1;
+
+ case PLAYLIST_RESULT_TOO_LARGE:
+ commandError(fd, ACK_ERROR_PLAYLIST_MAX,
+ "playlist is at the max size");
+ return -1;
+ }
+
+ assert(0);
+ return -1;
+}
+
static void addCommand(const char *name,
int reqPermission,
int minargs,
@@ -245,7 +293,7 @@ static int handleUrlHandlers(int fd, mpd_unused int *permission,
static int handleTagTypes(int fd, mpd_unused int *permission,
mpd_unused int argc, mpd_unused char *argv[])
{
- printTagTypes(fd);
+ tag_print_types(fd);
return 0;
}
@@ -253,21 +301,25 @@ static int handlePlay(int fd, mpd_unused int *permission,
int argc, char *argv[])
{
int song = -1;
+ enum playlist_result result;
if (argc == 2 && check_int(fd, &song, argv[1], need_positive) < 0)
return -1;
- return playPlaylist(fd, song, 0);
+ result = playPlaylist(song, 0);
+ return print_playlist_result(fd, result);
}
static int handlePlayId(int fd, mpd_unused int *permission,
int argc, char *argv[])
{
int id = -1;
+ enum playlist_result result;
if (argc == 2 && check_int(fd, &id, argv[1], need_positive) < 0)
return -1;
- return playPlaylistById(fd, id, 0);
+ result = playPlaylistById(id, 0);
+ return print_playlist_result(fd, result);
}
static int handleStop(mpd_unused int fd, mpd_unused int *permission,
@@ -281,11 +333,13 @@ static int handleCurrentSong(int fd, mpd_unused int *permission,
mpd_unused int argc, mpd_unused char *argv[])
{
int song = getPlaylistCurrentSong();
+ enum playlist_result result;
- if (song >= 0) {
- return playlistInfo(fd, song);
- } else
+ if (song < 0)
return 0;
+
+ result = playlistInfo(fd, song);
+ return print_playlist_result(fd, result);
}
static int handlePause(int fd, mpd_unused int *permission,
@@ -323,44 +377,50 @@ static int commandStatus(mpd_unused int fd, mpd_unused int *permission,
break;
}
- fdprintf(fd, "%s: %i\n", COMMAND_STATUS_VOLUME, getVolumeLevel());
- fdprintf(fd, "%s: %i\n", COMMAND_STATUS_REPEAT,
- getPlaylistRepeatStatus());
- fdprintf(fd, "%s: %i\n", COMMAND_STATUS_RANDOM,
- getPlaylistRandomStatus());
- fdprintf(fd, "%s: %li\n", COMMAND_STATUS_PLAYLIST,
- getPlaylistVersion());
- fdprintf(fd, "%s: %i\n", COMMAND_STATUS_PLAYLIST_LENGTH,
- getPlaylistLength());
- fdprintf(fd, "%s: %i\n", COMMAND_STATUS_CROSSFADE,
- (int)(ob_get_xfade() + 0.5));
-
- fdprintf(fd, "%s: %s\n", COMMAND_STATUS_STATE, state);
+ fdprintf(fd,
+ COMMAND_STATUS_VOLUME ": %i\n"
+ COMMAND_STATUS_REPEAT ": %i\n"
+ COMMAND_STATUS_RANDOM ": %i\n"
+ COMMAND_STATUS_PLAYLIST ": %li\n"
+ COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
+ COMMAND_STATUS_CROSSFADE ": %i\n"
+ COMMAND_STATUS_STATE ": %s\n",
+ getVolumeLevel(),
+ getPlaylistRepeatStatus(),
+ getPlaylistRandomStatus(),
+ getPlaylistVersion(),
+ getPlaylistLength(),
+ (int)(ob_get_xfade() + 0.5),
+ state);
song = getPlaylistCurrentSong();
if (song >= 0) {
- fdprintf(fd, "%s: %i\n", COMMAND_STATUS_SONG, song);
- fdprintf(fd, "%s: %i\n", COMMAND_STATUS_SONGID,
- getPlaylistSongId(song));
+ fdprintf(fd,
+ COMMAND_STATUS_SONG ": %i\n"
+ COMMAND_STATUS_SONGID ": %i\n",
+ song, getPlaylistSongId(song));
}
if (ob_get_state() != OB_STATE_STOP) {
- fdprintf(fd, "%s: %lu:%lu\n", COMMAND_STATUS_TIME,
- ob_get_elapsed_time(), ob_get_total_time());
- fdprintf(fd, "%s: %u\n", COMMAND_STATUS_BITRATE,
- ob_get_bit_rate());
- fdprintf(fd, "%s: %u:%u:%u\n", COMMAND_STATUS_AUDIO,
- ob_get_sample_rate(), ob_get_bits(),
- ob_get_channels());
+ fdprintf(fd,
+ COMMAND_STATUS_TIME ": %lu:%lu\n"
+ COMMAND_STATUS_BITRATE ": %u\n"
+ COMMAND_STATUS_AUDIO ": %u:%u:%u\n",
+ ob_get_elapsed_time(),
+ ob_get_total_time(),
+ ob_get_bit_rate(),
+ ob_get_sample_rate(),
+ ob_get_bits(),
+ ob_get_channels());
}
if ((updateJobId = isUpdatingDB())) {
- fdprintf(fd, "%s: %i\n", COMMAND_STATUS_UPDATING_DB,
- updateJobId);
+ fdprintf(fd, COMMAND_STATUS_UPDATING_DB ": %i\n",
+ updateJobId);
}
if (player_errno != PLAYER_ERROR_NONE) {
- fdprintf(fd, "%s: %s\n", COMMAND_STATUS_ERROR,
- player_strerror());
+ fdprintf(fd, COMMAND_STATUS_ERROR ": %s\n",
+ player_strerror());
}
return 0;
@@ -382,66 +442,85 @@ static int handleAdd(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
char *path = argv[1];
+ enum playlist_result result;
if (isRemoteUrl(path))
- return addToPlaylist(fd, path, NULL);
+ return addToPlaylist(path, NULL);
+
+ result = addAllIn(path);
+ if (result == (enum playlist_result)-1) {
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
+ return -1;
+ }
- return addAllIn(fd, path);
+ return print_playlist_result(fd, result);
}
static int handleAddId(int fd, mpd_unused int *permission,
int argc, char *argv[])
{
int added_id;
- int ret = addToPlaylist(fd, argv[1], &added_id);
-
- if (!ret) {
- if (argc == 3) {
- int to;
- if (check_int(fd, &to, argv[2],
- check_integer, argv[2]) < 0)
- return -1;
- ret = moveSongInPlaylistById(fd, added_id, to);
- if (ret) { /* move failed */
- deleteFromPlaylistById(fd, added_id);
- return ret;
- }
+ enum playlist_result result = addToPlaylist(argv[1], &added_id);
+
+ if (result == PLAYLIST_RESULT_SUCCESS)
+ return result;
+
+ if (argc == 3) {
+ int to;
+ if (check_int(fd, &to, argv[2],
+ check_integer, argv[2]) < 0)
+ return -1;
+ result = moveSongInPlaylistById(added_id, to);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ int ret = print_playlist_result(fd, result);
+ deleteFromPlaylistById(added_id);
+ return ret;
}
- fdprintf(fd, "Id: %d\n", added_id);
}
- return ret;
+
+ fdprintf(fd, "Id: %d\n", added_id);
+ return result;
}
static int handleDelete(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
int song;
+ enum playlist_result result;
if (check_int(fd, &song, argv[1], need_positive) < 0)
return -1;
- return deleteFromPlaylist(fd, song);
+
+ result = deleteFromPlaylist(song);
+ return print_playlist_result(fd, result);
}
static int handleDeleteId(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
int id;
+ enum playlist_result result;
if (check_int(fd, &id, argv[1], need_positive) < 0)
return -1;
- return deleteFromPlaylistById(fd, id);
+
+ result = deleteFromPlaylistById(id);
+ return print_playlist_result(fd, result);
}
static int handlePlaylist(int fd, mpd_unused int *permission,
mpd_unused int argc, mpd_unused char *argv[])
{
- return showPlaylist(fd);
+ showPlaylist(fd);
+ return 0;
}
-static int handleShuffle(int fd, mpd_unused int *permission,
+static int handleShuffle(mpd_unused int fd, mpd_unused int *permission,
mpd_unused int argc, mpd_unused char *argv[])
{
- return shufflePlaylist(fd);
+ shufflePlaylist();
+ return 0;
}
static int handleClear(mpd_unused int fd, mpd_unused int *permission,
@@ -454,25 +533,43 @@ static int handleClear(mpd_unused int fd, mpd_unused int *permission,
static int handleSave(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- return savePlaylist(fd, argv[1]);
+ enum playlist_result result;
+
+ result = savePlaylist(argv[1]);
+ return print_playlist_result(fd, result);
}
static int handleLoad(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- return loadPlaylist(fd, argv[1]);
+ enum playlist_result result;
+
+ result = loadPlaylist(fd, argv[1]);
+ return print_playlist_result(fd, result);
}
static int handleListPlaylist(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- return PlaylistInfo(fd, argv[1], 0);
+ int ret;
+
+ ret = PlaylistInfo(fd, argv[1], 0);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST, "No such playlist");
+
+ return ret;
}
static int handleListPlaylistInfo(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- return PlaylistInfo(fd, argv[1], 1);
+ int ret;
+
+ ret = PlaylistInfo(fd, argv[1], 1);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST, "No such playlist");
+
+ return ret;
}
static int handleLsInfo(int fd, mpd_unused int *permission,
@@ -483,8 +580,10 @@ static int handleLsInfo(int fd, mpd_unused int *permission,
if (argc == 2)
path = argv[1];
- if (printDirectoryInfo(fd, path) < 0)
+ if (printDirectoryInfo(fd, path) < 0) {
+ commandError(fd, ACK_ERROR_NO_EXIST, "directory not found");
return -1;
+ }
if (isRootDirectory(path))
return lsPlaylists(fd, path);
@@ -495,13 +594,19 @@ static int handleLsInfo(int fd, mpd_unused int *permission,
static int handleRm(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- return deletePlaylist(fd, argv[1]);
+ enum playlist_result result;
+
+ result = deletePlaylist(argv[1]);
+ return print_playlist_result(fd, result);
}
static int handleRename(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- return renameStoredPlaylist(fd, argv[1], argv[2]);
+ enum playlist_result result;
+
+ result = renameStoredPlaylist(argv[1], argv[2]);
+ return print_playlist_result(fd, result);
}
static int handlePlaylistChanges(int fd, mpd_unused int *permission,
@@ -528,20 +633,26 @@ static int handlePlaylistInfo(int fd, mpd_unused int *permission,
int argc, char *argv[])
{
int song = -1;
+ enum playlist_result result;
if (argc == 2 && check_int(fd, &song, argv[1], need_positive) < 0)
return -1;
- return playlistInfo(fd, song);
+
+ result = playlistInfo(fd, song);
+ return print_playlist_result(fd, result);
}
static int handlePlaylistId(int fd, mpd_unused int *permission,
int argc, char *argv[])
{
int id = -1;
+ enum playlist_result result;
if (argc == 2 && check_int(fd, &id, argv[1], need_positive) < 0)
return -1;
- return playlistId(fd, id);
+
+ result = playlistId(fd, id);
+ return print_playlist_result(fd, result);
}
static int handleFind(int fd, mpd_unused int *permission,
@@ -560,6 +671,9 @@ static int handleFind(int fd, mpd_unused int *permission,
}
ret = findSongsIn(fd, NULL, numItems, items);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
freeLocateTagItemArray(numItems, items);
@@ -582,6 +696,9 @@ static int handleSearch(int fd, mpd_unused int *permission,
}
ret = searchForSongsIn(fd, NULL, numItems, items);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
freeLocateTagItemArray(numItems, items);
@@ -604,6 +721,9 @@ static int handleCount(int fd, mpd_unused int *permission,
}
ret = searchStatsForSongsIn(fd, NULL, numItems, items);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
freeLocateTagItemArray(numItems, items);
@@ -654,11 +774,13 @@ static int handlePlaylistDelete(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[]) {
char *playlist = argv[1];
int from;
+ enum playlist_result result;
if (check_int(fd, &from, argv[2], check_integer, argv[2]) < 0)
return -1;
- return removeOneSongFromStoredPlaylistByPath(fd, playlist, from);
+ result = removeOneSongFromStoredPlaylistByPath(playlist, from);
+ return print_playlist_result(fd, result);
}
static int handlePlaylistMove(int fd, mpd_unused int *permission,
@@ -666,13 +788,15 @@ static int handlePlaylistMove(int fd, mpd_unused int *permission,
{
char *playlist = argv[1];
int from, to;
+ enum playlist_result result;
if (check_int(fd, &from, argv[2], check_integer, argv[2]) < 0)
return -1;
if (check_int(fd, &to, argv[3], check_integer, argv[3]) < 0)
return -1;
- return moveSongInStoredPlaylistByPath(fd, playlist, from, to);
+ result = moveSongInStoredPlaylistByPath(playlist, from, to);
+ return print_playlist_result(fd, result);
}
static int listHandleUpdate(int fd,
@@ -738,30 +862,49 @@ static int handleListAll(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
char *directory = NULL;
+ int ret;
if (argc == 2)
directory = argv[1];
- return printAllIn(fd, directory);
+
+ ret = printAllIn(fd, directory);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
+
+ return ret;
}
static int handleVolume(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- int change;
+ int change, ret;
if (check_int(fd, &change, argv[1], need_integer) < 0)
return -1;
- return changeVolumeLevel(fd, change, 1);
+
+ ret = changeVolumeLevel(change, 1);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_SYSTEM,
+ "problems setting volume");
+
+ return ret;
}
static int handleSetVol(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- int level;
+ int level, ret;
if (check_int(fd, &level, argv[1], need_integer) < 0)
return -1;
- return changeVolumeLevel(fd, level, 0);
+
+ ret = changeVolumeLevel(level, 0);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_SYSTEM,
+ "problems setting volume");
+
+ return ret;
}
static int handleRepeat(int fd, mpd_unused int *permission,
@@ -771,7 +914,15 @@ static int handleRepeat(int fd, mpd_unused int *permission,
if (check_int(fd, &status, argv[1], need_integer) < 0)
return -1;
- return setPlaylistRepeatStatus(fd, status);
+
+ if (status != 0 && status != 1) {
+ commandError(fd, ACK_ERROR_ARG,
+ "\"%i\" is not 0 or 1", status);
+ return -1;
+ }
+
+ setPlaylistRepeatStatus(status);
+ return 0;
}
static int handleRandom(int fd, mpd_unused int *permission,
@@ -781,7 +932,15 @@ static int handleRandom(int fd, mpd_unused int *permission,
if (check_int(fd, &status, argv[1], need_integer) < 0)
return -1;
- return setPlaylistRandomStatus(fd, status);
+
+ if (status != 0 && status != 1) {
+ commandError(fd, ACK_ERROR_ARG,
+ "\"%i\" is not 0 or 1", status);
+ return -1;
+ }
+
+ setPlaylistRandomStatus(status);
+ return 0;
}
static int handleStats(int fd, mpd_unused int *permission,
@@ -844,6 +1003,10 @@ static int handleList(int fd, mpd_unused int *permission,
if (conditionals)
freeLocateTagItemArray(numConditionals, conditionals);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
+
return ret;
}
@@ -851,82 +1014,102 @@ static int handleMove(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
int from, to;
+ enum playlist_result result;
if (check_int(fd, &from, argv[1], check_integer, argv[1]) < 0)
return -1;
if (check_int(fd, &to, argv[2], check_integer, argv[2]) < 0)
return -1;
- return moveSongInPlaylist(fd, from, to);
+ result = moveSongInPlaylist(from, to);
+ return print_playlist_result(fd, result);
}
static int handleMoveId(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
int id, to;
+ enum playlist_result result;
if (check_int(fd, &id, argv[1], check_integer, argv[1]) < 0)
return -1;
if (check_int(fd, &to, argv[2], check_integer, argv[2]) < 0)
return -1;
- return moveSongInPlaylistById(fd, id, to);
+ result = moveSongInPlaylistById(id, to);
+ return print_playlist_result(fd, result);
}
static int handleSwap(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
int song1, song2;
+ enum playlist_result result;
if (check_int(fd, &song1, argv[1], check_integer, argv[1]) < 0)
return -1;
if (check_int(fd, &song2, argv[2], check_integer, argv[2]) < 0)
return -1;
- return swapSongsInPlaylist(fd, song1, song2);
+ result = swapSongsInPlaylist(song1, song2);
+ return print_playlist_result(fd, result);
}
static int handleSwapId(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
int id1, id2;
+ enum playlist_result result;
if (check_int(fd, &id1, argv[1], check_integer, argv[1]) < 0)
return -1;
if (check_int(fd, &id2, argv[2], check_integer, argv[2]) < 0)
return -1;
- return swapSongsInPlaylistById(fd, id1, id2);
+ result = swapSongsInPlaylistById(id1, id2);
+ return print_playlist_result(fd, result);
}
static int handleSeek(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
int song, seek_time;
+ enum playlist_result result;
if (check_int(fd, &song, argv[1], check_integer, argv[1]) < 0)
return -1;
if (check_int(fd, &seek_time, argv[2], check_integer, argv[2]) < 0)
return -1;
- return seekSongInPlaylist(fd, song, seek_time);
+
+ result = seekSongInPlaylist(song, seek_time);
+ return print_playlist_result(fd, result);
}
static int handleSeekId(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
int id, seek_time;
+ enum playlist_result result;
if (check_int(fd, &id, argv[1], check_integer, argv[1]) < 0)
return -1;
if (check_int(fd, &seek_time, argv[2], check_integer, argv[2]) < 0)
return -1;
- return seekSongInPlaylistById(fd, id, seek_time);
+
+ result = seekSongInPlaylistById(id, seek_time);
+ return print_playlist_result(fd, result);
}
static int handleListAllInfo(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
char *directory = NULL;
+ int ret;
if (argc == 2)
directory = argv[1];
- return printInfoForAllIn(fd, directory);
+ ret = printInfoForAllIn(fd, directory);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
+
+ return ret;
}
static int handlePing(mpd_unused int fd, mpd_unused int *permission,
@@ -961,21 +1144,31 @@ static int handleCrossfade(int fd, mpd_unused int *permission,
static int handleEnableDevice(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- int device;
+ int device, ret;
if (check_int(fd, &device, argv[1], check_non_negative, argv[1]) < 0)
return -1;
- return enableAudioDevice(fd, device);
+
+ ret = enableAudioDevice(device);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST, "No such audio output");
+
+ return ret;
}
static int handleDisableDevice(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- int device;
+ int device, ret;
if (check_int(fd, &device, argv[1], check_non_negative, argv[1]) < 0)
return -1;
- return disableAudioDevice(fd, device);
+
+ ret = disableAudioDevice(device);
+ if (ret == -1)
+ commandError(fd, ACK_ERROR_NO_EXIST, "No such audio output");
+
+ return ret;
}
static int handleDevices(int fd, mpd_unused int *permission,
@@ -1027,7 +1220,10 @@ static int handleNotcommands(int fd, mpd_unused int *permission,
static int handlePlaylistClear(int fd, mpd_unused int *permission,
mpd_unused int argc, char *argv[])
{
- return clearStoredPlaylist(fd, argv[1]);
+ enum playlist_result result;
+
+ result = clearStoredPlaylist(argv[1]);
+ return print_playlist_result(fd, result);
}
static int handlePlaylistAdd(int fd, mpd_unused int *permission,
@@ -1035,11 +1231,20 @@ static int handlePlaylistAdd(int fd, mpd_unused int *permission,
{
char *playlist = argv[1];
char *path = argv[2];
+ enum playlist_result result;
if (isRemoteUrl(path))
- return addToStoredPlaylist(fd, path, playlist);
+ result = addToStoredPlaylist(path, playlist);
+ else
+ result = addAllInToStoredPlaylist(path, playlist);
+
+ if (result == (enum playlist_result)-1) {
+ commandError(fd, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
+ return -1;
+ }
- return addAllInToStoredPlaylist(fd, path, playlist);
+ return print_playlist_result(fd, result);
}
void initCommands(void)
diff --git a/src/dbUtils.c b/src/dbUtils.c
index 519c1802d..0eb485f1e 100644
--- a/src/dbUtils.c
+++ b/src/dbUtils.c
@@ -24,7 +24,7 @@
#include "playlist.h"
#include "song.h"
#include "tag.h"
-#include "tagTracker.h"
+#include "strset.h"
#include "log.h"
#include "storedPlaylist.h"
@@ -45,7 +45,7 @@ typedef struct _SearchStats {
unsigned long playTime;
} SearchStats;
-static int countSongsInDirectory(mpd_unused int fd, Directory * directory,
+static int countSongsInDirectory(Directory * directory,
void *data)
{
int *count = (int *)data;
@@ -55,24 +55,32 @@ static int countSongsInDirectory(mpd_unused int fd, Directory * directory,
return 0;
}
-static int printDirectoryInDirectory(int fd, Directory * directory,
- mpd_unused void *data)
+static int printDirectoryInDirectory(Directory * directory, void *data)
{
+ int fd = (int)(size_t)data;
if (directory->path) {
fdprintf(fd, "directory: %s\n", getDirectoryPath(directory));
}
return 0;
}
-static int printSongInDirectory(int fd, Song * song, mpd_unused void *data)
+static int printSongInDirectory(Song * song, mpd_unused void *data)
{
+ int fd = (int)(size_t)data;
printSongUrl(fd, song);
return 0;
}
-static int searchInDirectory(int fd, Song * song, void *data)
+struct search_data {
+ int fd;
+ LocateTagItemArray array;
+};
+
+static int searchInDirectory(Song * song, void *_data)
{
- LocateTagItemArray *array = data;
+ struct search_data *data = _data;
+ int fd = data->fd;
+ LocateTagItemArray *array = &data->array;
if (strstrSearchTags(song, array->numItems, array->items))
printSongInfo(fd, song);
@@ -80,23 +88,25 @@ static int searchInDirectory(int fd, Song * song, void *data)
return 0;
}
-int searchForSongsIn(int fd, char *name, int numItems, LocateTagItem * items)
+int searchForSongsIn(int fd, const char *name, int numItems,
+ LocateTagItem * items)
{
int ret;
int i;
char **originalNeedles = xmalloc(numItems * sizeof(char *));
- LocateTagItemArray array;
+ struct search_data data;
for (i = 0; i < numItems; i++) {
originalNeedles[i] = items[i].needle;
items[i].needle = strDupToUpper(originalNeedles[i]);
}
- array.numItems = numItems;
- array.items = items;
+ data.fd = fd;
+ data.array.numItems = numItems;
+ data.array.items = items;
- ret = traverseAllIn(fd, name, searchInDirectory, NULL, &array);
+ ret = traverseAllIn(name, searchInDirectory, NULL, &data);
for (i = 0; i < numItems; i++) {
free(items[i].needle);
@@ -108,9 +118,11 @@ int searchForSongsIn(int fd, char *name, int numItems, LocateTagItem * items)
return ret;
}
-static int findInDirectory(int fd, Song * song, void *data)
+static int findInDirectory(Song * song, void *_data)
{
- LocateTagItemArray *array = data;
+ struct search_data *data = _data;
+ int fd = data->fd;
+ LocateTagItemArray *array = &data->array;
if (tagItemsFoundAndMatches(song, array->numItems, array->items))
printSongInfo(fd, song);
@@ -118,14 +130,15 @@ static int findInDirectory(int fd, Song * song, void *data)
return 0;
}
-int findSongsIn(int fd, char *name, int numItems, LocateTagItem * items)
+int findSongsIn(int fd, const char *name, int numItems, LocateTagItem * items)
{
- LocateTagItemArray array;
+ struct search_data data;
- array.numItems = numItems;
- array.items = items;
+ data.fd = fd;
+ data.array.numItems = numItems;
+ data.array.items = items;
- return traverseAllIn(fd, name, findInDirectory, NULL, (void *)&array);
+ return traverseAllIn(name, findInDirectory, NULL, &data);
}
static void printSearchStats(int fd, SearchStats *stats)
@@ -134,7 +147,7 @@ static void printSearchStats(int fd, SearchStats *stats)
fdprintf(fd, "playtime: %li\n", stats->playTime);
}
-static int searchStatsInDirectory(mpd_unused int fd, Song * song, void *data)
+static int searchStatsInDirectory(Song * song, void *data)
{
SearchStats *stats = data;
@@ -148,7 +161,7 @@ static int searchStatsInDirectory(mpd_unused int fd, Song * song, void *data)
return 0;
}
-int searchStatsForSongsIn(int fd, char *name, int numItems,
+int searchStatsForSongsIn(int fd, const char *name, int numItems,
LocateTagItem * items)
{
SearchStats stats;
@@ -159,49 +172,60 @@ int searchStatsForSongsIn(int fd, char *name, int numItems,
stats.numberOfSongs = 0;
stats.playTime = 0;
- ret = traverseAllIn(fd, name, searchStatsInDirectory, NULL, &stats);
+ ret = traverseAllIn(name, searchStatsInDirectory, NULL, &stats);
if (ret == 0)
printSearchStats(fd, &stats);
return ret;
}
-int printAllIn(int fd, char *name)
+int printAllIn(int fd, const char *name)
{
- return traverseAllIn(fd, name, printSongInDirectory,
- printDirectoryInDirectory, NULL);
+ return traverseAllIn(name, printSongInDirectory,
+ printDirectoryInDirectory, (void*)(size_t)fd);
}
-static int directoryAddSongToPlaylist(int fd, Song * song,
- mpd_unused void *data)
+static int directoryAddSongToPlaylist(Song * song, mpd_unused void *data)
{
- return addSongToPlaylist(fd, song, NULL);
+ return addSongToPlaylist(song, NULL);
}
-static int directoryAddSongToStoredPlaylist(int fd, Song *song, void *data)
+struct add_data {
+ const char *path;
+};
+
+static int directoryAddSongToStoredPlaylist(Song *song, void *_data)
{
- if (appendSongToStoredPlaylistByPath(fd, (char *)data, song) != 0)
+ struct add_data *data = _data;
+
+ if (appendSongToStoredPlaylistByPath(data->path, song) != 0)
return -1;
return 0;
}
-int addAllIn(int fd, char *name)
+int addAllIn(const char *name)
{
- return traverseAllIn(fd, name, directoryAddSongToPlaylist, NULL, NULL);
+ return traverseAllIn(name, directoryAddSongToPlaylist, NULL, NULL);
}
-int addAllInToStoredPlaylist(int fd, char *name, char *utf8file)
+int addAllInToStoredPlaylist(const char *name, const char *utf8file)
{
- return traverseAllIn(fd, name, directoryAddSongToStoredPlaylist, NULL,
- (void *)utf8file);
+ struct add_data data = {
+ .path = utf8file,
+ };
+
+ return traverseAllIn(name, directoryAddSongToStoredPlaylist, NULL,
+ &data);
}
-static int directoryPrintSongInfo(int fd, Song * song, mpd_unused void *data)
+static int directoryPrintSongInfo(Song * song, void *data)
{
+ int fd = (int)(size_t)data;
+
return printSongInfo(fd, song);
}
-static int sumSongTime(mpd_unused int fd, Song * song, void *data)
+static int sumSongTime(Song * song, void *data)
{
unsigned long *sum_time = (unsigned long *)data;
@@ -211,28 +235,28 @@ static int sumSongTime(mpd_unused int fd, Song * song, void *data)
return 0;
}
-int printInfoForAllIn(int fd, char *name)
+int printInfoForAllIn(int fd, const char *name)
{
- return traverseAllIn(fd, name, directoryPrintSongInfo,
- printDirectoryInDirectory, NULL);
+ return traverseAllIn(name, directoryPrintSongInfo,
+ printDirectoryInDirectory, (void*)(size_t)fd);
}
-int countSongsIn(int fd, char *name)
+int countSongsIn(const char *name)
{
int count = 0;
void *ptr = (void *)&count;
- traverseAllIn(fd, name, NULL, countSongsInDirectory, ptr);
+ traverseAllIn(name, NULL, countSongsInDirectory, ptr);
return count;
}
-unsigned long sumSongTimesIn(int fd, char *name)
+unsigned long sumSongTimesIn(const char *name)
{
unsigned long dbPlayTime = 0;
void *ptr = (void *)&dbPlayTime;
- traverseAllIn(fd, name, sumSongTime, NULL, ptr);
+ traverseAllIn(name, sumSongTime, NULL, ptr);
return dbPlayTime;
}
@@ -254,10 +278,11 @@ static void freeListCommandItem(ListCommandItem * item)
free(item);
}
-static void visitTag(int fd, Song * song, enum tag_type tagType)
+static void visitTag(int fd, struct strset *set,
+ Song * song, enum tag_type tagType)
{
int i;
- MpdTag *tag = song->tag;
+ struct mpd_tag *tag = song->tag;
if (tagType == LOCATE_TAG_FILE_TYPE) {
printSongUrl(fd, song);
@@ -268,19 +293,26 @@ static void visitTag(int fd, Song * song, enum tag_type tagType)
return;
for (i = 0; i < tag->numOfItems; i++) {
- if (tag->items[i].type == tagType) {
- visitInTagTracker(tagType, tag->items[i].value);
+ if (tag->items[i]->type == tagType) {
+ strset_add(set, tag->items[i]->value);
}
}
}
-static int listUniqueTagsInDirectory(int fd, Song * song, void *data)
+struct list_tags_data {
+ int fd;
+ ListCommandItem *item;
+ struct strset *set;
+};
+
+static int listUniqueTagsInDirectory(Song * song, void *_data)
{
- ListCommandItem *item = data;
+ struct list_tags_data *data = _data;
+ ListCommandItem *item = data->item;
if (tagItemsFoundAndMatches(song, item->numConditionals,
item->conditionals)) {
- visitTag(fd, song, item->tagType);
+ visitTag(data->fd, data->set, song, item->tagType);
}
return 0;
@@ -290,18 +322,26 @@ int listAllUniqueTags(int fd, int type, int numConditionals,
LocateTagItem * conditionals)
{
int ret;
+ struct list_tags_data data;
ListCommandItem *item = newListCommandItem(type, numConditionals,
conditionals);
+ data.item = item;
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
- resetVisitedFlagsInTagTracker(type);
+ data.set = strset_new();
}
- ret = traverseAllIn(fd, NULL, listUniqueTagsInDirectory, NULL,
- (void *)item);
+ ret = traverseAllIn(NULL, listUniqueTagsInDirectory, NULL, &data);
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
- printVisitedInTagTracker(fd, type);
+ const char *value;
+
+ strset_rewind(data.set);
+
+ while ((value = strset_next(data.set)) != NULL)
+ fdprintf(fd, "%s: %s\n", mpdTagItemKeys[type], value);
+
+ strset_free(data.set);
}
freeListCommandItem(item);
@@ -309,9 +349,7 @@ int listAllUniqueTags(int fd, int type, int numConditionals,
return ret;
}
-static int sumSavedFilenameMemoryInDirectory(mpd_unused int fd,
- Directory * dir,
- void *data)
+static int sumSavedFilenameMemoryInDirectory(Directory * dir, void *data)
{
int *sum = data;
@@ -324,8 +362,7 @@ static int sumSavedFilenameMemoryInDirectory(mpd_unused int fd,
return 0;
}
-static int sumSavedFilenameMemoryInSong(mpd_unused int fd, Song * song,
- void *data)
+static int sumSavedFilenameMemoryInSong(Song * song, void *data)
{
int *sum = data;
@@ -338,7 +375,7 @@ void printSavedMemoryFromFilenames(void)
{
int sum = 0;
- traverseAllIn(STDERR_FILENO, NULL, sumSavedFilenameMemoryInSong,
+ traverseAllIn(NULL, sumSavedFilenameMemoryInSong,
sumSavedFilenameMemoryInDirectory, (void *)&sum);
DEBUG("saved memory from filenames: %i\n", sum);
diff --git a/src/dbUtils.h b/src/dbUtils.h
index f2237eab7..592b62e95 100644
--- a/src/dbUtils.h
+++ b/src/dbUtils.h
@@ -21,25 +21,25 @@
#include "locate.h"
-int printAllIn(int fd, char *name);
+int printAllIn(int fd, const char *name);
-int addAllIn(int fd, char *name);
+int addAllIn(const char *name);
-int addAllInToStoredPlaylist(int fd, char *name, char *utf8file);
+int addAllInToStoredPlaylist(const char *name, const char *utf8file);
-int printInfoForAllIn(int fd, char *name);
+int printInfoForAllIn(int fd, const char *name);
-int searchForSongsIn(int fd, char *name, int numItems,
+int searchForSongsIn(int fd, const char *name, int numItems,
LocateTagItem * items);
-int findSongsIn(int fd, char *name, int numItems, LocateTagItem * items);
+int findSongsIn(int fd, const char *name, int numItems, LocateTagItem * items);
-int searchStatsForSongsIn(int fd, char *name, int numItems,
+int searchStatsForSongsIn(int fd, const char *name, int numItems,
LocateTagItem * items);
-int countSongsIn(int fd, char *name);
+int countSongsIn(const char *name);
-unsigned long sumSongTimesIn(int fd, char *name);
+unsigned long sumSongTimesIn(const char *name);
int listAllUniqueTags(int fd, int type, int numConditiionals,
LocateTagItem * conditionals);
diff --git a/src/directory.c b/src/directory.c
index 46d309c76..2175c54a0 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -848,10 +848,8 @@ int printDirectoryInfo(int fd, const char *name)
{
Directory *directory;
- if ((directory = getDirectory(name)) == NULL) {
- commandError(fd, ACK_ERROR_NO_EXIST, "directory not found");
+ if ((directory = getDirectory(name)) == NULL)
return -1;
- }
printDirectoryList(fd, directory->subDirectories);
printSongInfoFromList(fd, directory->songs);
@@ -901,7 +899,7 @@ static void readDirectoryInfo(FILE * fp, Directory * directory)
{
char buffer[MPD_PATH_MAX * 2];
int bufferSize = MPD_PATH_MAX * 2;
- char *key;
+ char key[MPD_PATH_MAX * 2];
Directory *subDirectory;
int strcmpRet;
char *name;
@@ -911,7 +909,7 @@ static void readDirectoryInfo(FILE * fp, Directory * directory)
while (myFgets(buffer, bufferSize, fp)
&& 0 != strncmp(DIRECTORY_END, buffer, strlen(DIRECTORY_END))) {
if (0 == strncmp(DIRECTORY_DIR, buffer, strlen(DIRECTORY_DIR))) {
- key = xstrdup(&(buffer[strlen(DIRECTORY_DIR)]));
+ strcpy(key, &(buffer[strlen(DIRECTORY_DIR)]));
if (!myFgets(buffer, bufferSize, fp))
FATAL("Error reading db, fgets\n");
/* for compatibility with db's prior to 0.11 */
@@ -925,7 +923,7 @@ static void readDirectoryInfo(FILE * fp, Directory * directory)
strlen(DIRECTORY_BEGIN))) {
FATAL("Error reading db at line: %s\n", buffer);
}
- name = xstrdup(&(buffer[strlen(DIRECTORY_BEGIN)]));
+ name = &(buffer[strlen(DIRECTORY_BEGIN)]);
while (nextDirNode && (strcmpRet =
strcmp(key,
@@ -951,8 +949,6 @@ static void readDirectoryInfo(FILE * fp, Directory * directory)
(void *)subDirectory);
}
- free(name);
- free(key);
readDirectoryInfo(fp, subDirectory);
} else if (0 == strncmp(SONG_BEGIN, buffer, strlen(SONG_BEGIN))) {
readSongInfoIntoList(fp, directory->songs, directory);
@@ -1155,8 +1151,8 @@ int readDirectoryDB(void)
readDirectoryInfo(fp, mp3rootDirectory);
while (fclose(fp) && errno == EINTR) ;
- stats.numberOfSongs = countSongsIn(STDERR_FILENO, NULL);
- stats.dbPlayTime = sumSongTimesIn(STDERR_FILENO, NULL);
+ stats.numberOfSongs = countSongsIn(NULL);
+ stats.dbPlayTime = sumSongTimesIn(NULL);
if (stat(dbFile, &st) == 0)
directory_dbModTime = st.st_mtime;
@@ -1182,11 +1178,10 @@ void updateMp3Directory(void)
return;
}
-static int traverseAllInSubDirectory(int fd, Directory * directory,
- int (*forEachSong) (int, Song *,
- void *),
- int (*forEachDir) (int, Directory *,
- void *), void *data)
+static int traverseAllInSubDirectory(Directory * directory,
+ int (*forEachSong) (Song *, void *),
+ int (*forEachDir) (Directory *, void *),
+ void *data)
{
ListNode *node = directory->songs->firstNode;
Song *song;
@@ -1194,7 +1189,7 @@ static int traverseAllInSubDirectory(int fd, Directory * directory,
int errFlag = 0;
if (forEachDir) {
- errFlag = forEachDir(fd, directory, data);
+ errFlag = forEachDir(directory, data);
if (errFlag)
return errFlag;
}
@@ -1202,7 +1197,7 @@ static int traverseAllInSubDirectory(int fd, Directory * directory,
if (forEachSong) {
while (node != NULL && !errFlag) {
song = (Song *) node->data;
- errFlag = forEachSong(fd, song, data);
+ errFlag = forEachSong(song, data);
node = node->nextNode;
}
if (errFlag)
@@ -1213,7 +1208,7 @@ static int traverseAllInSubDirectory(int fd, Directory * directory,
while (node != NULL && !errFlag) {
dir = (Directory *) node->data;
- errFlag = traverseAllInSubDirectory(fd, dir, forEachSong,
+ errFlag = traverseAllInSubDirectory(dir, forEachSong,
forEachDir, data);
node = node->nextNode;
}
@@ -1221,23 +1216,21 @@ static int traverseAllInSubDirectory(int fd, Directory * directory,
return errFlag;
}
-int traverseAllIn(int fd, char *name,
- int (*forEachSong) (int, Song *, void *),
- int (*forEachDir) (int, Directory *, void *), void *data)
+int traverseAllIn(const char *name,
+ int (*forEachSong) (Song *, void *),
+ int (*forEachDir) (Directory *, void *), void *data)
{
Directory *directory;
if ((directory = getDirectory(name)) == NULL) {
Song *song;
if ((song = getSongFromDB(name)) && forEachSong) {
- return forEachSong(fd, song, data);
+ return forEachSong(song, data);
}
- commandError(fd, ACK_ERROR_NO_EXIST,
- "directory or file not found");
return -1;
}
- return traverseAllInSubDirectory(fd, directory, forEachSong, forEachDir,
+ return traverseAllInSubDirectory(directory, forEachSong, forEachDir,
data);
}
@@ -1258,8 +1251,8 @@ void initMp3Directory(void)
mp3rootDirectory = newDirectory(NULL, NULL);
exploreDirectory(mp3rootDirectory);
freeAllDirectoryStats(mp3rootDirectory);
- stats.numberOfSongs = countSongsIn(STDERR_FILENO, NULL);
- stats.dbPlayTime = sumSongTimesIn(STDERR_FILENO, NULL);
+ stats.numberOfSongs = countSongsIn(NULL);
+ stats.dbPlayTime = sumSongTimesIn(NULL);
}
static Song *getSongDetails(const char *file, const char **shortnameRet,
@@ -1305,7 +1298,7 @@ static Song *getSongDetails(const char *file, const char **shortnameRet,
return (Song *) song;
}
-Song *getSongFromDB(char *file)
+Song *getSongFromDB(const char *file)
{
return getSongDetails(file, NULL, NULL);
}
diff --git a/src/directory.h b/src/directory.h
index acb173fc0..19dada309 100644
--- a/src/directory.h
+++ b/src/directory.h
@@ -60,13 +60,13 @@ int readDirectoryDB(void);
void updateMp3Directory(void);
-Song *getSongFromDB(char *file);
+Song *getSongFromDB(const char *file);
time_t getDbModTime(void);
-int traverseAllIn(int fd, char *name,
- int (*forEachSong) (int, Song *, void *),
- int (*forEachDir) (int, Directory *, void *), void *data);
+int traverseAllIn(const char *name,
+ int (*forEachSong) (Song *, void *),
+ int (*forEachDir) (Directory *, void *), void *data);
#define getDirectoryPath(dir) ((dir && dir->path) ? dir->path : "")
diff --git a/src/inputPlugin.h b/src/inputPlugin.h
index 161a0db59..0fd39ea9f 100644
--- a/src/inputPlugin.h
+++ b/src/inputPlugin.h
@@ -54,7 +54,7 @@ typedef int (*InputPlugin_fileDecodeFunc) (char *path);
/* file should be the full path! Returns NULL if a tag cannot be found
* or read */
-typedef MpdTag *(*InputPlugin_tagDupFunc) (char *file);
+typedef struct mpd_tag *(*InputPlugin_tagDupFunc) (char *file);
typedef struct _InputPlugin {
const char *name;
diff --git a/src/inputPlugins/_flac_common.c b/src/inputPlugins/_flac_common.c
index 6890c7c50..9950f75db 100644
--- a/src/inputPlugins/_flac_common.c
+++ b/src/inputPlugins/_flac_common.c
@@ -101,7 +101,7 @@ static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
static unsigned int commentMatchesAddToTag(const
FLAC__StreamMetadata_VorbisComment_Entry
* entry, unsigned int itemType,
- MpdTag ** tag)
+ struct mpd_tag ** tag)
{
const char *str;
size_t slen;
@@ -123,10 +123,10 @@ static unsigned int commentMatchesAddToTag(const
if ((vlen > 0) && (0 == strncasecmp(str, (char *)entry->entry, slen))
&& (*(entry->entry + slen) == '=')) {
if (!*tag)
- *tag = newMpdTag();
+ *tag = tag_new();
- addItemToMpdTagWithLen(*tag, itemType,
- (char *)(entry->entry + slen + 1), vlen);
+ tag_add_item_n(*tag, itemType,
+ (char *)(entry->entry + slen + 1), vlen);
return 1;
}
@@ -134,8 +134,8 @@ static unsigned int commentMatchesAddToTag(const
return 0;
}
-MpdTag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
- MpdTag * tag)
+struct mpd_tag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
+ struct mpd_tag * tag)
{
unsigned int i, j;
FLAC__StreamMetadata_VorbisComment_Entry *comments;
diff --git a/src/inputPlugins/_flac_common.h b/src/inputPlugins/_flac_common.h
index 6fe5bd744..a1b0cac8f 100644
--- a/src/inputPlugins/_flac_common.h
+++ b/src/inputPlugins/_flac_common.h
@@ -146,7 +146,7 @@ typedef struct {
FLAC__uint64 position;
InputStream *inStream;
ReplayGainInfo *replayGainInfo;
- MpdTag *tag;
+ struct mpd_tag *tag;
} FlacData;
/* initializes a given FlacData struct */
@@ -157,8 +157,8 @@ void flac_error_common_cb(const char *plugin,
FLAC__StreamDecoderErrorStatus status,
FlacData * data);
-MpdTag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
- MpdTag * tag);
+struct mpd_tag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
+ struct mpd_tag *tag);
/* keep this inlined, this is just macro but prettier :) */
static inline enum dc_action flacSendChunk(FlacData * data)
diff --git a/src/inputPlugins/aac_plugin.c b/src/inputPlugins/aac_plugin.c
index 512e73e53..dc97c1e08 100644
--- a/src/inputPlugins/aac_plugin.c
+++ b/src/inputPlugins/aac_plugin.c
@@ -553,14 +553,14 @@ out:
return 0;
}
-static MpdTag *aacTagDup(char *file)
+static struct mpd_tag *aacTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int file_time = getAacTotalTime(file);
if (file_time >= 0) {
- if ((ret = id3Dup(file)) == NULL)
- ret = newMpdTag();
+ if ((ret = tag_id3_load(file)) == NULL)
+ ret = tag_new();
ret->time = file_time;
} else {
DEBUG("aacTagDup: Failed to get total song time from: %s\n",
diff --git a/src/inputPlugins/audiofile_plugin.c b/src/inputPlugins/audiofile_plugin.c
index fcebf562b..6fcc98239 100644
--- a/src/inputPlugins/audiofile_plugin.c
+++ b/src/inputPlugins/audiofile_plugin.c
@@ -116,14 +116,14 @@ static int audiofile_decode(char *path)
return 0;
}
-static MpdTag *audiofileTagDup(char *file)
+static struct mpd_tag *audiofileTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int total_time = getAudiofileTotalTime(file);
if (total_time >= 0) {
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = total_time;
} else {
DEBUG
diff --git a/src/inputPlugins/flac_plugin.c b/src/inputPlugins/flac_plugin.c
index de0648c90..89e988a03 100644
--- a/src/inputPlugins/flac_plugin.c
+++ b/src/inputPlugins/flac_plugin.c
@@ -292,9 +292,9 @@ static FLAC__StreamDecoderWriteStatus flacWrite(const flac_decoder *dec,
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
-static MpdTag *flacMetadataDup(char *file, int *vorbisCommentFound)
+static struct mpd_tag *flacMetadataDup(char *file, int *vorbisCommentFound)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
FLAC__Metadata_SimpleIterator *it;
FLAC__StreamMetadata *block = NULL;
@@ -338,7 +338,7 @@ static MpdTag *flacMetadataDup(char *file, int *vorbisCommentFound)
*vorbisCommentFound = 1;
} else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = ((float)block->data.stream_info.
total_samples) /
block->data.stream_info.sample_rate + 0.5;
@@ -350,9 +350,9 @@ static MpdTag *flacMetadataDup(char *file, int *vorbisCommentFound)
return ret;
}
-static MpdTag *flacTagDup(char *file)
+static struct mpd_tag *flacTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int foundVorbisComment = 0;
ret = flacMetadataDup(file, &foundVorbisComment);
@@ -362,10 +362,10 @@ static MpdTag *flacTagDup(char *file)
return NULL;
}
if (!foundVorbisComment) {
- MpdTag *temp = id3Dup(file);
+ struct mpd_tag *temp = tag_id3_load(file);
if (temp) {
temp->time = ret->time;
- freeMpdTag(ret);
+ tag_free(ret);
ret = temp;
}
}
@@ -466,9 +466,9 @@ static int flac_decode(InputStream * inStream)
/* some of this stuff is duplicated from oggflac_plugin.c */
extern InputPlugin oggflacPlugin;
-static MpdTag *oggflac_tag_dup(char *file)
+static struct mpd_tag *oggflac_tag_dup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
FLAC__Metadata_Iterator *it;
FLAC__StreamMetadata *block;
FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
@@ -484,7 +484,7 @@ static MpdTag *oggflac_tag_dup(char *file)
ret = copyVorbisCommentBlockToMpdTag(block, ret);
} else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = ((float)block->data.stream_info.
total_samples) /
block->data.stream_info.sample_rate + 0.5;
diff --git a/src/inputPlugins/mod_plugin.c b/src/inputPlugins/mod_plugin.c
index df938f0ed..6b09e3cf0 100644
--- a/src/inputPlugins/mod_plugin.c
+++ b/src/inputPlugins/mod_plugin.c
@@ -48,14 +48,21 @@ static BOOL mod_mpd_IsThere(void)
return 1;
}
+static char drv_name[] = "MPD";
+static char drv_version[] = "MPD Output Driver v0.1";
+
+#if (LIBMIKMOD_VERSION > 0x030106)
+static char drv_alias[] = "mpd";
+#endif
+
static MDRIVER drv_mpd = {
NULL,
- "MPD",
- "MPD Output Driver v0.1",
+ drv_name,
+ drv_version,
0,
255,
#if (LIBMIKMOD_VERSION > 0x030106)
- "mpd", /* Alias */
+ drv_alias,
#if (LIBMIKMOD_VERSION >= 0x030200)
NULL, /* CmdLineHelp */
#endif
@@ -92,6 +99,8 @@ static int mod_mikModInitError;
static int mod_initMikMod(void)
{
+ static char params[] = "";
+
if (mod_mikModInitError)
return -1;
@@ -110,7 +119,7 @@ static int mod_initMikMod(void)
md_mode = (DMODE_SOFT_MUSIC | DMODE_INTERP | DMODE_STEREO |
DMODE_16BITS);
- if (MikMod_Init("")) {
+ if (MikMod_Init(params)) {
ERROR("Could not init MikMod: %s\n",
MikMod_strerror(MikMod_errno));
mod_mikModInitError = 1;
@@ -205,9 +214,9 @@ static int mod_decode(char *path)
return 0;
}
-static MpdTag *modTagDup(char *file)
+static struct mpd_tag *modTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
MODULE *moduleHandle;
char *title;
@@ -224,12 +233,12 @@ static MpdTag *modTagDup(char *file)
}
Player_Free(moduleHandle);
- ret = newMpdTag();
+ ret = tag_new();
ret->time = 0;
title = xstrdup(Player_LoadTitle(file));
if (title)
- addItemToMpdTag(ret, TAG_ITEM_TITLE, title);
+ tag_add_item(ret, TAG_ITEM_TITLE, title);
MikMod_Exit();
diff --git a/src/inputPlugins/mp3_plugin.c b/src/inputPlugins/mp3_plugin.c
index f1304f401..ff3de80a3 100644
--- a/src/inputPlugins/mp3_plugin.c
+++ b/src/inputPlugins/mp3_plugin.c
@@ -301,13 +301,13 @@ static ReplayGainInfo *parseId3ReplayGainInfo(struct id3_tag *tag)
#ifdef HAVE_ID3TAG
static void mp3_parseId3Tag(mp3DecodeData * data, size_t tagsize,
- MpdTag ** mpdTag, ReplayGainInfo ** replayGainInfo)
+ struct mpd_tag ** mpdTag, ReplayGainInfo ** replayGainInfo)
{
struct id3_tag *id3Tag = NULL;
id3_length_t count;
id3_byte_t const *id3_data;
id3_byte_t *allocated = NULL;
- MpdTag *tmpMpdTag;
+ struct mpd_tag *tmpMpdTag;
ReplayGainInfo *tmpReplayGainInfo;
count = data->stream.bufend - data->stream.this_frame;
@@ -348,10 +348,10 @@ static void mp3_parseId3Tag(mp3DecodeData * data, size_t tagsize,
goto fail;
if (mpdTag) {
- tmpMpdTag = parseId3Tag(id3Tag);
+ tmpMpdTag = tag_id3_import(id3Tag);
if (tmpMpdTag) {
if (*mpdTag)
- freeMpdTag(*mpdTag);
+ tag_free(*mpdTag);
*mpdTag = tmpMpdTag;
}
}
@@ -373,7 +373,7 @@ fail:
#endif
static enum mp3_action
-decodeNextFrameHeader(mp3DecodeData * data, MpdTag ** tag,
+decodeNextFrameHeader(mp3DecodeData * data, struct mpd_tag ** tag,
ReplayGainInfo ** replayGainInfo)
{
enum mad_layer layer;
@@ -688,7 +688,7 @@ static int parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
}
static int decodeFirstFrame(mp3DecodeData * data,
- MpdTag ** tag, ReplayGainInfo ** replayGainInfo)
+ struct mpd_tag ** tag, ReplayGainInfo ** replayGainInfo)
{
struct xing xing;
struct lame lame;
@@ -811,7 +811,7 @@ static int getMp3TotalTime(char *file)
}
static int openMp3FromInputStream(InputStream * inStream, mp3DecodeData * data,
- MpdTag ** tag,
+ struct mpd_tag ** tag,
ReplayGainInfo ** replayGainInfo)
{
initMp3DecodeData(data, inStream);
@@ -819,7 +819,7 @@ static int openMp3FromInputStream(InputStream * inStream, mp3DecodeData * data,
if (decodeFirstFrame(data, tag, replayGainInfo) < 0) {
mp3DecodeDataFinalize(data);
if (tag && *tag)
- freeMpdTag(*tag);
+ tag_free(*tag);
return -1;
}
@@ -923,13 +923,12 @@ mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
}
if (data->inStream->metaTitle) {
- MpdTag *tag = newMpdTag();
+ struct mpd_tag *tag = tag_new();
if (data->inStream->metaName) {
- addItemToMpdTag(tag,
- TAG_ITEM_NAME,
- data->inStream->metaName);
+ tag_add_item(tag, TAG_ITEM_NAME,
+ data->inStream->metaName);
}
- addItemToMpdTag(tag, TAG_ITEM_TITLE,
+ tag_add_item(tag, TAG_ITEM_TITLE,
data->inStream->metaTitle);
free(data->inStream->metaTitle);
data->inStream->metaTitle = NULL;
@@ -1033,7 +1032,7 @@ static void initAudioFormatFromMp3DecodeData(mp3DecodeData * data,
static int mp3_decode(InputStream * inStream)
{
mp3DecodeData data;
- MpdTag *tag = NULL;
+ struct mpd_tag *tag = NULL;
ReplayGainInfo *replayGainInfo = NULL;
if (openMp3FromInputStream(inStream, &data, &tag, &replayGainInfo) < 0) {
@@ -1051,23 +1050,23 @@ static int mp3_decode(InputStream * inStream)
if (inStream->metaTitle) {
if (tag)
- freeMpdTag(tag);
- tag = newMpdTag();
- addItemToMpdTag(tag, TAG_ITEM_TITLE, inStream->metaTitle);
+ tag_free(tag);
+ tag = tag_new();
+ tag_add_item(tag, TAG_ITEM_TITLE, inStream->metaTitle);
free(inStream->metaTitle);
inStream->metaTitle = NULL;
if (inStream->metaName) {
- addItemToMpdTag(tag, TAG_ITEM_NAME, inStream->metaName);
+ tag_add_item(tag, TAG_ITEM_NAME, inStream->metaName);
}
} else if (tag) {
if (inStream->metaName) {
- clearItemsFromMpdTag(tag, TAG_ITEM_NAME);
- addItemToMpdTag(tag, TAG_ITEM_NAME, inStream->metaName);
+ tag_clear_items_by_type(tag, TAG_ITEM_NAME);
+ tag_add_item(tag, TAG_ITEM_NAME, inStream->metaName);
}
} else if (inStream->metaName) {
- tag = newMpdTag();
+ tag = tag_new();
if (inStream->metaName) {
- addItemToMpdTag(tag, TAG_ITEM_NAME, inStream->metaName);
+ tag_add_item(tag, TAG_ITEM_NAME, inStream->metaName);
}
}
if (tag)
@@ -1081,18 +1080,18 @@ static int mp3_decode(InputStream * inStream)
return 0;
}
-static MpdTag *mp3_tagDup(char *file)
+static struct mpd_tag *mp3_tagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int total_time;
- ret = id3Dup(file);
+ ret = tag_id3_load(file);
total_time = getMp3TotalTime(file);
if (total_time >= 0) {
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = total_time;
} else {
DEBUG("mp3_tagDup: Failed to get total song time from: %s\n",
diff --git a/src/inputPlugins/mp4_plugin.c b/src/inputPlugins/mp4_plugin.c
index 8e3d02354..cc2b89efc 100644
--- a/src/inputPlugins/mp4_plugin.c
+++ b/src/inputPlugins/mp4_plugin.c
@@ -284,9 +284,9 @@ static int mp4_decode(InputStream * inStream)
return 0;
}
-static MpdTag *mp4DataDup(char *file, int *mp4MetadataFound)
+static struct mpd_tag *mp4DataDup(char *file, int *mp4MetadataFound)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
InputStream inStream;
mp4ff_t *mp4fh;
mp4ff_callback_t *callback;
@@ -322,14 +322,14 @@ static MpdTag *mp4DataDup(char *file, int *mp4MetadataFound)
return NULL;
}
- ret = newMpdTag();
+ ret = tag_new();
file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
scale = mp4ff_time_scale(mp4fh, track);
if (scale < 0) {
mp4ff_close(mp4fh);
closeInputStream(&inStream);
free(callback);
- freeMpdTag(ret);
+ tag_free(ret);
return NULL;
}
ret->time = ((float)file_time) / scale + 0.5;
@@ -341,25 +341,25 @@ static MpdTag *mp4DataDup(char *file, int *mp4MetadataFound)
mp4ff_meta_get_by_index(mp4fh, i, &item, &value);
if (0 == strcasecmp("artist", item)) {
- addItemToMpdTag(ret, TAG_ITEM_ARTIST, value);
+ tag_add_item(ret, TAG_ITEM_ARTIST, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("title", item)) {
- addItemToMpdTag(ret, TAG_ITEM_TITLE, value);
+ tag_add_item(ret, TAG_ITEM_TITLE, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("album", item)) {
- addItemToMpdTag(ret, TAG_ITEM_ALBUM, value);
+ tag_add_item(ret, TAG_ITEM_ALBUM, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("track", item)) {
- addItemToMpdTag(ret, TAG_ITEM_TRACK, value);
+ tag_add_item(ret, TAG_ITEM_TRACK, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("disc", item)) { /* Is that the correct id? */
- addItemToMpdTag(ret, TAG_ITEM_DISC, value);
+ tag_add_item(ret, TAG_ITEM_DISC, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("genre", item)) {
- addItemToMpdTag(ret, TAG_ITEM_GENRE, value);
+ tag_add_item(ret, TAG_ITEM_GENRE, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("date", item)) {
- addItemToMpdTag(ret, TAG_ITEM_DATE, value);
+ tag_add_item(ret, TAG_ITEM_DATE, value);
*mp4MetadataFound = 1;
}
@@ -373,19 +373,19 @@ static MpdTag *mp4DataDup(char *file, int *mp4MetadataFound)
return ret;
}
-static MpdTag *mp4TagDup(char *file)
+static struct mpd_tag *mp4TagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int mp4MetadataFound = 0;
ret = mp4DataDup(file, &mp4MetadataFound);
if (!ret)
return NULL;
if (!mp4MetadataFound) {
- MpdTag *temp = id3Dup(file);
+ struct mpd_tag *temp = tag_id3_load(file);
if (temp) {
temp->time = ret->time;
- freeMpdTag(ret);
+ tag_free(ret);
ret = temp;
}
}
diff --git a/src/inputPlugins/mpc_plugin.c b/src/inputPlugins/mpc_plugin.c
index ea27d1dbf..e5502a2f0 100644
--- a/src/inputPlugins/mpc_plugin.c
+++ b/src/inputPlugins/mpc_plugin.c
@@ -273,9 +273,9 @@ static float mpcGetTime(char *file)
return total_time;
}
-static MpdTag *mpcTagDup(char *file)
+static struct mpd_tag *mpcTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
float total_time = mpcGetTime(file);
if (total_time < 0) {
@@ -284,11 +284,11 @@ static MpdTag *mpcTagDup(char *file)
return NULL;
}
- ret = apeDup(file);
+ ret = tag_ape_load(file);
if (!ret)
- ret = id3Dup(file);
+ ret = tag_id3_load(file);
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = total_time;
return ret;
diff --git a/src/inputPlugins/oggflac_plugin.c b/src/inputPlugins/oggflac_plugin.c
index 4b3bb0a6b..841030481 100644
--- a/src/inputPlugins/oggflac_plugin.c
+++ b/src/inputPlugins/oggflac_plugin.c
@@ -28,6 +28,8 @@
#include "../utils.h"
#include "../log.h"
+#include <OggFLAC/seekable_stream_decoder.h>
+
static void oggflac_cleanup(FlacData * data,
OggFLAC__SeekableStreamDecoder * decoder)
{
@@ -216,7 +218,7 @@ static void of_metadata_dup_cb(mpd_unused const OggFLAC__SeekableStreamDecoder *
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
if (!data->tag)
- data->tag = newMpdTag();
+ data->tag = tag_new();
data->tag->time = ((float)block->data.stream_info.
total_samples) /
block->data.stream_info.sample_rate + 0.5;
@@ -300,7 +302,7 @@ fail:
}
/* public functions: */
-static MpdTag *oggflac_TagDup(char *file)
+static struct mpd_tag *oggflac_TagDup(char *file)
{
InputStream inStream;
OggFLAC__SeekableStreamDecoder *decoder;
diff --git a/src/inputPlugins/oggvorbis_plugin.c b/src/inputPlugins/oggvorbis_plugin.c
index d14ae2bac..bb8af4465 100644
--- a/src/inputPlugins/oggvorbis_plugin.c
+++ b/src/inputPlugins/oggvorbis_plugin.c
@@ -145,7 +145,7 @@ static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
static unsigned int ogg_parseCommentAddToTag(char *comment,
unsigned int itemType,
- MpdTag ** tag)
+ struct mpd_tag ** tag)
{
const char *needle;
unsigned int len;
@@ -163,9 +163,9 @@ static unsigned int ogg_parseCommentAddToTag(char *comment,
if (strncasecmp(comment, needle, len) == 0 && *(comment + len) == '=') {
if (!*tag)
- *tag = newMpdTag();
+ *tag = tag_new();
- addItemToMpdTag(*tag, itemType, comment + len + 1);
+ tag_add_item(*tag, itemType, comment + len + 1);
return 1;
}
@@ -173,9 +173,9 @@ static unsigned int ogg_parseCommentAddToTag(char *comment,
return 0;
}
-static MpdTag *oggCommentsParse(char **comments)
+static struct mpd_tag *oggCommentsParse(char **comments)
{
- MpdTag *tag = NULL;
+ struct mpd_tag *tag = NULL;
while (*comments) {
int j;
@@ -193,18 +193,18 @@ static void putOggCommentsIntoOutputBuffer(char *streamName,
char **comments,
float cur_time)
{
- MpdTag *tag;
+ struct mpd_tag *tag;
tag = oggCommentsParse(comments);
if (!tag && streamName) {
- tag = newMpdTag();
+ tag = tag_new();
}
if (!tag)
return;
if (streamName) {
- clearItemsFromMpdTag(tag, TAG_ITEM_NAME);
- addItemToMpdTag(tag, TAG_ITEM_NAME, streamName);
+ tag_clear_items_by_type(tag, TAG_ITEM_NAME);
+ tag_add_item(tag, TAG_ITEM_NAME, streamName);
}
metadata_pipe_send(tag, cur_time);
@@ -332,9 +332,9 @@ static int oggvorbis_decode(InputStream * inStream)
return 0;
}
-static MpdTag *oggvorbis_TagDup(char *file)
+static struct mpd_tag *oggvorbis_TagDup(char *file)
{
- MpdTag *ret;
+ struct mpd_tag *ret;
FILE *fp;
OggVorbis_File vf;
@@ -352,7 +352,7 @@ static MpdTag *oggvorbis_TagDup(char *file)
ret = oggCommentsParse(ov_comment(&vf, -1)->user_comments);
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = (int)(ov_time_total(&vf, -1) + 0.5);
ov_clear(&vf);
diff --git a/src/inputPlugins/wavpack_plugin.c b/src/inputPlugins/wavpack_plugin.c
index c7e024a41..8bba8ae2b 100644
--- a/src/inputPlugins/wavpack_plugin.c
+++ b/src/inputPlugins/wavpack_plugin.c
@@ -224,34 +224,38 @@ static char *wavpack_tag(WavpackContext *wpc, char *key)
static ReplayGainInfo *wavpack_replaygain(WavpackContext *wpc)
{
+ static char replaygain_track_gain[] = "replaygain_track_gain";
+ static char replaygain_album_gain[] = "replaygain_album_gain";
+ static char replaygain_track_peak[] = "replaygain_track_peak";
+ static char replaygain_album_peak[] = "replaygain_album_peak";
ReplayGainInfo *replayGainInfo;
int found = 0;
char *value;
replayGainInfo = newReplayGainInfo();
- value = wavpack_tag(wpc, "replaygain_track_gain");
+ value = wavpack_tag(wpc, replaygain_track_gain);
if (value) {
replayGainInfo->trackGain = atof(value);
free(value);
found = 1;
}
- value = wavpack_tag(wpc, "replaygain_album_gain");
+ value = wavpack_tag(wpc, replaygain_album_gain);
if (value) {
replayGainInfo->albumGain = atof(value);
free(value);
found = 1;
}
- value = wavpack_tag(wpc, "replaygain_track_peak");
+ value = wavpack_tag(wpc, replaygain_track_peak);
if (value) {
replayGainInfo->trackPeak = atof(value);
free(value);
found = 1;
}
- value = wavpack_tag(wpc, "replaygain_album_peak");
+ value = wavpack_tag(wpc, replaygain_album_peak);
if (value) {
replayGainInfo->albumPeak = atof(value);
free(value);
@@ -270,10 +274,10 @@ static ReplayGainInfo *wavpack_replaygain(WavpackContext *wpc)
/*
* Reads metainfo from the specified file.
*/
-static MpdTag *wavpack_tagdup(char *fname)
+static struct mpd_tag *wavpack_tagdup(char *fname)
{
WavpackContext *wpc;
- MpdTag *tag;
+ struct mpd_tag *tag;
char error[ERRORLEN];
char *s;
int ssize;
@@ -285,12 +289,7 @@ static MpdTag *wavpack_tagdup(char *fname)
return NULL;
}
- tag = newMpdTag();
- if (tag == NULL) {
- ERROR("failed to newMpdTag()\n");
- return NULL;
- }
-
+ tag = tag_new();
tag->time =
(float)WavpackGetNumSamples(wpc) / WavpackGetSampleRate(wpc);
@@ -314,7 +313,7 @@ static MpdTag *wavpack_tagdup(char *fname)
}
WavpackGetTagItem(wpc, tagtypes[i].name, s, j);
- addItemToMpdTag(tag, tagtypes[i].type, s);
+ tag_add_item(tag, tagtypes[i].type, s);
}
}
diff --git a/src/list.c b/src/list.c
index 0f1f31ad4..bc158c513 100644
--- a/src/list.c
+++ b/src/list.c
@@ -301,11 +301,6 @@ int deleteFromList(List * list, const char *key)
return 1;
}
-static void free_const_string(const char *p)
-{
- free((char *)p);
-}
-
void deleteNodeFromList(List * list, ListNode * node)
{
assert(list != NULL);
@@ -326,7 +321,7 @@ void deleteNodeFromList(List * list, ListNode * node)
}
if (list->strdupKeys)
- free_const_string(node->key);
+ free(deconst_ptr(node->key));
free(node);
list->numberOfNodes--;
@@ -353,7 +348,7 @@ void freeList(void *list)
while (tmpNode != NULL) {
tmpNode2 = tmpNode->nextNode;
if (((List *) list)->strdupKeys)
- free_const_string(tmpNode->key);
+ free(deconst_ptr(tmpNode->key));
if (((List *) list)->freeDataFunc) {
((List *) list)->freeDataFunc(tmpNode->data);
}
diff --git a/src/locate.c b/src/locate.c
index f68afdedb..76e229f4c 100644
--- a/src/locate.c
+++ b/src/locate.c
@@ -142,11 +142,11 @@ static int strstrSearchTag(Song * song, enum tag_type type, char *str)
for (i = 0; i < song->tag->numOfItems && !ret; i++) {
if (type != LOCATE_TAG_ANY_TYPE &&
- song->tag->items[i].type != type) {
+ song->tag->items[i]->type != type) {
continue;
}
- duplicate = strDupToUpper(song->tag->items[i].value);
+ duplicate = strDupToUpper(song->tag->items[i]->value);
if (strstr(duplicate, str))
ret = 1;
free(duplicate);
@@ -186,11 +186,11 @@ static int tagItemFoundAndMatches(Song * song, enum tag_type type, char *str)
for (i = 0; i < song->tag->numOfItems; i++) {
if (type != LOCATE_TAG_ANY_TYPE &&
- song->tag->items[i].type != type) {
+ song->tag->items[i]->type != type) {
continue;
}
- if (0 == strcmp(str, song->tag->items[i].value))
+ if (0 == strcmp(str, song->tag->items[i]->value))
return 1;
}
diff --git a/src/main.c b/src/main.c
index 363359831..a5941b848 100644
--- a/src/main.c
+++ b/src/main.c
@@ -393,7 +393,7 @@ int main(int argc, char *argv[])
killFromPidFile();
initStats();
- initTagConfig();
+ tag_lib_init();
initLog(options.verbose);
if (options.createDB <= 0)
diff --git a/src/metadata_pipe.c b/src/metadata_pipe.c
index 5508b97c8..d968dee27 100644
--- a/src/metadata_pipe.c
+++ b/src/metadata_pipe.c
@@ -24,17 +24,13 @@
#include "outputBuffer.h"
#include "gcc.h"
-/* These are defined in outputBuffer_accessors.h, cleanup is needed */
-mpd_uint8 ob_get_decoder_sequence(void);
-mpd_uint8 ob_get_player_sequence(void);
-
static struct ringbuf *mp;
/* Each one of these is a packet inside the metadata pipe */
struct tag_container {
float metadata_time;
mpd_uint8 seq; /* ob.seq_decoder at time of metadata_pipe_send() */
- MpdTag *tag; /* our payload */
+ struct mpd_tag *tag; /* our payload */
};
/*
@@ -43,13 +39,13 @@ struct tag_container {
* done from one thread, so it will never block or clobber.
*/
static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
-static MpdTag *current_tag; /* requires read_lock for both r/w access */
+static struct mpd_tag *current_tag; /* requires read_lock for both r/w access */
static void metadata_pipe_finish(void)
{
ringbuf_free(mp);
if (current_tag)
- freeMpdTag(current_tag);
+ tag_free(current_tag);
}
void init_metadata_pipe(void)
@@ -58,7 +54,7 @@ void init_metadata_pipe(void)
atexit(metadata_pipe_finish);
}
-void metadata_pipe_send(MpdTag *tag, float metadata_time)
+void metadata_pipe_send(struct mpd_tag *tag, float metadata_time)
{
struct tag_container tc;
size_t written;
@@ -68,7 +64,7 @@ void metadata_pipe_send(MpdTag *tag, float metadata_time)
if (mpd_unlikely(ringbuf_write_space(mp)
< sizeof(struct tag_container))) {
DEBUG("metadata_pipe: insufficient buffer space, dropping\n");
- freeMpdTag(tag);
+ tag_free(tag);
return;
}
@@ -79,14 +75,14 @@ void metadata_pipe_send(MpdTag *tag, float metadata_time)
assert(written == sizeof(struct tag_container));
}
-MpdTag * metadata_pipe_recv(void)
+struct mpd_tag * metadata_pipe_recv(void)
{
struct tag_container tc;
size_t r;
static const size_t mpd_uint8_max = 255; /* XXX CLEANUP */
mpd_uint8 expect_seq = ob_get_player_sequence();
unsigned long current_time = ob_get_elapsed_time();
- MpdTag *tag = NULL;
+ struct mpd_tag *tag = NULL;
if (pthread_mutex_trylock(&read_lock) == EBUSY)
return NULL;
@@ -99,20 +95,20 @@ retry:
if (expect_seq == tc.seq) {
if (current_time < tc.metadata_time)
goto out; /* not ready for tag yet */
- if (mpdTagsAreEqual(tc.tag, current_tag)) {
- freeMpdTag(tc.tag);
+ if (tag_equal(tc.tag, current_tag)) {
+ tag_free(tc.tag);
ringbuf_read_advance(mp, sizeof(struct tag_container));
goto out; /* nothing changed, don't bother */
}
- tag = mpdTagDup(tc.tag);
+ tag = tag_dup(tc.tag);
if (current_tag)
- freeMpdTag(current_tag);
+ tag_free(current_tag);
current_tag = tc.tag;
ringbuf_read_advance(mp, sizeof(struct tag_container));
} else if (expect_seq > tc.seq ||
(!expect_seq && tc.seq == mpd_uint8_max)) {
DEBUG("metadata_pipe: reader is ahead of writer\n");
- freeMpdTag(tc.tag);
+ tag_free(tc.tag);
ringbuf_read_advance(mp, sizeof(struct tag_container));
goto retry; /* read and skip packets */
} else {
@@ -124,14 +120,14 @@ out:
return tag;
}
-MpdTag *metadata_pipe_current(void)
+struct mpd_tag *metadata_pipe_current(void)
{
- MpdTag *tag;
+ struct mpd_tag *tag;
assert(! pthread_equal(pthread_self(), dc.thread));
if (pthread_mutex_trylock(&read_lock) == EBUSY)
return NULL;
- tag = current_tag ? mpdTagDup(current_tag) : NULL;
+ tag = current_tag ? tag_dup(current_tag) : NULL;
pthread_mutex_unlock(&read_lock);
return tag;
@@ -146,11 +142,11 @@ void metadata_pipe_clear(void)
while ((r = ringbuf_read(mp, &tc, sizeof(struct tag_container)))) {
assert(r == sizeof(struct tag_container));
- freeMpdTag(tc.tag);
+ tag_free(tc.tag);
}
if (current_tag) {
- freeMpdTag(current_tag);
+ tag_free(current_tag);
current_tag = NULL;
}
diff --git a/src/metadata_pipe.h b/src/metadata_pipe.h
index e54c67584..cb891aa80 100644
--- a/src/metadata_pipe.h
+++ b/src/metadata_pipe.h
@@ -29,21 +29,21 @@ void init_metadata_pipe(void);
* DO NOT FREE the tag placed into the pipe; that is that job of the
* caller of metadata_pipe_recv() or metadata_pipe_clear().
*/
-void metadata_pipe_send(MpdTag * tag, float metadata_time);
+void metadata_pipe_send(struct mpd_tag * tag, float metadata_time);
/*
- * Reads and consumes one MpdTag pointer off the pipe. The caller
- * of this MUST free the MpdTag pointer after it is done using it.
+ * Reads and consumes one struct mpd_tag pointer off the pipe. The caller
+ * of this MUST free the struct mpd_tag pointer after it is done using it.
*/
-MpdTag * metadata_pipe_recv(void);
+struct mpd_tag * metadata_pipe_recv(void);
/*
- * Returns the last read MpdTag from metadata_pipe_recv(), caller
+ * Returns the last read struct mpd_tag from metadata_pipe_recv(), caller
* must free this pointer when it is done using it.
*/
-MpdTag * metadata_pipe_current(void);
+struct mpd_tag * metadata_pipe_current(void);
-/* Clears all MpdTag pointers on the pipe, freeing all associated elements */
+/* Clears all struct mpd_tag pointers on the pipe, freeing all associated elements */
void metadata_pipe_clear(void);
#endif /* METADATA_PIPE_H */
diff --git a/src/mpd_types.h b/src/mpd_types.h
index ed573c4a4..dbdfc6865 100644
--- a/src/mpd_types.h
+++ b/src/mpd_types.h
@@ -40,9 +40,4 @@ typedef unsigned long mpd_uint32;
typedef signed long mpd_sint32;
#endif
-union const_hack {
- const char *in;
- char *out;
-};
-
#endif
diff --git a/src/outputBuffer.c b/src/outputBuffer.c
index 8a41924db..ac0f420d8 100644
--- a/src/outputBuffer.c
+++ b/src/outputBuffer.c
@@ -28,6 +28,8 @@
#include "log.h"
#include "action_status.h"
#include "decode.h"
+#include "metadata_pipe.h"
+#include "playlist.h"
/* typically have 2048-4096 of these structs, so pack tightly */
struct ob_chunk {
@@ -408,13 +410,13 @@ static void new_song_chunk(struct ob_chunk *a)
static void send_next_tag(void)
{
- static MpdTag *last_tag;
- MpdTag *tag;
+ static struct mpd_tag *last_tag;
+ struct mpd_tag *tag;
if ((tag = metadata_pipe_recv())) { /* streaming tag */
DEBUG("Caught new metadata! %p\n", tag);
sendMetadataToAudioDevice(tag);
- freeMpdTag(tag);
+ tag_free(tag);
wakeup_main_task(); /* call sync_metadata() in playlist.c */
} else if ((tag = playlist_current_tag())) { /* static file tag */
/* shouldn't need mpdTagsAreEqual here for static tags */
diff --git a/src/outputBuffer.h b/src/outputBuffer.h
index 3fc440af3..022f7ecdc 100644
--- a/src/outputBuffer.h
+++ b/src/outputBuffer.h
@@ -106,4 +106,7 @@ void ob_flush(void);
void config_output_buffer(void);
void init_output_buffer(void);
+mpd_uint8 ob_get_decoder_sequence(void);
+
+mpd_uint8 ob_get_player_sequence(void);
#endif
diff --git a/src/outputBuffer_ob_send.h b/src/outputBuffer_ob_send.h
index d2c99b3f2..310d5f8fd 100644
--- a/src/outputBuffer_ob_send.h
+++ b/src/outputBuffer_ob_send.h
@@ -122,7 +122,7 @@ ob_send(void *data, size_t len,
start_playback();
return dc.action;
}
- data += c_len;
+ data = (unsigned char *)data + c_len;
}
}
}
diff --git a/src/path.c b/src/path.c
index 6aaff84cf..ceb00c5de 100644
--- a/src/path.c
+++ b/src/path.c
@@ -47,7 +47,7 @@ static char *path_conv_charset(char *dest, const char *to,
char *fs_charset_to_utf8(char *dst, const char *str)
{
char *ret = path_conv_charset(dst, "UTF-8", fsCharset, str);
- return (ret && !validUtf8String(ret)) ? NULL : ret;
+ return (ret && !validUtf8String(ret, strlen(ret))) ? NULL : ret;
}
char *utf8_to_fs_charset(char *dst, const char *str)
diff --git a/src/pcm_utils.c b/src/pcm_utils.c
index f716c279d..90856fa1d 100644
--- a/src/pcm_utils.c
+++ b/src/pcm_utils.c
@@ -149,6 +149,7 @@ static int pcm_getSampleRateConverter(void)
const char *conf = getConfigParamValue(CONF_SAMPLERATE_CONVERTER);
long convalgo;
char *test;
+ const char *test2;
size_t len;
if (!conf) {
@@ -162,12 +163,12 @@ static int pcm_getSampleRateConverter(void)
len = strlen(conf);
for (convalgo = 0 ; ; convalgo++) {
- test = (char *)src_get_name(convalgo);
- if (!test) {
+ test2 = src_get_name(convalgo);
+ if (!test2) {
convalgo = SRC_SINC_FASTEST;
break;
}
- if (strncasecmp(test, conf, len) == 0)
+ if (strncasecmp(test2, conf, len) == 0)
goto out;
}
@@ -239,7 +240,7 @@ static size_t pcm_convertSampleRate(mpd_sint8 channels, mpd_uint32 inSampleRate,
data->data_out = xrealloc(data->data_out, dataOutSize);
}
- src_short_to_float_array((short *)inBuffer, data->data_in,
+ src_short_to_float_array((const short *)inBuffer, data->data_in,
data->input_frames * channels);
error = src_process(convState->state, data);
@@ -305,7 +306,7 @@ static char *pcm_convertChannels(mpd_sint8 channels, const char *inBuffer,
static char *buf;
static size_t len;
char *outBuffer = NULL;
- mpd_sint16 *in;
+ const mpd_sint16 *in;
mpd_sint16 *out;
int inSamples, i;
@@ -320,7 +321,7 @@ static char *pcm_convertChannels(mpd_sint8 channels, const char *inBuffer,
outBuffer = buf;
inSamples = inSize >> 1;
- in = (mpd_sint16 *)inBuffer;
+ in = (const mpd_sint16 *)inBuffer;
out = (mpd_sint16 *)outBuffer;
for (i = 0; i < inSamples; i++) {
*out++ = *in;
@@ -338,7 +339,7 @@ static char *pcm_convertChannels(mpd_sint8 channels, const char *inBuffer,
outBuffer = buf;
inSamples = inSize >> 2;
- in = (mpd_sint16 *)inBuffer;
+ in = (const mpd_sint16 *)inBuffer;
out = (mpd_sint16 *)outBuffer;
for (i = 0; i < inSamples; i++) {
*out = (*in++) / 2;
@@ -359,7 +360,7 @@ static const char *pcm_convertTo16bit(mpd_sint8 bits, const char *inBuffer,
static char *buf;
static size_t len;
char *outBuffer = NULL;
- mpd_sint8 *in;
+ const mpd_sint8 *in;
mpd_sint16 *out;
size_t i;
@@ -372,7 +373,7 @@ static const char *pcm_convertTo16bit(mpd_sint8 bits, const char *inBuffer,
}
outBuffer = buf;
- in = (mpd_sint8 *)inBuffer;
+ in = (const mpd_sint8 *)inBuffer;
out = (mpd_sint16 *)outBuffer;
for (i = 0; i < inSize; i++)
*out++ = (*in++) << 8;
diff --git a/src/playlist.c b/src/playlist.c
index 0512951fd..ca79393b0 100644
--- a/src/playlist.c
+++ b/src/playlist.c
@@ -238,12 +238,12 @@ void clearPlaylist(void)
incrPlaylistVersion();
}
-int clearStoredPlaylist(int fd, char *utf8file)
+int clearStoredPlaylist(const char *utf8file)
{
- return removeAllFromStoredPlaylistByPath(fd, utf8file);
+ return removeAllFromStoredPlaylistByPath(utf8file);
}
-int showPlaylist(int fd)
+void showPlaylist(int fd)
{
int i;
char path_max_tmp[MPD_PATH_MAX];
@@ -252,8 +252,6 @@ int showPlaylist(int fd)
fdprintf(fd, "%i:%s\n", i,
get_song_url(path_max_tmp, playlist.songs[i]));
}
-
- return 0;
}
void savePlaylistState(FILE *fp)
@@ -303,13 +301,12 @@ static void loadPlaylistFromStateFile(FILE *fp, char *buffer,
song = atoi(temp);
if (!(temp = strtok(NULL, "")))
state_file_fatal();
- if (!addToPlaylist(STDERR_FILENO, temp, NULL)
+ if (addToPlaylist(temp, NULL) == PLAYLIST_RESULT_SUCCESS
&& current == song) {
if (state == OB_STATE_PAUSE)
ob_trigger_action(OB_ACTION_PAUSE_SET);
if (state != OB_STATE_STOP) {
- seekSongInPlaylist(STDERR_FILENO,
- playlist.length - 1,
+ seekSongInPlaylist(playlist.length - 1,
seek_time);
}
}
@@ -349,9 +346,9 @@ void readPlaylistState(FILE *fp)
if (strcmp
(&(buffer[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
"1") == 0) {
- setPlaylistRepeatStatus(STDERR_FILENO, 1);
+ setPlaylistRepeatStatus(1);
} else
- setPlaylistRepeatStatus(STDERR_FILENO, 0);
+ setPlaylistRepeatStatus(0);
} else
if (strncmp
(buffer, PLAYLIST_STATE_FILE_CROSSFADE,
@@ -368,9 +365,9 @@ void readPlaylistState(FILE *fp)
(buffer
[strlen(PLAYLIST_STATE_FILE_RANDOM)]),
"1") == 0) {
- setPlaylistRandomStatus(STDERR_FILENO, 1);
+ setPlaylistRandomStatus(1);
} else
- setPlaylistRandomStatus(STDERR_FILENO, 0);
+ setPlaylistRandomStatus(0);
} else if (strncmp(buffer, PLAYLIST_STATE_FILE_CURRENT,
strlen(PLAYLIST_STATE_FILE_CURRENT))
== 0) {
@@ -430,7 +427,7 @@ int playlistChangesPosId(int fd, mpd_uint32 version)
return 0;
}
-int playlistInfo(int fd, int song)
+enum playlist_result playlistInfo(int fd, int song)
{
int i;
int begin = 0;
@@ -440,36 +437,31 @@ int playlistInfo(int fd, int song)
begin = song;
end = song + 1;
}
- if (song >= playlist.length) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "song doesn't exist: \"%i\"", song);
- return -1;
- }
+ if (song >= playlist.length)
+ return PLAYLIST_RESULT_BAD_RANGE;
for (i = begin; i < end; i++)
printPlaylistSongInfo(fd, i);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-# define checkSongId(id) { \
- if(id < 0 || id >= PLAYLIST_HASH_MULT*playlist_max_length || \
- playlist.idToPosition[id] == -1 ) \
- { \
- commandError(fd, ACK_ERROR_NO_EXIST, \
- "song id doesn't exist: \"%i\"", id); \
- return -1; \
- } \
+static int song_id_exists(int id)
+{
+ return id >= 0 && id < PLAYLIST_HASH_MULT*playlist_max_length &&
+ playlist.idToPosition[id] != -1;
}
-int playlistId(int fd, int id)
+enum playlist_result playlistId(int fd, int id)
{
int i;
int begin = 0;
int end = playlist.length;
if (id >= 0) {
- checkSongId(id);
+ if (!song_id_exists(id))
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
begin = playlist.idToPosition[id];
end = begin + 1;
}
@@ -477,7 +469,7 @@ int playlistId(int fd, int id)
for (i = begin; i < end; i++)
printPlaylistSongInfo(fd, i);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
static void swapSongs(int song1, int song2)
@@ -485,6 +477,11 @@ static void swapSongs(int song1, int song2)
Song *sTemp;
int iTemp;
+ assert(song1 < playlist.length);
+ assert(song1 >= 0);
+ assert(song2 < playlist.length);
+ assert(song2 >= 0);
+
sTemp = playlist.songs[song1];
playlist.songs[song1] = playlist.songs[song2];
playlist.songs[song2] = sTemp;
@@ -592,7 +589,7 @@ static int clear_queue(void)
return playlist.queued;
}
-int addToPlaylist(int fd, char *url, int *added_id)
+enum playlist_result addToPlaylist(const char *url, int *added_id)
{
Song *song;
@@ -601,52 +598,41 @@ int addToPlaylist(int fd, char *url, int *added_id)
if ((song = getSongFromDB(url))) {
} else if (!(isValidRemoteUtf8Url(url) &&
(song = newSong(url, SONG_TYPE_URL, NULL)))) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "\"%s\" is not in the music db or is "
- "not a valid url", url);
- return -1;
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
}
- return addSongToPlaylist(fd, song, added_id);
+ return addSongToPlaylist(song, added_id);
}
-int addToStoredPlaylist(int fd, char *url, char *utf8file)
+int addToStoredPlaylist(const char *url, const char *utf8file)
{
Song *song;
DEBUG("add to stored playlist: %s\n", url);
song = getSongFromDB(url);
- if (song) {
- appendSongToStoredPlaylistByPath(fd, utf8file, song);
- return 0;
- }
+ if (song)
+ return appendSongToStoredPlaylistByPath(utf8file, song);
if (!isValidRemoteUtf8Url(url))
- goto fail;
+ return ACK_ERROR_NO_EXIST;
song = newSong(url, SONG_TYPE_URL, NULL);
if (song) {
- appendSongToStoredPlaylistByPath(fd, utf8file, song);
+ int ret = appendSongToStoredPlaylistByPath(utf8file, song);
freeJustSong(song);
- return 0;
+ return ret;
}
-fail:
- commandError(fd, ACK_ERROR_NO_EXIST, "\"%s\" is not in the music db"
- "or is not a valid url", url);
- return -1;
+ return ACK_ERROR_NO_EXIST;
}
-int addSongToPlaylist(int fd, Song * song, int *added_id)
+enum playlist_result addSongToPlaylist(Song * song, int *added_id)
{
int id;
- if (playlist.length == playlist_max_length) {
- commandError(fd, ACK_ERROR_PLAYLIST_MAX,
- "playlist is at the max size");
- return -1;
- }
+ if (playlist.length == playlist_max_length)
+ return PLAYLIST_RESULT_TOO_LARGE;
if (playlist_state == PLAYLIST_STATE_PLAY) {
if (playlist.queued >= 0
@@ -689,24 +675,17 @@ int addSongToPlaylist(int fd, Song * song, int *added_id)
if (added_id)
*added_id = id;
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-int swapSongsInPlaylist(int fd, int song1, int song2)
+enum playlist_result swapSongsInPlaylist(int song1, int song2)
{
int queuedSong = -1;
int currentSong;
- if (song1 < 0 || song1 >= playlist.length) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "song doesn't exist: \"%i\"", song1);
- return -1;
- }
- if (song2 < 0 || song2 >= playlist.length) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "song doesn't exist: \"%i\"", song2);
- return -1;
- }
+ if (song1 < 0 || song1 >= playlist.length ||
+ song2 < 0 || song2 >= playlist.length)
+ return PLAYLIST_RESULT_BAD_RANGE;
if (playlist_state == PLAYLIST_STATE_PLAY) {
if (playlist.queued >= 0) {
@@ -744,15 +723,15 @@ int swapSongsInPlaylist(int fd, int song1, int song2)
incrPlaylistVersion();
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-int swapSongsInPlaylistById(int fd, int id1, int id2)
+enum playlist_result swapSongsInPlaylistById(int id1, int id2)
{
- checkSongId(id1);
- checkSongId(id2);
+ if (!song_id_exists(id1) || !song_id_exists(id2))
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
- return swapSongsInPlaylist(fd, playlist.idToPosition[id1],
+ return swapSongsInPlaylist(playlist.idToPosition[id1],
playlist.idToPosition[id2]);
}
@@ -763,18 +742,15 @@ int swapSongsInPlaylistById(int fd, int id1, int id2)
playlist.songMod[to] = playlist.version; \
}
-int deleteFromPlaylist(int fd, int song)
+enum playlist_result deleteFromPlaylist(int song)
{
int i;
int songOrder;
int stop_current = 0;
int prev_queued = playlist.queued;
- if (song < 0 || song >= playlist.length) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "song doesn't exist: \"%i\"", song);
- return -1;
- }
+ if (song < 0 || song >= playlist.length)
+ return PLAYLIST_RESULT_BAD_RANGE;
if (playlist_state == PLAYLIST_STATE_PLAY) {
if (prev_queued >= 0
@@ -839,14 +815,15 @@ int deleteFromPlaylist(int fd, int song)
queueNextSongInPlaylist();
}
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-int deleteFromPlaylistById(int fd, int id)
+enum playlist_result deleteFromPlaylistById(int id)
{
- checkSongId(id);
+ if (!song_id_exists(id))
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
- return deleteFromPlaylist(fd, playlist.idToPosition[id]);
+ return deleteFromPlaylist(playlist.idToPosition[id]);
}
void deleteASongFromPlaylist(Song * song)
@@ -858,7 +835,7 @@ void deleteASongFromPlaylist(Song * song)
for (i = 0; i < playlist.length; i++) {
if (song == playlist.songs[i]) {
- deleteFromPlaylist(STDERR_FILENO, i);
+ deleteFromPlaylist(i);
}
}
}
@@ -904,7 +881,7 @@ static void play_order_num(int order_num, float seek_time)
playlist.current = order_num;
}
-int playPlaylist(int fd, int song, int stopOnError)
+enum playlist_result playPlaylist(int song, int stopOnError)
{
int i = song;
@@ -914,11 +891,11 @@ int playPlaylist(int fd, int song, int stopOnError)
if (song == -1) {
if (playlist.length == 0)
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
if (playlist_state == PLAYLIST_STATE_PLAY) {
ob_trigger_action(OB_ACTION_PAUSE_UNSET);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
if (playlist.current >= 0 && playlist.current < playlist.length) {
i = playlist.current;
@@ -926,9 +903,7 @@ int playPlaylist(int fd, int song, int stopOnError)
i = 0;
}
} else if (song < 0 || song >= playlist.length) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "song doesn't exist: \"%i\"", song);
- return -1;
+ return PLAYLIST_RESULT_BAD_RANGE;
}
if (playlist.random) {
@@ -940,6 +915,7 @@ int playPlaylist(int fd, int song, int stopOnError)
playlist.current = 0;
swapOrder(i, playlist.current);
i = playlist.current;
+ randomizeOrder(i + 1, playlist.length - 1);
}
}
@@ -949,22 +925,23 @@ int playPlaylist(int fd, int song, int stopOnError)
ERROR(__FILE__ ": %d current:%d\n", __LINE__, playlist.current);
ob_trigger_action(OB_ACTION_PAUSE_UNSET);
play_order_num(i, 0);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-int playPlaylistById(int fd, int id, int stopOnError)
+enum playlist_result playPlaylistById(int id, int stopOnError)
{
if (id == -1) {
- return playPlaylist(fd, id, stopOnError);
+ return playPlaylist(id, stopOnError);
}
- checkSongId(id);
+ if (!song_id_exists(id))
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playPlaylist(fd, playlist.idToPosition[id], stopOnError);
+ return playPlaylist(playlist.idToPosition[id], stopOnError);
}
/* This is used when we stream data out to shout while playing static files */
-MpdTag *playlist_current_tag(void)
+struct mpd_tag *playlist_current_tag(void)
{
Song *song = song_at(playlist.current);
@@ -976,18 +953,18 @@ MpdTag *playlist_current_tag(void)
static void sync_metadata(void)
{
Song *song;
- MpdTag *tag;
+ struct mpd_tag *tag;
if (!(tag = metadata_pipe_current()))
return;
song = song_at(playlist.current);
if (!song || song->type != SONG_TYPE_URL ||
- mpdTagsAreEqual(song->tag, tag)) {
- freeMpdTag(tag);
+ tag_equal(song->tag, tag)) {
+ tag_free(tag);
return;
}
if (song->tag)
- freeMpdTag(song->tag);
+ tag_free(song->tag);
song->tag = tag;
playlist.songMod[playlist.order[playlist.current]] = playlist.version;
incrPlaylistVersion();
@@ -1037,24 +1014,17 @@ int getPlaylistRandomStatus(void)
return playlist.random;
}
-int setPlaylistRepeatStatus(int fd, int status)
+void setPlaylistRepeatStatus(int status)
{
- if (status != 0 && status != 1) {
- commandError(fd, ACK_ERROR_ARG, "\"%i\" is not 0 or 1", status);
- return -1;
- }
-
if (playlist_state == PLAYLIST_STATE_PLAY) {
if (playlist.repeat && !status && playlist.queued == 0)
clear_queue();
}
playlist.repeat = status;
-
- return 0;
}
-int moveSongInPlaylist(int fd, int from, int to)
+enum playlist_result moveSongInPlaylist(int from, int to)
{
int i;
Song *tmpSong;
@@ -1062,21 +1032,15 @@ int moveSongInPlaylist(int fd, int from, int to)
int currentSong;
int queued_is_current = (playlist.queued == playlist.current);
- if (from < 0 || from >= playlist.length) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "song doesn't exist: \"%i\"", from);
- return -1;
- }
+ if (from < 0 || from >= playlist.length)
+ return PLAYLIST_RESULT_BAD_RANGE;
if ((to >= 0 && to >= playlist.length) ||
- (to < 0 && abs(to) > playlist.length)) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "song doesn't exist: \"%i\"", to);
- return -1;
- }
+ (to < 0 && abs(to) > playlist.length))
+ return PLAYLIST_RESULT_BAD_RANGE;
if (from == to) /* no-op */
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
/*
* (to < 0) => move to offset from current song
@@ -1086,7 +1050,7 @@ int moveSongInPlaylist(int fd, int from, int to)
if (to < 0 && playlist.current >= 0) {
if (currentSong == from)
/* no-op, can't be moved to offset of itself */
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
to = (currentSong + abs(to)) % playlist.length;
}
@@ -1141,14 +1105,15 @@ int moveSongInPlaylist(int fd, int from, int to)
queueNextSongInPlaylist();
incrPlaylistVersion();
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-int moveSongInPlaylistById(int fd, int id1, int to)
+enum playlist_result moveSongInPlaylistById(int id1, int to)
{
- checkSongId(id1);
+ if (!song_id_exists(id1))
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
- return moveSongInPlaylist(fd, playlist.idToPosition[id1], to);
+ return moveSongInPlaylist(playlist.idToPosition[id1], to);
}
static void orderPlaylist(void)
@@ -1171,7 +1136,14 @@ static void orderPlaylist(void)
static void swapOrder(int a, int b)
{
- int bak = playlist.order[a];
+ int bak;
+
+ assert(a < playlist.length);
+ assert(a >= 0);
+ assert(b < playlist.length);
+ assert(b >= 0);
+
+ bak = playlist.order[a];
playlist.order[a] = playlist.order[b];
playlist.order[b] = bak;
}
@@ -1191,8 +1163,8 @@ static void randomizeOrder(int start, int end)
playlist.queued <= end)
clear_queue();
- for (i = start; i <= end; i++) {
- ri = random() % (end - start + 1) + start;
+ for (i = start; i < end; i++) {
+ ri = random() % (end - i) + start;
if (ri == playlist.current)
playlist.current = i;
else if (i == playlist.current)
@@ -1204,15 +1176,10 @@ static void randomizeOrder(int start, int end)
DEBUG("%s:%d current: %d\n", __func__, __LINE__, playlist.current);
}
-int setPlaylistRandomStatus(int fd, int status)
+void setPlaylistRandomStatus(int status)
{
int statusWas = playlist.random;
- if (status != 0 && status != 1) {
- commandError(fd, ACK_ERROR_ARG, "\"%i\" is not 0 or 1", status);
- return -1;
- }
-
playlist.random = status;
if (status != statusWas) {
@@ -1226,8 +1193,6 @@ int setPlaylistRandomStatus(int fd, int status)
__func__,__LINE__,playlist.queued);
}
}
-
- return 0;
}
void previousSongInPlaylist(void)
@@ -1257,13 +1222,14 @@ void previousSongInPlaylist(void)
play_order_num(prev_order_num, 0);
}
-int shufflePlaylist(mpd_unused int fd)
+void shufflePlaylist(void)
{
int i;
int ri;
int playing_queued = 0;
+ int length = playlist.length;
- if (playlist.length > 1) {
+ if (length > 1) {
if (playlist_state == PLAYLIST_STATE_PLAY) {
if (playlist.queued == playlist.current)
playing_queued = 1;
@@ -1285,62 +1251,49 @@ int shufflePlaylist(mpd_unused int fd)
playlist.current = -1;
}
/* shuffle the rest of the list */
- for (; i < playlist.length; i++) {
- ri = random() % (playlist.length - 1) + 1;
+ for (; --length > 0; ++i) {
+ ri = random() % length + 1;
swapSongs(i, ri);
}
incrPlaylistVersion();
if (playlist_state == PLAYLIST_STATE_PLAY)
queueNextSongInPlaylist();
}
-
- return 0;
}
-int deletePlaylist(int fd, char *utf8file)
+enum playlist_result deletePlaylist(const char *utf8file)
{
char path_max_tmp[MPD_PATH_MAX];
utf8_to_fs_playlist_path(path_max_tmp, utf8file);
- if (!isPlaylist(path_max_tmp)) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "playlist \"%s\" not found", utf8file);
- return -1;
- }
+ if (!isPlaylist(path_max_tmp))
+ return PLAYLIST_RESULT_NO_SUCH_LIST;
- if (unlink(path_max_tmp) < 0) {
- commandError(fd, ACK_ERROR_SYSTEM,
- "problems deleting file");
- return -1;
- }
+ if (unlink(path_max_tmp) < 0)
+ return PLAYLIST_RESULT_ERRNO;
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-int savePlaylist(int fd, char *utf8file)
+enum playlist_result savePlaylist(const char *utf8file)
{
FILE *fp;
int i;
struct stat sb;
char path_max_tmp[MPD_PATH_MAX];
- if (!valid_playlist_name(fd, utf8file))
- return -1;
+ if (!is_valid_playlist_name(utf8file))
+ return PLAYLIST_RESULT_BAD_NAME;
utf8_to_fs_playlist_path(path_max_tmp, utf8file);
- if (!stat(path_max_tmp, &sb)) {
- commandError(fd, ACK_ERROR_EXIST, "a file or directory already "
- "exists with the name \"%s\"", utf8file);
- return -1;
- }
+ if (!stat(path_max_tmp, &sb))
+ return PLAYLIST_RESULT_LIST_EXISTS;
while (!(fp = fopen(path_max_tmp, "w")) && errno == EINTR);
- if (fp == NULL) {
- commandError(fd, ACK_ERROR_SYSTEM, "failed to create file");
- return -1;
- }
+ if (fp == NULL)
+ return PLAYLIST_RESULT_ERRNO;
for (i = 0; i < playlist.length; i++) {
char tmp[MPD_PATH_MAX];
@@ -1357,7 +1310,7 @@ int savePlaylist(int fd, char *utf8file)
while (fclose(fp) && errno == EINTR) ;
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
int getPlaylistCurrentSong(void)
@@ -1384,16 +1337,13 @@ int getPlaylistLength(void)
* This command will always return 0 regardless of whether or
* not the seek succeeded (it's always been the case, apparently)
*/
-int seekSongInPlaylist(int fd, int song, float seek_time)
+enum playlist_result seekSongInPlaylist(int song, float seek_time)
{
int i = song;
char path[MPD_PATH_MAX];
- if (song < 0 || song >= playlist.length) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "song doesn't exist: \"%i\"", song);
- return -1;
- }
+ if (song < 0 || song >= playlist.length)
+ return PLAYLIST_RESULT_BAD_RANGE;
if (playlist.random)
for (i = 0; song != playlist.order[i]; i++) ;
@@ -1406,7 +1356,7 @@ int seekSongInPlaylist(int fd, int song, float seek_time)
(playlist.current == i && playlist.queued == i)) {
dc_trigger_action(DC_ACTION_SEEK, seek_time);
if (dc.seek_where != DC_SEEK_MISMATCH)
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
/*
* if near end of decoding can cause seek to fail (since we're
* already on another song) (leading to DC_SEEK_MISMATCH),
@@ -1416,14 +1366,15 @@ int seekSongInPlaylist(int fd, int song, float seek_time)
DEBUG("playlist: seek %i:\"%s\"\n", i, get_song_url(path, song_at(i)));
play_order_num(i, seek_time);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-int seekSongInPlaylistById(int fd, int id, float seek_time)
+enum playlist_result seekSongInPlaylistById(int id, float seek_time)
{
- checkSongId(id);
+ if (!song_id_exists(id))
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
- return seekSongInPlaylist(fd, playlist.idToPosition[id], seek_time);
+ return seekSongInPlaylist(playlist.idToPosition[id], seek_time);
}
int getPlaylistSongId(int song)
@@ -1431,12 +1382,12 @@ int getPlaylistSongId(int song)
return playlist.positionToId[song];
}
-int PlaylistInfo(int fd, char *utf8file, int detail)
+int PlaylistInfo(int fd, const char *utf8file, int detail)
{
ListNode *node;
List *list;
- if (!(list = loadStoredPlaylist(fd, utf8file)))
+ if (!(list = loadStoredPlaylist(utf8file)))
return -1;
node = list->firstNode;
@@ -1463,18 +1414,18 @@ int PlaylistInfo(int fd, char *utf8file, int detail)
return 0;
}
-int loadPlaylist(int fd, char *utf8file)
+enum playlist_result loadPlaylist(int fd, const char *utf8file)
{
ListNode *node;
List *list;
- if (!(list = loadStoredPlaylist(fd, utf8file)))
- return -1;
+ if (!(list = loadStoredPlaylist(utf8file)))
+ return PLAYLIST_RESULT_NO_SUCH_LIST;
node = list->firstNode;
while (node != NULL) {
char *temp = node->data;
- if ((addToPlaylist(STDERR_FILENO, temp, NULL)) < 0) {
+ if ((addToPlaylist(temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
/* for windows compatibility, convert slashes */
char *temp2 = xstrdup(temp);
char *p = temp2;
@@ -1483,7 +1434,7 @@ int loadPlaylist(int fd, char *utf8file)
*p = '/';
p++;
}
- if ((addToPlaylist(STDERR_FILENO, temp2, NULL)) < 0) {
+ if ((addToPlaylist(temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
commandError(fd, ACK_ERROR_PLAYLIST_LOAD,
"can't add file \"%s\"", temp2);
}
@@ -1494,7 +1445,7 @@ int loadPlaylist(int fd, char *utf8file)
}
freeList(list);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
void searchForSongsInPlaylist(int fd, int numItems, LocateTagItem * items)
@@ -1539,18 +1490,11 @@ void findSongsInPlaylist(int fd, int numItems, LocateTagItem * items)
* protocol (and compatibility with all clients) to support idiots who
* put '\r' and '\n' in filenames isn't going to happen, either.
*/
-int valid_playlist_name(int err_fd, const char *utf8path)
-{
- if (strchr(utf8path, '/') ||
- strchr(utf8path, '\n') ||
- strchr(utf8path, '\r')) {
- commandError(err_fd, ACK_ERROR_ARG, "playlist name \"%s\" is "
- "invalid: playlist names may not contain slashes,"
- " newlines or carriage returns",
- utf8path);
- return 0;
- }
- return 1;
+int is_valid_playlist_name(const char *utf8path)
+{
+ return strchr(utf8path, '/') == NULL &&
+ strchr(utf8path, '\n') == NULL &&
+ strchr(utf8path, '\r') == NULL;
}
int playlist_playing(void)
diff --git a/src/playlist.h b/src/playlist.h
index 0bcc9680a..2d8d43ec6 100644
--- a/src/playlist.h
+++ b/src/playlist.h
@@ -24,6 +24,18 @@
#define PLAYLIST_FILE_SUFFIX "m3u"
#define PLAYLIST_COMMENT '#'
+enum playlist_result {
+ PLAYLIST_RESULT_SUCCESS,
+ PLAYLIST_RESULT_ERRNO,
+ PLAYLIST_RESULT_NO_SUCH_SONG,
+ PLAYLIST_RESULT_NO_SUCH_LIST,
+ PLAYLIST_RESULT_LIST_EXISTS,
+ PLAYLIST_RESULT_BAD_NAME,
+ PLAYLIST_RESULT_BAD_RANGE,
+ PLAYLIST_RESULT_NOT_PLAYING,
+ PLAYLIST_RESULT_TOO_LARGE
+};
+
extern int playlist_saveAbsolutePaths;
extern int playlist_max_length;
@@ -38,23 +50,23 @@ void savePlaylistState(FILE *);
void clearPlaylist(void);
-int clearStoredPlaylist(int fd, char *utf8file);
+int clearStoredPlaylist(const char *utf8file);
-int addToPlaylist(int fd, char *file, int *added_id);
+enum playlist_result addToPlaylist(const char *file, int *added_id);
-int addToStoredPlaylist(int fd, char *file, char *utf8file);
+int addToStoredPlaylist(const char *file, const char *utf8file);
-int addSongToPlaylist(int fd, Song * song, int *added_id);
+enum playlist_result addSongToPlaylist(Song * song, int *added_id);
-int showPlaylist(int fd);
+void showPlaylist(int fd);
-int deleteFromPlaylist(int fd, int song);
+enum playlist_result deleteFromPlaylist(int song);
-int deleteFromPlaylistById(int fd, int song);
+enum playlist_result deleteFromPlaylistById(int song);
-int playlistInfo(int fd, int song);
+enum playlist_result playlistInfo(int fd, int song);
-int playlistId(int fd, int song);
+enum playlist_result playlistId(int fd, int song);
Song *playlist_queued_song(void);
@@ -64,9 +76,9 @@ int playlist_playing(void);
void stopPlaylist(void);
-int playPlaylist(int fd, int song, int stopOnError);
+enum playlist_result playPlaylist(int song, int stopOnError);
-int playPlaylistById(int fd, int song, int stopOnError);
+enum playlist_result playPlaylistById(int song, int stopOnError);
void nextSongInPlaylist(void);
@@ -74,33 +86,31 @@ void syncPlayerAndPlaylist(void);
void previousSongInPlaylist(void);
-int shufflePlaylist(int fd);
-
-int savePlaylist(int fd, char *utf8file);
+void shufflePlaylist(void);
-int deletePlaylist(int fd, char *utf8file);
+enum playlist_result savePlaylist(const char *utf8file);
-int deletePlaylistById(int fd, char *utf8file);
+enum playlist_result deletePlaylist(const char *utf8file);
void deleteASongFromPlaylist(Song * song);
-int moveSongInPlaylist(int fd, int from, int to);
+enum playlist_result moveSongInPlaylist(int from, int to);
-int moveSongInPlaylistById(int fd, int id, int to);
+enum playlist_result moveSongInPlaylistById(int id, int to);
-int swapSongsInPlaylist(int fd, int song1, int song2);
+enum playlist_result swapSongsInPlaylist(int song1, int song2);
-int swapSongsInPlaylistById(int fd, int id1, int id2);
+enum playlist_result swapSongsInPlaylistById(int id1, int id2);
-int loadPlaylist(int fd, char *utf8file);
+enum playlist_result loadPlaylist(int fd, const char *utf8file);
int getPlaylistRepeatStatus(void);
-int setPlaylistRepeatStatus(int fd, int status);
+void setPlaylistRepeatStatus(int status);
int getPlaylistRandomStatus(void);
-int setPlaylistRandomStatus(int fd, int status);
+void setPlaylistRandomStatus(int status);
int getPlaylistCurrentSong(void);
@@ -110,9 +120,9 @@ int getPlaylistLength(void);
unsigned long getPlaylistVersion(void);
-int seekSongInPlaylist(int fd, int song, float seek_time);
+enum playlist_result seekSongInPlaylist(int song, float seek_time);
-int seekSongInPlaylistById(int fd, int id, float seek_time);
+enum playlist_result seekSongInPlaylistById(int id, float seek_time);
void playlistVersionChange(void);
@@ -120,12 +130,14 @@ int playlistChanges(int fd, mpd_uint32 version);
int playlistChangesPosId(int fd, mpd_uint32 version);
-int PlaylistInfo(int fd, char *utf8file, int detail);
+int PlaylistInfo(int fd, const char *utf8file, int detail);
void searchForSongsInPlaylist(int fd, int numItems, LocateTagItem * items);
void findSongsInPlaylist(int fd, int numItems, LocateTagItem * items);
-int valid_playlist_name(int err_fd, const char *utf8path);
+int is_valid_playlist_name(const char *utf8path);
+
+struct mpd_tag *playlist_current_tag(void);
#endif
diff --git a/src/song.c b/src/song.c
index cc1547d10..9b8b7d4db 100644
--- a/src/song.c
+++ b/src/song.c
@@ -32,7 +32,7 @@
#include "os_compat.h"
-Song *newNullSong(void)
+static Song *newNullSong(void)
{
Song *song = xmalloc(sizeof(Song));
@@ -92,7 +92,7 @@ void freeJustSong(Song * song)
{
free(song->url);
if (song->tag)
- freeMpdTag(song->tag);
+ tag_free(song->tag);
free(song);
}
@@ -147,7 +147,7 @@ int printSongInfo(int fd, Song * song)
printSongUrl(fd, song);
if (song->tag)
- printMpdTag(fd, song->tag);
+ tag_print(fd, song->tag);
return 0;
}
@@ -200,7 +200,8 @@ static void insertSongIntoList(SongList * list, ListNode ** nextSongNode,
} else if (cmpRet == 0) {
Song *tempSong = (Song *) ((*nextSongNode)->data);
if (tempSong->mtime != song->mtime) {
- freeMpdTag(tempSong->tag);
+ tag_free(tempSong->tag);
+ tag_end_add(song->tag);
tempSong->tag = song->tag;
tempSong->mtime = song->mtime;
song->tag = NULL;
@@ -239,9 +240,12 @@ void readSongInfoIntoList(FILE * fp, SongList * list, Directory * parentDir)
while (myFgets(buffer, bufferSize, fp) && 0 != strcmp(SONG_END, buffer)) {
if (0 == strncmp(SONG_KEY, buffer, strlen(SONG_KEY))) {
- if (song)
+ if (song) {
insertSongIntoList(list, &nextSongNode,
song->url, song);
+ if (song->tag != NULL)
+ tag_end_add(song->tag);
+ }
song = newNullSong();
song->url = xstrdup(buffer + strlen(SONG_KEY));
@@ -256,15 +260,21 @@ void readSongInfoIntoList(FILE * fp, SongList * list, Directory * parentDir)
song->url = xstrdup(&(buffer[strlen(SONG_FILE)]));
*/
} else if (matchesAnMpdTagItemKey(buffer, &itemType)) {
- if (!song->tag)
- song->tag = newMpdTag();
- addItemToMpdTag(song->tag, itemType,
- &(buffer
- [strlen(mpdTagItemKeys[itemType]) +
- 2]));
+ if (!song->tag) {
+ song->tag = tag_new();
+ tag_begin_add(song->tag);
+ }
+
+ tag_add_item(song->tag, itemType,
+ &(buffer
+ [strlen(mpdTagItemKeys[itemType]) +
+ 2]));
} else if (0 == strncmp(SONG_TIME, buffer, strlen(SONG_TIME))) {
- if (!song->tag)
- song->tag = newMpdTag();
+ if (!song->tag) {
+ song->tag = tag_new();
+ tag_begin_add(song->tag);
+ }
+
song->tag->time = atoi(&(buffer[strlen(SONG_TIME)]));
} else if (0 == strncmp(SONG_MTIME, buffer, strlen(SONG_MTIME))) {
song->mtime = atoi(&(buffer[strlen(SONG_MTIME)]));
@@ -273,8 +283,11 @@ void readSongInfoIntoList(FILE * fp, SongList * list, Directory * parentDir)
FATAL("songinfo: unknown line in db: %s\n", buffer);
}
- if (song)
+ if (song) {
insertSongIntoList(list, &nextSongNode, song->url, song);
+ if (song->tag != NULL)
+ tag_end_add(song->tag);
+ }
while (nextSongNode) {
nodeTemp = nextSongNode->nextNode;
@@ -295,7 +308,7 @@ int updateSongInfo(Song * song)
rmp2amp_r(abs_path, abs_path);
if (song->tag)
- freeMpdTag(song->tag);
+ tag_free(song->tag);
song->tag = NULL;
diff --git a/src/song.h b/src/song.h
index 8e2de410a..b7dc6c90b 100644
--- a/src/song.h
+++ b/src/song.h
@@ -36,15 +36,13 @@
typedef struct _Song {
char *url;
mpd_sint8 type;
- MpdTag *tag;
+ struct mpd_tag *tag;
struct _Directory *parentDir;
time_t mtime;
} Song;
typedef List SongList;
-Song *newNullSong(void);
-
Song *newSong(const char *url, int songType, struct _Directory *parentDir);
void freeSong(Song *);
diff --git a/src/stats.c b/src/stats.c
index 39ba39e9a..4555907db 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -1,5 +1,6 @@
/* the Music Player Daemon (MPD)
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,8 +22,10 @@
#include "directory.h"
#include "myfprintf.h"
#include "outputBuffer.h"
-#include "tagTracker.h"
+#include "tag.h"
+#include "strset.h"
#include "os_compat.h"
+#include "gcc.h"
Stats stats;
@@ -32,15 +35,59 @@ void initStats(void)
stats.numberOfSongs = 0;
}
+struct visit_data {
+ enum tag_type type;
+ struct strset *set;
+};
+
+static int visit_tag_items(Song *song, void *_data)
+{
+ const struct visit_data *data = _data;
+ unsigned i;
+
+ if (song->tag == NULL)
+ return 0;
+
+ for (i = 0; i < (unsigned)song->tag->numOfItems; ++i) {
+ const struct tag_item *item = song->tag->items[i];
+ if (item->type == data->type)
+ strset_add(data->set, item->value);
+ }
+
+ return 0;
+}
+
+static unsigned int getNumberOfTagItems(int type)
+{
+ struct visit_data data;
+ unsigned int ret;
+
+ data.type = type;
+ data.set = strset_new();
+
+ traverseAllIn(NULL, visit_tag_items, NULL, &data);
+
+ ret = strset_size(data.set);
+ strset_free(data.set);
+ return ret;
+}
+
int printStats(int fd)
{
- fdprintf(fd, "artists: %i\n", getNumberOfTagItems(TAG_ITEM_ARTIST));
- fdprintf(fd, "albums: %i\n", getNumberOfTagItems(TAG_ITEM_ALBUM));
- fdprintf(fd, "songs: %i\n", stats.numberOfSongs);
- fdprintf(fd, "uptime: %li\n", time(NULL) - stats.daemonStart);
- fdprintf(fd, "playtime: %li\n",
- (long)(ob_get_total_time() + 0.5));
- fdprintf(fd, "db_playtime: %li\n", stats.dbPlayTime);
- fdprintf(fd, "db_update: %li\n", getDbModTime());
+ fdprintf(fd,
+ "artists: %u\n"
+ "albums: %u\n"
+ "songs: %i\n"
+ "uptime: %li\n"
+ "playtime: %li\n"
+ "db_playtime: %li\n"
+ "db_update: %li\n",
+ getNumberOfTagItems(TAG_ITEM_ARTIST),
+ getNumberOfTagItems(TAG_ITEM_ALBUM),
+ stats.numberOfSongs,
+ time(NULL) - stats.daemonStart,
+ (long)(ob_get_total_time() + 0.5),
+ stats.dbPlayTime,
+ getDbModTime());
return 0;
}
diff --git a/src/storedPlaylist.c b/src/storedPlaylist.c
index 332f99456..c1452ddb9 100644
--- a/src/storedPlaylist.c
+++ b/src/storedPlaylist.c
@@ -19,8 +19,6 @@
#include "storedPlaylist.h"
#include "path.h"
#include "utils.h"
-#include "ack.h"
-#include "command.h"
#include "ls.h"
#include "directory.h"
#include "os_compat.h"
@@ -60,24 +58,24 @@ static ListNode *nodeOfStoredPlaylist(List *list, int idx)
return NULL;
}
-static int writeStoredPlaylistToPath(int fd, List *list, const char *utf8path)
+static enum playlist_result
+writeStoredPlaylistToPath(List *list, const char *utf8path)
{
ListNode *node;
FILE *file;
char *s;
char path_max_tmp[MPD_PATH_MAX];
- if (!utf8path || !valid_playlist_name(fd, utf8path))
- return -1;
+ assert(utf8path);
+
+ if (!is_valid_playlist_name(utf8path))
+ return PLAYLIST_RESULT_BAD_NAME;
utf8_to_fs_playlist_path(path_max_tmp, utf8path);
while (!(file = fopen(path_max_tmp, "w")) && errno == EINTR);
- if (file == NULL) {
- commandError(fd, ACK_ERROR_NO_EXIST, "could not open file "
- "\"%s\": %s", path_max_tmp, strerror(errno));
- return -1;
- }
+ if (file == NULL)
+ return PLAYLIST_RESULT_ERRNO;
node = list->firstNode;
while (node != NULL) {
@@ -89,10 +87,10 @@ static int writeStoredPlaylistToPath(int fd, List *list, const char *utf8path)
}
while (fclose(file) != 0 && errno == EINTR);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-List *loadStoredPlaylist(int fd, const char *utf8path)
+List *loadStoredPlaylist(const char *utf8path)
{
List *list;
FILE *file;
@@ -100,16 +98,13 @@ List *loadStoredPlaylist(int fd, const char *utf8path)
char path_max_tmp[MPD_PATH_MAX];
const size_t musicDir_len = strlen(musicDir);
- if (!valid_playlist_name(fd, utf8path))
+ if (!is_valid_playlist_name(utf8path))
return NULL;
utf8_to_fs_playlist_path(path_max_tmp, utf8path);
while (!(file = fopen(path_max_tmp, "r")) && errno == EINTR);
- if (file == NULL) {
- commandError(fd, ACK_ERROR_NO_EXIST, "could not open file "
- "\"%s\": %s", path_max_tmp, strerror(errno));
+ if (file == NULL)
return NULL;
- }
list = makeList(DEFAULT_FREE_DATA_FUNC, 0);
@@ -137,15 +132,13 @@ List *loadStoredPlaylist(int fd, const char *utf8path)
return list;
}
-static int moveSongInStoredPlaylist(int fd, List *list, int src, int dest)
+static int moveSongInStoredPlaylist(List *list, int src, int dest)
{
ListNode *srcNode, *destNode;
if (src >= list->numberOfNodes || dest >= list->numberOfNodes ||
- src < 0 || dest < 0 || src == dest) {
- commandError(fd, ACK_ERROR_ARG, "argument out of range");
+ src < 0 || dest < 0 || src == dest)
return -1;
- }
srcNode = nodeOfStoredPlaylist(list, src);
if (!srcNode)
@@ -199,90 +192,78 @@ static int moveSongInStoredPlaylist(int fd, List *list, int src, int dest)
return 0;
}
-int moveSongInStoredPlaylistByPath(int fd, const char *utf8path,
- int src, int dest)
+enum playlist_result
+moveSongInStoredPlaylistByPath(const char *utf8path, int src, int dest)
{
List *list;
+ enum playlist_result result;
- if (!(list = loadStoredPlaylist(fd, utf8path))) {
- commandError(fd, ACK_ERROR_UNKNOWN, "could not open playlist");
- return -1;
- }
+ if (!(list = loadStoredPlaylist(utf8path)))
+ return PLAYLIST_RESULT_NO_SUCH_LIST;
- if (moveSongInStoredPlaylist(fd, list, src, dest) != 0) {
+ if (moveSongInStoredPlaylist(list, src, dest) != 0) {
freeList(list);
- return -1;
+ return PLAYLIST_RESULT_BAD_RANGE;
}
- if (writeStoredPlaylistToPath(fd, list, utf8path) != 0) {
- commandError(fd, ACK_ERROR_UNKNOWN, "failed to save playlist");
- freeList(list);
- return -1;
- }
+ result = writeStoredPlaylistToPath(list, utf8path);
freeList(list);
- return 0;
+ return result;
}
-int removeAllFromStoredPlaylistByPath(int fd, const char *utf8path)
+enum playlist_result
+removeAllFromStoredPlaylistByPath(const char *utf8path)
{
char filename[MPD_PATH_MAX];
FILE *file;
- if (!valid_playlist_name(fd, utf8path))
- return -1;
+ if (!is_valid_playlist_name(utf8path))
+ return PLAYLIST_RESULT_BAD_NAME;
+
utf8_to_fs_playlist_path(filename, utf8path);
while (!(file = fopen(filename, "w")) && errno == EINTR);
- if (file == NULL) {
- commandError(fd, ACK_ERROR_NO_EXIST, "could not open file "
- "\"%s\": %s", filename, strerror(errno));
- return -1;
- }
+ if (file == NULL)
+ return PLAYLIST_RESULT_ERRNO;
while (fclose(file) != 0 && errno == EINTR);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-static int removeOneSongFromStoredPlaylist(int fd, List *list, int pos)
+static int removeOneSongFromStoredPlaylist(List *list, int pos)
{
ListNode *node = nodeOfStoredPlaylist(list, pos);
- if (!node) {
- commandError(fd, ACK_ERROR_ARG,
- "could not find song at position");
+ if (!node)
return -1;
- }
deleteNodeFromList(list, node);
return 0;
}
-int removeOneSongFromStoredPlaylistByPath(int fd, const char *utf8path, int pos)
+enum playlist_result
+removeOneSongFromStoredPlaylistByPath(const char *utf8path, int pos)
{
List *list;
+ enum playlist_result result;
- if (!(list = loadStoredPlaylist(fd, utf8path))) {
- commandError(fd, ACK_ERROR_UNKNOWN, "could not open playlist");
- return -1;
- }
+ if (!(list = loadStoredPlaylist(utf8path)))
+ return PLAYLIST_RESULT_NO_SUCH_LIST;
- if (removeOneSongFromStoredPlaylist(fd, list, pos) != 0) {
+ if (removeOneSongFromStoredPlaylist(list, pos) != 0) {
freeList(list);
- return -1;
+ return PLAYLIST_RESULT_BAD_RANGE;
}
- if (writeStoredPlaylistToPath(fd, list, utf8path) != 0) {
- commandError(fd, ACK_ERROR_UNKNOWN, "failed to save playlist");
- freeList(list);
- return -1;
- }
+ result = writeStoredPlaylistToPath(list, utf8path);
freeList(list);
- return 0;
+ return result;
}
-int appendSongToStoredPlaylistByPath(int fd, const char *utf8path, Song *song)
+enum playlist_result
+appendSongToStoredPlaylistByPath(const char *utf8path, Song *song)
{
FILE *file;
char *s;
@@ -290,25 +271,28 @@ int appendSongToStoredPlaylistByPath(int fd, const char *utf8path, Song *song)
char path_max_tmp[MPD_PATH_MAX];
char path_max_tmp2[MPD_PATH_MAX];
- if (!valid_playlist_name(fd, utf8path))
- return -1;
+ if (!is_valid_playlist_name(utf8path))
+ return PLAYLIST_RESULT_BAD_NAME;
utf8_to_fs_playlist_path(path_max_tmp, utf8path);
while (!(file = fopen(path_max_tmp, "a")) && errno == EINTR);
if (file == NULL) {
- commandError(fd, ACK_ERROR_NO_EXIST, "could not open file "
- "\"%s\": %s", path_max_tmp, strerror(errno));
- return -1;
+ int save_errno = errno;
+ while (fclose(file) != 0 && errno == EINTR);
+ errno = save_errno;
+ return PLAYLIST_RESULT_ERRNO;
}
+
if (fstat(fileno(file), &st) < 0) {
- commandError(fd, ACK_ERROR_NO_EXIST, "could not stat file "
- "\"%s\": %s", path_max_tmp, strerror(errno));
- return -1;
+ int save_errno = errno;
+ while (fclose(file) != 0 && errno == EINTR);
+ errno = save_errno;
+ return PLAYLIST_RESULT_ERRNO;
}
+
if (st.st_size >= ((MPD_PATH_MAX+1) * playlist_max_length)) {
- commandError(fd, ACK_ERROR_PLAYLIST_MAX,
- "playlist is at the max size");
- return -1;
+ while (fclose(file) != 0 && errno == EINTR);
+ return PLAYLIST_RESULT_TOO_LARGE;
}
s = utf8_to_fs_charset(path_max_tmp2, get_song_url(path_max_tmp, song));
@@ -319,40 +303,31 @@ int appendSongToStoredPlaylistByPath(int fd, const char *utf8path, Song *song)
fprintf(file, "%s\n", s);
while (fclose(file) != 0 && errno == EINTR);
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
-int renameStoredPlaylist(int fd, const char *utf8from, const char *utf8to)
+enum playlist_result
+renameStoredPlaylist(const char *utf8from, const char *utf8to)
{
struct stat st;
char from[MPD_PATH_MAX];
char to[MPD_PATH_MAX];
- if (!valid_playlist_name(fd, utf8from) ||
- !valid_playlist_name(fd, utf8to))
- return -1;
+ if (!is_valid_playlist_name(utf8from) ||
+ !is_valid_playlist_name(utf8to))
+ return PLAYLIST_RESULT_BAD_NAME;
utf8_to_fs_playlist_path(from, utf8from);
utf8_to_fs_playlist_path(to, utf8to);
- if (stat(from, &st) != 0) {
- commandError(fd, ACK_ERROR_NO_EXIST,
- "no playlist named \"%s\"", utf8from);
- return -1;
- }
+ if (stat(from, &st) != 0)
+ return PLAYLIST_RESULT_NO_SUCH_LIST;
- if (stat(to, &st) == 0) {
- commandError(fd, ACK_ERROR_EXIST, "a file or directory "
- "already exists with the name \"%s\"", utf8to);
- return -1;
- }
+ if (stat(to, &st) == 0)
+ return PLAYLIST_RESULT_LIST_EXISTS;
- if (rename(from, to) < 0) {
- commandError(fd, ACK_ERROR_UNKNOWN,
- "could not rename playlist \"%s\" to \"%s\": %s",
- utf8from, utf8to, strerror(errno));
- return -1;
- }
+ if (rename(from, to) < 0)
+ return PLAYLIST_RESULT_ERRNO;
- return 0;
+ return PLAYLIST_RESULT_SUCCESS;
}
diff --git a/src/storedPlaylist.h b/src/storedPlaylist.h
index a7806386b..964669d35 100644
--- a/src/storedPlaylist.h
+++ b/src/storedPlaylist.h
@@ -23,14 +23,21 @@
#include "list.h"
#include "playlist.h"
-List *loadStoredPlaylist(int fd, const char *utf8path);
+List *loadStoredPlaylist(const char *utf8path);
-int moveSongInStoredPlaylistByPath(int fd, const char *utf8path, int src, int dest);
-int removeAllFromStoredPlaylistByPath(int fd, const char *utf8path);
-int removeOneSongFromStoredPlaylistByPath(int fd, const char *utf8path, int pos);
+enum playlist_result
+moveSongInStoredPlaylistByPath(const char *utf8path, int src, int dest);
-int appendSongToStoredPlaylistByPath(int fd, const char *utf8path, Song *song);
+enum playlist_result
+removeAllFromStoredPlaylistByPath(const char *utf8path);
-int renameStoredPlaylist(int fd, const char *utf8from, const char *utf8to);
+enum playlist_result
+removeOneSongFromStoredPlaylistByPath(const char *utf8path, int pos);
+
+enum playlist_result
+appendSongToStoredPlaylistByPath(const char *utf8path, Song *song);
+
+enum playlist_result
+renameStoredPlaylist(const char *utf8from, const char *utf8to);
#endif
diff --git a/src/strset.c b/src/strset.c
new file mode 100644
index 000000000..475266370
--- /dev/null
+++ b/src/strset.c
@@ -0,0 +1,144 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "strset.h"
+#include "utils.h"
+#include "os_compat.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;
+}
+
+mpd_malloc struct strset *strset_new(void)
+{
+ struct strset *set = xcalloc(1, sizeof(*set));
+ 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;
+ free(slot);
+ slot = next;
+ }
+ }
+
+ 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 = xmalloc(sizeof(*slot));
+ 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
new file mode 100644
index 000000000..41ef0e1bd
--- /dev/null
+++ b/src/strset.h
@@ -0,0 +1,49 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 STRSET_H
+#define STRSET_H
+
+#include "gcc.h"
+
+struct strset;
+
+mpd_malloc struct strset *strset_new(void);
+
+void strset_free(struct strset *set);
+
+void strset_add(struct strset *set, const char *value);
+
+int strset_get(const struct strset *set, const char *value);
+
+unsigned strset_size(const struct strset *set);
+
+void strset_rewind(struct strset *set);
+
+const char *strset_next(struct strset *set);
+
+#endif
diff --git a/src/tag.c b/src/tag.c
index c1ccd0a42..f1acb7c79 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -17,27 +17,26 @@
*/
#include "tag.h"
+#include "tag_pool.h"
#include "myfprintf.h"
#include "utils.h"
#include "utf8.h"
#include "log.h"
#include "conf.h"
-#include "charConv.h"
-#include "tagTracker.h"
#include "song.h"
-#ifdef HAVE_ID3TAG
-# define isId3v1(tag) (id3_tag_options(tag, 0, 0) & ID3_TAG_OPTION_ID3V1)
-# ifndef ID3_FRAME_COMPOSER
-# define ID3_FRAME_COMPOSER "TCOM"
-# endif
-# ifndef ID3_FRAME_PERFORMER
-# define ID3_FRAME_PERFORMER "TOPE"
-# endif
-# ifndef ID3_FRAME_DISC
-# define ID3_FRAME_DISC "TPOS"
-# endif
+/**
+ * Maximum number of items managed in the bulk list; if it is
+ * exceeded, we switch back to "normal" reallocation.
+ */
+#define BULK_MAX 64
+
+static struct {
+#ifndef NDEBUG
+ int busy;
#endif
+ struct tag_item *items[BULK_MAX];
+} bulk;
const char *mpdTagItemKeys[TAG_NUM_OF_ITEM_TYPES] = {
"Artist",
@@ -55,7 +54,12 @@ const char *mpdTagItemKeys[TAG_NUM_OF_ITEM_TYPES] = {
static mpd_sint8 ignoreTagItems[TAG_NUM_OF_ITEM_TYPES];
-void initTagConfig(void)
+static size_t items_size(const struct mpd_tag *tag)
+{
+ return (tag->numOfItems * sizeof(struct tag_item *));
+}
+
+void tag_lib_init(void)
{
int quit = 0;
char *temp;
@@ -104,7 +108,7 @@ void initTagConfig(void)
free(temp);
}
-void printTagTypes(int fd)
+void tag_print_types(int fd)
{
int i;
@@ -114,7 +118,7 @@ void printTagTypes(int fd)
}
}
-void printMpdTag(int fd, MpdTag * tag)
+void tag_print(int fd, const struct mpd_tag *tag)
{
int i;
@@ -122,340 +126,14 @@ void printMpdTag(int fd, MpdTag * tag)
fdprintf(fd, SONG_TIME "%i\n", tag->time);
for (i = 0; i < tag->numOfItems; i++) {
- fdprintf(fd, "%s: %s\n", mpdTagItemKeys[tag->items[i].type],
- tag->items[i].value);
+ fdprintf(fd, "%s: %s\n", mpdTagItemKeys[tag->items[i]->type],
+ tag->items[i]->value);
}
}
-#ifdef HAVE_ID3TAG
-/* This will try to convert a string to utf-8,
- */
-static id3_utf8_t * processID3FieldString (int is_id3v1, const id3_ucs4_t *ucs4, int type)
+struct mpd_tag *tag_ape_load(const char *file)
{
- id3_utf8_t *utf8;
- id3_latin1_t *isostr;
- char *encoding;
-
- if (type == TAG_ITEM_GENRE)
- ucs4 = id3_genre_name(ucs4);
- /* use encoding field here? */
- if (is_id3v1 &&
- (encoding = getConfigParamValue(CONF_ID3V1_ENCODING))) {
- isostr = id3_ucs4_latin1duplicate(ucs4);
- if (mpd_unlikely(!isostr)) {
- return NULL;
- }
- setCharSetConversion("UTF-8", encoding);
- utf8 = xmalloc(strlen((char *)isostr) + 1);
- utf8 = (id3_utf8_t *)char_conv_str((char *)utf8, (char *)isostr);
- if (!utf8) {
- DEBUG("Unable to convert %s string to UTF-8: "
- "'%s'\n", encoding, isostr);
- free(isostr);
- return NULL;
- }
- free(isostr);
- } else {
- utf8 = id3_ucs4_utf8duplicate(ucs4);
- if (mpd_unlikely(!utf8)) {
- return NULL;
- }
- }
- return utf8;
-}
-
-static MpdTag *getID3Info(
- struct id3_tag *tag, const char *id, int type, MpdTag * mpdTag)
-{
- struct id3_frame const *frame;
- id3_ucs4_t const *ucs4;
- id3_utf8_t *utf8;
- union id3_field const *field;
- unsigned int nstrings, i;
-
- frame = id3_tag_findframe(tag, id, 0);
- /* Check frame */
- if (!frame)
- {
- return mpdTag;
- }
- /* Check fields in frame */
- if(frame->nfields == 0)
- {
- DEBUG(__FILE__": Frame has no fields\n");
- return mpdTag;
- }
-
- /* Starting with T is a stringlist */
- if (id[0] == 'T')
- {
- /* This one contains 2 fields:
- * 1st: Text encoding
- * 2: Stringlist
- * Shamefully this isn't the RL case.
- * But I am going to enforce it anyway.
- */
- if(frame->nfields != 2)
- {
- DEBUG(__FILE__": Invalid number '%i' of fields for TXX frame\n",frame->nfields);
- return mpdTag;
- }
- field = &frame->fields[0];
- /**
- * First field is encoding field.
- * This is ignored by mpd.
- */
- if(field->type != ID3_FIELD_TYPE_TEXTENCODING)
- {
- DEBUG(__FILE__": Expected encoding, found: %i\n",field->type);
- }
- /* Process remaining fields, should be only one */
- field = &frame->fields[1];
- /* Encoding field */
- if(field->type == ID3_FIELD_TYPE_STRINGLIST) {
- /* Get the number of strings available */
- nstrings = id3_field_getnstrings(field);
- for (i = 0; i < nstrings; i++) {
- ucs4 = id3_field_getstrings(field,i);
- if(!ucs4)
- continue;
- utf8 = processID3FieldString(isId3v1(tag),ucs4, type);
- if(!utf8)
- continue;
-
- if (mpdTag == NULL)
- mpdTag = newMpdTag();
- addItemToMpdTag(mpdTag, type, (char *)utf8);
- free(utf8);
- }
- }
- else {
- ERROR(__FILE__": Field type not processed: %i\n",(int)id3_field_gettextencoding(field));
- }
- }
- /* A comment frame */
- else if(!strcmp(ID3_FRAME_COMMENT, id))
- {
- /* A comment frame is different... */
- /* 1st: encoding
- * 2nd: Language
- * 3rd: String
- * 4th: FullString.
- * The 'value' we want is in the 4th field
- */
- if(frame->nfields == 4)
- {
- /* for now I only read the 4th field, with the fullstring */
- field = &frame->fields[3];
- if(field->type == ID3_FIELD_TYPE_STRINGFULL)
- {
- ucs4 = id3_field_getfullstring(field);
- if(ucs4)
- {
- utf8 = processID3FieldString(isId3v1(tag),ucs4, type);
- if(utf8)
- {
- if (mpdTag == NULL)
- mpdTag = newMpdTag();
- addItemToMpdTag(mpdTag, type, (char *)utf8);
- free(utf8);
- }
- }
- }
- else
- {
- DEBUG(__FILE__": 4th field in comment frame differs from expected, got '%i': ignoring\n",field->type);
- }
- }
- else
- {
- DEBUG(__FILE__": Invalid 'comments' tag, got '%i' fields instead of 4\n", frame->nfields);
- }
- }
- /* Unsupported */
- else {
- DEBUG(__FILE__": Unsupported tag type requrested\n");
- return mpdTag;
- }
-
- return mpdTag;
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-MpdTag *parseId3Tag(struct id3_tag * tag)
-{
- MpdTag *ret = NULL;
-
- ret = getID3Info(tag, ID3_FRAME_ARTIST, TAG_ITEM_ARTIST, ret);
- ret = getID3Info(tag, ID3_FRAME_TITLE, TAG_ITEM_TITLE, ret);
- ret = getID3Info(tag, ID3_FRAME_ALBUM, TAG_ITEM_ALBUM, ret);
- ret = getID3Info(tag, ID3_FRAME_TRACK, TAG_ITEM_TRACK, ret);
- ret = getID3Info(tag, ID3_FRAME_YEAR, TAG_ITEM_DATE, ret);
- ret = getID3Info(tag, ID3_FRAME_GENRE, TAG_ITEM_GENRE, ret);
- ret = getID3Info(tag, ID3_FRAME_COMPOSER, TAG_ITEM_COMPOSER, ret);
- ret = getID3Info(tag, ID3_FRAME_PERFORMER, TAG_ITEM_PERFORMER, ret);
- ret = getID3Info(tag, ID3_FRAME_COMMENT, TAG_ITEM_COMMENT, ret);
- ret = getID3Info(tag, ID3_FRAME_DISC, TAG_ITEM_DISC, ret);
-
- return ret;
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static int fillBuffer(void *buf, size_t size, FILE * stream,
- long offset, int whence)
-{
- if (fseek(stream, offset, whence) != 0) return 0;
- return fread(buf, 1, size, stream);
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static int getId3v2FooterSize(FILE * stream, long offset, int whence)
-{
- id3_byte_t buf[ID3_TAG_QUERYSIZE];
- int bufsize;
-
- bufsize = fillBuffer(buf, ID3_TAG_QUERYSIZE, stream, offset, whence);
- if (bufsize <= 0) return 0;
- return id3_tag_query(buf, bufsize);
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static struct id3_tag *getId3Tag(FILE * stream, long offset, int whence)
-{
- struct id3_tag *tag;
- id3_byte_t queryBuf[ID3_TAG_QUERYSIZE];
- id3_byte_t *tagBuf;
- int tagSize;
- int queryBufSize;
- int tagBufSize;
-
- /* It's ok if we get less than we asked for */
- queryBufSize = fillBuffer(queryBuf, ID3_TAG_QUERYSIZE,
- stream, offset, whence);
- if (queryBufSize <= 0) return NULL;
-
- /* Look for a tag header */
- tagSize = id3_tag_query(queryBuf, queryBufSize);
- if (tagSize <= 0) return NULL;
-
- /* Found a tag. Allocate a buffer and read it in. */
- tagBuf = xmalloc(tagSize);
- if (!tagBuf) return NULL;
-
- tagBufSize = fillBuffer(tagBuf, tagSize, stream, offset, whence);
- if (tagBufSize < tagSize) {
- free(tagBuf);
- return NULL;
- }
-
- tag = id3_tag_parse(tagBuf, tagBufSize);
-
- free(tagBuf);
-
- return tag;
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static struct id3_tag *findId3TagFromBeginning(FILE * stream)
-{
- struct id3_tag *tag;
- struct id3_tag *seektag;
- struct id3_frame *frame;
- int seek;
-
- tag = getId3Tag(stream, 0, SEEK_SET);
- if (!tag) {
- return NULL;
- } else if (isId3v1(tag)) {
- /* id3v1 tags don't belong here */
- id3_tag_delete(tag);
- return NULL;
- }
-
- /* We have an id3v2 tag, so let's look for SEEK frames */
- while ((frame = id3_tag_findframe(tag, "SEEK", 0))) {
- /* Found a SEEK frame, get it's value */
- seek = id3_field_getint(id3_frame_field(frame, 0));
- if (seek < 0)
- break;
-
- /* Get the tag specified by the SEEK frame */
- seektag = getId3Tag(stream, seek, SEEK_CUR);
- if (!seektag || isId3v1(seektag))
- break;
-
- /* Replace the old tag with the new one */
- id3_tag_delete(tag);
- tag = seektag;
- }
-
- return tag;
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static struct id3_tag *findId3TagFromEnd(FILE * stream)
-{
- struct id3_tag *tag;
- struct id3_tag *v1tag;
- int tagsize;
-
- /* Get an id3v1 tag from the end of file for later use */
- v1tag = getId3Tag(stream, -128, SEEK_END);
-
- /* Get the id3v2 tag size from the footer (located before v1tag) */
- tagsize = getId3v2FooterSize(stream, (v1tag ? -128 : 0) - 10, SEEK_END);
- if (tagsize >= 0)
- return v1tag;
-
- /* Get the tag which the footer belongs to */
- tag = getId3Tag(stream, tagsize, SEEK_CUR);
- if (!tag)
- return v1tag;
-
- /* We have an id3v2 tag, so ditch v1tag */
- id3_tag_delete(v1tag);
-
- return tag;
-}
-#endif
-
-MpdTag *id3Dup(char *file)
-{
- MpdTag *ret = NULL;
-#ifdef HAVE_ID3TAG
- struct id3_tag *tag;
- FILE *stream;
-
- stream = fopen(file, "r");
- if (!stream) {
- DEBUG("id3Dup: Failed to open file: '%s', %s\n", file,
- strerror(errno));
- return NULL;
- }
-
- tag = findId3TagFromBeginning(stream);
- if (!tag)
- tag = findId3TagFromEnd(stream);
-
- fclose(stream);
-
- if (!tag)
- return NULL;
- ret = parseId3Tag(tag);
- id3_tag_delete(tag);
-#endif
- return ret;
-}
-
-MpdTag *apeDup(char *file)
-{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
FILE *fp;
int tagCount;
char *buffer = NULL;
@@ -556,9 +234,9 @@ MpdTag *apeDup(char *file)
for (i = 0; i < 7; i++) {
if (strcasecmp(key, apeItems[i]) == 0) {
if (!ret)
- ret = newMpdTag();
- addItemToMpdTagWithLen(ret, tagItems[i],
- p, size);
+ ret = tag_new();
+ tag_add_item_n(ret, tagItems[i],
+ p, size);
}
}
}
@@ -574,22 +252,23 @@ fail:
return ret;
}
-MpdTag *newMpdTag(void)
+struct mpd_tag *tag_new(void)
{
- MpdTag *ret = xmalloc(sizeof(MpdTag));
+ struct mpd_tag *ret = xmalloc(sizeof(*ret));
ret->items = NULL;
ret->time = -1;
ret->numOfItems = 0;
return ret;
}
-static void deleteItem(MpdTag * tag, int idx)
+static void deleteItem(struct mpd_tag *tag, int idx)
{
assert(idx < tag->numOfItems);
tag->numOfItems--;
- removeTagItemString(tag->items[idx].type, tag->items[idx].value);
- /* free(tag->items[idx].value); */
+ pthread_mutex_lock(&tag_pool_lock);
+ tag_pool_put_item(tag->items[idx]);
+ pthread_mutex_unlock(&tag_pool_lock);
if (tag->numOfItems - idx > 0) {
memmove(tag->items + idx, tag->items + idx + 1,
@@ -597,20 +276,19 @@ static void deleteItem(MpdTag * tag, int idx)
}
if (tag->numOfItems > 0) {
- tag->items = xrealloc(tag->items,
- tag->numOfItems * sizeof(MpdTagItem));
+ tag->items = xrealloc(tag->items, items_size(tag));
} else {
free(tag->items);
tag->items = NULL;
}
}
-void clearItemsFromMpdTag(MpdTag * tag, enum tag_type type)
+void tag_clear_items_by_type(struct mpd_tag *tag, enum tag_type type)
{
int i;
for (i = 0; i < tag->numOfItems; i++) {
- if (tag->items[i].type == type) {
+ if (tag->items[i]->type == type) {
deleteItem(tag, i);
/* decrement since when just deleted this node */
i--;
@@ -618,17 +296,24 @@ void clearItemsFromMpdTag(MpdTag * tag, enum tag_type type)
}
}
-static void clearMpdTag(MpdTag * tag)
+static void clearMpdTag(struct mpd_tag *tag)
{
int i;
- for (i = 0; i < tag->numOfItems; i++) {
- removeTagItemString(tag->items[i].type, tag->items[i].value);
- /* free(tag->items[i].value); */
- }
+ pthread_mutex_lock(&tag_pool_lock);
+ for (i = 0; i < tag->numOfItems; i++)
+ tag_pool_put_item(tag->items[i]);
+ pthread_mutex_unlock(&tag_pool_lock);
- if (tag->items)
+ if (tag->items == bulk.items) {
+#ifndef NDEBUG
+ assert(bulk.busy);
+ bulk.busy = 0;
+#endif
+ } else if (tag->items) {
free(tag->items);
+ }
+
tag->items = NULL;
tag->numOfItems = 0;
@@ -636,31 +321,34 @@ static void clearMpdTag(MpdTag * tag)
tag->time = -1;
}
-void freeMpdTag(MpdTag * tag)
+void tag_free(struct mpd_tag *tag)
{
clearMpdTag(tag);
free(tag);
}
-MpdTag *mpdTagDup(MpdTag * tag)
+struct mpd_tag *tag_dup(const struct mpd_tag *tag)
{
- MpdTag *ret;
+ struct mpd_tag *ret;
int i;
if (!tag)
return NULL;
- ret = newMpdTag();
+ ret = tag_new();
ret->time = tag->time;
+ ret->numOfItems = tag->numOfItems;
+ ret->items = ret->numOfItems > 0 ? xmalloc(items_size(tag)) : NULL;
- for (i = 0; i < tag->numOfItems; i++) {
- addItemToMpdTag(ret, tag->items[i].type, tag->items[i].value);
- }
+ pthread_mutex_lock(&tag_pool_lock);
+ for (i = 0; i < tag->numOfItems; i++)
+ ret->items[i] = tag_pool_dup_item(tag->items[i]);
+ pthread_mutex_unlock(&tag_pool_lock);
return ret;
}
-int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2)
+int tag_equal(const struct mpd_tag *tag1, const struct mpd_tag *tag2)
{
int i;
@@ -676,9 +364,9 @@ int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2)
return 0;
for (i = 0; i < tag1->numOfItems; i++) {
- if (tag1->items[i].type != tag2->items[i].type)
+ if (tag1->items[i]->type != tag2->items[i]->type)
return 0;
- if (strcmp(tag1->items[i].value, tag2->items[i].value)) {
+ if (strcmp(tag1->items[i]->value, tag2->items[i]->value)) {
return 0;
}
}
@@ -686,39 +374,94 @@ int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2)
return 1;
}
-#define fixUtf8(str) { \
- if(str && !validUtf8String(str)) { \
- char * temp; \
- DEBUG("not valid utf8 in tag: %s\n",str); \
- temp = latin1StrToUtf8Dup(str); \
- free(str); \
- str = temp; \
- } \
+static inline const char *fix_utf8(const char *str, size_t *length_r) {
+ const char *temp;
+
+ assert(str != NULL);
+
+ if (validUtf8String(str, *length_r))
+ return str;
+
+ DEBUG("not valid utf8 in tag: %s\n",str);
+ temp = latin1StrToUtf8Dup(str);
+ *length_r = strlen(temp);
+ return temp;
}
-static void appendToTagItems(MpdTag * tag, enum tag_type type,
+void tag_begin_add(struct mpd_tag *tag)
+{
+ assert(!bulk.busy);
+ assert(tag != NULL);
+ assert(tag->items == NULL);
+ assert(tag->numOfItems == 0);
+
+#ifndef NDEBUG
+ bulk.busy = 1;
+#endif
+ tag->items = bulk.items;
+}
+
+void tag_end_add(struct mpd_tag *tag)
+{
+ if (tag->items == bulk.items) {
+ assert(tag->numOfItems <= BULK_MAX);
+
+ if (tag->numOfItems > 0) {
+ /* copy the tag items from the bulk list over
+ to a new list (which fits exactly) */
+ tag->items = xmalloc(items_size(tag));
+ memcpy(tag->items, bulk.items, items_size(tag));
+ } else
+ tag->items = NULL;
+ }
+
+#ifndef NDEBUG
+ bulk.busy = 0;
+#endif
+}
+
+static void appendToTagItems(struct mpd_tag *tag, enum tag_type type,
const char *value, size_t len)
{
unsigned int i = tag->numOfItems;
- char *duplicated = xmalloc(len + 1);
+ const char *p;
+
+ p = fix_utf8(value, &len);
+ if (memchr(p, '\n', len) != NULL) {
+ char *duplicated = xmalloc(len + 1);
+ memcpy(duplicated, p, len);
+ duplicated[len] = '\0';
+ if (p != value)
+ free(deconst_ptr(p));
+
+ stripReturnChar(duplicated);
+ p = duplicated;
+ }
- memcpy(duplicated, value, len);
- duplicated[len] = '\0';
+ tag->numOfItems++;
- fixUtf8(duplicated);
- stripReturnChar(duplicated);
+ if (tag->items != bulk.items)
+ /* bulk mode disabled */
+ tag->items = xrealloc(tag->items, items_size(tag));
+ else if (tag->numOfItems >= BULK_MAX) {
+ /* bulk list already full - switch back to non-bulk */
+ assert(bulk.busy);
- tag->numOfItems++;
- tag->items = xrealloc(tag->items, tag->numOfItems * sizeof(MpdTagItem));
+ tag->items = xmalloc(items_size(tag));
+ memcpy(tag->items, bulk.items,
+ items_size(tag) - sizeof(struct tag_item *));
+ }
- tag->items[i].type = type;
- tag->items[i].value = getTagItemString(type, duplicated);
+ pthread_mutex_lock(&tag_pool_lock);
+ tag->items[i] = tag_pool_get_item(type, p, len);
+ pthread_mutex_unlock(&tag_pool_lock);
- free(duplicated);
+ if (p != value)
+ free(deconst_ptr(p));
}
-void addItemToMpdTagWithLen(MpdTag * tag, enum tag_type itemType,
- const char *value, size_t len)
+void tag_add_item_n(struct mpd_tag *tag, enum tag_type itemType,
+ const char *value, size_t len)
{
if (ignoreTagItems[itemType])
{
diff --git a/src/tag.h b/src/tag.h
index a9bf19934..a05f7654a 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -19,14 +19,9 @@
#ifndef TAG_H
#define TAG_H
-#include "../config.h"
-
#include "mpd_types.h"
#include "os_compat.h"
-
-#ifdef HAVE_ID3TAG
-#include <id3tag.h>
-#endif
+#include "gcc.h"
enum tag_type {
TAG_ITEM_ARTIST,
@@ -45,45 +40,56 @@ enum tag_type {
extern const char *mpdTagItemKeys[];
-typedef struct _MpdTagItem {
+struct tag_item {
enum tag_type type;
- char *value;
-} MpdTagItem;
+ char value[1];
+} mpd_packed;
-typedef struct _MpdTag {
+struct mpd_tag {
int time;
- MpdTagItem *items;
+ struct tag_item **items;
mpd_uint8 numOfItems;
-} MpdTag;
+};
-#ifdef HAVE_ID3TAG
-MpdTag *parseId3Tag(struct id3_tag *);
-#endif
+struct mpd_tag *tag_ape_load(const char *file);
-MpdTag *apeDup(char *file);
+struct mpd_tag *tag_new(void);
-MpdTag *id3Dup(char *file);
+void tag_lib_init(void);
-MpdTag *newMpdTag(void);
+void tag_clear_items_by_type(struct mpd_tag *tag, enum tag_type itemType);
-void initTagConfig(void);
+void tag_free(struct mpd_tag *tag);
-void clearItemsFromMpdTag(MpdTag * tag, enum tag_type itemType);
+/**
+ * Gives an optional hint to the tag library that we will now add
+ * several tag items; this is used by the library to optimize memory
+ * allocation. Only one tag may be in this state, and this tag must
+ * not have any items yet. You must call tag_end_add() when you are
+ * done.
+ */
+void tag_begin_add(struct mpd_tag *tag);
-void freeMpdTag(MpdTag * tag);
+/**
+ * Finishes the operation started with tag_begin_add().
+ */
+void tag_end_add(struct mpd_tag *tag);
-void addItemToMpdTagWithLen(MpdTag * tag, enum tag_type itemType,
- const char *value, size_t len);
+void tag_add_item_n(struct mpd_tag *tag, enum tag_type itemType,
+ const char *value, size_t len);
-#define addItemToMpdTag(tag, itemType, value) \
- addItemToMpdTagWithLen(tag, itemType, value, strlen(value))
+static inline void tag_add_item(struct mpd_tag *tag, enum tag_type itemType,
+ const char *value)
+{
+ tag_add_item_n(tag, itemType, value, strlen(value));
+}
-void printTagTypes(int fd);
+void tag_print_types(int fd);
-void printMpdTag(int fd, MpdTag * tag);
+void tag_print(int fd, const struct mpd_tag *tag);
-MpdTag *mpdTagDup(MpdTag * tag);
+struct mpd_tag *tag_dup(const struct mpd_tag *tag);
-int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2);
+int tag_equal(const struct mpd_tag *tag1, const struct mpd_tag *tag2);
#endif
diff --git a/src/tagTracker.c b/src/tagTracker.c
deleted file mode 100644
index 892cc2955..000000000
--- a/src/tagTracker.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/* the Music Player Daemon (MPD)
- * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
- * This project's homepage is: http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "tagTracker.h"
-
-#include "tag.h"
-#include "tree.h"
-#include "utils.h"
-#include "myfprintf.h"
-#include "os_compat.h"
-
-static Tree *tagTrees[TAG_NUM_OF_ITEM_TYPES];
-
-typedef struct tagTrackerItem {
- int count;
- mpd_sint8 visited;
-} TagTrackerItem;
-
-static pthread_mutex_t tag_item_lock = PTHREAD_MUTEX_INITIALIZER;
-
-char *getTagItemString(int type, char *string)
-{
- TreeIterator iter;
- char *ret;
-
- pthread_mutex_lock(&tag_item_lock);
-
- if (tagTrees[type] == NULL)
- {
- tagTrees[type] = MakeTree((TreeCompareKeyFunction)strcmp,
- (TreeFreeFunction)free,
- (TreeFreeFunction)free);
- }
-
- if (FindInTree(tagTrees[type], string, &iter))
- {
- ((TagTrackerItem *)GetTreeKeyData(&iter)->data)->count++;
- ret = (char *)GetTreeKeyData(&iter)->key;
- }
- else
- {
- TagTrackerItem *item = xmalloc(sizeof(TagTrackerItem));
- char *key = xstrdup(string);
- item->count = 1;
- item->visited = 0;
- InsertInTree(tagTrees[type], key, item);
- ret = key;
- }
-
- pthread_mutex_unlock(&tag_item_lock);
- return ret;
-}
-
-void removeTagItemString(int type, char *string)
-{
- TreeIterator iter;
-
- assert(string);
-
- assert(tagTrees[type]);
- if (tagTrees[type] == NULL)
- return;
-
- pthread_mutex_lock(&tag_item_lock);
- if (FindInTree(tagTrees[type], string, &iter))
- {
- TagTrackerItem * item =
- (TagTrackerItem *)GetTreeKeyData(&iter)->data;
- item->count--;
- if (item->count <= 0)
- RemoveFromTreeByIterator(tagTrees[type], &iter);
- }
-
- if (GetTreeSize(tagTrees[type]) == 0)
- {
- FreeTree(tagTrees[type]);
- tagTrees[type] = NULL;
- }
- pthread_mutex_unlock(&tag_item_lock);
-}
-
-int getNumberOfTagItems(int type)
-{
- if (tagTrees[type] == NULL)
- return 0;
-
- return GetTreeSize(tagTrees[type]);
-}
-
-void resetVisitedFlagsInTagTracker(int type)
-{
- TreeIterator iter;
-
- if (!tagTrees[type])
- return;
-
- for (SetTreeIteratorToBegin(tagTrees[type], &iter);
- !IsTreeIteratorAtEnd(&iter);
- IncrementTreeIterator(&iter))
- {
- ((TagTrackerItem *)GetTreeKeyData(&iter)->data)->visited = 0;
- }
-}
-
-void visitInTagTracker(int type, const char *str)
-{
- TreeIterator iter;
-
- if (!tagTrees[type])
- return;
-
- if (!FindInTree(tagTrees[type], str, &iter))
- return;
-
- ((TagTrackerItem *)GetTreeKeyData(&iter)->data)->visited = 1;
-}
-
-void printVisitedInTagTracker(int fd, int type)
-{
- TreeIterator iter;
- TagTrackerItem * item;
-
- if (!tagTrees[type])
- return;
-
- for (SetTreeIteratorToBegin(tagTrees[type], &iter);
- !IsTreeIteratorAtEnd(&iter);
- IncrementTreeIterator(&iter))
- {
- item = ((TagTrackerItem *)GetTreeKeyData(&iter)->data);
-
- if (item->visited)
- {
- fdprintf(fd,
- "%s: %s\n",
- mpdTagItemKeys[type],
- (char *)GetTreeKeyData(&iter)->key);
- }
- }
-}
diff --git a/src/tag_id3.c b/src/tag_id3.c
new file mode 100644
index 000000000..83b78150a
--- /dev/null
+++ b/src/tag_id3.c
@@ -0,0 +1,367 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tag_id3.h"
+#include "tag.h"
+#include "utils.h"
+#include "log.h"
+#include "conf.h"
+#include "charConv.h"
+
+#ifdef HAVE_ID3TAG
+#include <id3tag.h>
+#endif
+
+#ifdef HAVE_ID3TAG
+# define isId3v1(tag) (id3_tag_options(tag, 0, 0) & ID3_TAG_OPTION_ID3V1)
+# ifndef ID3_FRAME_COMPOSER
+# define ID3_FRAME_COMPOSER "TCOM"
+# endif
+# ifndef ID3_FRAME_PERFORMER
+# define ID3_FRAME_PERFORMER "TOPE"
+# endif
+# ifndef ID3_FRAME_DISC
+# define ID3_FRAME_DISC "TPOS"
+# endif
+#endif
+
+#ifdef HAVE_ID3TAG
+/* This will try to convert a string to utf-8,
+ */
+static id3_utf8_t * processID3FieldString (int is_id3v1, const id3_ucs4_t *ucs4, int type)
+{
+ id3_utf8_t *utf8;
+ id3_latin1_t *isostr;
+ char *encoding;
+
+ if (type == TAG_ITEM_GENRE)
+ ucs4 = id3_genre_name(ucs4);
+ /* use encoding field here? */
+ if (is_id3v1 &&
+ (encoding = getConfigParamValue(CONF_ID3V1_ENCODING))) {
+ isostr = id3_ucs4_latin1duplicate(ucs4);
+ if (mpd_unlikely(!isostr)) {
+ return NULL;
+ }
+ setCharSetConversion("UTF-8", encoding);
+ utf8 = xmalloc(strlen((char *)isostr) + 1);
+ utf8 = (id3_utf8_t *)char_conv_str((char *)utf8, (char *)isostr);
+ if (!utf8) {
+ DEBUG("Unable to convert %s string to UTF-8: "
+ "'%s'\n", encoding, isostr);
+ free(isostr);
+ return NULL;
+ }
+ free(isostr);
+ } else {
+ utf8 = id3_ucs4_utf8duplicate(ucs4);
+ if (mpd_unlikely(!utf8)) {
+ return NULL;
+ }
+ }
+ return utf8;
+}
+
+static struct mpd_tag *getID3Info(
+ struct id3_tag *tag, const char *id, int type, struct mpd_tag *mpdTag)
+{
+ struct id3_frame const *frame;
+ id3_ucs4_t const *ucs4;
+ id3_utf8_t *utf8;
+ union id3_field const *field;
+ unsigned int nstrings, i;
+
+ frame = id3_tag_findframe(tag, id, 0);
+ /* Check frame */
+ if (!frame)
+ {
+ return mpdTag;
+ }
+ /* Check fields in frame */
+ if(frame->nfields == 0)
+ {
+ DEBUG(__FILE__": Frame has no fields\n");
+ return mpdTag;
+ }
+
+ /* Starting with T is a stringlist */
+ if (id[0] == 'T')
+ {
+ /* This one contains 2 fields:
+ * 1st: Text encoding
+ * 2: Stringlist
+ * Shamefully this isn't the RL case.
+ * But I am going to enforce it anyway.
+ */
+ if(frame->nfields != 2)
+ {
+ DEBUG(__FILE__": Invalid number '%i' of fields for TXX frame\n",frame->nfields);
+ return mpdTag;
+ }
+ field = &frame->fields[0];
+ /**
+ * First field is encoding field.
+ * This is ignored by mpd.
+ */
+ if(field->type != ID3_FIELD_TYPE_TEXTENCODING)
+ {
+ DEBUG(__FILE__": Expected encoding, found: %i\n",field->type);
+ }
+ /* Process remaining fields, should be only one */
+ field = &frame->fields[1];
+ /* Encoding field */
+ if(field->type == ID3_FIELD_TYPE_STRINGLIST) {
+ /* Get the number of strings available */
+ nstrings = id3_field_getnstrings(field);
+ for (i = 0; i < nstrings; i++) {
+ ucs4 = id3_field_getstrings(field,i);
+ if(!ucs4)
+ continue;
+ utf8 = processID3FieldString(isId3v1(tag),ucs4, type);
+ if(!utf8)
+ continue;
+
+ if (mpdTag == NULL)
+ mpdTag = tag_new();
+ tag_add_item(mpdTag, type, (char *)utf8);
+ free(utf8);
+ }
+ }
+ else {
+ ERROR(__FILE__": Field type not processed: %i\n",(int)id3_field_gettextencoding(field));
+ }
+ }
+ /* A comment frame */
+ else if(!strcmp(ID3_FRAME_COMMENT, id))
+ {
+ /* A comment frame is different... */
+ /* 1st: encoding
+ * 2nd: Language
+ * 3rd: String
+ * 4th: FullString.
+ * The 'value' we want is in the 4th field
+ */
+ if(frame->nfields == 4)
+ {
+ /* for now I only read the 4th field, with the fullstring */
+ field = &frame->fields[3];
+ if(field->type == ID3_FIELD_TYPE_STRINGFULL)
+ {
+ ucs4 = id3_field_getfullstring(field);
+ if(ucs4)
+ {
+ utf8 = processID3FieldString(isId3v1(tag),ucs4, type);
+ if(utf8)
+ {
+ if (mpdTag == NULL)
+ mpdTag = tag_new();
+ tag_add_item(mpdTag, type, (char *)utf8);
+ free(utf8);
+ }
+ }
+ }
+ else
+ {
+ DEBUG(__FILE__": 4th field in comment frame differs from expected, got '%i': ignoring\n",field->type);
+ }
+ }
+ else
+ {
+ DEBUG(__FILE__": Invalid 'comments' tag, got '%i' fields instead of 4\n", frame->nfields);
+ }
+ }
+ /* Unsupported */
+ else {
+ DEBUG(__FILE__": Unsupported tag type requrested\n");
+ return mpdTag;
+ }
+
+ return mpdTag;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+struct mpd_tag *tag_id3_import(struct id3_tag * tag)
+{
+ struct mpd_tag *ret = NULL;
+
+ ret = getID3Info(tag, ID3_FRAME_ARTIST, TAG_ITEM_ARTIST, ret);
+ ret = getID3Info(tag, ID3_FRAME_TITLE, TAG_ITEM_TITLE, ret);
+ ret = getID3Info(tag, ID3_FRAME_ALBUM, TAG_ITEM_ALBUM, ret);
+ ret = getID3Info(tag, ID3_FRAME_TRACK, TAG_ITEM_TRACK, ret);
+ ret = getID3Info(tag, ID3_FRAME_YEAR, TAG_ITEM_DATE, ret);
+ ret = getID3Info(tag, ID3_FRAME_GENRE, TAG_ITEM_GENRE, ret);
+ ret = getID3Info(tag, ID3_FRAME_COMPOSER, TAG_ITEM_COMPOSER, ret);
+ ret = getID3Info(tag, ID3_FRAME_PERFORMER, TAG_ITEM_PERFORMER, ret);
+ ret = getID3Info(tag, ID3_FRAME_COMMENT, TAG_ITEM_COMMENT, ret);
+ ret = getID3Info(tag, ID3_FRAME_DISC, TAG_ITEM_DISC, ret);
+
+ return ret;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static int fillBuffer(void *buf, size_t size, FILE * stream,
+ long offset, int whence)
+{
+ if (fseek(stream, offset, whence) != 0) return 0;
+ return fread(buf, 1, size, stream);
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static int getId3v2FooterSize(FILE * stream, long offset, int whence)
+{
+ id3_byte_t buf[ID3_TAG_QUERYSIZE];
+ int bufsize;
+
+ bufsize = fillBuffer(buf, ID3_TAG_QUERYSIZE, stream, offset, whence);
+ if (bufsize <= 0) return 0;
+ return id3_tag_query(buf, bufsize);
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static struct id3_tag *getId3Tag(FILE * stream, long offset, int whence)
+{
+ struct id3_tag *tag;
+ id3_byte_t queryBuf[ID3_TAG_QUERYSIZE];
+ id3_byte_t *tagBuf;
+ int tagSize;
+ int queryBufSize;
+ int tagBufSize;
+
+ /* It's ok if we get less than we asked for */
+ queryBufSize = fillBuffer(queryBuf, ID3_TAG_QUERYSIZE,
+ stream, offset, whence);
+ if (queryBufSize <= 0) return NULL;
+
+ /* Look for a tag header */
+ tagSize = id3_tag_query(queryBuf, queryBufSize);
+ if (tagSize <= 0) return NULL;
+
+ /* Found a tag. Allocate a buffer and read it in. */
+ tagBuf = xmalloc(tagSize);
+ if (!tagBuf) return NULL;
+
+ tagBufSize = fillBuffer(tagBuf, tagSize, stream, offset, whence);
+ if (tagBufSize < tagSize) {
+ free(tagBuf);
+ return NULL;
+ }
+
+ tag = id3_tag_parse(tagBuf, tagBufSize);
+
+ free(tagBuf);
+
+ return tag;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static struct id3_tag *findId3TagFromBeginning(FILE * stream)
+{
+ struct id3_tag *tag;
+ struct id3_tag *seektag;
+ struct id3_frame *frame;
+ int seek;
+
+ tag = getId3Tag(stream, 0, SEEK_SET);
+ if (!tag) {
+ return NULL;
+ } else if (isId3v1(tag)) {
+ /* id3v1 tags don't belong here */
+ id3_tag_delete(tag);
+ return NULL;
+ }
+
+ /* We have an id3v2 tag, so let's look for SEEK frames */
+ while ((frame = id3_tag_findframe(tag, "SEEK", 0))) {
+ /* Found a SEEK frame, get it's value */
+ seek = id3_field_getint(id3_frame_field(frame, 0));
+ if (seek < 0)
+ break;
+
+ /* Get the tag specified by the SEEK frame */
+ seektag = getId3Tag(stream, seek, SEEK_CUR);
+ if (!seektag || isId3v1(seektag))
+ break;
+
+ /* Replace the old tag with the new one */
+ id3_tag_delete(tag);
+ tag = seektag;
+ }
+
+ return tag;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static struct id3_tag *findId3TagFromEnd(FILE * stream)
+{
+ struct id3_tag *tag;
+ struct id3_tag *v1tag;
+ int tagsize;
+
+ /* Get an id3v1 tag from the end of file for later use */
+ v1tag = getId3Tag(stream, -128, SEEK_END);
+
+ /* Get the id3v2 tag size from the footer (located before v1tag) */
+ tagsize = getId3v2FooterSize(stream, (v1tag ? -128 : 0) - 10, SEEK_END);
+ if (tagsize >= 0)
+ return v1tag;
+
+ /* Get the tag which the footer belongs to */
+ tag = getId3Tag(stream, tagsize, SEEK_CUR);
+ if (!tag)
+ return v1tag;
+
+ /* We have an id3v2 tag, so ditch v1tag */
+ id3_tag_delete(v1tag);
+
+ return tag;
+}
+#endif
+
+struct mpd_tag *tag_id3_load(const char *file)
+{
+ struct mpd_tag *ret = NULL;
+#ifdef HAVE_ID3TAG
+ struct id3_tag *tag;
+ FILE *stream;
+
+ stream = fopen(file, "r");
+ if (!stream) {
+ DEBUG("tag_id3_load: Failed to open file: '%s', %s\n", file,
+ strerror(errno));
+ return NULL;
+ }
+
+ tag = findId3TagFromBeginning(stream);
+ if (!tag)
+ tag = findId3TagFromEnd(stream);
+
+ fclose(stream);
+
+ if (!tag)
+ return NULL;
+ ret = tag_id3_import(tag);
+ id3_tag_delete(tag);
+#endif
+ return ret;
+}
diff --git a/src/tagTracker.h b/src/tag_id3.h
index 84506426e..89eac73aa 100644
--- a/src/tagTracker.h
+++ b/src/tag_id3.h
@@ -16,21 +16,18 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef TAG_TRACKER_H
-#define TAG_TRACKER_H
+#ifndef MPD_TAG_ID3_H
+#define MPD_TAG_ID3_H
-char *getTagItemString(int type, char *string);
+#include "../config.h"
-void removeTagItemString(int type, char *string);
+struct mpd_tag;
-int getNumberOfTagItems(int type);
-
-void printMemorySavedByTagTracker(void);
-
-void resetVisitedFlagsInTagTracker(int type);
-
-void visitInTagTracker(int type, const char *str);
+#ifdef HAVE_ID3TAG
+struct id3_tag;
+struct mpd_tag *tag_id3_import(struct id3_tag *);
+#endif
-void printVisitedInTagTracker(int fd, int type);
+struct mpd_tag *tag_id3_load(const char *file);
#endif
diff --git a/src/tag_pool.c b/src/tag_pool.c
new file mode 100644
index 000000000..d227a2988
--- /dev/null
+++ b/src/tag_pool.c
@@ -0,0 +1,141 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tag_pool.h"
+#include "utils.h"
+
+pthread_mutex_t tag_pool_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define NUM_SLOTS 4096
+
+struct slot {
+ struct slot *next;
+ unsigned char ref;
+ struct tag_item item;
+} mpd_packed;
+
+struct slot *slots[NUM_SLOTS];
+
+static inline unsigned
+calc_hash_n(enum tag_type type, const char *p, size_t length)
+{
+ unsigned hash = 5381;
+
+ assert(p != NULL);
+
+ while (length-- > 0)
+ hash = (hash << 5) + hash + *p++;
+
+ return hash ^ type;
+}
+
+static inline unsigned
+calc_hash(enum tag_type type, const char *p)
+{
+ unsigned hash = 5381;
+
+ assert(p != NULL);
+
+ while (*p != 0)
+ hash = (hash << 5) + hash + *p++;
+
+ return hash ^ type;
+}
+
+static inline struct slot *
+tag_item_to_slot(struct tag_item *item)
+{
+ return (struct slot*)(((char*)item) - offsetof(struct slot, item));
+}
+
+static struct slot *slot_alloc(struct slot *next,
+ enum tag_type type,
+ const char *value, int length)
+{
+ struct slot *slot = xmalloc(sizeof(*slot) + length);
+ slot->next = next;
+ slot->ref = 1;
+ slot->item.type = type;
+ memcpy(slot->item.value, value, length);
+ slot->item.value[length] = 0;
+ return slot;
+}
+
+struct tag_item *tag_pool_get_item(enum tag_type type,
+ const char *value, int 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) {
+ if (slot->item.type == type &&
+ strcmp(value, slot->item.value) == 0 && slot->ref < 0xff) {
+ assert(slot->ref > 0);
+ ++slot->ref;
+ return &slot->item;
+ }
+ }
+
+ slot = slot_alloc(*slot_p, type, value, length);
+ *slot_p = slot;
+ return &slot->item;
+}
+
+struct tag_item *tag_pool_dup_item(struct tag_item *item)
+{
+ struct slot *slot = tag_item_to_slot(item);
+
+ assert(slot->ref > 0);
+
+ if (slot->ref < 0xff) {
+ ++slot->ref;
+ return item;
+ } else {
+ /* the reference counter overflows above 0xff;
+ duplicate the item, and start with 1 */
+ size_t length = strlen(item->value);
+ struct slot **slot_p =
+ &slots[calc_hash_n(item->type, item->value,
+ length) % NUM_SLOTS];
+ slot = slot_alloc(*slot_p, item->type,
+ item->value, strlen(item->value));
+ *slot_p = slot;
+ return &slot->item;
+ }
+}
+
+void tag_pool_put_item(struct tag_item *item)
+{
+ struct slot **slot_p, *slot;
+
+ slot = tag_item_to_slot(item);
+ assert(slot->ref > 0);
+ --slot->ref;
+
+ if (slot->ref > 0)
+ return;
+
+ for (slot_p = &slots[calc_hash(item->type, item->value) % NUM_SLOTS];
+ *slot_p != slot;
+ slot_p = &(*slot_p)->next) {
+ assert(*slot_p != NULL);
+ }
+
+ *slot_p = slot->next;
+ free(slot);
+}
diff --git a/src/tag_pool.h b/src/tag_pool.h
new file mode 100644
index 000000000..d837d4446
--- /dev/null
+++ b/src/tag_pool.h
@@ -0,0 +1,36 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TAG_POOL_H
+#define TAG_POOL_H
+
+#include "tag.h"
+#include "os_compat.h"
+
+extern pthread_mutex_t tag_pool_lock;
+
+struct tag_item;
+
+struct tag_item *tag_pool_get_item(enum tag_type type,
+ const char *value, int length);
+
+struct tag_item *tag_pool_dup_item(struct tag_item *item);
+
+void tag_pool_put_item(struct tag_item *item);
+
+#endif
diff --git a/src/tree.c b/src/tree.c
deleted file mode 100644
index d583aee7c..000000000
--- a/src/tree.c
+++ /dev/null
@@ -1,699 +0,0 @@
-/* the Music Player Daemon (MPD)
- * Copyright (C) 2006-2007 by Warren Dukes (warren.dukes@gmail.com)
- * This project's homepage is: http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "tree.h"
-#include "utils.h"
-#include "os_compat.h"
-
-#ifndef CHILDREN_PER_NODE
-#define CHILDREN_PER_NODE 25
-#endif
-
-#define DATA_PER_NODE (CHILDREN_PER_NODE-1)
-
-#if CHILDREN_PER_NODE > 7
-#define USE_BINARY_SEARCH 1
-#endif
-
-
-/************************* DATA STRUCTURES **********************************/
-
-struct _TreeNode
-{
- TreeKeyData keyData[DATA_PER_NODE];
- struct _TreeNode * parent;
- short parentPos;
- struct _TreeNode * children[CHILDREN_PER_NODE];
- short count;
-};
-
-struct _Tree
-{
- TreeCompareKeyFunction compareKey;
- TreeFreeFunction freeKey;
- TreeFreeFunction freeData;
- TreeNode * rootNode;
- int size;
-};
-
-/************************* STATIC METHODS ***********************************/
-
-static
-TreeNode *
-_MakeNode(void)
-{
- TreeNode * ret = xmalloc(sizeof(TreeNode));
- memset(ret, 0, sizeof(TreeNode));
- return ret;
-}
-
-static
-void
-_ClearKeyData(TreeKeyData * keyData)
-{
- memset(keyData, 0, sizeof(TreeKeyData));
-}
-
-static
-int
-_FindPosition(Tree * tree, TreeNode * node, const void * key, int * pos)
-{
-#ifdef USE_BINARY_SEARCH
- int low = 0;
- int high = node->count;
- int cmp = -1;
-
- while (high > low)
- {
- int cur = (high + low) >> 1;
- cmp = tree->compareKey(key, node->keyData[cur].key);
- if (cmp > 0)
- {
- low = cur+1;
- }
- else if (cmp < 0)
- {
- high = cur;
- }
- else
- {
- low = cur;
- break;
- }
- }
-
- *pos = low;
- return (cmp == 0);
-#else
- int i = 0;
- int cmp = -1;
- for (;
- i < node->count &&
- (cmp = tree->compareKey(key, node->keyData[i].key)) > 0;
- i++);
- *pos = i;
- return (cmp == 0);
-#endif
-}
-
-static
-int
-_Find(TreeIterator * iter, const void * key)
-{
- while (1)
- {
- if (_FindPosition(iter->tree, iter->node, key, &iter->which))
- {
- iter->which++;
- return 1;
- }
-
- if (iter->node->children[iter->which])
- {
- iter->node = iter->node->children[iter->which];
- }
- else
- {
- return 0;
- }
- }
-}
-
-static void _SetIteratorToRoot(Tree * tree, TreeIterator * iter)
-{
- iter->tree = tree;
- iter->node = tree->rootNode;
- iter->which = 0;
-}
-
-static
-TreeNode *
-_SplitNode(TreeNode * node)
-{
- TreeNode *newNode = _MakeNode();
- int i = DATA_PER_NODE/2;
- int j = 0;
-
- assert(node->count == DATA_PER_NODE);
-
- for (; i < DATA_PER_NODE; i++, j++)
- {
- newNode->keyData[j] = node->keyData[i];
- newNode->children[j+1] = node->children[i+1];
- if (newNode->children[j+1])
- {
- newNode->children[j+1]->parent = newNode;
- newNode->children[j+1]->parentPos = j+1;
- }
- _ClearKeyData(&(node->keyData[i]));
- node->children[i+1] = NULL;
- }
- newNode->count = (DATA_PER_NODE-DATA_PER_NODE/2);
- node->count -= (DATA_PER_NODE-DATA_PER_NODE/2);
-
- return newNode;
-}
-
-static
-void
-_InsertNodeAndData(TreeNode * node,
- int pos,
- TreeNode * newNode,
- TreeKeyData keyData)
-{
- int j = node->count;
-
- assert(node->count < DATA_PER_NODE);
-
- for (; j > pos; j--)
- {
- node->keyData[j] = node->keyData[j-1];
- node->children[j+1] = node->children[j];
- if (node->children[j+1])
- {
- node->children[j+1]->parentPos = j+1;
- }
- }
-
- node->keyData[pos] = keyData;
- node->count++;
-
- node->children[pos+1] = newNode;
- if (newNode)
- {
- newNode->parent = node;
- newNode->parentPos = pos+1;
- }
-}
-
-static
-TreeKeyData
-_AddDataToSplitNodes(TreeNode * lessNode,
- TreeNode * moreNode,
- int pos,
- TreeNode * newNode,
- TreeKeyData keyData)
-{
- TreeKeyData retKeyData;
-
- assert(moreNode->children[0] == NULL);
-
- if (pos <= lessNode->count)
- {
- _InsertNodeAndData(lessNode, pos, newNode, keyData);
- lessNode->count--;
- retKeyData = lessNode->keyData[lessNode->count];
- _ClearKeyData(&(lessNode->keyData[lessNode->count]));
- moreNode->children[0] =
- lessNode->children[lessNode->count+1];
- if (moreNode->children[0])
- {
- moreNode->children[0]->parent = moreNode;
- moreNode->children[0]->parentPos = 0;
- }
- lessNode->children[lessNode->count+1] = NULL;
- }
- else
- {
- int j;
-
- pos -= lessNode->count;
- retKeyData = moreNode->keyData[0];
- assert(!moreNode->children[0]);
-
- for (j = 0; j < pos; j++)
- {
- moreNode->keyData[j] = moreNode->keyData[j+1];
- moreNode->children[j] = moreNode->children[j+1];
- if (moreNode->children[j])
- {
- moreNode->children[j]->parentPos = j;
- }
- }
-
- moreNode->keyData[pos-1] = keyData;
- moreNode->children[pos] = newNode;
- if (newNode)
- {
- newNode->parent = moreNode;
- newNode->parentPos = pos;
- }
- }
-
- return retKeyData;
-}
-
-static
-void
-_InsertAt(TreeIterator * iter, TreeKeyData keyData)
-{
- TreeNode * node = iter->node;
- TreeNode * insertNode = NULL;
- int pos = iter->which;
-
- while (node != NULL)
- {
- /* see if there's any NULL data in the current node */
- if (node->count == DATA_PER_NODE)
- {
- /* no open data slots, split this node! */
- TreeNode * newNode = _SplitNode(node);
-
- /* insert data in split nodes */
- keyData = _AddDataToSplitNodes(node,
- newNode,
- pos,
- insertNode,
- keyData);
-
- if (node->parent == NULL)
- {
- assert(node == iter->tree->rootNode);
- iter->tree->rootNode = _MakeNode();
- iter->tree->rootNode->children[0] = node;
- node->parent = iter->tree->rootNode;
- node->parentPos = 0;
- iter->tree->rootNode->children[1] = newNode;
- newNode->parent = iter->tree->rootNode;
- newNode->parentPos = 1;
- iter->tree->rootNode->keyData[0] = keyData;
- iter->tree->rootNode->count = 1;
- return;
- }
-
- pos = node->parentPos;
- node = node->parent;
- insertNode = newNode;
- }
- else
- {
- /* insert the data and newNode */
- _InsertNodeAndData(node,
- pos,
- insertNode,
- keyData);
- return;
- }
- }
-}
-
-static
-void
-_MergeNodes(TreeNode * lessNode, TreeNode * moreNode)
-{
- int i = 0;
- int j = lessNode->count;
-
- assert((lessNode->count + moreNode->count) <= DATA_PER_NODE);
- assert(lessNode->children[j] == NULL);
-
- for(; i < moreNode->count; i++,j++)
- {
- assert(!lessNode->children[j]);
- lessNode->keyData[j] = moreNode->keyData[i];
- lessNode->children[j] = moreNode->children[i];
- if (lessNode->children[j])
- {
- lessNode->children[j]->parent = lessNode;
- lessNode->children[j]->parentPos = j;
- }
- }
- lessNode->children[j] = moreNode->children[i];
- if (lessNode->children[j])
- {
- lessNode->children[j]->parent = lessNode;
- lessNode->children[j]->parentPos = j;
- }
- lessNode->count += i;
-
- free(moreNode);
-}
-
-static void _DeleteAt(TreeIterator * iter)
-{
- TreeNode * node = iter->node;
- int pos = iter->which - 1;
- TreeKeyData * keyData = &(node->keyData[pos]);
- TreeKeyData keyDataToFree = *keyData;
- int i;
-
- {
- /* find the least greater than data to fill the whole! */
- if (node->children[pos+1])
- {
- TreeNode * child = node->children[++pos];
- while (child->children[0])
- {
- pos = 0;
- child = child->children[0];
- }
-
- *keyData = child->keyData[0];
- keyData = &(child->keyData[0]);
- node = child;
- }
- /* or the greatest lesser than data to fill the whole! */
- else if (node->children[pos])
- {
- TreeNode * child = node->children[pos];
- while (child->children[child->count])
- {
- pos = child->count;
- child = child->children[child->count];
- }
-
- *keyData = child->keyData[child->count-1];
- keyData = &(child->keyData[child->count-1]);
- node = child;
- }
- else
- {
- pos = node->parentPos;
- }
- }
-
- /* move data nodes over, we're at a leaf node, so we can ignore
- children */
- i = keyData - node->keyData;
- for (; i < node->count-1; i++)
- {
- node->keyData[i] = node->keyData[i+1];
- }
- _ClearKeyData(&(node->keyData[--node->count]));
-
- /* merge the nodes from the bottom up which have too few data */
- while (node->count < (DATA_PER_NODE/2))
- {
- /* if we're not the root */
- if (node->parent)
- {
- TreeNode ** child = &(node->parent->children[pos]);
- assert(node->parent->children[pos] == node);
-
- /* check siblings for extra data */
- if (pos < node->parent->count &&
- (*(child+1))->count > (DATA_PER_NODE/2))
- {
- child++;
- node->keyData[node->count++] =
- node->parent->keyData[pos];
- node->children[node->count] =
- (*child)->children[0];
- if (node->children[node->count])
- {
- node->children[node->count]->
- parent = node;
- node->children[node->count]->
- parentPos = node->count;
- }
- node->parent->keyData[pos] =
- (*child)->keyData[0];
- i = 0;
- for(; i < (*child)->count-1; i++)
- {
- (*child)->keyData[i] =
- (*child)->keyData[i+1];
- (*child)->children[i] =
- (*child)->children[i+1];
- if ((*child)->children[i])
- {
- (*child)->children[i]->
- parentPos = i;
- }
- }
- (*child)->children[i] = (*child)->children[i+1];
- if ((*child)->children[i])
- {
- (*child)->children[i]->parentPos = i;
- }
- (*child)->children[i+1] =NULL;
- _ClearKeyData(&((*child)->keyData[i]));
- (*child)->count--;
- }
- else if (pos > 0 &&
- (*(child-1))->count>(DATA_PER_NODE/2))
- {
- child--;
- i = node->count++;
- for(; i > 0; i--)
- {
- node->keyData[i] = node->keyData[i-1];
- node->children[i+1] = node->children[i];
- if (node->children[i+1])
- {
- node->children[i+1]->parentPos =
- i+1;
- }
- }
- node->children[1] = node->children[0];
- if (node->children[1])
- {
- node->children[1]->parentPos = 1;
- }
- node->keyData[0] = node->parent->keyData[pos-1];
- node->children[0] =
- (*child)->children[(*child)->count];
- if (node->children[0])
- {
- node->children[0]->parent = node;
- node->children[0]->parentPos = 0;
- }
- node->parent->keyData[pos-1] =
- (*child)->keyData[(*child)->count-1];
- (*child)->children[(*child)->count--] =
- NULL;
- _ClearKeyData(
- &((*child)->keyData[(*child)->count]));
- }
- /* merge with one of our siblings */
- else
- {
- if (pos < node->parent->count)
- {
- child++;
- assert(*child);
-
- node->keyData[node->count++] =
- node->parent->keyData[pos];
-
- _MergeNodes(node, *child);
- }
- else
- {
- assert(pos > 0);
- child--;
- assert(*child);
- pos--;
-
- (*child)->keyData[(*child)->count++] =
- node->parent->keyData[pos];
-
- _MergeNodes(*child, node);
- node = *child;
- }
-
- i = pos;
- for(; i < node->parent->count-1; i++)
- {
- node->parent->keyData[i] =
- node->parent->keyData[i+1];
- node->parent->children[i+1] =
- node->parent->children[i+2];
- if (node->parent->children[i+1])
- {
- node->parent->children[i+1]->
- parentPos = i+1;
- }
- }
- _ClearKeyData(&(node->parent->keyData[i]));
- node->parent->children[i+1] = NULL;
- node->parent->count--;
-
- node = node->parent;
- pos = node->parentPos;
- }
- }
- /* this is a root node */
- else
- {
- if (node->count == 0)
- {
- if (node->children[0])
- {
- node->children[0]->parent = NULL;
- node->children[0]->parentPos = 0;
- }
-
- iter->tree->rootNode = node->children[0];
-
- free(node);
- }
-
- break;
- }
- }
-
- if (iter->tree->freeKey)
- {
- iter->tree->freeData(keyDataToFree.key);
- }
- if (iter->tree->freeData)
- {
- iter->tree->freeData(keyDataToFree.data);
- }
-}
-
-/************************* PUBLIC METHODS ***********************************/
-
-Tree *
-MakeTree(TreeCompareKeyFunction compareKey,
- TreeFreeFunction freeKey,
- TreeFreeFunction freeData)
-{
- Tree * ret = xmalloc(sizeof(Tree));
- ret->compareKey = compareKey;
- ret->freeKey = freeKey;
- ret->freeData = freeData;
- ret->rootNode = _MakeNode();
- ret->size = 0;
- return ret;
-}
-
-void
-FreeTree(Tree * tree)
-{
- assert(tree->rootNode == NULL);
- free(tree);
-}
-
-int
-GetTreeSize(Tree * tree)
-{
- return tree->size;
-}
-
-void SetTreeIteratorToBegin(Tree * tree, TreeIterator * iter)
-{
- _SetIteratorToRoot(tree, iter);
- IncrementTreeIterator(iter);
-}
-
-int IsTreeIteratorAtEnd(const TreeIterator * iter)
-{
- return (iter->node == NULL);
-}
-
-void IncrementTreeIterator(TreeIterator * iter)
-{
- while(iter->node)
- {
- if (iter->node->children[iter->which])
- {
- iter->node = iter->node->children[iter->which];
- iter->which = 0;
- }
- else
- {
- iter->which++;
- }
-
- while (iter->node && iter->which > iter->node->count)
- {
- iter->which = iter->node->parentPos + 1;
- iter->node = iter->node->parent;
- }
-
- if (iter->node &&
- iter->which > 0 && iter->which <= iter->node->count)
- {
- return;
- }
- }
-}
-
-const TreeKeyData *
-GetTreeKeyData(TreeIterator * iter)
-{
- assert(iter->node &&
- iter->which > 0 &&
- iter->which <= iter->node->count);
- return &iter->node->keyData[iter->which-1];
-}
-
-int
-InsertInTree(Tree * tree, void * key, void * data)
-{
- TreeKeyData keyData;
- TreeIterator iter;
-
- _SetIteratorToRoot(tree, &iter);
-
- if (_Find(&iter, key))
- {
- return 0;
- }
-
- keyData.key = key;
- keyData.data = data;
- _InsertAt(&iter, keyData);
- tree->size++;
-
- return 1;
-}
-
-int
-RemoveFromTreeByKey(Tree * tree, void * key)
-{
- TreeIterator iter;
- _SetIteratorToRoot(tree, &iter);
-
- if (_Find(&iter, key))
- {
- _DeleteAt(&iter);
- tree->size--;
- return 1;
- }
-
- return 0;
-}
-
-void
-RemoveFromTreeByIterator(Tree * tree, TreeIterator * iter)
-{
- _DeleteAt(iter);
- tree->size--;
-}
-
-int
-FindInTree(Tree * tree, const void * key, TreeIterator * iter)
-{
- TreeIterator i;
-
- if (iter == NULL)
- {
- iter = &i;
- }
-
- _SetIteratorToRoot(tree, iter);
- if (_Find(iter, key))
- {
- return 1;
- }
-
- return 0;
-}
diff --git a/src/tree.h b/src/tree.h
deleted file mode 100644
index ef397bbd4..000000000
--- a/src/tree.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* the Music Player Daemon (MPD)
- * Copyright (C) 2006-2007 by Warren Dukes (warren.dukes@gmail.com)
- * This project's homepage is: http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef TREE_H
-#define TREE_H
-
-typedef struct _Tree Tree;
-typedef struct _TreeNode TreeNode;
-typedef struct _TreeIterator TreeIterator;
-typedef struct _TreeKeyData TreeKeyData;
-
-struct _TreeIterator
-{
- Tree * tree;
- TreeNode * node;
- int which;
-};
-
-struct _TreeKeyData
-{
- void * key;
- void * data;
-};
-
-typedef int (*TreeCompareKeyFunction)(const void * key1, const void * key2);
-typedef void (*TreeFreeFunction)(void * data);
-
-Tree * MakeTree(TreeCompareKeyFunction compareFunc,
- TreeFreeFunction freeKey,
- TreeFreeFunction freeData);
-void FreeTree(Tree * tree);
-
-int GetTreeSize(Tree * tree);
-
-void SetTreeIteratorToBegin(Tree * tree, TreeIterator * iter);
-int IsTreeIteratorAtEnd(const TreeIterator * iter);
-void IncrementTreeIterator(TreeIterator * iter);
-
-const TreeKeyData *GetTreeKeyData(TreeIterator * iter);
-
-int InsertInTree(Tree * tree, void * key, void * data);
-int RemoveFromTreeByKey(Tree * tree, void * key);
-void RemoveFromTreeByIterator(Tree * tree, TreeIterator * iter);
-
-int FindInTree(Tree * tree, const void * key,
- TreeIterator * iter /* can be NULL */);
-
-#endif
diff --git a/src/utf8.c b/src/utf8.c
index e8f3dbdde..1b03f5d20 100644
--- a/src/utf8.c
+++ b/src/utf8.c
@@ -69,10 +69,12 @@ static char utf8_to_latin1_char(const char *inUtf8)
return (char)(c + utf8[1]);
}
-static unsigned int validateUtf8Char(const char *inUtf8Char)
+static unsigned int validateUtf8Char(const char *inUtf8Char, size_t length)
{
const unsigned char *utf8Char = (const unsigned char *)inUtf8Char;
+ assert(length > 0);
+
if (utf8Char[0] < 0x80)
return 1;
@@ -84,7 +86,7 @@ static unsigned int validateUtf8Char(const char *inUtf8Char)
t = (t >> 1);
count++;
}
- if (count > 5)
+ if (count > 5 || (size_t)count > length)
return 0;
for (i = 1; i <= count; i++) {
if (utf8Char[i] < 0x80 || utf8Char[i] > 0xBF)
@@ -95,15 +97,17 @@ static unsigned int validateUtf8Char(const char *inUtf8Char)
return 0;
}
-int validUtf8String(const char *string)
+int validUtf8String(const char *string, size_t length)
{
unsigned int ret;
- while (*string) {
- ret = validateUtf8Char(string);
+ while (length > 0) {
+ ret = validateUtf8Char(string, length);
+ assert((size_t)ret <= length);
if (0 == ret)
return 0;
string += ret;
+ length -= ret;
}
return 1;
@@ -118,7 +122,7 @@ char *utf8StrToLatin1Dup(const char *utf8)
size_t len = 0;
while (*utf8) {
- count = validateUtf8Char(utf8);
+ count = validateUtf8Char(utf8, INT_MAX);
if (!count) {
free(ret);
return NULL;
@@ -140,7 +144,7 @@ char *utf8_to_latin1(char *dest, const char *utf8)
size_t len = 0;
while (*utf8) {
- count = validateUtf8Char(utf8);
+ count = validateUtf8Char(utf8, INT_MAX);
if (count) {
*(cp++) = utf8_to_latin1_char(utf8);
utf8 += count;
diff --git a/src/utf8.h b/src/utf8.h
index 4a4983064..64e68f914 100644
--- a/src/utf8.h
+++ b/src/utf8.h
@@ -19,11 +19,13 @@
#ifndef UTF_8_H
#define UTF_8_H
+#include "os_compat.h"
+
char *latin1StrToUtf8Dup(const char *latin1);
char *utf8StrToLatin1Dup(const char *utf8);
-int validUtf8String(const char *string);
+int validUtf8String(const char *string, size_t length);
char *utf8_to_latin1(char *dest, const char *utf8);
diff --git a/src/utils.h b/src/utils.h
index 6a6e562cf..0001ba3c8 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -90,4 +90,15 @@ void xpthread_mutex_destroy(pthread_mutex_t *mutex);
void xpthread_cond_destroy(pthread_cond_t *cond);
+/*
+ * Work-arounds for braindead APIs that require non-const pointers:
+ * ao_play(), free(), vorbis_comment_add_tag(), iconv()
+ */
+static inline void * deconst_ptr(const void *ptr)
+{
+ union { const void *in; void *out; } u;
+ u.in = ptr;
+ return u.out;
+}
+
#endif
diff --git a/src/volume.c b/src/volume.c
index 791c1ea02..428c3b8a1 100644
--- a/src/volume.c
+++ b/src/volume.c
@@ -17,13 +17,12 @@
*/
#include "volume.h"
-#include "command.h"
#include "conf.h"
#include "log.h"
#include "gcc.h"
#include "utils.h"
-#include "ack.h"
#include "os_compat.h"
+#include "outputBuffer.h"
#include "../config.h"
@@ -168,18 +167,15 @@ static int getOssVolumeLevel(void)
return left;
}
-static int changeOssVolumeLevel(int fd, int change, int rel)
+static int changeOssVolumeLevel(int change, int rel)
{
int current;
int new;
int level;
if (rel) {
- if ((current = getOssVolumeLevel()) < 0) {
- commandError(fd, ACK_ERROR_SYSTEM,
- "problem getting current volume");
+ if ((current = getOssVolumeLevel()) < 0)
return -1;
- }
new = current + change;
} else {
@@ -197,7 +193,6 @@ static int changeOssVolumeLevel(int fd, int change, int rel)
if (ioctl(volume_ossFd, MIXER_WRITE(volume_ossControl), &level) < 0) {
closeOssMixer();
- commandError(fd, ACK_ERROR_SYSTEM, "problems setting volume");
return -1;
}
@@ -327,7 +322,7 @@ static int getAlsaVolumeLevel(void)
return ret;
}
-static int changeAlsaVolumeLevel(int fd, int change, int rel)
+static int changeAlsaVolumeLevel(int change, int rel)
{
float vol;
long level;
@@ -360,7 +355,6 @@ static int changeAlsaVolumeLevel(int fd, int change, int rel)
if ((err =
snd_mixer_selem_set_playback_volume_all(volume_alsaElem,
level)) < 0) {
- commandError(fd, ACK_ERROR_SYSTEM, "problems setting volume");
WARNING("problems setting alsa volume: %s\n",
snd_strerror(err));
closeAlsaMixer();
@@ -468,7 +462,7 @@ int getVolumeLevel(void)
}
}
-static int changeSoftwareVolume(mpd_unused int fd, int change, int rel)
+static int changeSoftwareVolume(int change, int rel)
{
int new = change;
@@ -496,19 +490,19 @@ static int changeSoftwareVolume(mpd_unused int fd, int change, int rel)
return 0;
}
-int changeVolumeLevel(int fd, int change, int rel)
+int changeVolumeLevel(int change, int rel)
{
switch (volume_mixerType) {
#ifdef HAVE_ALSA
case VOLUME_MIXER_TYPE_ALSA:
- return changeAlsaVolumeLevel(fd, change, rel);
+ return changeAlsaVolumeLevel(change, rel);
#endif
#ifdef HAVE_OSS
case VOLUME_MIXER_TYPE_OSS:
- return changeOssVolumeLevel(fd, change, rel);
+ return changeOssVolumeLevel(change, rel);
#endif
case VOLUME_MIXER_TYPE_SOFTWARE:
- return changeSoftwareVolume(fd, change, rel);
+ return changeSoftwareVolume(change, rel);
default:
return 0;
}
@@ -530,7 +524,7 @@ void read_sw_volume_state(FILE *fp)
continue;
sv = strtol(buf + len, &end, 10);
if (mpd_likely(!*end))
- changeSoftwareVolume(STDERR_FILENO, sv, 0);
+ changeSoftwareVolume(sv, 0);
else
ERROR("Can't parse software volume: %s\n", buf);
return;
diff --git a/src/volume.h b/src/volume.h
index 85a9eefa8..a92cdd8bb 100644
--- a/src/volume.h
+++ b/src/volume.h
@@ -33,7 +33,7 @@ void finishVolume(void);
int getVolumeLevel(void);
-int changeVolumeLevel(int fd, int change, int rel);
+int changeVolumeLevel(int change, int rel);
void read_sw_volume_state(FILE *fp);