aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/archive_list.c2
-rw-r--r--src/command.c2
-rw-r--r--src/conf.c9
-rw-r--r--src/decoder/pcm_decoder_plugin.c84
-rw-r--r--src/decoder/pcm_decoder_plugin.h33
-rw-r--r--src/decoder_list.c2
-rw-r--r--src/decoder_plugin.c2
-rw-r--r--src/input/cdio_paranoia_input_plugin.c387
-rw-r--r--src/input/cdio_paranoia_input_plugin.h28
-rw-r--r--src/input_registry.c7
-rw-r--r--src/ls.c3
-rw-r--r--src/output/osx_plugin.c130
-rw-r--r--src/playlist/extm3u_playlist_plugin.c3
-rw-r--r--src/playlist_database.c3
-rw-r--r--src/playlist_list.c2
-rw-r--r--src/playlist_print.c2
-rw-r--r--src/playlist_queue.c8
-rw-r--r--src/playlist_queue.h6
-rw-r--r--src/playlist_song.c12
-rw-r--r--src/playlist_song.h8
-rw-r--r--src/server_socket.c5
-rw-r--r--src/song_save.c3
-rw-r--r--src/stored_playlist.c20
-rw-r--r--src/string_util.c47
-rw-r--r--src/string_util.h77
-rw-r--r--src/tokenizer.c7
-rw-r--r--src/utils.c13
-rw-r--r--src/utils.h11
28 files changed, 845 insertions, 71 deletions
diff --git a/src/archive_list.c b/src/archive_list.c
index 2656726b5..648f68575 100644
--- a/src/archive_list.c
+++ b/src/archive_list.c
@@ -20,7 +20,7 @@
#include "config.h"
#include "archive_list.h"
#include "archive_plugin.h"
-#include "utils.h"
+#include "string_util.h"
#include "archive/bz2_archive_plugin.h"
#include "archive/iso9660_archive_plugin.h"
#include "archive/zzip_archive_plugin.h"
diff --git a/src/command.c b/src/command.c
index 781547b44..ccdaf264a 100644
--- a/src/command.c
+++ b/src/command.c
@@ -747,7 +747,7 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
enum playlist_result result;
- result = playlist_open_into_queue(argv[1], &g_playlist);
+ result = playlist_open_into_queue(argv[1], &g_playlist, true);
if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
return result;
diff --git a/src/conf.c b/src/conf.c
index 705942085..c759a1940 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -20,6 +20,7 @@
#include "config.h"
#include "conf.h"
#include "utils.h"
+#include "string_util.h"
#include "tokenizer.h"
#include "path.h"
#include "glib_compat.h"
@@ -269,7 +270,7 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
}
(*count)++;
- line = g_strchug(line);
+ line = strchug_fast(line);
if (*line == 0 || *line == CONF_COMMENT)
continue;
@@ -277,7 +278,7 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
/* end of this block; return from the function
(and from this "while" loop) */
- line = g_strchug(line + 1);
+ line = strchug_fast(line + 1);
if (*line != 0 && *line != CONF_COMMENT) {
config_param_free(ret);
g_set_error(error_r, config_quark(), 0,
@@ -355,7 +356,7 @@ config_read_file(const char *file, GError **error_r)
count++;
- line = g_strchug(string);
+ line = strchug_fast(string);
if (*line == 0 || *line == CONF_COMMENT)
continue;
@@ -401,7 +402,7 @@ config_read_file(const char *file, GError **error_r)
return false;
}
- line = g_strchug(line + 1);
+ line = strchug_fast(line + 1);
if (*line != 0 && *line != CONF_COMMENT) {
g_set_error(error_r, config_quark(), 0,
"line %i: Unknown tokens after '{'",
diff --git a/src/decoder/pcm_decoder_plugin.c b/src/decoder/pcm_decoder_plugin.c
new file mode 100644
index 000000000..f63f1ec46
--- /dev/null
+++ b/src/decoder/pcm_decoder_plugin.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "decoder/pcm_decoder_plugin.h"
+#include "decoder_api.h"
+
+#include <glib.h>
+#include <unistd.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "pcm"
+
+static void
+pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
+{
+ static const struct audio_format audio_format = {
+ .sample_rate = 44100,
+ .format = SAMPLE_FORMAT_S16,
+ .channels = 2,
+ };
+ GError *error = NULL;
+ enum decoder_command cmd;
+
+ double time_to_size = audio_format_time_to_size(&audio_format);
+
+ float total_time = -1;
+ if (is->size >= 0)
+ total_time = is->size / time_to_size;
+
+ decoder_initialized(decoder, &audio_format, true, total_time);
+
+ do {
+ char buffer[4096];
+
+ size_t nbytes = decoder_read(decoder, is,
+ buffer, sizeof(buffer));
+ cmd = nbytes > 0
+ ? decoder_data(decoder, is,
+ buffer, nbytes, 0)
+ : decoder_get_command(decoder);
+ if (cmd == DECODE_COMMAND_SEEK) {
+ goffset offset = (goffset)(time_to_size *
+ decoder_seek_where(decoder));
+ if (input_stream_seek(is, offset, SEEK_SET, &error)) {
+ decoder_command_finished(decoder);
+ } else {
+ g_warning("seeking failed: %s", error->message);
+ g_error_free(error);
+ decoder_seek_error(decoder);
+ }
+
+ cmd = DECODE_COMMAND_NONE;
+ }
+ } while (cmd == DECODE_COMMAND_NONE);
+}
+
+static const char *const pcm_mime_types[] = {
+ /* for streams obtained by the cdio_paranoia input plugin */
+ "audio/x-mpd-cdda-pcm",
+ NULL
+};
+
+const struct decoder_plugin pcm_decoder_plugin = {
+ .name = "pcm",
+ .stream_decode = pcm_stream_decode,
+ .mime_types = pcm_mime_types,
+};
diff --git a/src/decoder/pcm_decoder_plugin.h b/src/decoder/pcm_decoder_plugin.h
new file mode 100644
index 000000000..716ae0c3e
--- /dev/null
+++ b/src/decoder/pcm_decoder_plugin.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * Not really a decoder; this plugin forwards its input data "as-is".
+ *
+ * It was written only to support the "cdio_paranoia" input plugin,
+ * which does not need a decoder.
+ */
+
+#ifndef MPD_DECODER_PCM_H
+#define MPD_DECODER_PCM_H
+
+extern const struct decoder_plugin pcm_decoder_plugin;
+
+#endif
diff --git a/src/decoder_list.c b/src/decoder_list.c
index d76050023..4d8562be6 100644
--- a/src/decoder_list.c
+++ b/src/decoder_list.c
@@ -23,6 +23,7 @@
#include "utils.h"
#include "conf.h"
#include "mpd_error.h"
+#include "decoder/pcm_decoder_plugin.h"
#include <glib.h>
@@ -102,6 +103,7 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef HAVE_GME
&gme_decoder_plugin,
#endif
+ &pcm_decoder_plugin,
NULL
};
diff --git a/src/decoder_plugin.c b/src/decoder_plugin.c
index 062dad364..62f85a645 100644
--- a/src/decoder_plugin.c
+++ b/src/decoder_plugin.c
@@ -19,7 +19,7 @@
#include "config.h"
#include "decoder_plugin.h"
-#include "utils.h"
+#include "string_util.h"
#include <assert.h>
diff --git a/src/input/cdio_paranoia_input_plugin.c b/src/input/cdio_paranoia_input_plugin.c
new file mode 100644
index 000000000..3450ae949
--- /dev/null
+++ b/src/input/cdio_paranoia_input_plugin.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * CD-Audio handling (requires libcdio_paranoia)
+ */
+
+#include "config.h"
+#include "input/cdio_paranoia_input_plugin.h"
+#include "input_plugin.h"
+#include "refcount.h"
+#include "pcm_buffer.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <assert.h>
+
+#include <cdio/paranoia.h>
+#include <cdio/cd_types.h>
+
+struct input_cdio_paranoia {
+ struct input_stream base;
+
+ cdrom_drive_t *drv;
+ CdIo_t *cdio;
+ cdrom_paranoia_t *para;
+
+ int endian;
+
+ lsn_t lsn_from, lsn_to;
+ int lsn_relofs;
+
+ int trackno;
+
+ char buffer[CDIO_CD_FRAMESIZE_RAW];
+ int buffer_lsn;
+
+ struct pcm_buffer conv_buffer;
+};
+
+static inline GQuark
+cdio_quark(void)
+{
+ return g_quark_from_static_string("cdio");
+}
+
+static void
+input_cdio_close(struct input_stream *is)
+{
+ struct input_cdio_paranoia *i = (struct input_cdio_paranoia *)is;
+
+ pcm_buffer_deinit(&i->conv_buffer);
+
+ if (i->para)
+ cdio_paranoia_free(i->para);
+ if (i->drv)
+ cdio_cddap_close_no_free_cdio( i->drv);
+ if (i->cdio)
+ cdio_destroy( i->cdio );
+
+ input_stream_deinit(&i->base);
+ g_free(i);
+}
+
+struct cdio_uri {
+ char device[64];
+ int track;
+};
+
+static bool
+parse_cdio_uri(struct cdio_uri *dest, const char *src, GError **error_r)
+{
+ if (!g_str_has_prefix(src, "cdda://"))
+ return false;
+
+ src += 7;
+
+ if (*src == 0) {
+ /* play the whole CD in the default drive */
+ dest->device[0] = 0;
+ dest->track = -1;
+ return true;
+ }
+
+ const char *slash = strrchr(src, '/');
+ if (slash == NULL) {
+ /* play the whole CD in the specified drive */
+ g_strlcpy(dest->device, src, sizeof(dest->device));
+ dest->track = -1;
+ return true;
+ }
+
+ size_t device_length = slash - src;
+ if (device_length >= sizeof(dest->device))
+ device_length = sizeof(dest->device) - 1;
+
+ memcpy(dest->device, src, device_length);
+ dest->device[device_length] = 0;
+
+ const char *track = slash + 1;
+
+ char *endptr;
+ dest->track = strtoul(track, &endptr, 10);
+ if (*endptr != 0) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "Malformed track number");
+ return false;
+ }
+
+ if (endptr == track)
+ /* play the whole CD */
+ dest->track = -1;
+
+ return true;
+}
+
+static char *
+cdio_detect_device(void)
+{
+ char **devices = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false);
+ if (devices == NULL)
+ return NULL;
+
+ char *device = g_strdup(devices[0]);
+ cdio_free_device_list(devices);
+
+ return device;
+}
+
+static struct input_stream *
+input_cdio_open(const char *uri, GError **error_r)
+{
+ struct input_cdio_paranoia *i;
+
+ struct cdio_uri parsed_uri;
+ if (!parse_cdio_uri(&parsed_uri, uri, error_r))
+ return NULL;
+
+ i = g_new(struct input_cdio_paranoia, 1);
+ input_stream_init(&i->base, &input_plugin_cdio_paranoia, uri);
+
+ /* initialize everything (should be already) */
+ i->drv = NULL;
+ i->cdio = NULL;
+ i->para = NULL;
+ i->trackno = parsed_uri.track;
+ pcm_buffer_init(&i->conv_buffer);
+
+ /* get list of CD's supporting CD-DA */
+ char *device = parsed_uri.device[0] != 0
+ ? g_strdup(parsed_uri.device)
+ : cdio_detect_device();
+ if (device == NULL) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "Unable find or access a CD-ROM drive with an audio CD in it.");
+ input_cdio_close(&i->base);
+ return NULL;
+ }
+
+ /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in the list. */
+ i->cdio = cdio_open(device, DRIVER_UNKNOWN);
+ g_free(device);
+
+ i->drv = cdio_cddap_identify_cdio(i->cdio, 1, NULL);
+
+ if ( !i->drv ) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "Unable to identify audio CD disc.");
+ input_cdio_close(&i->base);
+ return NULL;
+ }
+
+ cdda_verbose_set(i->drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
+
+ if ( 0 != cdio_cddap_open(i->drv) ) {
+ g_set_error(error_r, cdio_quark(), 0, "Unable to open disc.");
+ input_cdio_close(&i->base);
+ return NULL;
+ }
+
+ i->endian = data_bigendianp(i->drv);
+ switch (i->endian) {
+ case -1:
+ g_debug("cdda: drive returns unknown audio data, assuming Little Endian");
+ i->endian = 0;
+ break;
+ case 0:
+ g_debug("cdda: drive returns audio data Little Endian.");
+ break;
+ case 1:
+ g_debug("cdda: drive returns audio data Big Endian.");
+ break;
+ default:
+ g_set_error(error_r, cdio_quark(), 0,
+ "Drive returns unknown data type %d", i->endian);
+ input_cdio_close(&i->base);
+ return NULL;
+ }
+
+ i->lsn_relofs = 0;
+
+ if (i->trackno >= 0) {
+ i->lsn_from = cdio_get_track_lsn(i->cdio, i->trackno);
+ i->lsn_to = cdio_get_track_last_lsn(i->cdio, i->trackno);
+ } else {
+ i->lsn_from = 0;
+ i->lsn_to = cdio_get_disc_last_lsn(i->cdio);
+ }
+
+ i->para = cdio_paranoia_init(i->drv);
+
+ /* Set reading mode for full paranoia, but allow skipping sectors. */
+ paranoia_modeset(i->para, PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
+
+ /* seek to beginning of the track */
+ cdio_paranoia_seek(i->para, i->lsn_from, SEEK_SET);
+
+ i->base.ready = true;
+ i->base.seekable = true;
+ i->base.size = (i->lsn_to - i->lsn_from + 1) * CDIO_CD_FRAMESIZE_RAW;
+
+ /* hack to make MPD select the "pcm" decoder plugin */
+ i->base.mime = g_strdup("audio/x-mpd-cdda-pcm");
+
+ return &i->base;
+}
+
+static bool
+input_cdio_seek(struct input_stream *is,
+ goffset offset, int whence, GError **error_r)
+{
+ struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+
+ /* calculate absolute offset */
+ switch (whence) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ offset += cis->base.offset;
+ break;
+ case SEEK_END:
+ offset += cis->base.size;
+ break;
+ }
+
+ if (offset < 0 || offset > cis->base.size) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "Invalid offset to seek %ld (%ld)",
+ (long int)offset, (long int)cis->base.size);
+ return false;
+ }
+
+ /* simple case */
+ if (offset == cis->base.offset)
+ return true;
+
+ /* calculate current LSN */
+ cis->lsn_relofs = offset / CDIO_CD_FRAMESIZE_RAW;
+ cis->base.offset = offset;
+
+ cdio_paranoia_seek(cis->para, cis->lsn_from + cis->lsn_relofs, SEEK_SET);
+
+ return true;
+}
+
+static inline size_t
+pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
+{
+ size_t cnt = length >> 1;
+ while (cnt > 0) {
+ *dst16++ = GUINT16_TO_LE(*src16++);
+ cnt--;
+ }
+ return length;
+}
+
+static size_t
+input_cdio_read(struct input_stream *is, void *ptr, size_t length,
+ GError **error_r)
+{
+ struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+ size_t nbytes = 0;
+ int diff;
+ size_t len, maxwrite;
+ int16_t *rbuf;
+ char *s_err, *s_mess;
+ char *wptr = (char *) ptr;
+
+ while (length > 0) {
+
+
+ /* end of track ? */
+ if (cis->lsn_from + cis->lsn_relofs > cis->lsn_to)
+ break;
+
+ //current sector was changed ?
+ if (cis->lsn_relofs != cis->buffer_lsn) {
+ rbuf = cdio_paranoia_read(cis->para, NULL);
+
+ s_err = cdda_errors(cis->drv);
+ if (s_err) {
+ g_warning("paranoia_read: %s", s_err );
+ free(s_err);
+ }
+ s_mess = cdda_messages(cis->drv);
+ if (s_mess) {
+ free(s_mess);
+ }
+ if (!rbuf) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "paranoia read error. Stopping.");
+ return 0;
+ }
+ //do the swapping if nessesary
+ if (cis->endian != 0) {
+ uint16_t *conv_buffer = pcm_buffer_get(&cis->conv_buffer, CDIO_CD_FRAMESIZE_RAW );
+ /* do endian conversion ! */
+ pcm16_to_wave( conv_buffer, (uint16_t*) rbuf, CDIO_CD_FRAMESIZE_RAW);
+ rbuf = (int16_t *)conv_buffer;
+ }
+ //store current buffer
+ memcpy(cis->buffer, rbuf, CDIO_CD_FRAMESIZE_RAW);
+ cis->buffer_lsn = cis->lsn_relofs;
+ } else {
+ //use cached sector
+ rbuf = (int16_t*) cis->buffer;
+ }
+
+ //correct offset
+ diff = cis->base.offset - cis->lsn_relofs * CDIO_CD_FRAMESIZE_RAW;
+
+ assert(diff >= 0 && diff < CDIO_CD_FRAMESIZE_RAW);
+
+ maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
+ len = (length < maxwrite? length : maxwrite);
+
+ //skip diff bytes from this lsn
+ memcpy(wptr, ((char*)rbuf) + diff, len);
+ //update pointer
+ wptr += len;
+ nbytes += len;
+
+ //update offset
+ cis->base.offset += len;
+ cis->lsn_relofs = cis->base.offset / CDIO_CD_FRAMESIZE_RAW;
+ //update length
+ length -= len;
+ }
+
+ return nbytes;
+}
+
+static bool
+input_cdio_eof(struct input_stream *is)
+{
+ struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+
+ return (cis->lsn_from + cis->lsn_relofs > cis->lsn_to);
+}
+
+const struct input_plugin input_plugin_cdio_paranoia = {
+ .open = input_cdio_open,
+ .close = input_cdio_close,
+ .seek = input_cdio_seek,
+ .read = input_cdio_read,
+ .eof = input_cdio_eof
+};
diff --git a/src/input/cdio_paranoia_input_plugin.h b/src/input/cdio_paranoia_input_plugin.h
new file mode 100644
index 000000000..3b22cbc28
--- /dev/null
+++ b/src/input/cdio_paranoia_input_plugin.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CDIO_PARANOIA_INPUT_PLUGIN_H
+#define MPD_CDIO_PARANOIA_INPUT_PLUGIN_H
+
+/**
+ * An input plugin based on libcdio_paranoia library.
+ */
+extern const struct input_plugin input_plugin_cdio_paranoia;
+
+#endif
diff --git a/src/input_registry.c b/src/input_registry.c
index 0b9b47d10..735ed4776 100644
--- a/src/input_registry.c
+++ b/src/input_registry.c
@@ -37,6 +37,10 @@
#include "input/mms_input_plugin.h"
#endif
+#ifdef ENABLE_CDIO_PARANOIA
+#include "input/cdio_paranoia_input_plugin.h"
+#endif
+
#include <glib.h>
const struct input_plugin *const input_plugins[] = {
@@ -53,6 +57,9 @@ const struct input_plugin *const input_plugins[] = {
#ifdef ENABLE_MMS
&input_plugin_mms,
#endif
+#ifdef ENABLE_CDIO_PARANOIA
+ &input_plugin_cdio_paranoia,
+#endif
NULL
};
diff --git a/src/ls.c b/src/ls.c
index c30765c62..2a90f9b64 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -49,6 +49,9 @@ static const char *remoteUrlPrefixes[] = {
"rtmpt://",
"rtmps://",
#endif
+#ifdef ENABLE_CDIO_PARANOIA
+ "cdda://",
+#endif
NULL
};
diff --git a/src/output/osx_plugin.c b/src/output/osx_plugin.c
index ce82656bd..b6d42c8f6 100644
--- a/src/output/osx_plugin.c
+++ b/src/output/osx_plugin.c
@@ -28,6 +28,11 @@
#define G_LOG_DOMAIN "osx"
struct osx_output {
+ /* configuration settings */
+ OSType component_subtype;
+ /* only applicable with kAudioUnitSubType_HALOutput */
+ const char *device_name;
+
AudioUnit au;
GMutex *mutex;
GCond *condition;
@@ -54,6 +59,26 @@ osx_output_test_default_device(void)
return true;
}
+static void
+osx_output_configure(struct osx_output *oo, const struct config_param *param)
+{
+ const char *device = config_get_block_string(param, "device", NULL);
+
+ if (device == NULL || 0 == strcmp(device, "default")) {
+ oo->component_subtype = kAudioUnitSubType_DefaultOutput;
+ oo->device_name = NULL;
+ }
+ else if (0 == strcmp(device, "system")) {
+ oo->component_subtype = kAudioUnitSubType_SystemOutput;
+ oo->device_name = NULL;
+ }
+ else {
+ oo->component_subtype = kAudioUnitSubType_HALOutput;
+ /* XXX am I supposed to g_strdup() this? */
+ oo->device_name = device;
+ }
+}
+
static void *
osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
G_GNUC_UNUSED const struct config_param *param,
@@ -61,6 +86,7 @@ osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
{
struct osx_output *oo = g_new(struct osx_output, 1);
+ osx_output_configure(oo, param);
oo->mutex = g_mutex_new();
oo->condition = g_cond_new();
@@ -156,6 +182,95 @@ osx_render(void *vdata,
}
static bool
+osx_output_set_device(struct osx_output *oo, GError **error)
+{
+ bool ret = true;
+ OSStatus status;
+ UInt32 size, numdevices;
+ AudioDeviceID *deviceids = NULL;
+ char name[256];
+ unsigned int i;
+
+ if (oo->component_subtype != kAudioUnitSubType_HALOutput)
+ goto done;
+
+ /* how many audio devices are there? */
+ status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+ &size,
+ NULL);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine number of OS X audio devices: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+
+ /* what are the available audio device IDs? */
+ numdevices = size / sizeof(AudioDeviceID);
+ deviceids = g_malloc(size);
+ status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+ &size,
+ deviceids);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine OS X audio device IDs: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+
+ /* which audio device matches oo->device_name? */
+ for (i = 0; i < numdevices; i++) {
+ size = sizeof(name);
+ status = AudioDeviceGetProperty(deviceids[i], 0, false,
+ kAudioDevicePropertyDeviceName,
+ &size, name);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine OS X device name "
+ "(device %u): %s",
+ (unsigned int) deviceids[i],
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+ if (strcmp(oo->device_name, name) == 0) {
+ g_debug("found matching device: ID=%u, name=%s",
+ (unsigned int) deviceids[i], name);
+ break;
+ }
+ }
+ if (i == numdevices) {
+ g_warning("Found no audio device with name '%s' "
+ "(will use default audio device)",
+ oo->device_name);
+ goto done;
+ }
+
+ status = AudioUnitSetProperty(oo->au,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ 0,
+ &(deviceids[i]),
+ sizeof(AudioDeviceID));
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to set OS X audio output device: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+ g_debug("set OS X audio output device ID=%u, name=%s",
+ (unsigned int) deviceids[i], name);
+
+done:
+ if (deviceids != NULL)
+ g_free(deviceids);
+ return ret;
+}
+
+static bool
osx_output_open(void *data, struct audio_format *audio_format, GError **error)
{
struct osx_output *od = data;
@@ -167,7 +282,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
ComponentResult result;
desc.componentType = kAudioUnitType_Output;
- desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentSubType = od->component_subtype;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
@@ -181,7 +296,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
status = OpenAComponent(comp, &od->au);
if (status != noErr) {
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), status,
"Unable to open OS X component: %s",
GetMacOSStatusCommentString(status));
return false;
@@ -190,12 +305,15 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
status = AudioUnitInitialize(od->au);
if (status != noErr) {
CloseComponent(od->au);
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), status,
"Unable to initialize OS X audio unit: %s",
GetMacOSStatusCommentString(status));
return false;
}
+ if (!osx_output_set_device(od, error))
+ return false;
+
callback.inputProc = osx_render;
callback.inputProcRefCon = od;
@@ -206,7 +324,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
if (result != noErr) {
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), result,
"unable to set callback for OS X audio unit");
return false;
}
@@ -247,7 +365,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
if (result != noErr) {
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), result,
"Unable to set format on OS X device");
return false;
}
@@ -262,7 +380,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
status = AudioOutputUnitStart(od->au);
if (status != 0) {
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), status,
"unable to start audio output: %s",
GetMacOSStatusCommentString(status));
return false;
diff --git a/src/playlist/extm3u_playlist_plugin.c b/src/playlist/extm3u_playlist_plugin.c
index 9a04aa066..91f6eed08 100644
--- a/src/playlist/extm3u_playlist_plugin.c
+++ b/src/playlist/extm3u_playlist_plugin.c
@@ -24,6 +24,7 @@
#include "uri.h"
#include "song.h"
#include "tag.h"
+#include "string_util.h"
#include <glib.h>
@@ -89,7 +90,7 @@ extm3u_parse_tag(const char *line)
/* 0 means unknown duration */
duration = 0;
- name = g_strchug(endptr + 1);
+ name = strchug_fast_c(endptr + 1);
if (*name == 0 && duration == 0)
/* no information available; don't allocate a tag
object */
diff --git a/src/playlist_database.c b/src/playlist_database.c
index 0a8a6f139..97da1c98e 100644
--- a/src/playlist_database.c
+++ b/src/playlist_database.c
@@ -21,6 +21,7 @@
#include "playlist_database.h"
#include "playlist_vector.h"
#include "text_file.h"
+#include "string_util.h"
#include <string.h>
#include <stdlib.h>
@@ -62,7 +63,7 @@ playlist_metadata_load(FILE *fp, struct playlist_vector *pv, const char *name,
}
*colon++ = 0;
- value = g_strchug(colon);
+ value = strchug_fast_c(colon);
if (strcmp(line, "mtime") == 0)
pm.mtime = strtol(value, NULL, 10);
diff --git a/src/playlist_list.c b/src/playlist_list.c
index 019654bfc..02d650c05 100644
--- a/src/playlist_list.c
+++ b/src/playlist_list.c
@@ -31,7 +31,7 @@
#include "playlist/flac_playlist_plugin.h"
#include "input_stream.h"
#include "uri.h"
-#include "utils.h"
+#include "string_util.h"
#include "conf.h"
#include "glib_compat.h"
#include "mpd_error.h"
diff --git a/src/playlist_print.c b/src/playlist_print.c
index 89ab2e5ab..b79dd6fa5 100644
--- a/src/playlist_print.c
+++ b/src/playlist_print.c
@@ -153,7 +153,7 @@ playlist_provider_print(struct client *client, const char *uri,
char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL;
while ((song = playlist_plugin_read(playlist)) != NULL) {
- song = playlist_check_translate_song(song, base_uri);
+ song = playlist_check_translate_song(song, base_uri, false);
if (song == NULL)
continue;
diff --git a/src/playlist_queue.c b/src/playlist_queue.c
index 635e23a28..43621da9f 100644
--- a/src/playlist_queue.c
+++ b/src/playlist_queue.c
@@ -27,14 +27,14 @@
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
- struct playlist *dest)
+ struct playlist *dest, bool secure)
{
enum playlist_result result;
struct song *song;
char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL;
while ((song = playlist_plugin_read(source)) != NULL) {
- song = playlist_check_translate_song(song, base_uri);
+ song = playlist_check_translate_song(song, base_uri, secure);
if (song == NULL)
continue;
@@ -53,7 +53,7 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
}
enum playlist_result
-playlist_open_into_queue(const char *uri, struct playlist *dest)
+playlist_open_into_queue(const char *uri, struct playlist *dest, bool secure)
{
struct input_stream *is;
struct playlist_provider *playlist = playlist_open_any(uri, &is);
@@ -61,7 +61,7 @@ playlist_open_into_queue(const char *uri, struct playlist *dest)
return PLAYLIST_RESULT_NO_SUCH_LIST;
enum playlist_result result =
- playlist_load_into_queue(uri, playlist, dest);
+ playlist_load_into_queue(uri, playlist, dest, secure);
playlist_plugin_close(playlist);
if (is != NULL)
diff --git a/src/playlist_queue.h b/src/playlist_queue.h
index 530d4b4be..9ffa51198 100644
--- a/src/playlist_queue.h
+++ b/src/playlist_queue.h
@@ -26,6 +26,8 @@
#include "playlist.h"
+#include <stdbool.h>
+
struct playlist_provider;
struct playlist;
@@ -38,14 +40,14 @@ struct playlist;
*/
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
- struct playlist *dest);
+ struct playlist *dest, bool secure);
/**
* Opens a playlist with a playlist plugin and append to the specified
* play queue.
*/
enum playlist_result
-playlist_open_into_queue(const char *uri, struct playlist *dest);
+playlist_open_into_queue(const char *uri, struct playlist *dest, bool secure);
#endif
diff --git a/src/playlist_song.c b/src/playlist_song.c
index 1e8e98795..572672bca 100644
--- a/src/playlist_song.c
+++ b/src/playlist_song.c
@@ -84,7 +84,8 @@ apply_song_metadata(struct song *dest, const struct song *src)
}
struct song *
-playlist_check_translate_song(struct song *song, const char *base_uri)
+playlist_check_translate_song(struct song *song, const char *base_uri,
+ bool secure)
{
struct song *dest;
@@ -111,16 +112,17 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
? map_uri_fs(base_uri)
: map_directory_fs(db_get_root());
- if (prefix == NULL || !g_str_has_prefix(uri, prefix) ||
- uri[strlen(prefix)] != '/') {
+ if (prefix != NULL && g_str_has_prefix(uri, prefix) &&
+ uri[strlen(prefix)] == '/')
+ uri += strlen(prefix) + 1;
+ else if (!secure) {
/* local files must be relative to the music
- directory */
+ directory when "secure" is enabled */
g_free(prefix);
song_free(song);
return NULL;
}
- uri += strlen(prefix) + 1;
g_free(prefix);
}
diff --git a/src/playlist_song.h b/src/playlist_song.h
index 5a2e4c2b0..29335e5ac 100644
--- a/src/playlist_song.h
+++ b/src/playlist_song.h
@@ -20,12 +20,18 @@
#ifndef MPD_PLAYLIST_SONG_H
#define MPD_PLAYLIST_SONG_H
+#include <stdbool.h>
+
/**
* Verifies the song, returns NULL if it is unsafe. Translate the
* song to a new song object within the database, if it is a local
* file. The old song object is freed.
+ *
+ * @param secure if true, then local files are only allowed if they
+ * are relative to base_uri
*/
struct song *
-playlist_check_translate_song(struct song *song, const char *base_uri);
+playlist_check_translate_song(struct song *song, const char *base_uri,
+ bool secure);
#endif
diff --git a/src/server_socket.c b/src/server_socket.c
index bb7a6f097..5eab4dc5a 100644
--- a/src/server_socket.c
+++ b/src/server_socket.c
@@ -18,6 +18,11 @@
*/
#include "config.h"
+
+#ifdef HAVE_STRUCT_UCRED
+#define _GNU_SOURCE 1
+#endif
+
#include "server_socket.h"
#include "socket_util.h"
#include "fd_util.h"
diff --git a/src/song_save.c b/src/song_save.c
index a1a573298..d33e99c27 100644
--- a/src/song_save.c
+++ b/src/song_save.c
@@ -24,6 +24,7 @@
#include "directory.h"
#include "tag.h"
#include "text_file.h"
+#include "string_util.h"
#include <glib.h>
@@ -96,7 +97,7 @@ song_load(FILE *fp, struct directory *parent, const char *uri,
}
*colon++ = 0;
- value = g_strchug(colon);
+ value = strchug_fast_c(colon);
if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) {
if (!song->tag) {
diff --git a/src/stored_playlist.c b/src/stored_playlist.c
index cd2818522..c75c00727 100644
--- a/src/stored_playlist.c
+++ b/src/stored_playlist.c
@@ -20,6 +20,7 @@
#include "config.h"
#include "stored_playlist.h"
#include "playlist_save.h"
+#include "text_file.h"
#include "song.h"
#include "mapper.h"
#include "path.h"
@@ -179,7 +180,6 @@ spl_load(const char *utf8path)
{
FILE *file;
GPtrArray *list;
- char buffer[MPD_PATH_MAX];
char *path_fs;
if (!spl_valid_name(utf8path) || map_spl_path() == NULL)
@@ -196,28 +196,20 @@ spl_load(const char *utf8path)
list = g_ptr_array_new();
- while (fgets(buffer, sizeof(buffer), file)) {
- char *s = buffer;
-
- if (*s == PLAYLIST_COMMENT)
+ GString *buffer = g_string_sized_new(1024);
+ char *s;
+ while ((s = read_text_line(file, buffer)) != NULL) {
+ if (*s == 0 || *s == PLAYLIST_COMMENT)
continue;
- g_strchomp(buffer);
-
if (!uri_has_scheme(s)) {
char *path_utf8;
- struct song *song;
path_utf8 = map_fs_to_utf8(s);
if (path_utf8 == NULL)
continue;
- song = db_get_song(path_utf8);
- g_free(path_utf8);
- if (song == NULL)
- continue;
-
- s = song_get_uri(song);
+ s = path_utf8;
} else
s = g_strdup(s);
diff --git a/src/string_util.c b/src/string_util.c
new file mode 100644
index 000000000..3ae759917
--- /dev/null
+++ b/src/string_util.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "string_util.h"
+
+#include <glib.h>
+
+#include <assert.h>
+
+const char *
+strchug_fast_c(const char *p)
+{
+ while (*p != 0 && g_ascii_isspace(*p))
+ ++p;
+
+ return p;
+}
+
+bool
+string_array_contains(const char *const* haystack, const char *needle)
+{
+ assert(haystack != NULL);
+ assert(needle != NULL);
+
+ for (; *haystack != NULL; ++haystack)
+ if (g_ascii_strcasecmp(*haystack, needle) == 0)
+ return true;
+
+ return false;
+}
diff --git a/src/string_util.h b/src/string_util.h
new file mode 100644
index 000000000..372354722
--- /dev/null
+++ b/src/string_util.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_STRING_UTIL_H
+#define MPD_STRING_UTIL_H
+
+#include <glib.h>
+
+#include <stdbool.h>
+
+/**
+ * Remove the "const" attribute from a string pointer. This is a
+ * dirty hack, don't use it unless you know what you're doing!
+ */
+G_GNUC_CONST
+static inline char *
+deconst_string(const char *p)
+{
+ union {
+ const char *in;
+ char *out;
+ } u = {
+ .in = p,
+ };
+
+ return u.out;
+}
+
+/**
+ * Returns a pointer to the first non-whitespace character in the
+ * string, or to the end of the string.
+ *
+ * This is a faster version of g_strchug(), because it does not move
+ * data.
+ */
+G_GNUC_PURE
+const char *
+strchug_fast_c(const char *p);
+
+/**
+ * Same as strchug_fast_c(), but works with a writable pointer.
+ */
+G_GNUC_PURE
+static inline char *
+strchug_fast(char *p)
+{
+ return deconst_string(strchug_fast_c(p));
+}
+
+/**
+ * Checks whether a string array contains the specified string.
+ *
+ * @param haystack a NULL terminated list of strings
+ * @param needle the string to search for; the comparison is
+ * case-insensitive for ASCII characters
+ * @return true if found
+ */
+bool
+string_array_contains(const char *const* haystack, const char *needle);
+
+#endif
diff --git a/src/tokenizer.c b/src/tokenizer.c
index 2b9e05070..358d804dc 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -19,6 +19,7 @@
#include "config.h"
#include "tokenizer.h"
+#include "string_util.h"
#include <stdbool.h>
#include <assert.h>
@@ -72,7 +73,7 @@ tokenizer_next_word(char **input_p, GError **error_r)
/* a whitespace: the word ends here */
*input = 0;
/* skip all following spaces, too */
- input = g_strchug(input + 1);
+ input = strchug_fast(input + 1);
break;
}
@@ -126,7 +127,7 @@ tokenizer_next_unquoted(char **input_p, GError **error_r)
/* a whitespace: the word ends here */
*input = 0;
/* skip all following spaces, too */
- input = g_strchug(input + 1);
+ input = strchug_fast(input + 1);
break;
}
@@ -205,7 +206,7 @@ tokenizer_next_string(char **input_p, GError **error_r)
/* finish the string and return it */
*dest = 0;
- *input_p = g_strchug(input);
+ *input_p = strchug_fast(input);
return word;
}
diff --git a/src/utils.c b/src/utils.c
index 53494cc5d..39f4fe4d1 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -101,16 +101,3 @@ char *parsePath(char *path)
}
#endif
}
-
-bool
-string_array_contains(const char *const* haystack, const char *needle)
-{
- assert(haystack != NULL);
- assert(needle != NULL);
-
- for (; *haystack != NULL; ++haystack)
- if (g_ascii_strcasecmp(*haystack, needle) == 0)
- return true;
-
- return false;
-}
diff --git a/src/utils.h b/src/utils.h
index 629056637..3941643dc 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -33,15 +33,4 @@
char *parsePath(char *path);
-/**
- * Checks whether a string array contains the specified string.
- *
- * @param haystack a NULL terminated list of strings
- * @param needle the string to search for; the comparison is
- * case-insensitive for ASCII characters
- * @return true if found
- */
-bool
-string_array_contains(const char *const* haystack, const char *needle);
-
#endif