diff --git a/doc/protocol.rst b/doc/protocol.rst index 5272fc4a3c..8a219af2f1 100644 --- a/doc/protocol.rst +++ b/doc/protocol.rst @@ -213,6 +213,8 @@ of: songs in the given directory (relative to the music directory). +- ``(directory 'VALUE')``: non-recursive version of ``(base 'VALUE')``. + - ``(modified-since 'VALUE')``: compares the file's time stamp with the given value (ISO 8601 or UNIX time stamp). diff --git a/src/db/Selection.cxx b/src/db/Selection.cxx index 4df4d9ed8d..a5bac32f72 100644 --- a/src/db/Selection.cxx +++ b/src/db/Selection.cxx @@ -3,6 +3,7 @@ #include "Selection.hxx" #include "song/Filter.hxx" +#include "song/BaseSongFilter.hxx" DatabaseSelection::DatabaseSelection(const char *_uri, bool _recursive, const SongFilter *_filter) noexcept @@ -10,10 +11,11 @@ DatabaseSelection::DatabaseSelection(const char *_uri, bool _recursive, { /* optimization: if the caller didn't specify a base URI, pick the one from SongFilter */ - if (uri.empty() && filter != nullptr) { - auto base = filter->GetBase(); - if (base != nullptr) - uri = base; + if (filter != nullptr) { + Base base = filter->GetBase(); + if (uri.empty() && base.uri != nullptr) + uri = base.uri; + recursive = base.recursive; } } diff --git a/src/song/BaseSongFilter.hxx b/src/song/BaseSongFilter.hxx index c5ada242d6..7e3baa897d 100644 --- a/src/song/BaseSongFilter.hxx +++ b/src/song/BaseSongFilter.hxx @@ -8,18 +8,24 @@ class BaseSongFilter final : public ISongFilter { std::string value; + bool recursive; public: + BaseSongFilter(const BaseSongFilter &) = default; template - explicit BaseSongFilter(V &&_value) - :value(std::forward(_value)) {} + explicit BaseSongFilter(V &&_value, bool _recursive) + :value(std::forward(_value)), recursive(_recursive) {} const char *GetValue() const noexcept { return value.c_str(); } + bool IsRecursive() const noexcept { + return recursive; + } + ISongFilterPtr Clone() const noexcept override { return std::make_unique(*this); } diff --git a/src/song/Filter.cxx b/src/song/Filter.cxx index fe05592927..1dd42b98ee 100644 --- a/src/song/Filter.cxx +++ b/src/song/Filter.cxx @@ -43,6 +43,7 @@ enum { LOCATE_TAG_FILE_TYPE, LOCATE_TAG_ANY_TYPE, LOCATE_TAG_ADDED_SINCE, + LOCATE_TAG_DIRECTORY_TYPE, }; /** @@ -62,6 +63,9 @@ locate_parse_type(const char *str) noexcept if (strcmp(str, "base") == 0) return LOCATE_TAG_BASE_TYPE; + if (strcmp(str, "directory") == 0) + return LOCATE_TAG_DIRECTORY_TYPE; + if (strcmp(str, "modified-since") == 0) return LOCATE_TAG_MODIFIED_SINCE; @@ -385,7 +389,13 @@ SongFilter::ParseExpression(const char *&s, bool fold_case, bool strip_diacritic throw std::runtime_error("')' expected"); s = StripLeft(s + 1); - return std::make_unique(std::move(value)); + return std::make_unique(std::move(value), true); + } else if (type == LOCATE_TAG_DIRECTORY_TYPE) { + auto value = ExpectQuoted(s); + if (*s != ')') + throw std::runtime_error("')' expected"); + s = StripLeft(s + 1); + return std::make_unique(std::move(value), false); } else if (type == LOCATE_TAG_AUDIO_FORMAT) { bool mask; if (s[0] == '=' && s[1] == '=') @@ -456,7 +466,13 @@ SongFilter::Parse(const char *tag_string, const char *value, bool fold_case, boo if (!uri_safe_local(value)) throw std::runtime_error("Bad URI"); - and_filter.AddItem(std::make_unique(value)); + and_filter.AddItem(std::make_unique(value, true)); + break; + + case LOCATE_TAG_DIRECTORY_TYPE: + if (!uri_safe_local(value)) + throw std::runtime_error("Bad URI"); + and_filter.AddItem(std::make_unique(value, false)); break; case LOCATE_TAG_MODIFIED_SINCE: @@ -557,16 +573,15 @@ SongFilter::HasFoldCase() const noexcept }); } -const char * +const Base SongFilter::GetBase() const noexcept { for (const auto &i : and_filter.GetItems()) { const auto *f = dynamic_cast(i.get()); if (f != nullptr) - return f->GetValue(); + return Base{f->GetValue(), f->IsRecursive()}; } - - return nullptr; + return Base{nullptr, true}; } SongFilter @@ -586,7 +601,7 @@ SongFilter::WithoutBasePrefix(const std::string_view prefix) const noexcept ++s; if (*s != 0) - result.and_filter.AddItem(std::make_unique(s)); + result.and_filter.AddItem(std::make_unique(s, f->IsRecursive())); continue; } diff --git a/src/song/Filter.hxx b/src/song/Filter.hxx index 8139bd3e02..30b902975a 100644 --- a/src/song/Filter.hxx +++ b/src/song/Filter.hxx @@ -29,6 +29,11 @@ enum TagType : uint8_t; struct LightSong; +/** + * Base URI and recursive flag. + */ +struct Base { const char *uri; bool recursive; }; + class SongFilter { AndSongFilter and_filter; @@ -80,11 +85,11 @@ public: bool HasFoldCase() const noexcept; /** - * Returns the "base" specification (if there is one) or - * nullptr. + * Returns the "base" specification, containing URI (if any) and + * recursive flag (default to true). */ [[gnu::pure]] - const char *GetBase() const noexcept; + const Base GetBase() const noexcept; /** * Create a copy of the filter with the given prefix stripped