aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am13
-rw-r--r--configure.ac14
-rw-r--r--src/Expat.cxx76
-rw-r--r--src/Expat.hxx63
-rw-r--r--src/PlaylistRegistry.cxx4
-rw-r--r--src/playlist/AsxPlaylistPlugin.cxx114
-rw-r--r--src/playlist/RssPlaylistPlugin.cxx110
-rw-r--r--src/playlist/XspfPlaylistPlugin.cxx92
8 files changed, 225 insertions, 261 deletions
diff --git a/Makefile.am b/Makefile.am
index a63c9d1d7..7db87fd98 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1016,11 +1016,13 @@ libplaylist_plugins_a_SOURCES = \
src/playlist/EmbeddedCuePlaylistPlugin.hxx \
src/PlaylistRegistry.cxx src/PlaylistRegistry.hxx
libplaylist_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
+ $(EXPAT_CFLAGS) \
$(YAJL_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS))
PLAYLIST_LIBS = \
libplaylist_plugins.a \
+ $(EXPAT_LIBS) \
$(FLAC_LIBS)
if ENABLE_DESPOTIFY
@@ -1036,10 +1038,9 @@ libplaylist_plugins_a_SOURCES += \
PLAYLIST_LIBS += $(YAJL_LIBS)
endif
-if HAVE_GLIB
+if HAVE_EXPAT
libplaylist_plugins_a_SOURCES += \
- src/playlist/PlsPlaylistPlugin.cxx \
- src/playlist/PlsPlaylistPlugin.hxx \
+ src/Expat.cxx src/Expat.hxx \
src/playlist/XspfPlaylistPlugin.cxx \
src/playlist/XspfPlaylistPlugin.hxx \
src/playlist/AsxPlaylistPlugin.cxx \
@@ -1048,6 +1049,12 @@ libplaylist_plugins_a_SOURCES += \
src/playlist/RssPlaylistPlugin.hxx
endif
+if HAVE_GLIB
+libplaylist_plugins_a_SOURCES += \
+ src/playlist/PlsPlaylistPlugin.cxx \
+ src/playlist/PlsPlaylistPlugin.hxx
+endif
+
#
# Filter plugins
#
diff --git a/configure.ac b/configure.ac
index 58cd5af06..0af602fe4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -229,6 +229,11 @@ AC_ARG_ENABLE(libmpdclient,
[enable support for the MPD client]),,
enable_libmpdclient=auto)
+AC_ARG_ENABLE(expat,
+ AS_HELP_STRING([--enable-expat],
+ [enable the expat XML parser]),,
+ enable_expat=auto)
+
AC_ARG_ENABLE(adplug,
AS_HELP_STRING([--enable-adplug],
[enable the AdPlug decoder plugin (default: auto)]),,
@@ -639,6 +644,15 @@ fi
AM_CONDITIONAL(HAVE_LIBMPDCLIENT, test x$enable_libmpdclient = xyes)
+dnl -------------------------------- expat --------------------------------
+MPD_AUTO_PKG(expat, EXPAT, [expat],
+ [expat XML parser], [expat not found])
+if test x$enable_expat = xyes; then
+ AC_DEFINE(HAVE_EXPAT, 1, [Define to use the expat XML parser])
+fi
+
+AM_CONDITIONAL(HAVE_EXPAT, test x$enable_expat = xyes)
+
dnl --------------------------------- inotify ---------------------------------
AC_CHECK_FUNCS(inotify_init inotify_init1)
diff --git a/src/Expat.cxx b/src/Expat.cxx
new file mode 100644
index 000000000..5d5a8be24
--- /dev/null
+++ b/src/Expat.cxx
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2003-2014 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 "Expat.hxx"
+#include "InputStream.hxx"
+#include "util/ASCII.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+
+#include <string.h>
+
+static constexpr Domain expat_domain("expat");
+
+void
+ExpatParser::SetError(Error &error)
+{
+ XML_Error code = XML_GetErrorCode(parser);
+ error.Format(expat_domain, int(code), "XML parser failed: %s",
+ XML_ErrorString(code));
+}
+
+bool
+ExpatParser::Parse(InputStream &is, Error &error)
+{
+ assert(is.ready);
+
+ while (true) {
+ char buffer[4096];
+ size_t nbytes = is.LockRead(buffer, sizeof(buffer), error);
+ if (nbytes == 0)
+ break;
+
+ if (XML_Parse(parser, buffer, nbytes, false) != XML_STATUS_OK) {
+ SetError(error);
+ return false;
+ }
+ }
+
+ if (error.IsDefined())
+ return false;
+
+ if (XML_Parse(parser, "", 0, true) != XML_STATUS_OK) {
+ SetError(error);
+ return false;
+ }
+
+ return true;
+}
+
+const char *
+ExpatParser::GetAttributeCase(const XML_Char **atts,
+ const char *name)
+{
+ for (unsigned i = 0; atts[i] != nullptr; ++i)
+ if (StringEqualsCaseASCII(atts[i], name))
+ return atts[i] + strlen(name) + 1;
+
+ return nullptr;
+}
diff --git a/src/Expat.hxx b/src/Expat.hxx
new file mode 100644
index 000000000..e0de30e7e
--- /dev/null
+++ b/src/Expat.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003-2014 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_EXPAT_HXX
+#define MPD_EXPAT_HXX
+
+#include "check.h"
+#include "Compiler.h"
+
+#include <expat.h>
+
+struct InputStream;
+class Error;
+
+class ExpatParser final {
+ const XML_Parser parser;
+
+public:
+ ExpatParser(void *userData)
+ :parser(XML_ParserCreate(nullptr)) {
+ XML_SetUserData(parser, userData);
+ }
+
+ ~ExpatParser() {
+ XML_ParserFree(parser);
+ }
+
+ void SetElementHandler(XML_StartElementHandler start,
+ XML_EndElementHandler end) {
+ XML_SetElementHandler(parser, start, end);
+ }
+
+ void SetCharacterDataHandler(XML_CharacterDataHandler charhndl) {
+ XML_SetCharacterDataHandler(parser, charhndl);
+ }
+
+ bool Parse(InputStream &is, Error &error);
+
+ gcc_pure
+ static const char *GetAttributeCase(const XML_Char **atts,
+ const char *name);
+
+private:
+ void SetError(Error &error);
+};
+
+#endif
diff --git a/src/PlaylistRegistry.cxx b/src/PlaylistRegistry.cxx
index 17cad3db5..ddfd39613 100644
--- a/src/PlaylistRegistry.cxx
+++ b/src/PlaylistRegistry.cxx
@@ -46,10 +46,12 @@
const struct playlist_plugin *const playlist_plugins[] = {
&extm3u_playlist_plugin,
&m3u_playlist_plugin,
- &xspf_playlist_plugin,
&pls_playlist_plugin,
+#ifdef HAVE_EXPAT
+ &xspf_playlist_plugin,
&asx_playlist_plugin,
&rss_playlist_plugin,
+#endif
#ifdef ENABLE_DESPOTIFY
&despotify_playlist_plugin,
#endif
diff --git a/src/playlist/AsxPlaylistPlugin.cxx b/src/playlist/AsxPlaylistPlugin.cxx
index c06200283..4e48dbf8d 100644
--- a/src/playlist/AsxPlaylistPlugin.cxx
+++ b/src/playlist/AsxPlaylistPlugin.cxx
@@ -21,20 +21,13 @@
#include "AsxPlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx"
#include "MemorySongEnumerator.hxx"
-#include "InputStream.hxx"
#include "Song.hxx"
#include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx"
#include "util/Error.hxx"
-#include "util/Domain.hxx"
+#include "Expat.hxx"
#include "Log.hxx"
-#include <glib.h>
-
-#include <string.h>
-
-static constexpr Domain asx_domain("asx");
-
/**
* This is the state object for the GLib XML parser.
*/
@@ -71,23 +64,9 @@ struct AsxParser {
};
-static const gchar *
-get_attribute(const gchar **attribute_names, const gchar **attribute_values,
- const gchar *name)
-{
- for (unsigned i = 0; attribute_names[i] != nullptr; ++i)
- if (StringEqualsCaseASCII(attribute_names[i], name))
- return attribute_values[i];
-
- return nullptr;
-}
-
-static void
-asx_start_element(gcc_unused GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+asx_start_element(void *user_data, const XML_Char *element_name,
+ const XML_Char **atts)
{
AsxParser *parser = (AsxParser *)user_data;
@@ -103,9 +82,8 @@ asx_start_element(gcc_unused GMarkupParseContext *context,
case AsxParser::ENTRY:
if (StringEqualsCaseASCII(element_name, "ref")) {
- const gchar *href = get_attribute(attribute_names,
- attribute_values,
- "href");
+ const char *href =
+ ExpatParser::GetAttributeCase(atts, "href");
if (href != nullptr)
parser->location = href;
} else if (StringEqualsCaseASCII(element_name, "author"))
@@ -119,10 +97,8 @@ asx_start_element(gcc_unused GMarkupParseContext *context,
}
}
-static void
-asx_end_element(gcc_unused GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+asx_end_element(void *user_data, const XML_Char *element_name)
{
AsxParser *parser = (AsxParser *)user_data;
@@ -144,10 +120,8 @@ asx_end_element(gcc_unused GMarkupParseContext *context,
}
}
-static void
-asx_text(gcc_unused GMarkupParseContext *context,
- const gchar *text, gsize text_len,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+asx_char_data(void *user_data, const XML_Char *s, int len)
{
AsxParser *parser = (AsxParser *)user_data;
@@ -156,23 +130,13 @@ asx_text(gcc_unused GMarkupParseContext *context,
break;
case AsxParser::ENTRY:
- if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) {
- parser->tag_builder.AddItem(parser->tag_type,
- text, text_len);
- }
+ if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
+ parser->tag_builder.AddItem(parser->tag_type, s, len);
break;
}
}
-static const GMarkupParser asx_parser = {
- asx_start_element,
- asx_end_element,
- asx_text,
- nullptr,
- nullptr,
-};
-
/*
* The playlist object
*
@@ -182,57 +146,21 @@ static SongEnumerator *
asx_open_stream(InputStream &is)
{
AsxParser parser;
- bool success;
- Error error2;
- GError *error = nullptr;
-
- /* parse the ASX XML file */
-
- GMarkupParseContext *context =
- g_markup_parse_context_new(&asx_parser,
- G_MARKUP_TREAT_CDATA_AS_TEXT,
- &parser, nullptr);
-
- while (true) {
- char buffer[1024];
- size_t nbytes = is.LockRead(buffer, sizeof(buffer), error2);
- if (nbytes == 0) {
- if (error2.IsDefined()) {
- g_markup_parse_context_free(context);
- LogError(error2);
- return nullptr;
- }
-
- break;
- }
- success = g_markup_parse_context_parse(context, buffer, nbytes,
- &error);
- if (!success) {
- FormatErrno(asx_domain,
- "XML parser failed: %s", error->message);
- g_error_free(error);
- g_markup_parse_context_free(context);
+ {
+ ExpatParser expat(&parser);
+ expat.SetElementHandler(asx_start_element, asx_end_element);
+ expat.SetCharacterDataHandler(asx_char_data);
+
+ Error error;
+ if (!expat.Parse(is, error)) {
+ LogError(error);
return nullptr;
}
}
- success = g_markup_parse_context_end_parse(context, &error);
- if (!success) {
- FormatErrno(asx_domain,
- "XML parser failed: %s", error->message);
- g_error_free(error);
- g_markup_parse_context_free(context);
- return nullptr;
- }
-
parser.songs.reverse();
- MemorySongEnumerator *playlist =
- new MemorySongEnumerator(std::move(parser.songs));
-
- g_markup_parse_context_free(context);
-
- return playlist;
+ return new MemorySongEnumerator(std::move(parser.songs));
}
static const char *const asx_suffixes[] = {
diff --git a/src/playlist/RssPlaylistPlugin.cxx b/src/playlist/RssPlaylistPlugin.cxx
index 558c74619..604f99baf 100644
--- a/src/playlist/RssPlaylistPlugin.cxx
+++ b/src/playlist/RssPlaylistPlugin.cxx
@@ -21,20 +21,13 @@
#include "RssPlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx"
#include "MemorySongEnumerator.hxx"
-#include "InputStream.hxx"
#include "Song.hxx"
#include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx"
#include "util/Error.hxx"
-#include "util/Domain.hxx"
+#include "Expat.hxx"
#include "Log.hxx"
-#include <glib.h>
-
-#include <string.h>
-
-static constexpr Domain rss_domain("rss");
-
/**
* This is the state object for the GLib XML parser.
*/
@@ -71,23 +64,9 @@ struct RssParser {
:state(ROOT) {}
};
-static const gchar *
-get_attribute(const gchar **attribute_names, const gchar **attribute_values,
- const gchar *name)
-{
- for (unsigned i = 0; attribute_names[i] != nullptr; ++i)
- if (StringEqualsCaseASCII(attribute_names[i], name))
- return attribute_values[i];
-
- return nullptr;
-}
-
-static void
-rss_start_element(gcc_unused GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+rss_start_element(void *user_data, const XML_Char *element_name,
+ const XML_Char **atts)
{
RssParser *parser = (RssParser *)user_data;
@@ -103,9 +82,8 @@ rss_start_element(gcc_unused GMarkupParseContext *context,
case RssParser::ITEM:
if (StringEqualsCaseASCII(element_name, "enclosure")) {
- const gchar *href = get_attribute(attribute_names,
- attribute_values,
- "url");
+ const char *href =
+ ExpatParser::GetAttributeCase(atts, "url");
if (href != nullptr)
parser->location = href;
} else if (StringEqualsCaseASCII(element_name, "title"))
@@ -117,10 +95,8 @@ rss_start_element(gcc_unused GMarkupParseContext *context,
}
}
-static void
-rss_end_element(gcc_unused GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+rss_end_element(void *user_data, const XML_Char *element_name)
{
RssParser *parser = (RssParser *)user_data;
@@ -142,10 +118,8 @@ rss_end_element(gcc_unused GMarkupParseContext *context,
}
}
-static void
-rss_text(gcc_unused GMarkupParseContext *context,
- const gchar *text, gsize text_len,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+rss_char_data(void *user_data, const XML_Char *s, int len)
{
RssParser *parser = (RssParser *)user_data;
@@ -155,21 +129,12 @@ rss_text(gcc_unused GMarkupParseContext *context,
case RssParser::ITEM:
if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
- parser->tag_builder.AddItem(parser->tag_type,
- text, text_len);
+ parser->tag_builder.AddItem(parser->tag_type, s, len);
break;
}
}
-static const GMarkupParser rss_parser = {
- rss_start_element,
- rss_end_element,
- rss_text,
- nullptr,
- nullptr,
-};
-
/*
* The playlist object
*
@@ -179,58 +144,21 @@ static SongEnumerator *
rss_open_stream(InputStream &is)
{
RssParser parser;
- GMarkupParseContext *context;
- char buffer[1024];
- size_t nbytes;
- bool success;
- Error error2;
- GError *error = nullptr;
-
- /* parse the RSS XML file */
-
- context = g_markup_parse_context_new(&rss_parser,
- G_MARKUP_TREAT_CDATA_AS_TEXT,
- &parser, nullptr);
- while (true) {
- nbytes = is.LockRead(buffer, sizeof(buffer), error2);
- if (nbytes == 0) {
- if (error2.IsDefined()) {
- g_markup_parse_context_free(context);
- LogError(error2);
- return nullptr;
- }
-
- break;
- }
+ {
+ ExpatParser expat(&parser);
+ expat.SetElementHandler(rss_start_element, rss_end_element);
+ expat.SetCharacterDataHandler(rss_char_data);
- success = g_markup_parse_context_parse(context, buffer, nbytes,
- &error);
- if (!success) {
- FormatError(rss_domain,
- "XML parser failed: %s", error->message);
- g_error_free(error);
- g_markup_parse_context_free(context);
+ Error error;
+ if (!expat.Parse(is, error)) {
+ LogError(error);
return nullptr;
}
}
- success = g_markup_parse_context_end_parse(context, &error);
- if (!success) {
- FormatError(rss_domain,
- "XML parser failed: %s", error->message);
- g_error_free(error);
- g_markup_parse_context_free(context);
- return nullptr;
- }
-
parser.songs.reverse();
- MemorySongEnumerator *playlist =
- new MemorySongEnumerator(std::move(parser.songs));
-
- g_markup_parse_context_free(context);
-
- return playlist;
+ return new MemorySongEnumerator(std::move(parser.songs));
}
static const char *const rss_suffixes[] = {
diff --git a/src/playlist/XspfPlaylistPlugin.cxx b/src/playlist/XspfPlaylistPlugin.cxx
index 7c20df57d..2157dd678 100644
--- a/src/playlist/XspfPlaylistPlugin.cxx
+++ b/src/playlist/XspfPlaylistPlugin.cxx
@@ -26,10 +26,9 @@
#include "tag/TagBuilder.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
+#include "Expat.hxx"
#include "Log.hxx"
-#include <glib.h>
-
#include <string.h>
static constexpr Domain xspf_domain("xspf");
@@ -70,12 +69,9 @@ struct XspfParser {
:state(ROOT) {}
};
-static void
-xspf_start_element(gcc_unused GMarkupParseContext *context,
- const gchar *element_name,
- gcc_unused const gchar **attribute_names,
- gcc_unused const gchar **attribute_values,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+xspf_start_element(void *user_data, const XML_Char *element_name,
+ gcc_unused const XML_Char **atts)
{
XspfParser *parser = (XspfParser *)user_data;
@@ -124,10 +120,8 @@ xspf_start_element(gcc_unused GMarkupParseContext *context,
}
}
-static void
-xspf_end_element(gcc_unused GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+xspf_end_element(void *user_data, const XML_Char *element_name)
{
XspfParser *parser = (XspfParser *)user_data;
@@ -165,10 +159,8 @@ xspf_end_element(gcc_unused GMarkupParseContext *context,
}
}
-static void
-xspf_text(gcc_unused GMarkupParseContext *context,
- const gchar *text, gsize text_len,
- gpointer user_data, gcc_unused GError **error)
+static void XMLCALL
+xspf_char_data(void *user_data, const XML_Char *s, int len)
{
XspfParser *parser = (XspfParser *)user_data;
@@ -181,26 +173,17 @@ xspf_text(gcc_unused GMarkupParseContext *context,
case XspfParser::TRACK:
if (!parser->location.empty() &&
parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
- parser->tag_builder.AddItem(parser->tag_type,
- text, text_len);
+ parser->tag_builder.AddItem(parser->tag_type, s, len);
break;
case XspfParser::LOCATION:
- parser->location.assign(text, text_len);
+ parser->location.assign(s, len);
break;
}
}
-static const GMarkupParser xspf_parser = {
- xspf_start_element,
- xspf_end_element,
- xspf_text,
- nullptr,
- nullptr,
-};
-
/*
* The playlist object
*
@@ -210,58 +193,21 @@ static SongEnumerator *
xspf_open_stream(InputStream &is)
{
XspfParser parser;
- GMarkupParseContext *context;
- char buffer[1024];
- size_t nbytes;
- bool success;
- Error error2;
- GError *error = nullptr;
-
- /* parse the XSPF XML file */
-
- context = g_markup_parse_context_new(&xspf_parser,
- G_MARKUP_TREAT_CDATA_AS_TEXT,
- &parser, nullptr);
-
- while (true) {
- nbytes = is.LockRead(buffer, sizeof(buffer), error2);
- if (nbytes == 0) {
- if (error2.IsDefined()) {
- g_markup_parse_context_free(context);
- LogError(error2);
- return nullptr;
- }
-
- break;
- }
- success = g_markup_parse_context_parse(context, buffer, nbytes,
- &error);
- if (!success) {
- FormatError(xspf_domain,
- "XML parser failed: %s", error->message);
- g_error_free(error);
- g_markup_parse_context_free(context);
+ {
+ ExpatParser expat(&parser);
+ expat.SetElementHandler(xspf_start_element, xspf_end_element);
+ expat.SetCharacterDataHandler(xspf_char_data);
+
+ Error error;
+ if (!expat.Parse(is, error)) {
+ LogError(error);
return nullptr;
}
}
- success = g_markup_parse_context_end_parse(context, &error);
- if (!success) {
- FormatError(xspf_domain,
- "XML parser failed: %s", error->message);
- g_error_free(error);
- g_markup_parse_context_free(context);
- return nullptr;
- }
-
parser.songs.reverse();
- MemorySongEnumerator *playlist =
- new MemorySongEnumerator(std::move(parser.songs));
-
- g_markup_parse_context_free(context);
-
- return playlist;
+ return new MemorySongEnumerator(std::move(parser.songs));
}
static const char *const xspf_suffixes[] = {