Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
10 changes: 6 additions & 4 deletions src/db/Selection.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@

#include "Selection.hxx"
#include "song/Filter.hxx"
#include "song/BaseSongFilter.hxx"

DatabaseSelection::DatabaseSelection(const char *_uri, bool _recursive,
const SongFilter *_filter) noexcept
:uri(_uri), filter(_filter), recursive(_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;
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/song/BaseSongFilter.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,24 @@

class BaseSongFilter final : public ISongFilter {
std::string value;
bool recursive;

public:

BaseSongFilter(const BaseSongFilter &) = default;

template<typename V>
explicit BaseSongFilter(V &&_value)
:value(std::forward<V>(_value)) {}
explicit BaseSongFilter(V &&_value, bool _recursive)
:value(std::forward<V>(_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<BaseSongFilter>(*this);
}
Expand Down
29 changes: 22 additions & 7 deletions src/song/Filter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum {
LOCATE_TAG_FILE_TYPE,
LOCATE_TAG_ANY_TYPE,
LOCATE_TAG_ADDED_SINCE,
LOCATE_TAG_DIRECTORY_TYPE,
};

/**
Expand All @@ -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;

Expand Down Expand Up @@ -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<BaseSongFilter>(std::move(value));
return std::make_unique<BaseSongFilter>(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<BaseSongFilter>(std::move(value), false);
} else if (type == LOCATE_TAG_AUDIO_FORMAT) {
bool mask;
if (s[0] == '=' && s[1] == '=')
Expand Down Expand Up @@ -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<BaseSongFilter>(value));
and_filter.AddItem(std::make_unique<BaseSongFilter>(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<BaseSongFilter>(value, false));
break;

case LOCATE_TAG_MODIFIED_SINCE:
Expand Down Expand Up @@ -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<const BaseSongFilter *>(i.get());
if (f != nullptr)
return f->GetValue();
return Base{f->GetValue(), f->IsRecursive()};
}

return nullptr;
return Base{nullptr, true};
}

SongFilter
Expand All @@ -586,7 +601,7 @@ SongFilter::WithoutBasePrefix(const std::string_view prefix) const noexcept
++s;

if (*s != 0)
result.and_filter.AddItem(std::make_unique<BaseSongFilter>(s));
result.and_filter.AddItem(std::make_unique<BaseSongFilter>(s, f->IsRecursive()));

continue;
}
Expand Down
11 changes: 8 additions & 3 deletions src/song/Filter.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down