Commit 333d226e authored by Max Kellermann's avatar Max Kellermann

SongFilter: convert to a C++ class

parent 04a9dec9
......@@ -177,7 +177,6 @@ mpd_headers = \
src/riff.h \
src/aiff.h \
src/queue.h \
src/queue_print.h \
src/queue_save.h \
src/refcount.h \
src/replay_gain_config.h \
......@@ -333,7 +332,7 @@ src_mpd_SOURCES = \
src/playlist_vector.c \
src/playlist_database.c \
src/queue.c \
src/QueuePrint.cxx \
src/QueuePrint.cxx src/QueuePrint.hxx \
src/queue_save.c \
src/replay_gain_config.c \
src/replay_gain_info.c \
......
......@@ -59,25 +59,18 @@ handle_lsinfo2(struct client *client, int argc, char *argv[])
static enum command_return
handle_match(struct client *client, int argc, char *argv[], bool fold_case)
{
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1, fold_case);
if (list == NULL) {
SongFilter filter;
if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
return COMMAND_RETURN_ERROR;
}
const DatabaseSelection selection("", true, list);
const DatabaseSelection selection("", true, &filter);
GError *error = NULL;
enum command_return ret =
db_selection_print(client, selection, true, &error)
return db_selection_print(client, selection, true, &error)
? COMMAND_RETURN_OK
: print_error(client, error);
locate_item_list_free(list);
return ret;
}
enum command_return
......@@ -95,22 +88,16 @@ handle_search(struct client *client, int argc, char *argv[])
static enum command_return
handle_match_add(struct client *client, int argc, char *argv[], bool fold_case)
{
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1, fold_case);
if (list == NULL) {
SongFilter filter;
if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
return COMMAND_RETURN_ERROR;
}
GError *error = NULL;
enum command_return ret =
findAddIn(client->player_control, "", list, &error)
return findAddIn(client->player_control, "", &filter, &error)
? COMMAND_RETURN_OK
: print_error(client, error);
locate_item_list_free(list);
return ret;
}
enum command_return
......@@ -130,45 +117,31 @@ handle_searchaddpl(struct client *client, int argc, char *argv[])
{
const char *playlist = argv[1];
struct locate_item_list *list =
locate_item_list_parse(argv + 2, argc - 2, true);
if (list == NULL) {
SongFilter filter;
if (!filter.Parse(argc - 2, argv + 2, true)) {
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
return COMMAND_RETURN_ERROR;
}
GError *error = NULL;
enum command_return ret =
search_add_to_playlist("", playlist, list, &error)
return search_add_to_playlist("", playlist, &filter, &error)
? COMMAND_RETURN_OK
: print_error(client, error);
locate_item_list_free(list);
return ret;
}
enum command_return
handle_count(struct client *client, int argc, char *argv[])
{
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1, false);
if (list == NULL) {
SongFilter filter;
if (!filter.Parse(argc - 1, argv + 1, false)) {
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
return COMMAND_RETURN_ERROR;
}
GError *error = NULL;
enum command_return ret =
searchStatsForSongsIn(client, "", list, &error)
return searchStatsForSongsIn(client, "", &filter, &error)
? COMMAND_RETURN_OK
: print_error(client, error);
locate_item_list_free(list);
return ret;
}
enum command_return
......@@ -188,7 +161,6 @@ handle_listall(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
enum command_return
handle_list(struct client *client, int argc, char *argv[])
{
struct locate_item_list *conditionals;
unsigned tagType = locate_parse_type(argv[1]);
if (tagType == TAG_NUM_OF_ITEM_TYPES) {
......@@ -203,6 +175,7 @@ handle_list(struct client *client, int argc, char *argv[])
}
/* for compatibility with < 0.12.0 */
SongFilter *filter;
if (argc == 3) {
if (tagType != TAG_ALBUM) {
command_error(client, ACK_ERROR_ARG,
......@@ -211,28 +184,25 @@ handle_list(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
conditionals =
locate_item_list_new_single((unsigned)TAG_ARTIST,
argv[2]);
filter = new SongFilter((unsigned)TAG_ARTIST, argv[2]);
} else if (argc > 2) {
conditionals =
locate_item_list_parse(argv + 2, argc - 2, false);
if (conditionals == NULL) {
filter = new SongFilter();
if (!filter->Parse(argc - 2, argv + 2, false)) {
delete filter;
command_error(client, ACK_ERROR_ARG,
"not able to parse args");
return COMMAND_RETURN_ERROR;
}
} else
conditionals = nullptr;
filter = nullptr;
GError *error = NULL;
enum command_return ret =
listAllUniqueTags(client, tagType, conditionals, &error)
listAllUniqueTags(client, tagType, filter, &error)
? COMMAND_RETURN_OK
: print_error(client, error);
if (conditionals != nullptr)
locate_item_list_free(conditionals);
delete filter;
return ret;
}
......
......@@ -39,14 +39,14 @@ AddSong(const char *playlist_path_utf8,
bool
search_add_to_playlist(const char *uri, const char *playlist_path_utf8,
const struct locate_item_list *criteria,
const SongFilter *filter,
GError **error_r)
{
const Database *db = GetDatabase(error_r);
if (db == nullptr)
return false;
const DatabaseSelection selection(uri, true, criteria);
const DatabaseSelection selection(uri, true, filter);
using namespace std::placeholders;
const auto f = std::bind(AddSong, playlist_path_utf8, _1, _2);
......
......@@ -23,12 +23,12 @@
#include "gcc.h"
#include "gerror.h"
struct locate_item_list;
class SongFilter;
gcc_nonnull(1,2)
bool
search_add_to_playlist(const char *uri, const char *path_utf8,
const struct locate_item_list *criteria,
const SongFilter *filter,
GError **error_r);
#endif
......@@ -120,12 +120,12 @@ db_selection_print(struct client *client, const DatabaseSelection &selection,
return false;
using namespace std::placeholders;
const auto d = selection.match == nullptr
const auto d = selection.filter == nullptr
? std::bind(PrintDirectory, client, _1)
: VisitDirectory();
const auto s = std::bind(full ? PrintSongFull : PrintSongBrief,
client, _1);
const auto p = selection.match == nullptr
const auto p = selection.filter == nullptr
? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
client, _1, _2)
: VisitPlaylist();
......@@ -155,14 +155,14 @@ stats_visitor_song(SearchStats &stats, song &song)
bool
searchStatsForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria,
const SongFilter *filter,
GError **error_r)
{
const Database *db = GetDatabase(error_r);
if (db == nullptr)
return false;
const DatabaseSelection selection(name, true, criteria);
const DatabaseSelection selection(name, true, filter);
SearchStats stats;
stats.numberOfSongs = 0;
......@@ -211,14 +211,14 @@ PrintUniqueTag(struct client *client, enum tag_type tag_type,
bool
listAllUniqueTags(struct client *client, int type,
const struct locate_item_list *criteria,
const SongFilter *filter,
GError **error_r)
{
const Database *db = GetDatabase(error_r);
if (db == nullptr)
return false;
const DatabaseSelection selection("", true, criteria);
const DatabaseSelection selection("", true, filter);
if (type == LOCATE_TAG_FILE_TYPE) {
using namespace std::placeholders;
......
......@@ -26,7 +26,7 @@
#include <stdbool.h>
struct client;
struct locate_item_list;
class SongFilter;
struct DatabaseSelection;
struct db_visitor;
......@@ -47,13 +47,13 @@ printInfoForAllIn(struct client *client, const char *uri_utf8,
gcc_nonnull(1,2)
bool
searchStatsForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria,
const SongFilter *filter,
GError **error_r);
gcc_nonnull(1)
bool
listAllUniqueTags(struct client *client, int type,
const struct locate_item_list *criteria,
const SongFilter *filter,
GError **error_r);
#endif
......@@ -46,13 +46,13 @@ AddToQueue(struct player_control *pc, song &song, GError **error_r)
bool
findAddIn(struct player_control *pc, const char *uri,
const struct locate_item_list *criteria, GError **error_r)
const SongFilter *filter, GError **error_r)
{
const Database *db = GetDatabase(error_r);
if (db == nullptr)
return false;
const DatabaseSelection selection(uri, true, criteria);
const DatabaseSelection selection(uri, true, filter);
using namespace std::placeholders;
const auto f = std::bind(AddToQueue, pc, _1, _2);
......
......@@ -23,12 +23,12 @@
#include "gcc.h"
#include "gerror.h"
struct locate_item_list;
class SongFilter;
struct player_control;
gcc_nonnull(1,2)
bool
findAddIn(struct player_control *pc, const char *name,
const struct locate_item_list *criteria, GError **error_r);
const SongFilter *filter, GError **error_r);
#endif
......@@ -23,5 +23,5 @@
bool
DatabaseSelection::Match(const song &song) const
{
return match == nullptr || locate_list_song_match(&song, match);
return filter == nullptr || filter->Match(song);
}
......@@ -25,7 +25,7 @@
#include <assert.h>
#include <stddef.h>
struct locate_item_list;
class SongFilter;
struct song;
struct DatabaseSelection {
......@@ -41,11 +41,11 @@ struct DatabaseSelection {
*/
bool recursive;
const locate_item_list *match;
const SongFilter *filter;
DatabaseSelection(const char *_uri, bool _recursive,
const locate_item_list *_match=nullptr)
:uri(_uri), recursive(_recursive), match(_match) {
const SongFilter *_filter=nullptr)
:uri(_uri), recursive(_recursive), filter(_filter) {
assert(uri != NULL);
}
......
......@@ -291,7 +291,7 @@ directory_sort(struct directory *directory)
}
bool
directory::Walk(bool recursive, const locate_item_list *match,
directory::Walk(bool recursive, const SongFilter *filter,
VisitDirectory visit_directory, VisitSong visit_song,
VisitPlaylist visit_playlist,
GError **error_r) const
......@@ -301,8 +301,7 @@ directory::Walk(bool recursive, const locate_item_list *match,
if (visit_song) {
struct song *song;
directory_for_each_song(song, this)
if ((match == NULL ||
locate_list_song_match(song, match)) &&
if ((filter == nullptr || filter->Match(*song)) &&
!visit_song(*song, error_r))
return false;
}
......@@ -321,7 +320,7 @@ directory::Walk(bool recursive, const locate_item_list *match,
return false;
if (recursive &&
!child->Walk(recursive, match,
!child->Walk(recursive, filter,
visit_directory, visit_song, visit_playlist,
error_r))
return false;
......
......@@ -19,6 +19,7 @@
#include "config.h"
#include "PlaylistPrint.hxx"
#include "QueuePrint.hxx"
extern "C" {
#include "playlist_list.h"
......@@ -26,7 +27,6 @@ extern "C" {
#include "playlist_any.h"
#include "playlist_song.h"
#include "playlist.h"
#include "queue_print.h"
#include "stored_playlist.h"
#include "song_print.h"
#include "song.h"
......@@ -91,9 +91,9 @@ playlist_print_current(struct client *client, const struct playlist *playlist)
void
playlist_print_find(struct client *client, const struct playlist *playlist,
const struct locate_item_list *list)
const SongFilter &filter)
{
queue_find(client, &playlist->queue, list);
queue_find(client, &playlist->queue, filter);
}
void
......
......@@ -26,7 +26,7 @@
struct client;
struct playlist;
struct locate_item_list;
class SongFilter;
/**
* Sends the whole playlist to the client, song URIs only.
......@@ -66,7 +66,7 @@ playlist_print_current(struct client *client, const struct playlist *playlist);
*/
void
playlist_print_find(struct client *client, const struct playlist *playlist,
const struct locate_item_list *list);
const SongFilter &filter);
/**
* Print detailed changes since the specified playlist version.
......
......@@ -246,18 +246,13 @@ static enum command_return
handle_playlist_match(struct client *client, int argc, char *argv[],
bool fold_case)
{
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1, fold_case);
if (list == NULL) {
SongFilter filter;
if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
return COMMAND_RETURN_ERROR;
}
playlist_print_find(client, &g_playlist, list);
locate_item_list_free(list);
playlist_print_find(client, &g_playlist, filter);
return COMMAND_RETURN_OK;
}
......
......@@ -18,10 +18,10 @@
*/
#include "config.h"
#include "QueuePrint.hxx"
#include "SongFilter.hxx"
extern "C" {
#include "queue_print.h"
#include "queue.h"
#include "song.h"
#include "song_print.h"
......@@ -96,12 +96,12 @@ queue_print_changes_position(struct client *client, const struct queue *queue,
void
queue_find(struct client *client, const struct queue *queue,
const struct locate_item_list *criteria)
const SongFilter &filter)
{
for (unsigned i = 0; i < queue_length(queue); i++) {
const struct song *song = queue_get(queue, i);
if (locate_list_song_match(song, criteria))
if (filter.Match(*song))
queue_print_song_info(client, queue, i);
}
}
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -22,14 +22,14 @@
* client.
*/
#ifndef QUEUE_PRINT_H
#define QUEUE_PRINT_H
#ifndef MPD_QUEUE_PRINT_HXX
#define MPD_QUEUE_PRINT_HXX
#include <stdint.h>
struct client;
struct queue;
struct locate_item_list;
class SongFilter;
void
queue_print_info(struct client *client, const struct queue *queue,
......@@ -49,6 +49,6 @@ queue_print_changes_position(struct client *client, const struct queue *queue,
void
queue_find(struct client *client, const struct queue *queue,
const struct locate_item_list *criteria);
const SongFilter &filter);
#endif
......@@ -35,27 +35,6 @@ extern "C" {
#define LOCATE_TAG_FILE_KEY_OLD "filename"
#define LOCATE_TAG_ANY_KEY "any"
/* struct used for search, find, list queries */
struct locate_item {
uint8_t tag;
bool fold_case;
/* what we are looking for */
char *needle;
};
/**
* An array of struct locate_item objects.
*/
struct locate_item_list {
/** number of items */
unsigned length;
/** this is a variable length array */
struct locate_item items[1];
};
unsigned
locate_parse_type(const char *str)
{
......@@ -69,107 +48,52 @@ locate_parse_type(const char *str)
return tag_name_parse_i(str);
}
static bool
locate_item_init(struct locate_item *item,
const char *type_string, const char *needle,
bool fold_case)
{
item->tag = locate_parse_type(type_string);
if (item->tag == TAG_NUM_OF_ITEM_TYPES)
return false;
item->fold_case = fold_case;
item->needle = fold_case
? g_utf8_casefold(needle, -1)
: g_strdup(needle);
return true;
}
void
locate_item_list_free(struct locate_item_list *list)
SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
:tag(_tag), fold_case(_fold_case),
value(fold_case
? g_utf8_casefold(_value, -1)
: g_strdup(_value))
{
for (unsigned i = 0; i < list->length; ++i)
g_free(list->items[i].needle);
g_free(list);
}
static struct locate_item_list *
locate_item_list_new(unsigned length)
{
struct locate_item_list *list = (struct locate_item_list *)
g_malloc(sizeof(*list) - sizeof(list->items[0]) +
length * sizeof(list->items[0]));
list->length = length;
return list;
}
struct locate_item_list *
locate_item_list_new_single(unsigned tag, const char *needle)
{
struct locate_item_list *list = locate_item_list_new(1);
list->items[0].tag = tag;
list->items[0].fold_case = false;
list->items[0].needle = g_strdup(needle);
return list;
}
struct locate_item_list *
locate_item_list_parse(char *argv[], unsigned argc, bool fold_case)
SongFilter::Item::~Item()
{
if (argc == 0 || argc % 2 != 0)
return NULL;
struct locate_item_list *list = locate_item_list_new(argc / 2);
for (unsigned i = 0; i < list->length; ++i) {
if (!locate_item_init(&list->items[i], argv[i * 2],
argv[i * 2 + 1], fold_case)) {
locate_item_list_free(list);
return NULL;
}
}
return list;
g_free(value);
}
gcc_pure
static bool
locate_string_match(const struct locate_item *item, const char *value)
bool
SongFilter::Item::StringMatch(const char *s) const
{
assert(item != NULL);
assert(value != NULL);
assert(value != nullptr);
assert(s != nullptr);
if (item->fold_case) {
char *p = g_utf8_casefold(value, -1);
const bool result = strstr(p, item->needle) != NULL;
if (fold_case) {
char *p = g_utf8_casefold(s, -1);
const bool result = strstr(p, value) != NULL;
g_free(p);
return result;
} else {
return strcmp(value, item->needle) == 0;
return strcmp(s, value) == 0;
}
}
gcc_pure
static bool
locate_tag_match(const struct locate_item *item, const struct tag *tag)
bool
SongFilter::Item::Match(const tag_item &item) const
{
assert(item != NULL);
assert(tag != NULL);
return (tag == LOCATE_TAG_ANY_TYPE || (unsigned)item.type == tag) &&
StringMatch(item.value);
}
bool
SongFilter::Item::Match(const struct tag &_tag) const
{
bool visited_types[TAG_NUM_OF_ITEM_TYPES];
memset(visited_types, 0, sizeof(visited_types));
std::fill(visited_types, visited_types + TAG_NUM_OF_ITEM_TYPES, false);
for (unsigned i = 0; i < tag->num_items; i++) {
visited_types[tag->items[i]->type] = true;
if (item->tag != LOCATE_TAG_ANY_TYPE &&
tag->items[i]->type != item->tag)
continue;
for (unsigned i = 0; i < _tag.num_items; i++) {
visited_types[_tag.items[i]->type] = true;
if (locate_string_match(item, tag->items[i]->value))
if (Match(*_tag.items[i]))
return true;
}
......@@ -179,36 +103,67 @@ locate_tag_match(const struct locate_item *item, const struct tag *tag)
* empty (first char is a \0), then it's a match as well and
* we should return true.
*/
if (*item->needle == 0 && item->tag != LOCATE_TAG_ANY_TYPE &&
!visited_types[item->tag])
if (*value == 0 && tag < TAG_NUM_OF_ITEM_TYPES &&
!visited_types[tag])
return true;
return false;
}
gcc_pure
static bool
locate_song_match(const struct locate_item *item, const struct song *song)
bool
SongFilter::Item::Match(const song &song) const
{
if (item->tag == LOCATE_TAG_FILE_TYPE ||
item->tag == LOCATE_TAG_ANY_TYPE) {
char *uri = song_get_uri(song);
const bool result = locate_string_match(item, uri);
if (tag == LOCATE_TAG_FILE_TYPE || tag == LOCATE_TAG_ANY_TYPE) {
char *uri = song_get_uri(&song);
const bool result = StringMatch(uri);
g_free(uri);
if (result || item->tag == LOCATE_TAG_FILE_TYPE)
if (result || tag == LOCATE_TAG_FILE_TYPE)
return result;
}
return song->tag != NULL && locate_tag_match(item, song->tag);
return song.tag != NULL && Match(*song.tag);
}
SongFilter::SongFilter(unsigned tag, const char *value, bool fold_case)
{
items.push_back(Item(tag, value, fold_case));
}
SongFilter::~SongFilter()
{
/* this destructor exists here just so it won't get inlined */
}
bool
SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
{
unsigned tag = locate_parse_type(tag_string);
if (tag == TAG_NUM_OF_ITEM_TYPES)
return false;
items.push_back(Item(tag, value, fold_case));
return true;
}
bool
SongFilter::Parse(unsigned argc, char *argv[], bool fold_case)
{
if (argc == 0 || argc % 2 != 0)
return false;
for (unsigned i = 0; i < argc; i += 2)
if (!Parse(argv[i], argv[i + 1], fold_case))
return false;
return true;
}
bool
locate_list_song_match(const struct song *song,
const struct locate_item_list *criteria)
SongFilter::Match(const song &song) const
{
for (unsigned i = 0; i < criteria->length; i++)
if (!locate_song_match(&criteria->items[i], song))
for (const auto &i : items)
if (!i.Match(song))
return false;
return true;
......
......@@ -22,15 +22,82 @@
#include "gcc.h"
#include <list>
#include <stdint.h>
#include <stdbool.h>
#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10
#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
struct locate_item_list;
struct tag;
struct tag_item;
struct song;
class SongFilter {
class Item {
uint8_t tag;
bool fold_case;
char *value;
public:
gcc_nonnull(3)
Item(unsigned tag, const char *value, bool fold_case=false);
Item(const Item &other) = delete;
Item(Item &&other)
:tag(other.tag), fold_case(other.fold_case),
value(other.value) {
other.value = nullptr;
}
~Item();
Item &operator=(const Item &other) = delete;
unsigned GetTag() const {
return tag;
}
gcc_pure gcc_nonnull(2)
bool StringMatch(const char *s) const;
gcc_pure
bool Match(const tag_item &tag_item) const;
gcc_pure
bool Match(const struct tag &tag) const;
gcc_pure
bool Match(const song &song) const;
};
std::list<Item> items;
public:
SongFilter() = default;
gcc_nonnull(3)
SongFilter(unsigned tag, const char *value, bool fold_case=false);
~SongFilter();
gcc_nonnull(2,3)
bool Parse(const char *tag, const char *value, bool fold_case=false);
gcc_nonnull(3)
bool Parse(unsigned argc, char *argv[], bool fold_case=false);
gcc_pure
bool Match(const tag &tag) const;
gcc_pure
bool Match(const song &song) const;
};
/**
* @return #TAG_NUM_OF_ITEM_TYPES on error
*/
......@@ -38,23 +105,4 @@ gcc_pure
unsigned
locate_parse_type(const char *str);
gcc_malloc
struct locate_item_list *
locate_item_list_new_single(unsigned tag, const char *needle);
/* return number of items or -1 on error */
gcc_nonnull(1)
struct locate_item_list *
locate_item_list_parse(char *argv[], unsigned argc, bool fold_case);
gcc_nonnull(1)
void
locate_item_list_free(struct locate_item_list *list);
gcc_pure
gcc_nonnull(1,2)
bool
locate_list_song_match(const struct song *song,
const struct locate_item_list *criteria);
#endif
......@@ -282,7 +282,7 @@ SimpleDatabase::Visit(const DatabaseSelection &selection,
return false;
db_lock();
bool ret = directory->Walk(selection.recursive, selection.match,
bool ret = directory->Walk(selection.recursive, selection.filter,
visit_directory, visit_song, visit_playlist,
error_r);
db_unlock();
......
......@@ -55,7 +55,10 @@
struct song;
struct db_visitor;
struct locate_item_list;
#ifdef __cplusplus
class SongFilter;
#endif
struct directory {
/**
......@@ -97,7 +100,7 @@ struct directory {
/**
* Caller must lock #db_mutex.
*/
bool Walk(bool recursive, const locate_item_list *match,
bool Walk(bool recursive, const SongFilter *match,
VisitDirectory visit_directory, VisitSong visit_song,
VisitPlaylist visit_playlist,
GError **error_r) const;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment