aboutsummaryrefslogtreecommitdiffstats
path: root/src/tag
diff options
context:
space:
mode:
Diffstat (limited to 'src/tag')
-rw-r--r--src/tag/Aiff.cxx4
-rw-r--r--src/tag/Aiff.hxx2
-rw-r--r--src/tag/ApeLoader.cxx10
-rw-r--r--src/tag/ApeLoader.hxx2
-rw-r--r--src/tag/ApeReplayGain.cxx19
-rw-r--r--src/tag/ApeReplayGain.hxx2
-rw-r--r--src/tag/ApeTag.cxx2
-rw-r--r--src/tag/ApeTag.hxx4
-rw-r--r--src/tag/MixRamp.cxx79
-rw-r--r--src/tag/MixRamp.hxx33
-rw-r--r--src/tag/ReplayGain.cxx86
-rw-r--r--src/tag/ReplayGain.hxx33
-rw-r--r--src/tag/Riff.cxx3
-rw-r--r--src/tag/Riff.hxx2
-rw-r--r--src/tag/Set.cxx117
-rw-r--r--src/tag/Set.hxx73
-rw-r--r--src/tag/Tag.cxx121
-rw-r--r--src/tag/Tag.hxx114
-rw-r--r--src/tag/TagBuilder.cxx173
-rw-r--r--src/tag/TagBuilder.hxx70
-rw-r--r--src/tag/TagConfig.cxx18
-rw-r--r--src/tag/TagConfig.hxx4
-rw-r--r--src/tag/TagHandler.cxx6
-rw-r--r--src/tag/TagHandler.hxx11
-rw-r--r--src/tag/TagId3.cxx182
-rw-r--r--src/tag/TagId3.hxx12
-rw-r--r--src/tag/TagItem.hxx2
-rw-r--r--src/tag/TagNames.c4
-rw-r--r--src/tag/TagPool.cxx93
-rw-r--r--src/tag/TagPool.hxx2
-rw-r--r--src/tag/TagRva2.cxx6
-rw-r--r--src/tag/TagRva2.hxx4
-rw-r--r--src/tag/TagSettings.c2
-rw-r--r--src/tag/TagSettings.h2
-rw-r--r--src/tag/TagString.cxx97
-rw-r--r--src/tag/TagString.hxx8
-rw-r--r--src/tag/TagTable.cxx12
-rw-r--r--src/tag/TagTable.hxx11
-rw-r--r--src/tag/TagType.h4
-rw-r--r--src/tag/VorbisComment.cxx41
-rw-r--r--src/tag/VorbisComment.hxx34
41 files changed, 1068 insertions, 436 deletions
diff --git a/src/tag/Aiff.cxx b/src/tag/Aiff.cxx
index 73e46e49f..c2498c9e9 100644
--- a/src/tag/Aiff.cxx
+++ b/src/tag/Aiff.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -26,9 +26,7 @@
#include <limits>
#include <stdint.h>
-#include <sys/types.h>
#include <sys/stat.h>
-#include <unistd.h>
#include <string.h>
static constexpr Domain aiff_domain("aiff");
diff --git a/src/tag/Aiff.hxx b/src/tag/Aiff.hxx
index 9000be7f8..cd323ee2e 100644
--- a/src/tag/Aiff.hxx
+++ b/src/tag/Aiff.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/ApeLoader.cxx b/src/tag/ApeLoader.cxx
index 8251efe10..f473c910e 100644
--- a/src/tag/ApeLoader.cxx
+++ b/src/tag/ApeLoader.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -22,8 +22,6 @@
#include "system/ByteOrder.hxx"
#include "fs/FileSystem.hxx"
-#include <glib.h>
-
#include <stdint.h>
#include <assert.h>
#include <stdio.h>
@@ -61,9 +59,9 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
remaining -= sizeof(footer);
assert(remaining > 10);
- char *buffer = (char *)g_malloc(remaining);
+ char *buffer = new char[remaining];
if (fread(buffer, 1, remaining, fp) != remaining) {
- g_free(buffer);
+ delete[] buffer;
return false;
}
@@ -98,7 +96,7 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
remaining -= size;
}
- g_free(buffer);
+ delete[] buffer;
return true;
}
diff --git a/src/tag/ApeLoader.hxx b/src/tag/ApeLoader.hxx
index 915c363b4..ce82cc35d 100644
--- a/src/tag/ApeLoader.hxx
+++ b/src/tag/ApeLoader.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/ApeReplayGain.cxx b/src/tag/ApeReplayGain.cxx
index cc65fb79d..345f45710 100644
--- a/src/tag/ApeReplayGain.cxx
+++ b/src/tag/ApeReplayGain.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -20,7 +20,7 @@
#include "config.h"
#include "ApeReplayGain.hxx"
#include "ApeLoader.hxx"
-#include "ReplayGainInfo.hxx"
+#include "ReplayGain.hxx"
#include "util/ASCII.hxx"
#include "fs/Path.hxx"
@@ -43,20 +43,7 @@ replay_gain_ape_callback(unsigned long flags, const char *key,
memcpy(value, _value, value_length);
value[value_length] = 0;
- if (StringEqualsCaseASCII(key, "replaygain_track_gain")) {
- info.tuples[REPLAY_GAIN_TRACK].gain = atof(value);
- return true;
- } else if (StringEqualsCaseASCII(key, "replaygain_album_gain")) {
- info.tuples[REPLAY_GAIN_ALBUM].gain = atof(value);
- return true;
- } else if (StringEqualsCaseASCII(key, "replaygain_track_peak")) {
- info.tuples[REPLAY_GAIN_TRACK].peak = atof(value);
- return true;
- } else if (StringEqualsCaseASCII(key, "replaygain_album_peak")) {
- info.tuples[REPLAY_GAIN_ALBUM].peak = atof(value);
- return true;
- } else
- return false;
+ return ParseReplayGainTag(info, key, value);
}
bool
diff --git a/src/tag/ApeReplayGain.hxx b/src/tag/ApeReplayGain.hxx
index 865add6f1..03c899c5c 100644
--- a/src/tag/ApeReplayGain.hxx
+++ b/src/tag/ApeReplayGain.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/ApeTag.cxx b/src/tag/ApeTag.cxx
index 2df53947a..f714a1624 100644
--- a/src/tag/ApeTag.cxx
+++ b/src/tag/ApeTag.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/ApeTag.hxx b/src/tag/ApeTag.hxx
index e35edc381..edebf076c 100644
--- a/src/tag/ApeTag.hxx
+++ b/src/tag/ApeTag.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -34,6 +34,6 @@ extern const struct tag_table ape_tags[];
*/
bool
tag_ape_scan2(Path path_fs,
- const struct tag_handler *handler, void *handler_ctx);
+ const tag_handler *handler, void *handler_ctx);
#endif
diff --git a/src/tag/MixRamp.cxx b/src/tag/MixRamp.cxx
new file mode 100644
index 000000000..e1b6e43c5
--- /dev/null
+++ b/src/tag/MixRamp.cxx
@@ -0,0 +1,79 @@
+/*
+ * 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 "MixRamp.hxx"
+#include "VorbisComment.hxx"
+#include "MixRampInfo.hxx"
+#include "util/ASCII.hxx"
+
+#include <assert.h>
+
+template<typename T>
+static bool
+ParseMixRampTagTemplate(MixRampInfo &info, const T t)
+{
+ const char *value;
+
+ if ((value = t["mixramp_start"]) != nullptr) {
+ info.SetStart(value);
+ return true;
+ } else if ((value = t["mixramp_end"]) != nullptr) {
+ info.SetEnd(value);
+ return true;
+ } else
+ return false;
+
+}
+
+bool
+ParseMixRampTag(MixRampInfo &info, const char *name, const char *value)
+{
+ assert(name != nullptr);
+ assert(value != nullptr);
+
+ struct NameValue {
+ const char *name;
+ const char *value;
+
+ gcc_pure
+ const char *operator[](const char *n) const {
+ return StringEqualsCaseASCII(name, n)
+ ? value
+ : nullptr;
+ }
+ };
+
+ return ParseMixRampTagTemplate(info, NameValue{name, value});
+}
+
+bool
+ParseMixRampVorbis(MixRampInfo &info, const char *entry)
+{
+ struct VorbisCommentEntry {
+ const char *entry;
+
+ gcc_pure
+ const char *operator[](const char *n) const {
+ return vorbis_comment_value(entry, n);
+ }
+ };
+
+ return ParseMixRampTagTemplate(info, VorbisCommentEntry{entry});
+}
diff --git a/src/tag/MixRamp.hxx b/src/tag/MixRamp.hxx
new file mode 100644
index 000000000..5b4e2dc30
--- /dev/null
+++ b/src/tag/MixRamp.hxx
@@ -0,0 +1,33 @@
+/*
+ * 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_TAG_MIXRAMP_HXX
+#define MPD_TAG_MIXRAMP_HXX
+
+#include "check.h"
+
+class MixRampInfo;
+
+bool
+ParseMixRampTag(MixRampInfo &info, const char *name, const char *value);
+
+bool
+ParseMixRampVorbis(MixRampInfo &info, const char *entry);
+
+#endif
diff --git a/src/tag/ReplayGain.cxx b/src/tag/ReplayGain.cxx
new file mode 100644
index 000000000..83a48f243
--- /dev/null
+++ b/src/tag/ReplayGain.cxx
@@ -0,0 +1,86 @@
+/*
+ * 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 "ReplayGain.hxx"
+#include "VorbisComment.hxx"
+#include "ReplayGainInfo.hxx"
+#include "util/ASCII.hxx"
+
+#include <assert.h>
+#include <stdlib.h>
+
+template<typename T>
+static bool
+ParseReplayGainTagTemplate(ReplayGainInfo &info, const T t)
+{
+ const char *value;
+
+ if ((value = t["replaygain_track_gain"]) != nullptr) {
+ info.tuples[REPLAY_GAIN_TRACK].gain = atof(value);
+ return true;
+ } else if ((value = t["replaygain_album_gain"]) != nullptr) {
+ info.tuples[REPLAY_GAIN_ALBUM].gain = atof(value);
+ return true;
+ } else if ((value = t["replaygain_track_peak"]) != nullptr) {
+ info.tuples[REPLAY_GAIN_TRACK].peak = atof(value);
+ return true;
+ } else if ((value = t["replaygain_album_peak"]) != nullptr) {
+ info.tuples[REPLAY_GAIN_ALBUM].peak = atof(value);
+ return true;
+ } else
+ return false;
+
+}
+
+bool
+ParseReplayGainTag(ReplayGainInfo &info, const char *name, const char *value)
+{
+ assert(name != nullptr);
+ assert(value != nullptr);
+
+ struct NameValue {
+ const char *name;
+ const char *value;
+
+ gcc_pure
+ const char *operator[](const char *n) const {
+ return StringEqualsCaseASCII(name, n)
+ ? value
+ : nullptr;
+ }
+ };
+
+ return ParseReplayGainTagTemplate(info, NameValue{name, value});
+}
+
+bool
+ParseReplayGainVorbis(ReplayGainInfo &info, const char *entry)
+{
+ struct VorbisCommentEntry {
+ const char *entry;
+
+ gcc_pure
+ const char *operator[](const char *n) const {
+ return vorbis_comment_value(entry, n);
+ }
+ };
+
+ return ParseReplayGainTagTemplate(info, VorbisCommentEntry{entry});
+}
diff --git a/src/tag/ReplayGain.hxx b/src/tag/ReplayGain.hxx
new file mode 100644
index 000000000..2bf5e0db1
--- /dev/null
+++ b/src/tag/ReplayGain.hxx
@@ -0,0 +1,33 @@
+/*
+ * 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_TAG_REPLAY_GAIN_HXX
+#define MPD_TAG_REPLAY_GAIN_HXX
+
+#include "check.h"
+
+struct ReplayGainInfo;
+
+bool
+ParseReplayGainTag(ReplayGainInfo &info, const char *name, const char *value);
+
+bool
+ParseReplayGainVorbis(ReplayGainInfo &info, const char *entry);
+
+#endif
diff --git a/src/tag/Riff.cxx b/src/tag/Riff.cxx
index ac162bc24..c630f082d 100644
--- a/src/tag/Riff.cxx
+++ b/src/tag/Riff.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -26,7 +26,6 @@
#include <limits>
#include <stdint.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
diff --git a/src/tag/Riff.hxx b/src/tag/Riff.hxx
index fbbdfaaf6..a9af67b7a 100644
--- a/src/tag/Riff.hxx
+++ b/src/tag/Riff.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/Set.cxx b/src/tag/Set.cxx
new file mode 100644
index 000000000..6a55a450f
--- /dev/null
+++ b/src/tag/Set.cxx
@@ -0,0 +1,117 @@
+/*
+ * 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 "Set.hxx"
+#include "TagBuilder.hxx"
+#include "TagSettings.h"
+
+#include <assert.h>
+
+/**
+ * Copy all tag items of the specified type.
+ */
+static bool
+CopyTagItem(TagBuilder &dest, TagType dest_type,
+ const Tag &src, TagType src_type)
+{
+ bool found = false;
+
+ for (const auto &item : src) {
+ if (item.type == src_type) {
+ dest.AddItem(dest_type, item.value);
+ found = true;
+ }
+ }
+
+ return found;
+}
+
+/**
+ * Copy all tag items of the specified type. Fall back to "Artist" if
+ * there is no "AlbumArtist".
+ */
+static void
+CopyTagItem(TagBuilder &dest, const Tag &src, TagType type)
+{
+ if (!CopyTagItem(dest, type, src, type) &&
+ type == TAG_ALBUM_ARTIST)
+ CopyTagItem(dest, type, src, TAG_ARTIST);
+}
+
+/**
+ * Copy all tag items of the types in the mask.
+ */
+static void
+CopyTagMask(TagBuilder &dest, const Tag &src, uint32_t mask)
+{
+ for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
+ if ((mask & (1u << i)) != 0)
+ CopyTagItem(dest, src, TagType(i));
+}
+
+void
+TagSet::InsertUnique(const Tag &src, TagType type, const char *value,
+ uint32_t group_mask)
+{
+ TagBuilder builder;
+ if (value == nullptr)
+ builder.AddEmptyItem(type);
+ else
+ builder.AddItem(type, value);
+ CopyTagMask(builder, src, group_mask);
+#if defined(__clang__) || GCC_CHECK_VERSION(4,8)
+ emplace(builder.Commit());
+#else
+ insert(builder.Commit());
+#endif
+}
+
+bool
+TagSet::CheckUnique(TagType dest_type,
+ const Tag &tag, TagType src_type,
+ uint32_t group_mask)
+{
+ bool found = false;
+
+ for (const auto &item : tag) {
+ if (item.type == src_type) {
+ InsertUnique(tag, dest_type, item.value, group_mask);
+ found = true;
+ }
+ }
+
+ return found;
+}
+
+void
+TagSet::InsertUnique(const Tag &tag,
+ TagType type, uint32_t group_mask)
+{
+ static_assert(sizeof(group_mask) * 8 >= TAG_NUM_OF_ITEM_TYPES,
+ "Mask is too small");
+
+ assert((group_mask & (1u << unsigned(type))) == 0);
+
+ if (!CheckUnique(type, tag, type, group_mask) &&
+ (type != TAG_ALBUM_ARTIST ||
+ ignore_tag_items[TAG_ALBUM_ARTIST] ||
+ /* fall back to "Artist" if no "AlbumArtist" was found */
+ !CheckUnique(type, tag, TAG_ARTIST, group_mask)))
+ InsertUnique(tag, type, nullptr, group_mask);
+}
diff --git a/src/tag/Set.hxx b/src/tag/Set.hxx
new file mode 100644
index 000000000..b5acfcb36
--- /dev/null
+++ b/src/tag/Set.hxx
@@ -0,0 +1,73 @@
+/*
+ * 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_TAG_SET_HXX
+#define MPD_TAG_SET_HXX
+
+#include "Compiler.h"
+#include "Tag.hxx"
+
+#include <set>
+
+#include <string.h>
+#include <stdint.h>
+
+/**
+ * Helper class for #TagSet which compares two #Tag objects.
+ */
+struct TagLess {
+ gcc_pure
+ bool operator()(const Tag &a, const Tag &b) const {
+ if (a.num_items != b.num_items)
+ return a.num_items < b.num_items;
+
+ const unsigned n = a.num_items;
+ for (unsigned i = 0; i < n; ++i) {
+ const TagItem &ai = *a.items[i];
+ const TagItem &bi = *b.items[i];
+ if (ai.type != bi.type)
+ return unsigned(ai.type) < unsigned(bi.type);
+
+ const int cmp = strcmp(ai.value, bi.value);
+ if (cmp != 0)
+ return cmp < 0;
+ }
+
+ return false;
+ }
+};
+
+/**
+ * A set of #Tag objects.
+ */
+class TagSet : public std::set<Tag, TagLess> {
+public:
+ void InsertUnique(const Tag &tag,
+ TagType type, uint32_t group_mask);
+
+private:
+ void InsertUnique(const Tag &src, TagType type, const char *value,
+ uint32_t group_mask);
+
+ bool CheckUnique(TagType dest_type,
+ const Tag &tag, TagType src_type,
+ uint32_t group_mask);
+};
+
+#endif
diff --git a/src/tag/Tag.cxx b/src/tag/Tag.cxx
index 6bf070429..92ac4214c 100644
--- a/src/tag/Tag.cxx
+++ b/src/tag/Tag.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -22,9 +22,9 @@
#include "TagPool.hxx"
#include "TagString.hxx"
#include "TagSettings.h"
+#include "TagBuilder.hxx"
#include "util/ASCII.hxx"
-#include <glib.h>
#include <assert.h>
#include <string.h>
@@ -58,16 +58,10 @@ tag_name_parse_i(const char *name)
return TAG_NUM_OF_ITEM_TYPES;
}
-static size_t
-items_size(const Tag &tag)
-{
- return tag.num_items * sizeof(TagItem *);
-}
-
void
Tag::Clear()
{
- time = -1;
+ duration = SignedSongTime::Negative();
has_playlist = false;
tag_pool_lock.lock();
@@ -75,28 +69,18 @@ Tag::Clear()
tag_pool_put_item(items[i]);
tag_pool_lock.unlock();
- g_free(items);
+ delete[] items;
items = nullptr;
num_items = 0;
}
-Tag::~Tag()
-{
- tag_pool_lock.lock();
- for (int i = num_items; --i >= 0; )
- tag_pool_put_item(items[i]);
- tag_pool_lock.unlock();
-
- g_free(items);
-}
-
Tag::Tag(const Tag &other)
- :time(other.time), has_playlist(other.has_playlist),
- items(nullptr),
- num_items(other.num_items)
+ :duration(other.duration), has_playlist(other.has_playlist),
+ num_items(other.num_items),
+ items(nullptr)
{
if (num_items > 0) {
- items = (TagItem **)g_malloc(items_size(other));
+ items = new TagItem *[num_items];
tag_pool_lock.lock();
for (unsigned i = 0; i < num_items; i++)
@@ -108,46 +92,9 @@ Tag::Tag(const Tag &other)
Tag *
Tag::Merge(const Tag &base, const Tag &add)
{
- unsigned n;
-
- /* allocate new tag object */
-
- Tag *ret = new Tag();
- ret->time = add.time > 0 ? add.time : base.time;
- ret->num_items = base.num_items + add.num_items;
- ret->items = ret->num_items > 0
- ? (TagItem **)g_malloc(items_size(*ret))
- : nullptr;
-
- tag_pool_lock.lock();
-
- /* copy all items from "add" */
-
- for (unsigned i = 0; i < add.num_items; ++i)
- ret->items[i] = tag_pool_dup_item(add.items[i]);
-
- n = add.num_items;
-
- /* copy additional items from "base" */
-
- for (unsigned i = 0; i < base.num_items; ++i)
- if (!add.HasType(base.items[i]->type))
- ret->items[n++] = tag_pool_dup_item(base.items[i]);
-
- tag_pool_lock.unlock();
-
- assert(n <= ret->num_items);
-
- if (n < ret->num_items) {
- /* some tags were not copied - shrink ret->items */
- assert(n > 0);
-
- ret->num_items = n;
- ret->items = (TagItem **)
- g_realloc(ret->items, items_size(*ret));
- }
-
- return ret;
+ TagBuilder builder(add);
+ builder.Complement(base);
+ return builder.CommitNew();
}
Tag *
@@ -171,9 +118,9 @@ Tag::GetValue(TagType type) const
{
assert(type < TAG_NUM_OF_ITEM_TYPES);
- for (unsigned i = 0; i < num_items; i++)
- if (items[i]->type == type)
- return items[i]->value;
+ for (const auto &item : *this)
+ if (item.type == type)
+ return item.value;
return nullptr;
}
@@ -183,43 +130,3 @@ Tag::HasType(TagType type) const
{
return GetValue(type) != nullptr;
}
-
-void
-Tag::AddItemInternal(TagType type, const char *value, size_t len)
-{
- unsigned int i = num_items;
-
- char *p = FixTagString(value, len);
- if (p != nullptr) {
- value = p;
- len = strlen(value);
- }
-
- num_items++;
-
- items = (TagItem **)g_realloc(items, items_size(*this));
-
- tag_pool_lock.lock();
- items[i] = tag_pool_get_item(type, value, len);
- tag_pool_lock.unlock();
-
- g_free(p);
-}
-
-void
-Tag::AddItem(TagType type, const char *value, size_t len)
-{
- if (ignore_tag_items[type])
- return;
-
- if (value == nullptr || len == 0)
- return;
-
- AddItemInternal(type, value, len);
-}
-
-void
-Tag::AddItem(TagType type, const char *value)
-{
- AddItem(type, value, strlen(value));
-}
diff --git a/src/tag/Tag.hxx b/src/tag/Tag.hxx
index 5846e7a9d..f1d3d5767 100644
--- a/src/tag/Tag.hxx
+++ b/src/tag/Tag.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -20,11 +20,13 @@
#ifndef MPD_TAG_HXX
#define MPD_TAG_HXX
-#include "TagType.h"
-#include "TagItem.hxx"
+#include "TagType.h" // IWYU pragma: export
+#include "TagItem.hxx" // IWYU pragma: export
+#include "Chrono.hxx"
#include "Compiler.h"
#include <algorithm>
+#include <iterator>
#include <stddef.h>
@@ -34,12 +36,10 @@
*/
struct Tag {
/**
- * The duration of the song (in seconds). A value of zero
- * means that the length is unknown. If the duration is
- * really between zero and one second, you should round up to
- * 1.
+ * The duration of the song. A negative value means that the
+ * length is unknown.
*/
- int time;
+ SignedSongTime duration;
/**
* Does this file have an embedded playlist (e.g. embedded CUE
@@ -47,23 +47,23 @@ struct Tag {
*/
bool has_playlist;
+ /** the total number of tag items in the #items array */
+ unsigned short num_items;
+
/** an array of tag items */
TagItem **items;
- /** the total number of tag items in the #items array */
- unsigned num_items;
-
/**
* Create an empty tag.
*/
- Tag():time(-1), has_playlist(false),
- items(nullptr), num_items(0) {}
+ Tag():duration(SignedSongTime::Negative()), has_playlist(false),
+ num_items(0), items(nullptr) {}
Tag(const Tag &other);
Tag(Tag &&other)
- :time(other.time), has_playlist(other.has_playlist),
- items(other.items), num_items(other.num_items) {
+ :duration(other.duration), has_playlist(other.has_playlist),
+ num_items(other.num_items), items(other.items) {
other.items = nullptr;
other.num_items = 0;
}
@@ -71,12 +71,14 @@ struct Tag {
/**
* Free the tag object and all its items.
*/
- ~Tag();
+ ~Tag() {
+ Clear();
+ }
Tag &operator=(const Tag &other) = delete;
Tag &operator=(Tag &&other) {
- time = other.time;
+ duration = other.duration;
has_playlist = other.has_playlist;
std::swap(items, other.items);
std::swap(num_items, other.num_items);
@@ -84,8 +86,8 @@ struct Tag {
}
/**
- * Returns true if the tag contains no items. This ignores the "time"
- * attribute.
+ * Returns true if the tag contains no items. This ignores
+ * the "duration" attribute.
*/
bool IsEmpty() const {
return num_items == 0;
@@ -95,7 +97,7 @@ struct Tag {
* Returns true if the tag contains any information.
*/
bool IsDefined() const {
- return !IsEmpty() || time >= 0;
+ return !IsEmpty() || !duration.IsNegative();
}
/**
@@ -104,24 +106,6 @@ struct Tag {
void Clear();
/**
- * Appends a new tag item.
- *
- * @param type the type of the new tag item
- * @param value the value of the tag item (not null-terminated)
- * @param len the length of #value
- */
- void AddItem(TagType type, const char *value, size_t len);
-
- /**
- * Appends a new tag item.
- *
- * @param tag the #tag object
- * @param type the type of the new tag item
- * @param value the value of the tag item (null-terminated)
- */
- void AddItem(TagType type, const char *value);
-
- /**
* Merges the data from two tags. If both tags share data for the
* same TagType, only data from "add" is used.
*
@@ -153,8 +137,58 @@ struct Tag {
gcc_pure
bool HasType(TagType type) const;
-private:
- void AddItemInternal(TagType type, const char *value, size_t len);
+ class const_iterator {
+ friend struct Tag;
+ const TagItem *const*cursor;
+
+ constexpr const_iterator(const TagItem *const*_cursor)
+ :cursor(_cursor) {}
+
+ public:
+ constexpr const TagItem &operator*() const {
+ return **cursor;
+ }
+
+ constexpr const TagItem *operator->() const {
+ return *cursor;
+ }
+
+ const_iterator &operator++() {
+ ++cursor;
+ return *this;
+ }
+
+ const_iterator operator++(int) {
+ auto result = cursor++;
+ return const_iterator{result};
+ }
+
+ const_iterator &operator--() {
+ --cursor;
+ return *this;
+ }
+
+ const_iterator operator--(int) {
+ auto result = cursor--;
+ return const_iterator{result};
+ }
+
+ constexpr bool operator==(const_iterator other) const {
+ return cursor == other.cursor;
+ }
+
+ constexpr bool operator!=(const_iterator other) const {
+ return cursor != other.cursor;
+ }
+ };
+
+ const_iterator begin() const {
+ return const_iterator{items};
+ }
+
+ const_iterator end() const {
+ return const_iterator{items + num_items};
+ }
};
/**
diff --git a/src/tag/TagBuilder.cxx b/src/tag/TagBuilder.cxx
index 083b43d69..93518f6e9 100644
--- a/src/tag/TagBuilder.cxx
+++ b/src/tag/TagBuilder.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -23,24 +23,92 @@
#include "TagPool.hxx"
#include "TagString.hxx"
#include "Tag.hxx"
-
-#include <glib.h>
+#include "util/WritableBuffer.hxx"
#include <assert.h>
#include <string.h>
+#include <stdlib.h>
-void
-TagBuilder::Clear()
+TagBuilder::TagBuilder(const Tag &other)
+ :duration(other.duration), has_playlist(other.has_playlist)
{
- time = -1;
- has_playlist = false;
+ items.reserve(other.num_items);
tag_pool_lock.lock();
+ for (unsigned i = 0, n = other.num_items; i != n; ++i)
+ items.push_back(tag_pool_dup_item(other.items[i]));
+ tag_pool_lock.unlock();
+}
+
+TagBuilder::TagBuilder(Tag &&other)
+ :duration(other.duration), has_playlist(other.has_playlist)
+{
+ /* move all TagItem pointers from the Tag object; we don't
+ need to contact the tag pool, because all we do is move
+ references */
+ items.reserve(other.num_items);
+ std::copy_n(other.items, other.num_items, std::back_inserter(items));
+
+ /* discard the pointers from the Tag object */
+ other.num_items = 0;
+ delete[] other.items;
+ other.items = nullptr;
+}
+
+TagBuilder &
+TagBuilder::operator=(const TagBuilder &other)
+{
+ /* copy all attributes */
+ duration = other.duration;
+ has_playlist = other.has_playlist;
+ items = other.items;
+
+ /* increment the tag pool refcounters */
+ tag_pool_lock.lock();
for (auto i : items)
- tag_pool_put_item(i);
+ tag_pool_dup_item(i);
tag_pool_lock.unlock();
+ return *this;
+}
+
+TagBuilder &
+TagBuilder::operator=(TagBuilder &&other)
+{
+ duration = other.duration;
+ has_playlist = other.has_playlist;
+ items = std::move(other.items);
+
+ return *this;
+}
+
+TagBuilder &
+TagBuilder::operator=(Tag &&other)
+{
+ duration = other.duration;
+ has_playlist = other.has_playlist;
+
+ /* move all TagItem pointers from the Tag object; we don't
+ need to contact the tag pool, because all we do is move
+ references */
items.clear();
+ items.reserve(other.num_items);
+ std::copy_n(other.items, other.num_items, std::back_inserter(items));
+
+ /* discard the pointers from the Tag object */
+ other.num_items = 0;
+ delete[] other.items;
+ other.items = nullptr;
+
+ return *this;
+}
+
+void
+TagBuilder::Clear()
+{
+ duration = SignedSongTime::Negative();
+ has_playlist = false;
+ RemoveAll();
}
void
@@ -48,7 +116,7 @@ TagBuilder::Commit(Tag &tag)
{
tag.Clear();
- tag.time = time;
+ tag.duration = duration;
tag.has_playlist = has_playlist;
/* move all TagItem pointers to the new Tag object without
@@ -57,7 +125,7 @@ TagBuilder::Commit(Tag &tag)
object */
const unsigned n_items = items.size();
tag.num_items = n_items;
- tag.items = g_new(TagItem *, n_items);
+ tag.items = new TagItem *[n_items];
std::copy_n(items.begin(), n_items, tag.items);
items.clear();
@@ -66,14 +134,51 @@ TagBuilder::Commit(Tag &tag)
Clear();
}
-Tag *
+Tag
TagBuilder::Commit()
{
+ Tag tag;
+ Commit(tag);
+ return tag;
+}
+
+Tag *
+TagBuilder::CommitNew()
+{
Tag *tag = new Tag();
Commit(*tag);
return tag;
}
+bool
+TagBuilder::HasType(TagType type) const
+{
+ for (auto i : items)
+ if (i->type == type)
+ return true;
+
+ return false;
+}
+
+void
+TagBuilder::Complement(const Tag &other)
+{
+ if (duration.IsNegative())
+ duration = other.duration;
+
+ has_playlist |= other.has_playlist;
+
+ items.reserve(items.size() + other.num_items);
+
+ tag_pool_lock.lock();
+ for (unsigned i = 0, n = other.num_items; i != n; ++i) {
+ TagItem *item = other.items[i];
+ if (!HasType(item->type))
+ items.push_back(tag_pool_dup_item(item));
+ }
+ tag_pool_lock.unlock();
+}
+
inline void
TagBuilder::AddItemInternal(TagType type, const char *value, size_t length)
{
@@ -83,17 +188,17 @@ TagBuilder::AddItemInternal(TagType type, const char *value, size_t length)
#endif
assert(length > 0);
- char *p = FixTagString(value, length);
- if (p != nullptr) {
- value = p;
- length = strlen(value);
+ auto f = FixTagString(value, length);
+ if (!f.IsNull()) {
+ value = f.data;
+ length = f.size;
}
tag_pool_lock.lock();
auto i = tag_pool_get_item(type, value, length);
tag_pool_lock.unlock();
- g_free(p);
+ free(f.data);
items.push_back(i);
}
@@ -122,3 +227,39 @@ TagBuilder::AddItem(TagType type, const char *value)
AddItem(type, value, strlen(value));
}
+
+void
+TagBuilder::AddEmptyItem(TagType type)
+{
+ tag_pool_lock.lock();
+ auto i = tag_pool_get_item(type, "", 0);
+ tag_pool_lock.unlock();
+
+ items.push_back(i);
+}
+
+void
+TagBuilder::RemoveAll()
+{
+ tag_pool_lock.lock();
+ for (auto i : items)
+ tag_pool_put_item(i);
+ tag_pool_lock.unlock();
+
+ items.clear();
+}
+
+void
+TagBuilder::RemoveType(TagType type)
+{
+ const auto begin = items.begin(), end = items.end();
+
+ items.erase(std::remove_if(begin, end,
+ [type](TagItem *item) {
+ if (item->type != type)
+ return false;
+ tag_pool_put_item(item);
+ return true;
+ }),
+ end);
+}
diff --git a/src/tag/TagBuilder.hxx b/src/tag/TagBuilder.hxx
index ffc60a1b2..aff581313 100644
--- a/src/tag/TagBuilder.hxx
+++ b/src/tag/TagBuilder.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -21,6 +21,7 @@
#define MPD_TAG_BUILDER_HXX
#include "TagType.h"
+#include "Chrono.hxx"
#include "Compiler.h"
#include <vector>
@@ -35,12 +36,10 @@ struct Tag;
*/
class TagBuilder {
/**
- * The duration of the song (in seconds). A value of zero
- * means that the length is unknown. If the duration is
- * really between zero and one second, you should round up to
- * 1.
+ * The duration of the song. A negative value means that the
+ * length is unknown.
*/
- int time;
+ SignedSongTime duration;
/**
* Does this file have an embedded playlist (e.g. embedded CUE
@@ -56,18 +55,25 @@ public:
* Create an empty tag.
*/
TagBuilder()
- :time(-1), has_playlist(false) {}
+ :duration(SignedSongTime::Negative()), has_playlist(false) {}
~TagBuilder() {
Clear();
}
TagBuilder(const TagBuilder &other) = delete;
- TagBuilder &operator=(const TagBuilder &other) = delete;
+
+ explicit TagBuilder(const Tag &other);
+ explicit TagBuilder(Tag &&other);
+
+ TagBuilder &operator=(const TagBuilder &other);
+ TagBuilder &operator=(TagBuilder &&other);
+
+ TagBuilder &operator=(Tag &&other);
/**
- * Returns true if the tag contains no items. This ignores the "time"
- * attribute.
+ * Returns true if the tag contains no items. This ignores
+ * the "duration" attribute.
*/
bool IsEmpty() const {
return items.empty();
@@ -78,7 +84,7 @@ public:
*/
gcc_pure
bool IsDefined() const {
- return time >= 0 || has_playlist || !IsEmpty();
+ return !duration.IsNegative() || has_playlist || !IsEmpty();
}
void Clear();
@@ -90,14 +96,20 @@ public:
void Commit(Tag &tag);
/**
+ * Create a new #Tag instance from data in this object. This
+ * object is empty afterwards.
+ */
+ Tag Commit();
+
+ /**
* Create a new #Tag instance from data in this object. The
* returned object is owned by the caller. This object is
* empty afterwards.
*/
- Tag *Commit();
+ Tag *CommitNew();
- void SetTime(int _time) {
- time = _time;
+ void SetDuration(SignedSongTime _duration) {
+ duration = _duration;
}
void SetHasPlaylist(bool _has_playlist) {
@@ -109,6 +121,19 @@ public:
}
/**
+ * Checks whether the tag contains one or more items with
+ * the specified type.
+ */
+ gcc_pure
+ bool HasType(TagType type) const;
+
+ /**
+ * Copy attributes and items from the other object that do not
+ * exist in this object.
+ */
+ void Complement(const Tag &other);
+
+ /**
* Appends a new tag item.
*
* @param type the type of the new tag item
@@ -127,6 +152,23 @@ public:
gcc_nonnull_all
void AddItem(TagType type, const char *value);
+ /**
+ * Appends a new tag item with an empty value. Do not use
+ * this unless you know what you're doing - because usually,
+ * empty values are discarded.
+ */
+ void AddEmptyItem(TagType type);
+
+ /**
+ * Removes all tag items.
+ */
+ void RemoveAll();
+
+ /**
+ * Removes all tag items of the specified type.
+ */
+ void RemoveType(TagType type);
+
private:
gcc_nonnull_all
void AddItemInternal(TagType type, const char *value, size_t length);
diff --git a/src/tag/TagConfig.cxx b/src/tag/TagConfig.cxx
index b8be4fc4c..00f20d1c0 100644
--- a/src/tag/TagConfig.cxx
+++ b/src/tag/TagConfig.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -21,16 +21,16 @@
#include "TagConfig.hxx"
#include "TagSettings.h"
#include "Tag.hxx"
-#include "ConfigGlobal.hxx"
-#include "ConfigOption.hxx"
+#include "config/ConfigGlobal.hxx"
+#include "config/ConfigOption.hxx"
#include "system/FatalError.hxx"
+#include "util/Alloc.hxx"
#include "util/ASCII.hxx"
-
-#include <glib.h>
+#include "util/StringUtil.hxx"
#include <algorithm>
-#include <string.h>
+#include <stdlib.h>
void
TagLoadConfig()
@@ -46,14 +46,14 @@ TagLoadConfig()
bool quit = false;
char *temp, *c, *s;
- temp = c = s = g_strdup(value);
+ temp = c = s = xstrdup(value);
do {
if (*s == ',' || *s == '\0') {
if (*s == '\0')
quit = true;
*s = '\0';
- c = g_strstrip(c);
+ c = Strip(c);
if (*c == 0)
continue;
@@ -70,5 +70,5 @@ TagLoadConfig()
s++;
} while (!quit);
- g_free(temp);
+ free(temp);
}
diff --git a/src/tag/TagConfig.hxx b/src/tag/TagConfig.hxx
index 5ec6766d4..0088e9757 100644
--- a/src/tag/TagConfig.hxx
+++ b/src/tag/TagConfig.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -20,8 +20,6 @@
#ifndef MPD_TAG_CONFIG_HXX
#define MPD_TAG_CONFIG_HXX
-#include "TagType.h"
-
void
TagLoadConfig();
diff --git a/src/tag/TagHandler.cxx b/src/tag/TagHandler.cxx
index 80982ef50..2cbb83242 100644
--- a/src/tag/TagHandler.cxx
+++ b/src/tag/TagHandler.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -23,11 +23,11 @@
#include "util/ASCII.hxx"
static void
-add_tag_duration(unsigned seconds, void *ctx)
+add_tag_duration(SongTime duration, void *ctx)
{
TagBuilder &tag = *(TagBuilder *)ctx;
- tag.SetTime(seconds);
+ tag.SetDuration(duration);
}
static void
diff --git a/src/tag/TagHandler.hxx b/src/tag/TagHandler.hxx
index b8c3c6b79..c12b605bc 100644
--- a/src/tag/TagHandler.hxx
+++ b/src/tag/TagHandler.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -22,6 +22,7 @@
#include "check.h"
#include "TagType.h"
+#include "Chrono.hxx"
#include <assert.h>
@@ -30,11 +31,11 @@
*/
struct tag_handler {
/**
- * Declare the duration of a song, in seconds. Do not call
+ * Declare the duration of a song. Do not call
* this when the duration could not be determined, because
* there is no magic value for "unknown duration".
*/
- void (*duration)(unsigned seconds, void *ctx);
+ void (*duration)(SongTime duration, void *ctx);
/**
* A tag has been read.
@@ -53,12 +54,12 @@ struct tag_handler {
static inline void
tag_handler_invoke_duration(const struct tag_handler *handler, void *ctx,
- unsigned seconds)
+ SongTime duration)
{
assert(handler != nullptr);
if (handler->duration != nullptr)
- handler->duration(seconds, ctx);
+ handler->duration(duration, ctx);
}
static inline void
diff --git a/src/tag/TagId3.cxx b/src/tag/TagId3.cxx
index df70a95e5..02dc58364 100644
--- a/src/tag/TagId3.cxx
+++ b/src/tag/TagId3.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -21,23 +21,28 @@
#include "TagId3.hxx"
#include "TagHandler.hxx"
#include "TagTable.hxx"
-#include "Tag.hxx"
#include "TagBuilder.hxx"
+#include "util/Alloc.hxx"
+#include "util/StringUtil.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
-#include "ConfigGlobal.hxx"
+#include "config/ConfigGlobal.hxx"
#include "Riff.hxx"
#include "Aiff.hxx"
#include "fs/Path.hxx"
#include "fs/FileSystem.hxx"
+#ifdef HAVE_GLIB
#include <glib.h>
+#endif
+
#include <id3tag.h>
+#include <string>
+
#include <stdio.h>
#include <stdlib.h>
-#include <errno.h>
#include <string.h>
# ifndef ID3_FRAME_COMPOSER
@@ -70,14 +75,11 @@ tag_is_id3v1(struct id3_tag *tag)
static id3_utf8_t *
tag_id3_getstring(const struct id3_frame *frame, unsigned i)
{
- union id3_field *field;
- const id3_ucs4_t *ucs4;
-
- field = id3_frame_field(frame, i);
+ id3_field *field = id3_frame_field(frame, i);
if (field == nullptr)
return nullptr;
- ucs4 = id3_field_getstring(field);
+ const id3_ucs4_t *ucs4 = id3_field_getstring(field);
if (ucs4 == nullptr)
return nullptr;
@@ -89,17 +91,16 @@ tag_id3_getstring(const struct id3_frame *frame, unsigned i)
static id3_utf8_t *
import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4)
{
- id3_utf8_t *utf8, *utf8_stripped;
- id3_latin1_t *isostr;
- const char *encoding;
+ id3_utf8_t *utf8;
+#ifdef HAVE_GLIB
/* use encoding field here? */
+ const char *encoding;
if (is_id3v1 &&
(encoding = config_get_string(CONF_ID3V1_ENCODING, nullptr)) != nullptr) {
- isostr = id3_ucs4_latin1duplicate(ucs4);
- if (G_UNLIKELY(!isostr)) {
+ id3_latin1_t *isostr = id3_ucs4_latin1duplicate(ucs4);
+ if (gcc_unlikely(isostr == nullptr))
return nullptr;
- }
utf8 = (id3_utf8_t *)
g_convert_with_fallback((const char*)isostr, -1,
@@ -110,19 +111,24 @@ import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4)
FormatWarning(id3_domain,
"Unable to convert %s string to UTF-8: '%s'",
encoding, isostr);
- g_free(isostr);
+ free(isostr);
return nullptr;
}
- g_free(isostr);
+ free(isostr);
} else {
+#else
+ (void)is_id3v1;
+#endif
utf8 = id3_ucs4_utf8duplicate(ucs4);
- if (G_UNLIKELY(!utf8)) {
+ if (gcc_unlikely(utf8 == nullptr))
return nullptr;
- }
+#ifdef HAVE_GLIB
}
+#endif
- utf8_stripped = (id3_utf8_t *)g_strdup(g_strstrip((gchar *)utf8));
- g_free(utf8);
+ id3_utf8_t *utf8_stripped = (id3_utf8_t *)
+ xstrdup(Strip((char *)utf8));
+ free(utf8);
return utf8_stripped;
}
@@ -139,17 +145,12 @@ tag_id3_import_text_frame(struct id3_tag *tag, const struct id3_frame *frame,
TagType type,
const struct tag_handler *handler, void *handler_ctx)
{
- id3_ucs4_t const *ucs4;
- id3_utf8_t *utf8;
- union id3_field const *field;
- unsigned int nstrings, i;
-
if (frame->nfields != 2)
return;
/* check the encoding field */
- field = id3_frame_field(frame, 0);
+ const id3_field *field = id3_frame_field(frame, 0);
if (field == nullptr || field->type != ID3_FIELD_TYPE_TEXTENCODING)
return;
@@ -160,22 +161,22 @@ tag_id3_import_text_frame(struct id3_tag *tag, const struct id3_frame *frame,
return;
/* Get the number of strings available */
- nstrings = id3_field_getnstrings(field);
- for (i = 0; i < nstrings; i++) {
- ucs4 = id3_field_getstrings(field, i);
+ const unsigned nstrings = id3_field_getnstrings(field);
+ for (unsigned i = 0; i < nstrings; i++) {
+ const id3_ucs4_t *ucs4 = id3_field_getstrings(field, i);
if (ucs4 == nullptr)
continue;
if (type == TAG_GENRE)
ucs4 = id3_genre_name(ucs4);
- utf8 = import_id3_string(tag_is_id3v1(tag), ucs4);
+ id3_utf8_t *utf8 = import_id3_string(tag_is_id3v1(tag), ucs4);
if (utf8 == nullptr)
continue;
tag_handler_invoke_tag(handler, handler_ctx,
type, (const char *)utf8);
- g_free(utf8);
+ free(utf8);
}
}
@@ -209,28 +210,24 @@ tag_id3_import_comment_frame(struct id3_tag *tag,
const struct tag_handler *handler,
void *handler_ctx)
{
- id3_ucs4_t const *ucs4;
- id3_utf8_t *utf8;
- union id3_field const *field;
-
if (frame->nfields != 4)
return;
/* for now I only read the 4th field, with the fullstring */
- field = id3_frame_field(frame, 3);
+ const id3_field *field = id3_frame_field(frame, 3);
if (field == nullptr)
return;
- ucs4 = id3_field_getfullstring(field);
+ const id3_ucs4_t *ucs4 = id3_field_getfullstring(field);
if (ucs4 == nullptr)
return;
- utf8 = import_id3_string(tag_is_id3v1(tag), ucs4);
+ id3_utf8_t *utf8 = import_id3_string(tag_is_id3v1(tag), ucs4);
if (utf8 == nullptr)
return;
tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8);
- g_free(utf8);
+ free(utf8);
}
/**
@@ -262,6 +259,8 @@ tag_id3_parse_txxx_name(const char *name)
{ "MusicBrainz Album Artist Id",
TAG_MUSICBRAINZ_ALBUMARTISTID },
{ "MusicBrainz Track Id", TAG_MUSICBRAINZ_TRACKID },
+ { "MusicBrainz Release Track Id",
+ TAG_MUSICBRAINZ_RELEASETRACKID },
{ nullptr, TAG_NUM_OF_ITEM_TYPES }
};
@@ -277,19 +276,15 @@ tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
void *handler_ctx)
{
for (unsigned i = 0;; ++i) {
- const struct id3_frame *frame;
- id3_utf8_t *name, *value;
- TagType type;
-
- frame = id3_tag_findframe(id3_tag, "TXXX", i);
+ const id3_frame *frame = id3_tag_findframe(id3_tag, "TXXX", i);
if (frame == nullptr)
break;
- name = tag_id3_getstring(frame, 1);
+ id3_utf8_t *name = tag_id3_getstring(frame, 1);
if (name == nullptr)
continue;
- value = tag_id3_getstring(frame, 2);
+ id3_utf8_t *value = tag_id3_getstring(frame, 2);
if (value == nullptr)
continue;
@@ -297,7 +292,7 @@ tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
(const char *)name,
(const char *)value);
- type = tag_id3_parse_txxx_name((const char*)name);
+ TagType type = tag_id3_parse_txxx_name((const char*)name);
free(name);
if (type != TAG_NUM_OF_ITEM_TYPES)
@@ -316,21 +311,15 @@ tag_id3_import_ufid(struct id3_tag *id3_tag,
const struct tag_handler *handler, void *handler_ctx)
{
for (unsigned i = 0;; ++i) {
- const struct id3_frame *frame;
- union id3_field *field;
- const id3_latin1_t *name;
- const id3_byte_t *value;
- id3_length_t length;
-
- frame = id3_tag_findframe(id3_tag, "UFID", i);
+ const id3_frame *frame = id3_tag_findframe(id3_tag, "UFID", i);
if (frame == nullptr)
break;
- field = id3_frame_field(frame, 0);
+ id3_field *field = id3_frame_field(frame, 0);
if (field == nullptr)
continue;
- name = id3_field_getlatin1(field);
+ const id3_latin1_t *name = id3_field_getlatin1(field);
if (name == nullptr ||
strcmp((const char *)name, "http://musicbrainz.org") != 0)
continue;
@@ -339,14 +328,15 @@ tag_id3_import_ufid(struct id3_tag *id3_tag,
if (field == nullptr)
continue;
- value = id3_field_getbinarydata(field, &length);
+ id3_length_t length;
+ const id3_byte_t *value =
+ id3_field_getbinarydata(field, &length);
if (value == nullptr || length == 0)
continue;
- char *p = g_strndup((const char *)value, length);
+ std::string p((const char *)value, length);
tag_handler_invoke_tag(handler, handler_ctx,
- TAG_MUSICBRAINZ_TRACKID, p);
- g_free(p);
+ TAG_MUSICBRAINZ_TRACKID, p.c_str());
}
}
@@ -360,6 +350,9 @@ scan_id3_tag(struct id3_tag *tag,
TAG_ALBUM_ARTIST, handler, handler_ctx);
tag_id3_import_text(tag, ID3_FRAME_ARTIST_SORT,
TAG_ARTIST_SORT, handler, handler_ctx);
+
+ tag_id3_import_text(tag, "TSOA", TAG_ALBUM_SORT, handler, handler_ctx);
+
tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST_SORT,
TAG_ALBUM_ARTIST_SORT, handler, handler_ctx);
tag_id3_import_text(tag, ID3_FRAME_TITLE, TAG_TITLE,
@@ -393,73 +386,57 @@ tag_id3_import(struct id3_tag *tag)
scan_id3_tag(tag, &add_tag_handler, &tag_builder);
return tag_builder.IsEmpty()
? nullptr
- : tag_builder.Commit();
+ : tag_builder.CommitNew();
}
-static int
+static size_t
fill_buffer(void *buf, size_t size, FILE *stream, long offset, int whence)
{
if (fseek(stream, offset, whence) != 0) return 0;
return fread(buf, 1, size, stream);
}
-static int
+static long
get_id3v2_footer_size(FILE *stream, long offset, int whence)
{
id3_byte_t buf[ID3_TAG_QUERYSIZE];
- int bufsize;
-
- bufsize = fill_buffer(buf, ID3_TAG_QUERYSIZE, stream, offset, whence);
- if (bufsize <= 0) return 0;
+ size_t bufsize = fill_buffer(buf, ID3_TAG_QUERYSIZE, stream, offset, whence);
+ if (bufsize == 0) return 0;
return id3_tag_query(buf, bufsize);
}
static struct id3_tag *
tag_id3_read(FILE *stream, long offset, int whence)
{
- struct id3_tag *tag;
- id3_byte_t query_buffer[ID3_TAG_QUERYSIZE];
- int tag_size;
- int query_buffer_size;
-
/* It's ok if we get less than we asked for */
- query_buffer_size = fill_buffer(query_buffer, ID3_TAG_QUERYSIZE,
- stream, offset, whence);
+ id3_byte_t query_buffer[ID3_TAG_QUERYSIZE];
+ size_t query_buffer_size = fill_buffer(query_buffer, ID3_TAG_QUERYSIZE,
+ stream, offset, whence);
if (query_buffer_size <= 0)
return nullptr;
/* Look for a tag header */
- tag_size = id3_tag_query(query_buffer, query_buffer_size);
+ long tag_size = id3_tag_query(query_buffer, query_buffer_size);
if (tag_size <= 0) return nullptr;
/* Found a tag. Allocate a buffer and read it in. */
- id3_byte_t *tag_buffer = (id3_byte_t *)g_malloc(tag_size);
- if (!tag_buffer)
- return nullptr;
-
+ id3_byte_t *tag_buffer = new id3_byte_t[tag_size];
int tag_buffer_size = fill_buffer(tag_buffer, tag_size,
stream, offset, whence);
if (tag_buffer_size < tag_size) {
- g_free(tag_buffer);
+ delete[] tag_buffer;
return nullptr;
}
- tag = id3_tag_parse(tag_buffer, tag_buffer_size);
-
- g_free(tag_buffer);
-
+ id3_tag *tag = id3_tag_parse(tag_buffer, tag_buffer_size);
+ delete[] tag_buffer;
return tag;
}
static struct id3_tag *
tag_id3_find_from_beginning(FILE *stream)
{
- struct id3_tag *tag;
- struct id3_tag *seektag;
- struct id3_frame *frame;
- int seek;
-
- tag = tag_id3_read(stream, 0, SEEK_SET);
+ id3_tag *tag = tag_id3_read(stream, 0, SEEK_SET);
if (!tag) {
return nullptr;
} else if (tag_is_id3v1(tag)) {
@@ -469,14 +446,15 @@ tag_id3_find_from_beginning(FILE *stream)
}
/* We have an id3v2 tag, so let's look for SEEK frames */
+ id3_frame *frame;
while ((frame = id3_tag_findframe(tag, "SEEK", 0))) {
/* Found a SEEK frame, get it's value */
- seek = id3_field_getint(id3_frame_field(frame, 0));
+ int seek = id3_field_getint(id3_frame_field(frame, 0));
if (seek < 0)
break;
/* Get the tag specified by the SEEK frame */
- seektag = tag_id3_read(stream, seek, SEEK_CUR);
+ id3_tag *seektag = tag_id3_read(stream, seek, SEEK_CUR);
if (!seektag || tag_is_id3v1(seektag))
break;
@@ -491,20 +469,16 @@ tag_id3_find_from_beginning(FILE *stream)
static struct id3_tag *
tag_id3_find_from_end(FILE *stream)
{
- struct id3_tag *tag;
- struct id3_tag *v1tag;
- int tagsize;
-
/* Get an id3v1 tag from the end of file for later use */
- v1tag = tag_id3_read(stream, -128, SEEK_END);
+ id3_tag *v1tag = tag_id3_read(stream, -128, SEEK_END);
/* Get the id3v2 tag size from the footer (located before v1tag) */
- tagsize = get_id3v2_footer_size(stream, (v1tag ? -128 : 0) - 10, SEEK_END);
+ int tagsize = get_id3v2_footer_size(stream, (v1tag ? -128 : 0) - 10, SEEK_END);
if (tagsize >= 0)
return v1tag;
/* Get the tag which the footer belongs to */
- tag = tag_id3_read(stream, tagsize, SEEK_CUR);
+ id3_tag *tag = tag_id3_read(stream, tagsize, SEEK_CUR);
if (!tag)
return v1tag;
@@ -527,16 +501,16 @@ tag_id3_riff_aiff_load(FILE *file)
/* too large, don't allocate so much memory */
return nullptr;
- id3_byte_t *buffer = (id3_byte_t *)g_malloc(size);
+ id3_byte_t *buffer = new id3_byte_t[size];
size_t ret = fread(buffer, size, 1, file);
if (ret != 1) {
LogWarning(id3_domain, "Failed to read RIFF chunk");
- g_free(buffer);
+ delete[] buffer;
return nullptr;
}
struct id3_tag *tag = id3_tag_parse(buffer, size);
- g_free(buffer);
+ delete[] buffer;
return tag;
}
@@ -545,7 +519,7 @@ tag_id3_load(Path path_fs, Error &error)
{
FILE *file = FOpen(path_fs, "rb");
if (file == nullptr) {
- error.FormatErrno("Failed to open file %s", path_fs);
+ error.FormatErrno("Failed to open file %s", path_fs.c_str());
return nullptr;
}
diff --git a/src/tag/TagId3.hxx b/src/tag/TagId3.hxx
index 749166116..1928d539d 100644
--- a/src/tag/TagId3.hxx
+++ b/src/tag/TagId3.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -33,10 +33,10 @@ class Error;
bool
tag_id3_scan(Path path_fs,
- const struct tag_handler *handler, void *handler_ctx);
+ const tag_handler *handler, void *handler_ctx);
Tag *
-tag_id3_import(struct id3_tag *);
+tag_id3_import(id3_tag *);
/**
* Loads the ID3 tags from the file into a libid3tag object. The
@@ -53,8 +53,8 @@ tag_id3_load(Path path_fs, Error &error);
*
*/
void
-scan_id3_tag(struct id3_tag *tag,
- const struct tag_handler *handler, void *handler_ctx);
+scan_id3_tag(id3_tag *tag,
+ const tag_handler *handler, void *handler_ctx);
#else
@@ -62,7 +62,7 @@ scan_id3_tag(struct id3_tag *tag,
static inline bool
tag_id3_scan(gcc_unused Path path_fs,
- gcc_unused const struct tag_handler *handler,
+ gcc_unused const tag_handler *handler,
gcc_unused void *handler_ctx)
{
return false;
diff --git a/src/tag/TagItem.hxx b/src/tag/TagItem.hxx
index 7c3100393..489ecde3a 100644
--- a/src/tag/TagItem.hxx
+++ b/src/tag/TagItem.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/TagNames.c b/src/tag/TagNames.c
index eac6fc59a..e051c5863 100644
--- a/src/tag/TagNames.c
+++ b/src/tag/TagNames.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -24,6 +24,7 @@ const char *const tag_item_names[TAG_NUM_OF_ITEM_TYPES] = {
[TAG_ARTIST] = "Artist",
[TAG_ARTIST_SORT] = "ArtistSort",
[TAG_ALBUM] = "Album",
+ [TAG_ALBUM_SORT] = "AlbumSort",
[TAG_ALBUM_ARTIST] = "AlbumArtist",
[TAG_ALBUM_ARTIST_SORT] = "AlbumArtistSort",
[TAG_TITLE] = "Title",
@@ -41,4 +42,5 @@ const char *const tag_item_names[TAG_NUM_OF_ITEM_TYPES] = {
[TAG_MUSICBRAINZ_ALBUMID] = "MUSICBRAINZ_ALBUMID",
[TAG_MUSICBRAINZ_ALBUMARTISTID] = "MUSICBRAINZ_ALBUMARTISTID",
[TAG_MUSICBRAINZ_TRACKID] = "MUSICBRAINZ_TRACKID",
+ [TAG_MUSICBRAINZ_RELEASETRACKID] = "MUSICBRAINZ_RELEASETRACKID",
};
diff --git a/src/tag/TagPool.cxx b/src/tag/TagPool.cxx
index cc28ea9a6..29f605337 100644
--- a/src/tag/TagPool.cxx
+++ b/src/tag/TagPool.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -20,23 +20,46 @@
#include "config.h"
#include "TagPool.hxx"
#include "TagItem.hxx"
-
-#include <glib.h>
+#include "util/Cast.hxx"
+#include "util/VarSize.hxx"
#include <assert.h>
#include <string.h>
+#include <stdlib.h>
Mutex tag_pool_lock;
-#define NUM_SLOTS 4096
+static constexpr size_t NUM_SLOTS = 4096;
-struct slot {
- struct slot *next;
+struct TagPoolSlot {
+ TagPoolSlot *next;
unsigned char ref;
TagItem item;
-} mpd_packed;
-static struct slot *slots[NUM_SLOTS];
+ TagPoolSlot(TagPoolSlot *_next, TagType type,
+ const char *value, size_t length)
+ :next(_next), ref(1) {
+ item.type = type;
+ memcpy(item.value, value, length);
+ item.value[length] = 0;
+ }
+
+ static TagPoolSlot *Create(TagPoolSlot *_next, TagType type,
+ const char *value, size_t length);
+} gcc_packed;
+
+TagPoolSlot *
+TagPoolSlot::Create(TagPoolSlot *_next, TagType type,
+ const char *value, size_t length)
+{
+ TagPoolSlot *dummy;
+ return NewVarSize<TagPoolSlot>(sizeof(dummy->item.value),
+ length + 1,
+ _next, type,
+ value, length);
+}
+
+static TagPoolSlot *slots[NUM_SLOTS];
static inline unsigned
calc_hash_n(TagType type, const char *p, size_t length)
@@ -64,35 +87,32 @@ calc_hash(TagType type, const char *p)
return hash ^ type;
}
-static inline struct slot *
+#if defined(__clang__) || GCC_CHECK_VERSION(4,7)
+ constexpr
+#endif
+static inline TagPoolSlot *
tag_item_to_slot(TagItem *item)
{
- return (struct slot*)(((char*)item) - offsetof(struct slot, item));
+ return &ContainerCast(*item, &TagPoolSlot::item);
}
-static struct slot *slot_alloc(struct slot *next,
- TagType type,
- const char *value, int length)
+static inline TagPoolSlot **
+tag_value_slot_p(TagType type, const char *value, size_t length)
{
- struct slot *slot;
-
- slot = (struct slot *)
- g_malloc(sizeof(*slot) - sizeof(slot->item.value) + length + 1);
- slot->next = next;
- slot->ref = 1;
- slot->item.type = type;
- memcpy(slot->item.value, value, length);
- slot->item.value[length] = 0;
- return slot;
+ return &slots[calc_hash_n(type, value, length) % NUM_SLOTS];
+}
+
+static inline TagPoolSlot **
+tag_value_slot_p(TagType type, const char *value)
+{
+ return &slots[calc_hash(type, value) % NUM_SLOTS];
}
TagItem *
tag_pool_get_item(TagType type, const char *value, size_t length)
{
- struct slot **slot_p, *slot;
-
- slot_p = &slots[calc_hash_n(type, value, length) % NUM_SLOTS];
- for (slot = *slot_p; slot != nullptr; slot = slot->next) {
+ auto slot_p = tag_value_slot_p(type, value, length);
+ for (auto slot = *slot_p; slot != nullptr; slot = slot->next) {
if (slot->item.type == type &&
length == strlen(slot->item.value) &&
memcmp(value, slot->item.value, length) == 0 &&
@@ -103,7 +123,7 @@ tag_pool_get_item(TagType type, const char *value, size_t length)
}
}
- slot = slot_alloc(*slot_p, type, value, length);
+ auto slot = TagPoolSlot::Create(*slot_p, type, value, length);
*slot_p = slot;
return &slot->item;
}
@@ -111,7 +131,7 @@ tag_pool_get_item(TagType type, const char *value, size_t length)
TagItem *
tag_pool_dup_item(TagItem *item)
{
- struct slot *slot = tag_item_to_slot(item);
+ TagPoolSlot *slot = tag_item_to_slot(item);
assert(slot->ref > 0);
@@ -122,11 +142,10 @@ tag_pool_dup_item(TagItem *item)
/* the reference counter overflows above 0xff;
duplicate the item, and start with 1 */
size_t length = strlen(item->value);
- struct slot **slot_p =
- &slots[calc_hash_n(item->type, item->value,
- length) % NUM_SLOTS];
- slot = slot_alloc(*slot_p, item->type,
- item->value, strlen(item->value));
+ auto slot_p = tag_value_slot_p(item->type,
+ item->value, length);
+ slot = TagPoolSlot::Create(*slot_p, item->type,
+ item->value, strlen(item->value));
*slot_p = slot;
return &slot->item;
}
@@ -135,7 +154,7 @@ tag_pool_dup_item(TagItem *item)
void
tag_pool_put_item(TagItem *item)
{
- struct slot **slot_p, *slot;
+ TagPoolSlot **slot_p, *slot;
slot = tag_item_to_slot(item);
assert(slot->ref > 0);
@@ -144,12 +163,12 @@ tag_pool_put_item(TagItem *item)
if (slot->ref > 0)
return;
- for (slot_p = &slots[calc_hash(item->type, item->value) % NUM_SLOTS];
+ for (slot_p = tag_value_slot_p(item->type, item->value);
*slot_p != slot;
slot_p = &(*slot_p)->next) {
assert(*slot_p != nullptr);
}
*slot_p = slot->next;
- g_free(slot);
+ DeleteVarSize(slot);
}
diff --git a/src/tag/TagPool.hxx b/src/tag/TagPool.hxx
index f41f3a724..990ee87bd 100644
--- a/src/tag/TagPool.hxx
+++ b/src/tag/TagPool.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/TagRva2.cxx b/src/tag/TagRva2.cxx
index 204001aa7..bbb6d11e6 100644
--- a/src/tag/TagRva2.cxx
+++ b/src/tag/TagRva2.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -21,10 +21,10 @@
#include "TagRva2.hxx"
#include "ReplayGainInfo.hxx"
+#include <id3tag.h>
+
#include <stdint.h>
#include <string.h>
-#include <glib.h>
-#include <id3tag.h>
enum rva2_channel {
CHANNEL_OTHER = 0x00,
diff --git a/src/tag/TagRva2.hxx b/src/tag/TagRva2.hxx
index 98154041a..df559f472 100644
--- a/src/tag/TagRva2.hxx
+++ b/src/tag/TagRva2.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -32,6 +32,6 @@ struct ReplayGainInfo;
* @return true on success
*/
bool
-tag_rva2_parse(struct id3_tag *tag, ReplayGainInfo &replay_gain_info);
+tag_rva2_parse(id3_tag *tag, ReplayGainInfo &replay_gain_info);
#endif
diff --git a/src/tag/TagSettings.c b/src/tag/TagSettings.c
index eb2db2ba5..e0c577c2b 100644
--- a/src/tag/TagSettings.c
+++ b/src/tag/TagSettings.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/TagSettings.h b/src/tag/TagSettings.h
index 245de2df5..33f89d4be 100644
--- a/src/tag/TagSettings.h
+++ b/src/tag/TagSettings.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
diff --git a/src/tag/TagString.cxx b/src/tag/TagString.cxx
index 9ab095249..4f07cd62a 100644
--- a/src/tag/TagString.cxx
+++ b/src/tag/TagString.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -19,52 +19,69 @@
#include "config.h"
#include "TagString.hxx"
-
-#include <glib.h>
+#include "util/Alloc.hxx"
+#include "util/WritableBuffer.hxx"
+#include "util/UTF8.hxx"
#include <assert.h>
#include <string.h>
+#include <stdlib.h>
+
+gcc_pure
+static const char *
+FindInvalidUTF8(const char *p, const char *const end)
+{
+ while (p < end) {
+ const size_t s = SequenceLengthUTF8(*p);
+ if (p + s > end)
+ /* partial sequence at end of string */
+ return p;
+
+ /* now call the other SequenceLengthUTF8() overload
+ which also validates the continuations */
+ const size_t t = SequenceLengthUTF8(p);
+ assert(s == t);
+ if (t == 0)
+ return p;
+
+ p += s;
+ }
+
+ return nullptr;
+}
/**
* Replace invalid sequences with the question mark.
*/
-static char *
-patch_utf8(const char *src, size_t length, const gchar *end)
+static WritableBuffer<char>
+patch_utf8(const char *src, size_t length, const char *_invalid)
{
/* duplicate the string, and replace invalid bytes in that
buffer */
- char *dest = g_strndup(src, length);
+ char *dest = (char *)xmemdup(src, length);
+ char *const end = dest + length;
+ char *invalid = dest + (_invalid - src);
do {
- dest[end - src] = '?';
- } while (!g_utf8_validate(end + 1, (src + length) - (end + 1), &end));
+ *invalid = '?';
+
+ const char *__invalid = FindInvalidUTF8(invalid + 1, end);
+ invalid = const_cast<char *>(__invalid);
+ } while (invalid != nullptr);
- return dest;
+ return { dest, length };
}
-static char *
+static WritableBuffer<char>
fix_utf8(const char *str, size_t length)
{
- const gchar *end;
- char *temp;
- gsize written;
-
- assert(str != nullptr);
-
/* check if the string is already valid UTF-8 */
- if (g_utf8_validate(str, length, &end))
+ const char *invalid = FindInvalidUTF8(str, str + length);
+ if (invalid == nullptr)
return nullptr;
- /* no, it's not - try to import it from ISO-Latin-1 */
- temp = g_convert(str, length, "utf-8", "iso-8859-1",
- nullptr, &written, nullptr);
- if (temp != nullptr)
- /* success! */
- return temp;
-
- /* no, still broken - there's no medication, just patch
- invalid sequences */
- return patch_utf8(str, length, end);
+ /* no, broken - patch invalid sequences */
+ return patch_utf8(str, length, invalid);
}
static bool
@@ -87,40 +104,36 @@ find_non_printable(const char *p, size_t length)
* Clears all non-printable characters, convert them to space.
* Returns nullptr if nothing needs to be cleared.
*/
-static char *
+static WritableBuffer<char>
clear_non_printable(const char *p, size_t length)
{
const char *first = find_non_printable(p, length);
- char *dest;
-
if (first == nullptr)
return nullptr;
- dest = g_strndup(p, length);
+ char *dest = (char *)xmemdup(p, length);
for (size_t i = first - p; i < length; ++i)
if (char_is_non_printable(dest[i]))
dest[i] = ' ';
- return dest;
+ return { dest, length };
}
-char *
+WritableBuffer<char>
FixTagString(const char *p, size_t length)
{
- char *utf8, *cleared;
-
- utf8 = fix_utf8(p, length);
- if (utf8 != nullptr) {
- p = utf8;
- length = strlen(p);
+ auto utf8 = fix_utf8(p, length);
+ if (!utf8.IsNull()) {
+ p = utf8.data;
+ length = utf8.size;
}
- cleared = clear_non_printable(p, length);
- if (cleared == nullptr)
+ WritableBuffer<char> cleared = clear_non_printable(p, length);
+ if (cleared.IsNull())
cleared = utf8;
else
- g_free(utf8);
+ free(utf8.data);
return cleared;
}
diff --git a/src/tag/TagString.hxx b/src/tag/TagString.hxx
index 79255dcd3..eccc2aa47 100644
--- a/src/tag/TagString.hxx
+++ b/src/tag/TagString.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -25,8 +25,10 @@
#include <stddef.h>
-gcc_malloc gcc_nonnull_all
-char *
+template<typename T> struct WritableBuffer;
+
+gcc_nonnull_all
+WritableBuffer<char>
FixTagString(const char *p, size_t length);
#endif
diff --git a/src/tag/TagTable.cxx b/src/tag/TagTable.cxx
index b51bc35ba..c6e1cff54 100644
--- a/src/tag/TagTable.cxx
+++ b/src/tag/TagTable.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -51,3 +51,13 @@ tag_table_lookup_i(const struct tag_table *table, const char *name)
return TAG_NUM_OF_ITEM_TYPES;
}
+
+const char *
+tag_table_lookup(const tag_table *table, TagType type)
+{
+ for (; table->name != nullptr; ++table)
+ if (table->type == type)
+ return table->name;
+
+ return nullptr;
+}
diff --git a/src/tag/TagTable.hxx b/src/tag/TagTable.hxx
index c050f61ac..095b4cbff 100644
--- a/src/tag/TagTable.hxx
+++ b/src/tag/TagTable.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -47,4 +47,13 @@ gcc_pure
TagType
tag_table_lookup_i(const tag_table *table, const char *name);
+/**
+ * Looks up a #TagType in a tag translation table and returns its
+ * string representation. Returns nullptr if the specified type was
+ * not found in the table.
+ */
+gcc_pure
+const char *
+tag_table_lookup(const tag_table *table, TagType type);
+
#endif
diff --git a/src/tag/TagType.h b/src/tag/TagType.h
index 0b8aa55d4..0aa6b4a51 100644
--- a/src/tag/TagType.h
+++ b/src/tag/TagType.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * 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
@@ -38,6 +38,7 @@ enum TagType
TAG_ARTIST,
TAG_ARTIST_SORT,
TAG_ALBUM,
+ TAG_ALBUM_SORT,
TAG_ALBUM_ARTIST,
TAG_ALBUM_ARTIST_SORT,
TAG_TITLE,
@@ -54,6 +55,7 @@ enum TagType
TAG_MUSICBRAINZ_ALBUMID,
TAG_MUSICBRAINZ_ALBUMARTISTID,
TAG_MUSICBRAINZ_TRACKID,
+ TAG_MUSICBRAINZ_RELEASETRACKID,
TAG_NUM_OF_ITEM_TYPES
};
diff --git a/src/tag/VorbisComment.cxx b/src/tag/VorbisComment.cxx
new file mode 100644
index 000000000..2dfc058d8
--- /dev/null
+++ b/src/tag/VorbisComment.cxx
@@ -0,0 +1,41 @@
+/*
+ * 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 "VorbisComment.hxx"
+#include "util/ASCII.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+const char *
+vorbis_comment_value(const char *entry, const char *name)
+{
+ assert(entry != nullptr);
+ assert(name != nullptr);
+ assert(*name != 0);
+
+ const size_t length = strlen(name);
+
+ if (StringEqualsCaseASCII(entry, name, length) &&
+ entry[length] == '=')
+ return entry + length + 1;
+
+ return nullptr;
+}
diff --git a/src/tag/VorbisComment.hxx b/src/tag/VorbisComment.hxx
new file mode 100644
index 000000000..1dd3371c8
--- /dev/null
+++ b/src/tag/VorbisComment.hxx
@@ -0,0 +1,34 @@
+/*
+ * 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_TAG_VORBIS_COMMENT_HXX
+#define MPD_TAG_VORBIS_COMMENT_HXX
+
+#include "check.h"
+#include "Compiler.h"
+
+/**
+ * Checks if the specified name matches the entry's name, and if yes,
+ * returns the comment value.
+ */
+gcc_pure
+const char *
+vorbis_comment_value(const char *entry, const char *name);
+
+#endif