From 060908d5c44ba54e0871a83973245ff44dc52103 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Mon, 12 Nov 2018 12:41:29 +0100
Subject: [PATCH] song/Filter: add operator "contains"

Closes #410
---
 NEWS                      |  1 +
 doc/protocol.rst          |  3 +++
 src/song/Filter.cxx       | 14 ++++++++++++++
 src/song/StringFilter.hxx |  4 +++-
 4 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index fa66df85a..53d81eeed 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
 ver 0.21.2 (not yet released)
 * protocol
   - operator "=~" matches a regular expression
+  - operator "contains" matches substrings
 * decoder
   - ffmpeg: require FFmpeg 3.1 or later
   - ffmpeg: fix broken sound with certain codecs
diff --git a/doc/protocol.rst b/doc/protocol.rst
index 4818b55fb..e0d750f29 100644
--- a/doc/protocol.rst
+++ b/doc/protocol.rst
@@ -154,6 +154,9 @@ of:
   ``AlbumArtist`` does not exist.
   ``VALUE`` is what to find.
 
+- ``(TAG contains 'VALUE')`` checks if the given value is a substring
+  of the tag value.
+
 - ``(TAG =~ 'VALUE')`` and ``(TAG !~ 'VALUE')`` use a Perl-compatible
   regular expression instead of doing a simple string comparison.
   (This feature is only available if :program:`MPD` was compiled with
diff --git a/src/song/Filter.cxx b/src/song/Filter.cxx
index 2c47ebb59..0725083a6 100644
--- a/src/song/Filter.cxx
+++ b/src/song/Filter.cxx
@@ -206,6 +206,20 @@ ExpectQuoted(const char *&s)
 static StringFilter
 ParseStringFilter(const char *&s, bool fold_case)
 {
+	if (auto after_contains = StringAfterPrefixIgnoreCase(s, "contains ")) {
+		s = StripLeft(after_contains);
+		auto value = ExpectQuoted(s);
+		return StringFilter(std::move(value),
+				    fold_case, true, false);
+	}
+
+	if (auto after_not_contains = StringAfterPrefixIgnoreCase(s, "!contains ")) {
+		s = StripLeft(after_not_contains);
+		auto value = ExpectQuoted(s);
+		return StringFilter(std::move(value),
+				    fold_case, true, true);
+	}
+
 	bool negated = false;
 
 #ifdef HAVE_PCRE
diff --git a/src/song/StringFilter.hxx b/src/song/StringFilter.hxx
index 0c7c3f5e8..b01a419a5 100644
--- a/src/song/StringFilter.hxx
+++ b/src/song/StringFilter.hxx
@@ -96,7 +96,9 @@ public:
 	const char *GetOperator() const noexcept {
 		return IsRegex()
 			? (negated ? "!~" : "=~")
-			: (negated ? "!=" : "==");
+			: (substring
+			   ? (negated ? "!contains" : "contains")
+			   : (negated ? "!=" : "=="));
 	}
 
 	gcc_pure