diff options
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 @@ -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]) { @@ -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); |