diff options
36 files changed, 921 insertions, 86 deletions
@@ -141,6 +141,9 @@ For the sticker database. libcue - http://libcue.sourceforge.net/ For CUE sheet support. +libcdio - http://www.gnu.org/software/libcdio/ +For playing audio CDs. + pkg-config ---------- diff --git a/Makefile.am b/Makefile.am index cf79d0903..873cc0925 100644 --- a/Makefile.am +++ b/Makefile.am @@ -99,6 +99,7 @@ mpd_headers = \ src/decoder/flac_pcm.h \ src/decoder/_flac_common.h \ src/decoder/_ogg_common.h \ + src/decoder/pcm_decoder_plugin.h \ src/input_init.h \ src/input_plugin.h \ src/input_registry.h \ @@ -108,6 +109,7 @@ mpd_headers = \ src/input/curl_input_plugin.h \ src/input/rewind_input_plugin.h \ src/input/mms_input_plugin.h \ + src/input/cdio_paranoia_input_plugin.h \ src/text_file.h \ src/text_input_stream.h \ src/icy_server.h \ @@ -214,6 +216,7 @@ mpd_headers = \ src/strset.h \ src/uri.h \ src/utils.h \ + src/string_util.h \ src/volume.h \ src/zeroconf.h src/zeroconf-internal.h \ src/locate.h \ @@ -347,6 +350,7 @@ src_mpd_SOURCES = \ src/strset.c \ src/uri.c \ src/utils.c \ + src/string_util.c \ src/volume.c \ src/locate.c \ src/stored_playlist.c \ @@ -461,6 +465,7 @@ DECODER_LIBS = \ $(CUE_LIBS) DECODER_SRC = \ + src/decoder/pcm_decoder_plugin.c \ src/decoder_buffer.c \ src/decoder_plugin.c \ src/decoder_list.c @@ -616,11 +621,13 @@ endif INPUT_CFLAGS = \ $(CURL_CFLAGS) \ + $(CDIO_PARANOIA_CFLAGS) \ $(FFMPEG_CFLAGS) \ $(MMS_CFLAGS) INPUT_LIBS = \ $(CURL_LIBS) \ + $(CDIO_PARANOIA_LIBS) \ $(FFMPEG_LIBS) \ $(MMS_LIBS) @@ -636,6 +643,10 @@ INPUT_SRC += src/input/curl_input_plugin.c \ src/icy_metadata.c endif +if ENABLE_CDIO_PARANOIA +INPUT_SRC += src/input/cdio_paranoia_input_plugin.c +endif + if HAVE_FFMPEG INPUT_SRC += src/input/ffmpeg_input_plugin.c endif @@ -849,7 +860,7 @@ test_read_conf_CPPFLAGS = $(AM_CPPFLAGS) \ test_read_conf_LDADD = $(MPD_LIBS) \ $(GLIB_LIBS) test_read_conf_SOURCES = test/read_conf.c \ - src/conf.c src/tokenizer.c src/utils.c + src/conf.c src/tokenizer.c src/utils.c src/string_util.c test_run_input_CPPFLAGS = $(AM_CPPFLAGS) \ $(ARCHIVE_CFLAGS) \ @@ -859,7 +870,7 @@ test_run_input_LDADD = $(MPD_LIBS) \ $(INPUT_LIBS) \ $(GLIB_LIBS) test_run_input_SOURCES = test/run_input.c \ - src/conf.c src/tokenizer.c src/utils.c \ + src/conf.c src/tokenizer.c src/utils.c src/string_util.c\ src/tag.c src/tag_pool.c src/tag_save.c \ src/fd_util.c \ $(ARCHIVE_SRC) \ @@ -877,7 +888,7 @@ test_dump_playlist_LDADD = $(MPD_LIBS) \ $(INPUT_LIBS) \ $(GLIB_LIBS) test_dump_playlist_SOURCES = test/dump_playlist.c \ - src/conf.c src/tokenizer.c src/utils.c \ + src/conf.c src/tokenizer.c src/utils.c src/string_util.c\ src/uri.c \ src/song.c src/tag.c src/tag_pool.c src/tag_save.c \ src/text_input_stream.c src/fifo_buffer.c \ @@ -906,7 +917,7 @@ test_run_decoder_LDADD = $(MPD_LIBS) \ $(INPUT_LIBS) $(DECODER_LIBS) \ $(GLIB_LIBS) test_run_decoder_SOURCES = test/run_decoder.c \ - src/conf.c src/tokenizer.c src/utils.c src/log.c \ + src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \ src/tag.c src/tag_pool.c \ src/replay_gain_info.c \ src/uri.c \ @@ -929,7 +940,7 @@ test_read_tags_LDADD = $(MPD_LIBS) \ $(INPUT_LIBS) $(DECODER_LIBS) \ $(GLIB_LIBS) test_read_tags_SOURCES = test/read_tags.c \ - src/conf.c src/tokenizer.c src/utils.c src/log.c \ + src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \ src/tag.c src/tag_pool.c \ src/replay_gain_info.c \ src/uri.c \ @@ -948,7 +959,7 @@ test_run_filter_LDADD = $(MPD_LIBS) \ test_run_filter_SOURCES = test/run_filter.c \ src/filter_plugin.c \ src/filter_registry.c \ - src/conf.c src/tokenizer.c src/utils.c \ + src/conf.c src/tokenizer.c src/utils.c src/string_util.c \ src/pcm_volume.c src/pcm_convert.c src/pcm_byteswap.c \ src/pcm_format.c src/pcm_channels.c src/pcm_dither.c \ src/pcm_pack.c \ @@ -969,7 +980,7 @@ if ENABLE_ENCODER noinst_PROGRAMS += test/run_encoder test_run_encoder_SOURCES = test/run_encoder.c \ src/conf.c src/tokenizer.c \ - src/utils.c \ + src/utils.c src/string_util.c \ src/tag.c src/tag_pool.c \ src/audio_check.c \ src/audio_format.c \ @@ -1025,7 +1036,7 @@ test_run_output_LDADD = $(MPD_LIBS) \ $(OUTPUT_LIBS) \ $(GLIB_LIBS) test_run_output_SOURCES = test/run_output.c \ - src/conf.c src/tokenizer.c src/utils.c src/log.c \ + src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \ src/audio_check.c \ src/audio_format.c \ src/audio_parser.c \ @@ -1061,7 +1072,7 @@ test_read_mixer_LDADD = $(MPD_LIBS) \ $(OUTPUT_LIBS) \ $(GLIB_LIBS) test_read_mixer_SOURCES = test/read_mixer.c \ - src/conf.c src/tokenizer.c src/utils.c src/log.c \ + src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \ src/mixer_control.c src/mixer_api.c \ src/filter_plugin.c \ src/filter/volume_filter_plugin.c \ @@ -1126,8 +1137,7 @@ endif doc/api/html/index.html: doc/doxygen.conf @mkdir -p $(@D) - [ "$(srcdir)" = "." ] || sed '/INPUT *=/ s/\([^ ]\+\/\)/$(subst /,\/,$(srcdir))\/\1/g' $(srcdir)/doc/doxygen.conf >doc/doxygen.conf - $(DOXYGEN) doc/doxygen.conf + $(DOXYGEN) $< all-local: $(DOCBOOK_HTML) doc/api/html/index.html @@ -1,3 +1,10 @@ +ver 0.17 (2010/??/??) +* input: + - cdio_paranoia: new input plugin to play audio CDs +* output: + - osx: allow user to specify other audio devices + + ver 0.16.1 (2010/01/09) * audio_check: fix parameter in prototype * add void casts to suppress "result unused" warnings (clang) diff --git a/configure.ac b/configure.ac index c8a568ab4..af5356f26 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(mpd, 0.16.1, musicpd-dev-team@lists.sourceforge.net) +AC_INIT(mpd, 0.17~git, musicpd-dev-team@lists.sourceforge.net) AC_CONFIG_SRCDIR([src/main.c]) AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects]) AM_CONFIG_HEADER(config.h) @@ -136,6 +136,11 @@ AC_ARG_ENABLE(bzip2, [enable bzip2 archive support (default: disabled)]),, enable_bzip2=no) +AC_ARG_ENABLE(cdio-paranoia, + AS_HELP_STRING([--enable-cdio-paranoia], + [enable support for audio CD support]),, + enable_cdio_paranoia=auto) + AC_ARG_ENABLE(cue, AS_HELP_STRING([--enable-cue], [enable support for libcue support]),, @@ -634,6 +639,16 @@ if test x$enable_lastfm = xyes; then fi AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes) +dnl ---------------------------------- libcue --------------------------------- +MPD_AUTO_PKG(cdio_paranoia, CDIO_PARANOIA, [libcdio_paranoia], + [libcdio_paranoia audio CD library], [libcdio_paranoia not found]) +if test x$enable_cdio_paranoia = xyes; then + AC_DEFINE([ENABLE_CDIO_PARANOIA], 1, + [Define to enable libcdio_paranoia support]) +fi + +AM_CONDITIONAL(ENABLE_CDIO_PARANOIA, test x$enable_cdio_paranoia = xyes) + dnl ---------------------------------- libogg --------------------------------- if test x$with_tremor == xno || test -z $with_tremor; then PKG_CHECK_MODULES(OGG, [ogg], enable_ogg=yes, enable_ogg=no) @@ -1311,7 +1326,7 @@ enable_osx=no case "$host_os" in darwin*) AC_DEFINE(HAVE_OSX, 1, [Define for compiling OS X support]) - MPD_LIBS="$MPD_LIBS -framework AudioUnit -framework CoreServices" + MPD_LIBS="$MPD_LIBS -framework AudioUnit -framework CoreAudio -framework CoreServices" enable_osx=yes ;; esac @@ -1571,6 +1586,7 @@ printf '\nStreaming support:\n\t' results(curl,[CURL]) results(lastfm,[Last.FM]) results(mms,[MMS]) +results(cdio_paranoia, [CDIO_PARANOIA]) printf '\n\n##########################################\n\n' @@ -1580,5 +1596,6 @@ dnl --------------------------------------------------------------------------- dnl Generate files dnl --------------------------------------------------------------------------- AC_OUTPUT(Makefile) +AC_OUTPUT(doc/doxygen.conf) echo 'MPD is ready for compilation, type "make" to begin.' diff --git a/doc/doxygen.conf b/doc/doxygen.conf.in index ddece77e8..8b123737c 100644 --- a/doc/doxygen.conf +++ b/doc/doxygen.conf.in @@ -31,7 +31,7 @@ PROJECT_NAME = MPD # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = @VERSION@ # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. @@ -534,7 +534,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = src/ +INPUT = @top_srcdir@/src/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/doc/protocol.xml b/doc/protocol.xml index 3eb5aa932..a02a94e32 100644 --- a/doc/protocol.xml +++ b/doc/protocol.xml @@ -198,6 +198,12 @@ <option>crossfade</option>, replay gain </para> </listitem> + <listitem> + <para> + <returnvalue>sticker</returnvalue>: the sticker database + has been modified. + </para> + </listitem> </itemizedlist> <para> While a client is waiting for <command>idle</command> diff --git a/doc/user.xml b/doc/user.xml index 17bbdf91f..4dc04ff2e 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -621,6 +621,17 @@ cd mpd-version</programlisting> Plays streams with the MMS protocol. </para> </section> + + <section> + <title><varname>cdio_paranoia</varname></title> + + <para> + Plays audio CDs. The URI has the form: + "<filename>cdda://[DEVICE][/TRACK]</filename>". The + simplest form <filename>cdda://</filename> plays the whole + disc in the default drive. + </para> + </section> </section> <section> 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..bca6b4354 --- /dev/null +++ b/src/input/cdio_paranoia_input_plugin.c @@ -0,0 +1,388 @@ +/* + * 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 = { + .name = "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_init.c b/src/input_init.c index 1438c3e52..11f020dad 100644 --- a/src/input_init.c +++ b/src/input_init.c @@ -24,6 +24,7 @@ #include "conf.h" #include "glib_compat.h" +#include <assert.h> #include <string.h> static inline GQuark @@ -67,6 +68,11 @@ input_stream_global_init(GError **error_r) for (unsigned i = 0; input_plugins[i] != NULL; ++i) { const struct input_plugin *plugin = input_plugins[i]; + + assert(plugin->name != NULL); + assert(*plugin->name != 0); + assert(plugin->open != NULL); + const struct config_param *param = input_plugin_config(plugin->name, &error); if (param == NULL && error != NULL) { 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 }; @@ -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 |