Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
aa0e4500c6 | ||
![]() |
4e6b8edf72 | ||
![]() |
ac0852b4e3 | ||
![]() |
6fe43ed969 | ||
![]() |
b34bc06624 | ||
![]() |
08e41e60e5 | ||
![]() |
10ec478a9c | ||
![]() |
86f1074905 | ||
![]() |
8e66b855a3 | ||
![]() |
e3bc85d7bf | ||
![]() |
6f242836e6 | ||
![]() |
f2c926f3b6 | ||
![]() |
aba18924ee | ||
![]() |
aa6bef54dd | ||
![]() |
528f5b9cb9 | ||
![]() |
96ae0ec93a | ||
![]() |
5a5229b499 | ||
![]() |
bba22c9c8c | ||
![]() |
694c437a2c | ||
![]() |
587172efa3 | ||
![]() |
2a926063b2 | ||
![]() |
d6f239e54f | ||
![]() |
b8989fafeb |
.gitignoreNEWS
android
autogen.shdoc
meson.buildmeson_options.txtpython/build
src
lib
net
song
BaseSongFilter.cxxEscape.cxxEscape.hxxFilter.cxxStringFilter.cxxStringFilter.hxxTagSongFilter.cxxTagSongFilter.hxxUriSongFilter.cxxUriSongFilter.hxxmeson.build
system
zeroconf
test
win32
80
.gitignore
vendored
80
.gitignore
vendored
@@ -1,88 +1,8 @@
|
||||
*.Plo
|
||||
*.Po
|
||||
*.a
|
||||
*.d
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
*.exe
|
||||
|
||||
*~
|
||||
|
||||
.#*
|
||||
.stgit*
|
||||
.deps
|
||||
.dirstamp
|
||||
|
||||
tags
|
||||
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/aclocal.m4
|
||||
/autom4te.cache
|
||||
/config.h
|
||||
/config.h.in
|
||||
/config.log
|
||||
/config.mk
|
||||
/config.status
|
||||
/config_detected.h
|
||||
/config_detected.mk
|
||||
/configure
|
||||
/configure.lineno
|
||||
/depmode
|
||||
/libtool
|
||||
/ltmain.sh
|
||||
/mkinstalldirs
|
||||
/output/
|
||||
/src/mpd
|
||||
/systemd/system/mpd.service
|
||||
/systemd/user/mpd.service
|
||||
/stamp-h1
|
||||
|
||||
/src/dsd2pcm/dsd2pcm
|
||||
/src/win32/mpd_win32_rc.rc
|
||||
|
||||
/doc/doxygen.conf
|
||||
/doc/protocol.html
|
||||
/doc/protocol
|
||||
/doc/user
|
||||
/doc/developer
|
||||
/doc/sticker
|
||||
/doc/api
|
||||
|
||||
/test/software_volume
|
||||
/test/run_convert
|
||||
/test/run_decoder
|
||||
/test/read_tags
|
||||
/test/run_filter
|
||||
/test/run_encoder
|
||||
/test/run_output
|
||||
/test/read_conf
|
||||
/test/run_input
|
||||
/test/read_mixer
|
||||
/test/dump_playlist
|
||||
/test/run_normalize
|
||||
/test/tmp
|
||||
/test/run_inotify
|
||||
/test/test_queue_priority
|
||||
/test/test_protocol
|
||||
/test/run_ntp_server
|
||||
/test/run_resolver
|
||||
/test/run_tcp_connect
|
||||
/test/test_pcm
|
||||
/test/dump_rva2
|
||||
/test/dump_text_file
|
||||
/test/test_util
|
||||
/test/test_byte_reverse
|
||||
/test/test_mixramp
|
||||
/test/test_vorbis_encoder
|
||||
/test/DumpDatabase
|
||||
|
||||
/lib/
|
||||
|
||||
/*.tar.gz
|
||||
/*.tar.bz2
|
||||
/*.tar.xz
|
||||
/mpd-*/
|
||||
|
||||
__pycache__/
|
||||
|
15
NEWS
15
NEWS
@@ -1,3 +1,18 @@
|
||||
ver 0.21.1 (2018/11/04)
|
||||
* protocol
|
||||
- allow escaping quotes in filter expressions
|
||||
- operator "==" never searches substrings in filter expressions
|
||||
* decoder
|
||||
- ffmpeg: fix build failure with non-standard FFmpeg installation path
|
||||
- flac: fix linker failure when building without FLAC support
|
||||
* encoder
|
||||
- vorbis: fix linker failure when building without Vorbis decoder
|
||||
* fix build failure on Linux-PowerPC
|
||||
* fix build failure on FreeBSD
|
||||
* eliminate DLL dependencies on Windows
|
||||
* add warning about buggy Boost version 1.67
|
||||
* require Meson 0.47.2 because a Meson 0.47.1 bug breaks our build
|
||||
|
||||
ver 0.21 (2018/10/31)
|
||||
* configuration
|
||||
- add "include" directive, allows including config files
|
||||
|
1
android/.gitignore
vendored
1
android/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
11
autogen.sh
11
autogen.sh
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf config.cache build
|
||||
mkdir build
|
||||
|
||||
aclocal -I m4 $ACLOCAL_FLAGS
|
||||
autoheader
|
||||
automake --add-missing $AUTOMAKE_FLAGS
|
||||
autoconf
|
2
doc/.gitignore
vendored
2
doc/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
/html/
|
||||
/doctrees/
|
@@ -38,9 +38,9 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.21'
|
||||
version = '0.21.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.21~git'
|
||||
release = version
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@@ -175,15 +175,49 @@ of:
|
||||
matches the audio format with the given mask (i.e. one
|
||||
or more attributes may be "*").
|
||||
|
||||
- ``(!EXPRESSION)``: negate an expression.
|
||||
- ``(!EXPRESSION)``: negate an expression. Note that each expression
|
||||
must be enclosed in parantheses, e.g. :code:`(!(artist == 'VALUE'))`
|
||||
(which is equivalent to :code:`(artist != 'VALUE')`)
|
||||
|
||||
- ``(EXPRESSION1 AND EXPRESSION2 ...)``: combine two or
|
||||
more expressions with logical "and".
|
||||
more expressions with logical "and". Note that each expression must
|
||||
be enclosed in parantheses, e.g. :code:`((artist == 'FOO') AND
|
||||
(album == 'BAR'))`
|
||||
|
||||
Prior to MPD 0.21, the syntax looked like this::
|
||||
|
||||
find TYPE VALUE
|
||||
|
||||
Escaping String Values
|
||||
----------------------
|
||||
|
||||
String values are quoted with single or double quotes, and special
|
||||
characters within those values must be escaped with the backslash
|
||||
(``\``). Keep in mind that the backslash is also the escape character
|
||||
on the protocol level, which means you may need to use double
|
||||
backslash.
|
||||
|
||||
Example expression which matches an artist named ``foo'bar"``::
|
||||
|
||||
(artist "foo\'bar\"")
|
||||
|
||||
At the protocol level, the command must look like this::
|
||||
|
||||
find "(artist \"foo\\'bar\\\"\")"
|
||||
|
||||
The double quotes enclosing the artist name must be escaped because
|
||||
they are inside a double-quoted ``find`` parameter. The single quote
|
||||
inside that artist name must be escaped with two backslashes; one to
|
||||
escape the single quote, and another one because the backslash inside
|
||||
the string inside the parameter needs to be escaped as well. The
|
||||
double quote has three confusing backslashes: two to build one
|
||||
backslash, and another one to escape the double quote on the protocol
|
||||
level. Phew!
|
||||
|
||||
To reduce confusion, you should use a library such as `libmpdclient
|
||||
<https://www.musicpd.org/libs/libmpdclient/>`_ which escapes command
|
||||
arguments for you.
|
||||
|
||||
.. _tags:
|
||||
|
||||
Tags
|
||||
@@ -361,7 +395,7 @@ Querying ``MPD``'s status
|
||||
- ``consume`` [#since_0_15]_: ``0`` or ``1``
|
||||
- ``playlist``: 31-bit unsigned integer, the playlist version number
|
||||
- ``playlistlength``: integer, the length of the playlist
|
||||
- ``state``: ``play``, ``stop, or ``pause``
|
||||
- ``state``: ``play``, ``stop``, or ``pause``
|
||||
- ``song``: playlist song number of the current song stopped on or playing
|
||||
- ``songid``: playlist songid of the current song stopped on or playing
|
||||
- ``nextsong`` [#since_0_15]_: playlist song number of the next song to be played
|
||||
|
@@ -54,7 +54,7 @@ Download the source tarball from the `MPD home page <https://musicpd.org>`_ and
|
||||
In any case, you need:
|
||||
|
||||
* a C++14 compiler (e.g. gcc 6.0 or clang 3.9)
|
||||
* `Meson 0.47 <http://mesonbuild.com/>`__ and `Ninja
|
||||
* `Meson 0.47.2 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* Boost 1.58
|
||||
* pkg-config
|
||||
|
@@ -1,8 +1,8 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.21',
|
||||
meson_version: '>= 0.47',
|
||||
version: '0.21.1',
|
||||
meson_version: '>= 0.47.2',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
'cpp_std=c++14'
|
||||
@@ -172,6 +172,11 @@ inc = include_directories(
|
||||
)
|
||||
|
||||
boost_dep = dependency('boost', version: '>= 1.58')
|
||||
if boost_dep.version() == '1.67'
|
||||
# https://github.com/MusicPlayerDaemon/MPD/pull/384
|
||||
# https://github.com/boostorg/lockfree/commit/12726cda009a855073b9bedbdce57b6ce7763da2
|
||||
warning('Your Boost version 1.67 is known to be buggy, and the MPD build will fail. Please upgrade to Boost 1.68 or later.')
|
||||
endif
|
||||
|
||||
sources = [
|
||||
version_cxx,
|
||||
|
@@ -133,7 +133,7 @@ option('wavpack', type: 'feature', description: 'WavPack decoder plugin')
|
||||
option('wildmidi', type: 'feature', description: 'WildMidi decoder plugin')
|
||||
|
||||
#
|
||||
# Decoder plugins
|
||||
# Encoder plugins
|
||||
#
|
||||
|
||||
option('vorbisenc', type: 'feature', description: 'Vorbis encoder plugin')
|
||||
|
@@ -18,5 +18,5 @@ class ZlibProject(Project):
|
||||
'INCLUDE_PATH='+ os.path.join(toolchain.install_prefix, 'include'),
|
||||
'LIBRARY_PATH=' + os.path.join(toolchain.install_prefix, 'lib'),
|
||||
'BINARY_PATH=' + os.path.join(toolchain.install_prefix, 'bin'),
|
||||
'SHARED_MODE=1'],
|
||||
],
|
||||
cwd=src, env=toolchain.env)
|
||||
|
@@ -26,4 +26,9 @@ ffmpeg = static_library(
|
||||
|
||||
ffmpeg_dep = declare_dependency(
|
||||
link_with: ffmpeg,
|
||||
dependencies: [
|
||||
libavformat_dep,
|
||||
libavcodec_dep,
|
||||
libavutil_dep,
|
||||
],
|
||||
)
|
||||
|
@@ -2,10 +2,15 @@ libflac_dep = dependency('flac', version: '>= 1.2', required: get_option('flac')
|
||||
libopus_dep = dependency('opus', required: get_option('opus'))
|
||||
libvorbis_dep = dependency('vorbis', required: get_option('vorbis'))
|
||||
|
||||
if need_encoder
|
||||
libvorbisenc_dep = dependency('vorbisenc', required: get_option('vorbisenc'))
|
||||
else
|
||||
libvorbisenc_dep = dependency('', required: false)
|
||||
libvorbisenc_dep = dependency('', required: false)
|
||||
if need_encoder and not get_option('vorbisenc').disabled()
|
||||
if libvorbis_dep.found()
|
||||
libvorbisenc_dep = dependency('vorbisenc', required: get_option('vorbisenc'))
|
||||
elif get_option('vorbisenc').enabled()
|
||||
error('Cannot build the Vorbis encoder without the Vorbis decoder')
|
||||
else
|
||||
message('Disabling the Vorbis encoder because the Vorbis decoder is disabled as well')
|
||||
endif
|
||||
endif
|
||||
|
||||
if libopus_dep.found() or libvorbis_dep.found() or libvorbisenc_dep.found()
|
||||
@@ -14,7 +19,7 @@ else
|
||||
libogg_dep = dependency('', required: false)
|
||||
endif
|
||||
|
||||
if not libogg_dep.found() or not libflac_dep.found()
|
||||
if not libogg_dep.found() and not libflac_dep.found()
|
||||
xiph_dep = dependency('', required: false)
|
||||
ogg_dep = dependency('', required: false)
|
||||
flac_dep = dependency('', required: false)
|
||||
|
@@ -53,20 +53,6 @@ class IPv4Address {
|
||||
uint8_t c, uint8_t d) noexcept {
|
||||
return {{{ a, b, c, d }}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x the 32 bit IP address in network byte order
|
||||
*/
|
||||
static constexpr struct in_addr ConstructInAddrBE(uint32_t x) noexcept {
|
||||
return (struct in_addr){{.S_addr=x}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x the 32 bit IP address in host byte order
|
||||
*/
|
||||
static constexpr struct in_addr ConstructInAddr(uint32_t x) noexcept {
|
||||
return ConstructInAddr(x >> 24, x >> 16, x >> 8, x);
|
||||
}
|
||||
#else
|
||||
|
||||
#ifdef __BIONIC__
|
||||
@@ -78,11 +64,19 @@ class IPv4Address {
|
||||
return ToBE32((a << 24) | (b << 16) | (c << 8) | d);
|
||||
}
|
||||
|
||||
static constexpr struct in_addr ConstructInAddr(uint8_t a, uint8_t b,
|
||||
uint8_t c, uint8_t d) noexcept {
|
||||
return { ConstructInAddrT(a, b, c, d) };
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @param x the 32 bit IP address in network byte order
|
||||
*/
|
||||
static constexpr struct in_addr ConstructInAddrBE(uint32_t x) noexcept {
|
||||
return { x };
|
||||
struct in_addr ia{};
|
||||
ia.s_addr = x;
|
||||
return ia;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,26 +86,16 @@ class IPv4Address {
|
||||
return ConstructInAddrBE(ToBE32(x));
|
||||
}
|
||||
|
||||
static constexpr struct in_addr ConstructInAddr(uint8_t a, uint8_t b,
|
||||
uint8_t c, uint8_t d) noexcept {
|
||||
return { ConstructInAddrT(a, b, c, d) };
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @param port the port number in host byte order
|
||||
*/
|
||||
static constexpr struct sockaddr_in Construct(struct in_addr address,
|
||||
uint16_t port) noexcept {
|
||||
return {
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
|
||||
sizeof(struct sockaddr_in),
|
||||
#endif
|
||||
AF_INET,
|
||||
ToBE16(port),
|
||||
address,
|
||||
{},
|
||||
};
|
||||
struct sockaddr_in sin{};
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = ToBE16(port);
|
||||
sin.sin_addr = address;
|
||||
return sin;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -68,16 +68,12 @@ class IPv6Address {
|
||||
static constexpr struct sockaddr_in6 Construct(struct in6_addr address,
|
||||
uint16_t port,
|
||||
uint32_t scope_id) noexcept {
|
||||
return {
|
||||
#if defined(__APPLE__)
|
||||
sizeof(struct sockaddr_in6),
|
||||
#endif
|
||||
AF_INET6,
|
||||
ToBE16(port),
|
||||
0,
|
||||
address,
|
||||
scope_id,
|
||||
};
|
||||
struct sockaddr_in6 sin{};
|
||||
sin.sin6_family = AF_INET6;
|
||||
sin.sin6_port = ToBE16(port);
|
||||
sin.sin6_addr = address;
|
||||
sin.sin6_scope_id = scope_id;
|
||||
return sin;
|
||||
}
|
||||
|
||||
public:
|
||||
|
@@ -10,14 +10,6 @@ if have_tcp and not get_option('ipv6').disabled()
|
||||
if not have_ipv6 and get_option('ipv6').enabled()
|
||||
error('IPv6 not supported by OS')
|
||||
endif
|
||||
|
||||
conf.set('HAVE_STRUCT_SOCKADDR_IN_SIN_LEN', c_compiler.has_member('struct sockaddr_in', 'sin_len', prefix: '''
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif'''))
|
||||
else
|
||||
have_ipv6 = false
|
||||
endif
|
||||
|
@@ -19,13 +19,14 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "BaseSongFilter.hxx"
|
||||
#include "Escape.hxx"
|
||||
#include "LightSong.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
|
||||
std::string
|
||||
BaseSongFilter::ToExpression() const noexcept
|
||||
{
|
||||
return "(base \"" + value + "\")";
|
||||
return "(base \"" + EscapeFilterString(value) + "\")";
|
||||
}
|
||||
|
||||
bool
|
||||
|
41
src/song/Escape.cxx
Normal file
41
src/song/Escape.cxx
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2003-2018 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 "Escape.hxx"
|
||||
|
||||
static constexpr bool
|
||||
MustEscape(char ch) noexcept
|
||||
{
|
||||
return ch == '"' || ch == '\'' || ch == '\\';
|
||||
}
|
||||
|
||||
std::string
|
||||
EscapeFilterString(const std::string &src) noexcept
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(src.length() + 16);
|
||||
|
||||
for (char ch : src) {
|
||||
if (MustEscape(ch))
|
||||
result.push_back('\\');
|
||||
result.push_back(ch);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
31
src/song/Escape.hxx
Normal file
31
src/song/Escape.hxx
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2003-2018 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_SONG_ESCAPE_HXX
|
||||
#define MPD_SONG_ESCAPE_HXX
|
||||
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
gcc_pure
|
||||
std::string
|
||||
EscapeFilterString(const std::string &src) noexcept;
|
||||
|
||||
#endif
|
@@ -91,8 +91,11 @@ locate_parse_type(const char *str) noexcept
|
||||
|
||||
SongFilter::SongFilter(TagType tag, const char *value, bool fold_case)
|
||||
{
|
||||
/* for compatibility with MPD 0.20 and older, "fold_case" also
|
||||
switches on "substring" */
|
||||
and_filter.AddItem(std::make_unique<TagSongFilter>(tag, value,
|
||||
fold_case, false));
|
||||
fold_case, fold_case,
|
||||
false));
|
||||
}
|
||||
|
||||
SongFilter::~SongFilter()
|
||||
@@ -173,13 +176,26 @@ ExpectQuoted(const char *&s)
|
||||
if (!IsQuote(quote))
|
||||
throw std::runtime_error("Quoted string expected");
|
||||
|
||||
const char *begin = s;
|
||||
const char *end = strchr(s, quote);
|
||||
if (end == nullptr)
|
||||
throw std::runtime_error("Closing quote not found");
|
||||
char buffer[4096];
|
||||
size_t length = 0;
|
||||
|
||||
s = StripLeft(end + 1);
|
||||
return {begin, end};
|
||||
while (*s != quote) {
|
||||
if (*s == '\\')
|
||||
/* backslash escapes the following character */
|
||||
++s;
|
||||
|
||||
if (*s == 0)
|
||||
throw std::runtime_error("Closing quote not found");
|
||||
|
||||
buffer[length++] = *s++;
|
||||
|
||||
if (length >= sizeof(buffer))
|
||||
throw std::runtime_error("Quoted value is too long");
|
||||
}
|
||||
|
||||
s = StripLeft(s + 1);
|
||||
|
||||
return {buffer, length};
|
||||
}
|
||||
|
||||
ISongFilterPtr
|
||||
@@ -283,11 +299,13 @@ SongFilter::ParseExpression(const char *&s, bool fold_case)
|
||||
if (type == LOCATE_TAG_FILE_TYPE)
|
||||
return std::make_unique<UriSongFilter>(std::move(value),
|
||||
fold_case,
|
||||
false,
|
||||
negated);
|
||||
|
||||
return std::make_unique<TagSongFilter>(TagType(type),
|
||||
std::move(value),
|
||||
fold_case, negated);
|
||||
fold_case, false,
|
||||
negated);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +330,10 @@ SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
|
||||
break;
|
||||
|
||||
case LOCATE_TAG_FILE_TYPE:
|
||||
/* for compatibility with MPD 0.20 and older,
|
||||
"fold_case" also switches on "substring" */
|
||||
and_filter.AddItem(std::make_unique<UriSongFilter>(value,
|
||||
fold_case,
|
||||
fold_case,
|
||||
false));
|
||||
break;
|
||||
@@ -321,9 +342,12 @@ SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
|
||||
if (tag == LOCATE_TAG_ANY_TYPE)
|
||||
tag = TAG_NUM_OF_ITEM_TYPES;
|
||||
|
||||
/* for compatibility with MPD 0.20 and older,
|
||||
"fold_case" also switches on "substring" */
|
||||
and_filter.AddItem(std::make_unique<TagSongFilter>(TagType(tag),
|
||||
value,
|
||||
fold_case,
|
||||
fold_case,
|
||||
false));
|
||||
break;
|
||||
}
|
||||
|
@@ -32,8 +32,12 @@ StringFilter::Match(const char *s) const noexcept
|
||||
#endif
|
||||
|
||||
if (fold_case) {
|
||||
return fold_case.IsIn(s);
|
||||
return substring
|
||||
? fold_case.IsIn(s)
|
||||
: fold_case == s;
|
||||
} else {
|
||||
return StringIsEqual(s, value.c_str());
|
||||
return substring
|
||||
? StringFind(s, value.c_str()) != nullptr
|
||||
: value == s;
|
||||
}
|
||||
}
|
||||
|
@@ -33,13 +33,19 @@ class StringFilter {
|
||||
*/
|
||||
IcuCompare fold_case;
|
||||
|
||||
/**
|
||||
* Search for substrings instead of matching the whole string?
|
||||
*/
|
||||
bool substring;
|
||||
|
||||
public:
|
||||
template<typename V>
|
||||
StringFilter(V &&_value, bool _fold_case)
|
||||
StringFilter(V &&_value, bool _fold_case, bool _substring)
|
||||
:value(std::forward<V>(_value)),
|
||||
fold_case(_fold_case
|
||||
? IcuCompare(value.c_str())
|
||||
: IcuCompare()) {}
|
||||
: IcuCompare()),
|
||||
substring(_substring) {}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return value.empty();
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "TagSongFilter.hxx"
|
||||
#include "Escape.hxx"
|
||||
#include "LightSong.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "tag/Fallback.hxx"
|
||||
@@ -30,7 +31,7 @@ TagSongFilter::ToExpression() const noexcept
|
||||
? "any"
|
||||
: tag_item_names[type];
|
||||
|
||||
return std::string("(") + name + " " + (negated ? "!=" : "==") + " \"" + filter.GetValue() + "\")";
|
||||
return std::string("(") + name + " " + (negated ? "!=" : "==") + " \"" + EscapeFilterString(filter.GetValue()) + "\")";
|
||||
}
|
||||
|
||||
bool
|
||||
|
@@ -42,9 +42,10 @@ class TagSongFilter final : public ISongFilter {
|
||||
|
||||
public:
|
||||
template<typename V>
|
||||
TagSongFilter(TagType _type, V &&_value, bool fold_case, bool _negated)
|
||||
TagSongFilter(TagType _type, V &&_value, bool fold_case, bool substring,
|
||||
bool _negated)
|
||||
:type(_type), negated(_negated),
|
||||
filter(std::forward<V>(_value), fold_case) {}
|
||||
filter(std::forward<V>(_value), fold_case, substring) {}
|
||||
|
||||
TagType GetTagType() const {
|
||||
return type;
|
||||
|
@@ -19,12 +19,13 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "UriSongFilter.hxx"
|
||||
#include "Escape.hxx"
|
||||
#include "LightSong.hxx"
|
||||
|
||||
std::string
|
||||
UriSongFilter::ToExpression() const noexcept
|
||||
{
|
||||
return std::string("(file ") + (negated ? "!=" : "==") + " \"" + filter.GetValue() + "\")";
|
||||
return std::string("(file ") + (negated ? "!=" : "==") + " \"" + EscapeFilterString(filter.GetValue()) + "\")";
|
||||
}
|
||||
|
||||
bool
|
||||
|
@@ -32,8 +32,9 @@ class UriSongFilter final : public ISongFilter {
|
||||
|
||||
public:
|
||||
template<typename V>
|
||||
UriSongFilter(V &&_value, bool fold_case, bool _negated)
|
||||
:filter(std::forward<V>(_value), fold_case),
|
||||
UriSongFilter(V &&_value, bool fold_case, bool substring,
|
||||
bool _negated)
|
||||
:filter(std::forward<V>(_value), fold_case, substring),
|
||||
negated(_negated) {}
|
||||
|
||||
const auto &GetValue() const noexcept {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
song = static_library(
|
||||
'song',
|
||||
'DetachedSong.cxx',
|
||||
'Escape.cxx',
|
||||
'StringFilter.cxx',
|
||||
'UriSongFilter.cxx',
|
||||
'BaseSongFilter.cxx',
|
||||
|
@@ -76,7 +76,7 @@ FileDescriptor::IsSocket() const noexcept
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
|
||||
bool
|
||||
FileDescriptor::Open(FileDescriptor dir, const char *pathname,
|
||||
|
@@ -116,7 +116,7 @@ public:
|
||||
return FileDescriptor(-1);
|
||||
}
|
||||
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
bool Open(FileDescriptor dir, const char *pathname,
|
||||
int flags, mode_t mode=0666) noexcept;
|
||||
#endif
|
||||
|
@@ -32,6 +32,12 @@
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef HOST_NAME_MAX
|
||||
/* HOST_NAME_MAX is not a portable macro; it is undefined on some
|
||||
systems */
|
||||
#define HOST_NAME_MAX 255
|
||||
#endif
|
||||
|
||||
static constexpr Domain zeroconf_domain("zeroconf");
|
||||
|
||||
/* The default service name to publish
|
||||
|
2
test/.gitignore
vendored
2
test/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
/run_neighbor_explorer
|
||||
/ReadApeTags
|
@@ -62,7 +62,8 @@ class CrossGccToolchain:
|
||||
self.cxxflags = common_flags
|
||||
self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include') + \
|
||||
' -DWINVER=0x0600 -D_WIN32_WINNT=0x0600'
|
||||
self.ldflags = '-L' + os.path.join(install_prefix, 'lib')
|
||||
self.ldflags = '-L' + os.path.join(install_prefix, 'lib') + \
|
||||
' -static-libstdc++ -static-libgcc'
|
||||
self.libs = ''
|
||||
|
||||
self.is_arm = arch.startswith('arm')
|
||||
|
Reference in New Issue
Block a user