Merge branch 'v0.21.x'

This commit is contained in:
Max Kellermann 2020-03-12 08:11:08 +01:00
commit 01632d37ef
13 changed files with 93 additions and 54 deletions

View File

@ -1,6 +1,6 @@
language: cpp language: cpp
matrix: jobs:
include: include:
# Ubuntu Bionic (18.04) with GCC 7 # Ubuntu Bionic (18.04) with GCC 7
- os: linux - os: linux
@ -126,6 +126,7 @@ matrix:
packages: packages:
- ccache - ccache
- meson - meson
update: true
env: env:
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1" - MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"

12
NEWS
View File

@ -35,6 +35,18 @@ ver 0.22 (not yet released)
* switch to C++17 * switch to C++17
- GCC 7 or clang 4 (or newer) recommended - GCC 7 or clang 4 (or newer) recommended
ver 0.21.21 (not yet released)
* configuration
- fix bug in "metadata_to_use" setting
* playlist
- xspf: fix corrupt tags in the presence of XML entities
* archive
- iso9660: skip empty file names to work around libcdio bug
* decoder
- gme: ignore empty tags
* output
- solaris: port to NetBSD
ver 0.21.20 (2020/02/16) ver 0.21.20 (2020/02/16)
* decoder * decoder
- audiofile, ffmpeg, sndfile: handle MIME type "audio/wav" - audiofile, ffmpeg, sndfile: handle MIME type "audio/wav"

View File

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd" package="org.musicpd"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="43" android:versionCode="44"
android:versionName="0.21.20"> android:versionName="0.21.21">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>

View File

@ -28,6 +28,7 @@
#include "input/InputStream.hxx" #include "input/InputStream.hxx"
#include "fs/Path.hxx" #include "fs/Path.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
#include "util/StringCompare.hxx"
#include <cdio/iso9660.h> #include <cdio/iso9660.h>
@ -99,7 +100,10 @@ Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
auto *statbuf = (iso9660_stat_t *) auto *statbuf = (iso9660_stat_t *)
_cdio_list_node_data(entnode); _cdio_list_node_data(entnode);
const char *filename = statbuf->filename; const char *filename = statbuf->filename;
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) if (StringIsEmpty(filename) ||
PathTraitsUTF8::IsSpecialFilename(filename))
/* skip empty names (libcdio bug?) */
/* skip special names like "." and ".." */
continue; continue;
size_t filename_length = strlen(filename); size_t filename_length = strlen(filename);

View File

@ -24,6 +24,7 @@
#include "storage/StorageInterface.hxx" #include "storage/StorageInterface.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "fs/FileInfo.hxx" #include "fs/FileInfo.hxx"
#include "fs/Traits.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <string> #include <string>
@ -146,8 +147,7 @@ WatchDirectory::GetUriFS() const noexcept
/* we don't look at "." / ".." nor files with newlines in their name */ /* we don't look at "." / ".." nor files with newlines in their name */
static bool skip_path(const char *path) static bool skip_path(const char *path)
{ {
return (path[0] == '.' && path[1] == 0) || return PathTraitsFS::IsSpecialFilename(path) ||
(path[0] == '.' && path[1] == '.' && path[2] == 0) ||
strchr(path, '\n') != nullptr; strchr(path, '\n') != nullptr;
} }

View File

@ -219,7 +219,7 @@ try {
LogError(std::current_exception()); LogError(std::current_exception());
} }
/* we don't look at "." / ".." nor files with newlines in their name */ /* we don't look at files with newlines in their name */
gcc_pure gcc_pure
static bool static bool
skip_path(const char *name_utf8) noexcept skip_path(const char *name_utf8) noexcept

View File

@ -28,6 +28,7 @@
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "fs/FileSystem.hxx" #include "fs/FileSystem.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/StringCompare.hxx"
#include "util/StringFormat.hxx" #include "util/StringFormat.hxx"
#include "util/StringView.hxx" #include "util/StringView.hxx"
#include "util/UriExtract.hxx" #include "util/UriExtract.hxx"
@ -223,7 +224,7 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
if (track_count > 1) if (track_count > 1)
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", song_num + 1).c_str()); handler.OnTag(TAG_TRACK, StringFormat<16>("%u", song_num + 1).c_str());
if (info.song != nullptr) { if (!StringIsEmpty(info.song)) {
if (track_count > 1) { if (track_count > 1) {
/* start numbering subtunes from 1 */ /* start numbering subtunes from 1 */
const auto tag_title = const auto tag_title =
@ -235,16 +236,16 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
handler.OnTag(TAG_TITLE, info.song); handler.OnTag(TAG_TITLE, info.song);
} }
if (info.author != nullptr) if (!StringIsEmpty(info.author))
handler.OnTag(TAG_ARTIST, info.author); handler.OnTag(TAG_ARTIST, info.author);
if (info.game != nullptr) if (!StringIsEmpty(info.game))
handler.OnTag(TAG_ALBUM, info.game); handler.OnTag(TAG_ALBUM, info.game);
if (info.comment != nullptr) if (!StringIsEmpty(info.comment))
handler.OnTag(TAG_COMMENT, info.comment); handler.OnTag(TAG_COMMENT, info.comment);
if (info.copyright != nullptr) if (!StringIsEmpty(info.copyright))
handler.OnTag(TAG_DATE, info.copyright); handler.OnTag(TAG_DATE, info.copyright);
} }

View File

@ -108,6 +108,12 @@ struct PathTraitsFS {
return IsSeparator(*p); return IsSeparator(*p);
} }
gcc_pure gcc_nonnull_all
static bool IsSpecialFilename(const_pointer name) noexcept {
return (name[0] == '.' && name[1] == 0) ||
(name[0] == '.' && name[1] == '.' && name[2] == 0);
}
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static size_t GetLength(const_pointer p) noexcept { static size_t GetLength(const_pointer p) noexcept {
return StringLength(p); return StringLength(p);
@ -216,6 +222,12 @@ struct PathTraitsUTF8 {
return IsSeparator(*p); return IsSeparator(*p);
} }
gcc_pure gcc_nonnull_all
static bool IsSpecialFilename(const_pointer name) noexcept {
return (name[0] == '.' && name[1] == 0) ||
(name[0] == '.' && name[1] == '.' && name[2] == 0);
}
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static size_t GetLength(const_pointer p) noexcept { static size_t GetLength(const_pointer p) noexcept {
return StringLength(p); return StringLength(p);

View File

@ -22,22 +22,23 @@
#include "system/FileDescriptor.hxx" #include "system/FileDescriptor.hxx"
#include "system/Error.hxx" #include "system/Error.hxx"
#include <sys/ioctl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#ifdef __sun #if defined(__sun)
#include <sys/audio.h> #include <sys/audio.h>
#include <sys/stropts.h> #include <sys/stropts.h>
#elif defined(__NetBSD__)
#include <sys/audioio.h>
#else #else
/* some fake declarations that allow build this plugin on systems /* some fake declarations that allow build this plugin on systems
other than Solaris, just to see if it compiles */ other than Solaris, just to see if it compiles */
#include <sys/ioctl.h>
#ifndef I_FLUSH #ifndef I_FLUSH
#define I_FLUSH 0 #define I_FLUSH 0
#endif #endif
@ -147,7 +148,11 @@ SolarisOutput::Play(const void *chunk, size_t size)
void void
SolarisOutput::Cancel() noexcept SolarisOutput::Cancel() noexcept
{ {
#if defined(AUDIO_FLUSH)
ioctl(fd.Get(), AUDIO_FLUSH);
#elif defined(I_FLUSH)
ioctl(fd.Get(), I_FLUSH); ioctl(fd.Get(), I_FLUSH);
#endif
} }
const struct AudioOutputPlugin solaris_output_plugin = { const struct AudioOutputPlugin solaris_output_plugin = {

View File

@ -23,6 +23,7 @@
#include "song/DetachedSong.hxx" #include "song/DetachedSong.hxx"
#include "input/InputStream.hxx" #include "input/InputStream.hxx"
#include "tag/Builder.hxx" #include "tag/Builder.hxx"
#include "tag/Table.hxx"
#include "util/StringView.hxx" #include "util/StringView.hxx"
#include "lib/expat/ExpatParser.hxx" #include "lib/expat/ExpatParser.hxx"
@ -43,8 +44,8 @@ struct XspfParser {
*/ */
enum { enum {
ROOT, PLAYLIST, TRACKLIST, TRACK, ROOT, PLAYLIST, TRACKLIST, TRACK,
LOCATION, TAG, LOCATION,
} state; } state = ROOT;
/** /**
* The current tag within the "track" element. This is only * The current tag within the "track" element. This is only
@ -60,8 +61,20 @@ struct XspfParser {
TagBuilder tag_builder; TagBuilder tag_builder;
XspfParser() std::string value;
:state(ROOT) {} };
static constexpr struct tag_table xspf_tag_elements[] = {
{ "title", TAG_TITLE },
/* TAG_COMPOSER would be more correct according to the XSPF
spec */
{ "creator", TAG_ARTIST },
{ "annotation", TAG_COMMENT },
{ "album", TAG_ALBUM },
{ "trackNum", TAG_TRACK },
{ nullptr, TAG_NUM_OF_ITEM_TYPES }
}; };
static void XMLCALL static void XMLCALL
@ -69,6 +82,7 @@ xspf_start_element(void *user_data, const XML_Char *element_name,
gcc_unused const XML_Char **atts) gcc_unused const XML_Char **atts)
{ {
auto *parser = (XspfParser *)user_data; auto *parser = (XspfParser *)user_data;
parser->value.clear();
switch (parser->state) { switch (parser->state) {
case XspfParser::ROOT: case XspfParser::ROOT:
@ -87,7 +101,6 @@ xspf_start_element(void *user_data, const XML_Char *element_name,
if (strcmp(element_name, "track") == 0) { if (strcmp(element_name, "track") == 0) {
parser->state = XspfParser::TRACK; parser->state = XspfParser::TRACK;
parser->location.clear(); parser->location.clear();
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
} }
break; break;
@ -95,21 +108,16 @@ xspf_start_element(void *user_data, const XML_Char *element_name,
case XspfParser::TRACK: case XspfParser::TRACK:
if (strcmp(element_name, "location") == 0) if (strcmp(element_name, "location") == 0)
parser->state = XspfParser::LOCATION; parser->state = XspfParser::LOCATION;
else if (strcmp(element_name, "title") == 0) else if (!parser->location.empty()) {
parser->tag_type = TAG_TITLE; parser->tag_type = tag_table_lookup(xspf_tag_elements,
else if (strcmp(element_name, "creator") == 0) element_name);
/* TAG_COMPOSER would be more correct if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
according to the XSPF spec */ parser->state = XspfParser::TAG;
parser->tag_type = TAG_ARTIST; }
else if (strcmp(element_name, "annotation") == 0)
parser->tag_type = TAG_COMMENT;
else if (strcmp(element_name, "album") == 0)
parser->tag_type = TAG_ALBUM;
else if (strcmp(element_name, "trackNum") == 0)
parser->tag_type = TAG_TRACK;
break; break;
case XspfParser::TAG:
case XspfParser::LOCATION: case XspfParser::LOCATION:
break; break;
} }
@ -143,15 +151,26 @@ xspf_end_element(void *user_data, const XML_Char *element_name)
parser->tag_builder.Commit()); parser->tag_builder.Commit());
parser->state = XspfParser::TRACKLIST; parser->state = XspfParser::TRACKLIST;
} else }
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
break; break;
case XspfParser::TAG:
if (!parser->value.empty())
parser->tag_builder.AddItem(parser->tag_type,
StringView(parser->value.data(),
parser->value.length()));
parser->state = XspfParser::TRACK;
break;
case XspfParser::LOCATION: case XspfParser::LOCATION:
parser->location = std::move(parser->value);
parser->state = XspfParser::TRACK; parser->state = XspfParser::TRACK;
break; break;
} }
parser->value.clear();
} }
static void XMLCALL static void XMLCALL
@ -163,19 +182,12 @@ xspf_char_data(void *user_data, const XML_Char *s, int len)
case XspfParser::ROOT: case XspfParser::ROOT:
case XspfParser::PLAYLIST: case XspfParser::PLAYLIST:
case XspfParser::TRACKLIST: case XspfParser::TRACKLIST:
break;
case XspfParser::TRACK: case XspfParser::TRACK:
if (!parser->location.empty() &&
parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
parser->tag_builder.AddItem(parser->tag_type,
StringView(s, len));
break; break;
case XspfParser::TAG:
case XspfParser::LOCATION: case XspfParser::LOCATION:
parser->location.assign(s, len); parser->value.append(s, len);
break; break;
} }
} }

View File

@ -144,21 +144,12 @@ LocalStorage::OpenDirectory(const char *uri_utf8)
return std::make_unique<LocalDirectoryReader>(MapFSOrThrow(uri_utf8)); return std::make_unique<LocalDirectoryReader>(MapFSOrThrow(uri_utf8));
} }
gcc_pure
static bool
SkipNameFS(PathTraitsFS::const_pointer name_fs) noexcept
{
return name_fs[0] == '.' &&
(name_fs[1] == 0 ||
(name_fs[1] == '.' && name_fs[2] == 0));
}
const char * const char *
LocalDirectoryReader::Read() noexcept LocalDirectoryReader::Read() noexcept
{ {
while (reader.ReadEntry()) { while (reader.ReadEntry()) {
const Path name_fs = reader.GetEntry(); const Path name_fs = reader.GetEntry();
if (SkipNameFS(name_fs.c_str())) if (PathTraitsFS::IsSpecialFilename(name_fs.c_str()))
continue; continue;
try { try {

View File

@ -89,7 +89,7 @@ public:
} }
void Unset(TagType tag) noexcept { void Unset(TagType tag) noexcept {
*this |= ~TagMask(tag); *this &= ~TagMask(tag);
} }
}; };

View File

@ -37,6 +37,7 @@
#include <stdexcept> #include <stdexcept>
#include <assert.h> #include <assert.h>
#include <stdlib.h>
StringBuffer<64> StringBuffer<64>
FormatISO8601(const struct tm &tm) noexcept FormatISO8601(const struct tm &tm) noexcept