diff options
Diffstat (limited to '')
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 |