diff options
author | Max Kellermann <max@duempel.org> | 2014-12-04 22:58:30 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2014-12-04 23:10:50 +0100 |
commit | aa4c7055f8e0746bd725ffc0cb140373fabcc405 (patch) | |
tree | 4d5add82c5dc726d86466cde26e28228b50b8c62 | |
parent | 4b70f9d213e8cb71bc225a73494625e31dfaa721 (diff) | |
download | mpd-aa4c7055f8e0746bd725ffc0cb140373fabcc405.tar.gz mpd-aa4c7055f8e0746bd725ffc0cb140373fabcc405.tar.xz mpd-aa4c7055f8e0746bd725ffc0cb140373fabcc405.zip |
playlist/pls: custom INI parser
Don't use GLib.
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | src/playlist/PlaylistRegistry.cxx | 3 | ||||
-rw-r--r-- | src/playlist/plugins/PlsPlaylistPlugin.cxx | 193 |
3 files changed, 109 insertions, 95 deletions
diff --git a/Makefile.am b/Makefile.am index 4aa1061e5..7111014c2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1397,6 +1397,8 @@ libplaylist_plugins_a_SOURCES = \ src/playlist/plugins/CuePlaylistPlugin.hxx \ src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx \ src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx \ + src/playlist/plugins/PlsPlaylistPlugin.cxx \ + src/playlist/plugins/PlsPlaylistPlugin.hxx \ src/playlist/PlaylistRegistry.cxx src/playlist/PlaylistRegistry.hxx libplaylist_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(EXPAT_CFLAGS) \ @@ -1434,12 +1436,6 @@ libplaylist_plugins_a_SOURCES += \ src/playlist/plugins/RssPlaylistPlugin.hxx endif -if HAVE_GLIB -libplaylist_plugins_a_SOURCES += \ - src/playlist/plugins/PlsPlaylistPlugin.cxx \ - src/playlist/plugins/PlsPlaylistPlugin.hxx -endif - # # Filter plugins # diff --git a/src/playlist/PlaylistRegistry.cxx b/src/playlist/PlaylistRegistry.cxx index aed8e1567..d7bc3600a 100644 --- a/src/playlist/PlaylistRegistry.cxx +++ b/src/playlist/PlaylistRegistry.cxx @@ -45,10 +45,7 @@ const struct playlist_plugin *const playlist_plugins[] = { &extm3u_playlist_plugin, &m3u_playlist_plugin, -#ifdef HAVE_GLIB - // TODO: enable without GLib &pls_playlist_plugin, -#endif #ifdef ENABLE_EXPAT &xspf_playlist_plugin, &asx_playlist_plugin, diff --git a/src/playlist/plugins/PlsPlaylistPlugin.cxx b/src/playlist/plugins/PlsPlaylistPlugin.cxx index f7724f522..4f7c5a1ff 100644 --- a/src/playlist/plugins/PlsPlaylistPlugin.cxx +++ b/src/playlist/plugins/PlsPlaylistPlugin.cxx @@ -21,121 +21,142 @@ #include "PlsPlaylistPlugin.hxx" #include "../PlaylistPlugin.hxx" #include "../MemorySongEnumerator.hxx" -#include "input/InputStream.hxx" +#include "input/TextInputStream.hxx" #include "DetachedSong.hxx" #include "tag/TagBuilder.hxx" +#include "util/ASCII.hxx" +#include "util/StringUtil.hxx" +#include "util/DivideString.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" -#include "Log.hxx" - -#include <glib.h> #include <string> -#include <stdio.h> -#include <stdio.h> +#include <stdlib.h> static constexpr Domain pls_domain("pls"); -static void -pls_parser(GKeyFile *keyfile, std::forward_list<DetachedSong> &songs) +static bool +FindPlaylistSection(TextInputStream &is) { - gchar *value; - GError *error = nullptr; - int num_entries = g_key_file_get_integer(keyfile, "playlist", - "NumberOfEntries", &error); - if (error) { - FormatError(pls_domain, - "Invalid PLS file: '%s'", error->message); - g_error_free(error); - error = nullptr; - - /* Hack to work around shoutcast failure to comform to spec */ - num_entries = g_key_file_get_integer(keyfile, "playlist", - "numberofentries", &error); - if (error) { - g_error_free(error); - error = nullptr; - } + char *line; + while ((line = is.ReadLine()) != nullptr) { + line = Strip(line); + if (StringEqualsCaseASCII(line, "[playlist]")) + return true; } - for (; num_entries > 0; --num_entries) { - char key[64]; - sprintf(key, "File%u", num_entries); - char *uri = g_key_file_get_string(keyfile, "playlist", key, - &error); - if(error) { - FormatError(pls_domain, "Invalid PLS entry %s: '%s'", - key, error->message); - g_error_free(error); - return; - } + return false; +} - TagBuilder tag; +static bool +ParsePls(TextInputStream &is, std::forward_list<DetachedSong> &songs) +{ + assert(songs.empty()); - sprintf(key, "Title%u", num_entries); - value = g_key_file_get_string(keyfile, "playlist", key, - nullptr); - if (value != nullptr) - tag.AddItem(TAG_TITLE, value); + if (!FindPlaylistSection(is)) + return false; - g_free(value); + unsigned n_entries = 0; - sprintf(key, "Length%u", num_entries); - int length = g_key_file_get_integer(keyfile, "playlist", key, - nullptr); - if (length > 0) - tag.SetDuration(SignedSongTime::FromS(length)); + struct Entry { + std::string file, title; + int length; - songs.emplace_front(uri, tag.Commit()); - g_free(uri); - } + Entry():length(-1) {} + }; -} + static constexpr unsigned MAX_ENTRIES = 65536; -static SongEnumerator * -pls_open_stream(InputStream &is) -{ - GError *error = nullptr; - Error error2; - - std::string kf_data; - - do { - char buffer[1024]; - size_t nbytes = is.LockRead(buffer, sizeof(buffer), error2); - if (nbytes == 0) { - if (error2.IsDefined()) { - LogError(error2); - return nullptr; - } + std::vector<Entry> entries; + + char *line; + while ((line = is.ReadLine()) != nullptr) { + line = Strip(line); + if (*line == 0 || *line == ';') + continue; + + if (*line == '[') + /* another section starts; we only want + [Playlist], so stop here */ break; + + const DivideString ds(line, '=', true); + if (!ds.IsDefined()) + continue; + + const char *const name = ds.GetFirst(); + const char *const value = ds.GetSecond(); + + if (StringEqualsCaseASCII(name, "NumberOfEntries")) { + n_entries = strtoul(value, nullptr, 10); + if (n_entries == 0) + /* empty file - nothing remains to be + done */ + return true; + + if (n_entries > MAX_ENTRIES) + n_entries = MAX_ENTRIES; + entries.resize(n_entries); + } else if (StringEqualsCaseASCII(name, "File", 4)) { + unsigned i = strtoul(name + 4, nullptr, 10); + if (i >= 1 && i <= (n_entries > 0 ? n_entries : MAX_ENTRIES)) { + if (entries.size() < i) + entries.resize(i); + entries[i - 1].file = value; + } + } else if (StringEqualsCaseASCII(name, "Title", 5)) { + unsigned i = strtoul(name + 5, nullptr, 10); + if (i >= 1 && i <= (n_entries > 0 ? n_entries : MAX_ENTRIES)) { + if (entries.size() < i) + entries.resize(i); + entries[i - 1].title = value; + } + } else if (StringEqualsCaseASCII(name, "Length", 6)) { + unsigned i = strtoul(name + 6, nullptr, 10); + if (i >= 1 && i <= (n_entries > 0 ? n_entries : MAX_ENTRIES)) { + if (entries.size() < i) + entries.resize(i); + entries[i - 1].length = atoi(value); + } } + } - kf_data.append(buffer, nbytes); - /* Limit to 64k */ - } while (kf_data.length() < 65536); + if (n_entries == 0) + /* no "NumberOfEntries" found */ + return false; - if (kf_data.empty()) { - LogWarning(pls_domain, "KeyFile parser failed: No Data"); - return nullptr; - } + auto i = songs.before_begin(); + for (const auto &entry : entries) { + const char *uri = entry.file.c_str(); - GKeyFile *keyfile = g_key_file_new(); - if (!g_key_file_load_from_data(keyfile, - kf_data.data(), kf_data.length(), - G_KEY_FILE_NONE, &error)) { - FormatError(pls_domain, - "KeyFile parser failed: %s", error->message); - g_error_free(error); - g_key_file_free(keyfile); - return nullptr; + TagBuilder tag; + if (!entry.title.empty()) + tag.AddItem(TAG_TITLE, entry.title.c_str()); + + if (entry.length > 0) + tag.SetDuration(SignedSongTime::FromS(entry.length)); + + i = songs.emplace_after(i, uri, tag.Commit()); } + return true; +} + +static bool +ParsePls(InputStream &is, std::forward_list<DetachedSong> &songs) +{ + TextInputStream tis(is); + return ParsePls(tis, songs); +} + +static SongEnumerator * +pls_open_stream(InputStream &is) +{ std::forward_list<DetachedSong> songs; - pls_parser(keyfile, songs); - g_key_file_free(keyfile); + if (!ParsePls(is, songs)) + return nullptr; return new MemorySongEnumerator(std::move(songs)); } |