diff --git a/Makefile.am b/Makefile.am index 4b69a8d59..0fa75615c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -276,6 +276,7 @@ src_mpd_SOURCES = \ src/db_print.c src/db_print.h \ src/db_plugin.h \ src/db_visitor.h \ + src/db_selection.h \ src/db/simple_db_plugin.c src/db/simple_db_plugin.h \ src/dirvec.c \ src/exclude.c \ diff --git a/src/database.c b/src/database.c index e3fa7442b..9c4d90a2b 100644 --- a/src/database.c +++ b/src/database.c @@ -21,6 +21,7 @@ #include "database.h" #include "db_error.h" #include "db_save.h" +#include "db_selection.h" #include "db_visitor.h" #include "db_plugin.h" #include "db/simple_db_plugin.h" @@ -108,9 +109,9 @@ db_get_song(const char *file) } bool -db_walk(const char *uri, - const struct db_visitor *visitor, void *ctx, - GError **error_r) +db_visit(const struct db_selection *selection, + const struct db_visitor *visitor, void *ctx, + GError **error_r) { if (db == NULL) { g_set_error_literal(error_r, db_quark(), DB_DISABLED, @@ -118,23 +119,18 @@ db_walk(const char *uri, return false; } - struct directory *directory = db_get_directory(uri); - if (directory == NULL) { - struct song *song; - if (visitor->song != NULL && - (song = db_get_song(uri)) != NULL) - return visitor->song(song, ctx, error_r); + return db_plugin_visit(db, selection, visitor, ctx, error_r); +} - g_set_error(error_r, db_quark(), DB_NOT_FOUND, - "No such directory: %s", uri); - return false; - } +bool +db_walk(const char *uri, + const struct db_visitor *visitor, void *ctx, + GError **error_r) +{ + struct db_selection selection; + db_selection_init(&selection, uri, true); - if (visitor->directory != NULL && - !visitor->directory(directory, ctx, error_r)) - return false; - - return directory_walk(directory, visitor, ctx, error_r); + return db_visit(&selection, visitor, ctx, error_r); } bool diff --git a/src/database.h b/src/database.h index 1696031d0..33f503652 100644 --- a/src/database.h +++ b/src/database.h @@ -29,6 +29,7 @@ struct config_param; struct directory; +struct db_selection; struct db_visitor; /** @@ -57,6 +58,12 @@ gcc_nonnull(1) struct song * db_get_song(const char *file); +gcc_nonnull(1,2) +bool +db_visit(const struct db_selection *selection, + const struct db_visitor *visitor, void *ctx, + GError **error_r); + gcc_nonnull(1,2) bool db_walk(const char *uri, diff --git a/src/db/simple_db_plugin.c b/src/db/simple_db_plugin.c index 8218635fc..e359b5e65 100644 --- a/src/db/simple_db_plugin.c +++ b/src/db/simple_db_plugin.c @@ -21,6 +21,8 @@ #include "simple_db_plugin.h" #include "db_internal.h" #include "db_error.h" +#include "db_selection.h" +#include "db_visitor.h" #include "db_save.h" #include "conf.h" #include "glib_compat.h" @@ -48,6 +50,17 @@ simple_db_quark(void) return g_quark_from_static_string("simple_db"); } +G_GNUC_PURE +static const struct directory * +simple_db_lookup_directory(const struct simple_db *db, const char *uri) +{ + assert(db != NULL); + assert(db->root != NULL); + assert(uri != NULL); + + return directory_lookup_directory(db->root, uri); +} + static struct db * simple_db_init(const struct config_param *param, GError **error_r) { @@ -230,6 +243,33 @@ simple_db_get_song(struct db *_db, const char *uri, GError **error_r) return song; } +static bool +simple_db_visit(struct db *_db, const struct db_selection *selection, + const struct db_visitor *visitor, void *ctx, + GError **error_r) +{ + const struct simple_db *db = (const struct simple_db *)_db; + const struct directory *directory = + simple_db_lookup_directory(db, selection->uri); + if (directory == NULL) { + struct song *song; + if (visitor->song != NULL && + (song = simple_db_get_song(_db, selection->uri, NULL)) != NULL) + return visitor->song(song, ctx, error_r); + + g_set_error(error_r, db_quark(), DB_NOT_FOUND, + "No such directory"); + return false; + } + + if (selection->recursive && visitor->directory != NULL && + !visitor->directory(directory, ctx, error_r)) + return false; + + return directory_walk(directory, selection->recursive, + visitor, ctx, error_r); +} + const struct db_plugin simple_db_plugin = { .name = "simple", .init = simple_db_init, @@ -237,6 +277,7 @@ const struct db_plugin simple_db_plugin = { .open = simple_db_open, .close = simple_db_close, .get_song = simple_db_get_song, + .visit = simple_db_visit, }; struct directory * diff --git a/src/db_plugin.h b/src/db_plugin.h index 5fec2529e..1c7e14ede 100644 --- a/src/db_plugin.h +++ b/src/db_plugin.h @@ -31,6 +31,8 @@ #include struct config_param; +struct db_selection; +struct db_visitor; struct db { const struct db_plugin *plugin; @@ -67,6 +69,13 @@ struct db_plugin { */ struct song *(*get_song)(struct db *db, const char *uri, GError **error_r); + + /** + * Visit the selected entities. + */ + bool (*visit)(struct db *db, const struct db_selection *selection, + const struct db_visitor *visitor, void *ctx, + GError **error_r); }; G_GNUC_MALLOC @@ -78,6 +87,7 @@ db_plugin_new(const struct db_plugin *plugin, const struct config_param *param, assert(plugin->init != NULL); assert(plugin->finish != NULL); assert(plugin->get_song != NULL); + assert(plugin->visit != NULL); assert(error_r == NULL || *error_r == NULL); struct db *db = plugin->init(param, error_r); @@ -129,4 +139,18 @@ db_plugin_get_song(struct db *db, const char *uri, GError **error_r) return db->plugin->get_song(db, uri, error_r); } +static inline bool +db_plugin_visit(struct db *db, const struct db_selection *selection, + const struct db_visitor *visitor, void *ctx, + GError **error_r) +{ + assert(db != NULL); + assert(db->plugin != NULL); + assert(selection != NULL); + assert(visitor != NULL); + assert(error_r == NULL || *error_r == NULL); + + return db->plugin->visit(db, selection, visitor, ctx, error_r); +} + #endif diff --git a/src/db_selection.h b/src/db_selection.h new file mode 100644 index 000000000..2cebb4907 --- /dev/null +++ b/src/db_selection.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2003-2011 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_DB_SELECTION_H +#define MPD_DB_SELECTION_H + +#include "gcc.h" + +#include + +struct directory; +struct song; + +struct db_selection { + /** + * The base URI of the search (UTF-8). Must not begin or end + * with a slash. NULL or an empty string searches the whole + * database. + */ + const char *uri; + + /** + * Recursively search all sub directories? + */ + bool recursive; +}; + +gcc_nonnull(1,2) +static inline void +db_selection_init(struct db_selection *selection, + const char *uri, bool recursive) +{ + assert(selection != NULL); + assert(uri != NULL); + + selection->uri = uri; + selection->recursive = recursive; +} + +#endif diff --git a/src/directory.c b/src/directory.c index 83fefb8c4..e6ccc60b7 100644 --- a/src/directory.c +++ b/src/directory.c @@ -169,7 +169,7 @@ directory_sort(struct directory *directory) } bool -directory_walk(struct directory *directory, +directory_walk(const struct directory *directory, bool recursive, const struct db_visitor *visitor, void *ctx, GError **error_r) { @@ -178,7 +178,7 @@ directory_walk(struct directory *directory, assert(error_r == NULL || *error_r == NULL); if (visitor->song != NULL) { - struct songvec *sv = &directory->songs; + const struct songvec *sv = &directory->songs; for (size_t i = 0; i < sv->nr; ++i) if (!visitor->song(sv->base[i], ctx, error_r)) return false; @@ -192,7 +192,8 @@ directory_walk(struct directory *directory, !visitor->directory(child, ctx, error_r)) return false; - if (!directory_walk(child, visitor, ctx, error_r)) + if (recursive && + !directory_walk(child, recursive, visitor, ctx, error_r)) return false; } diff --git a/src/directory.h b/src/directory.h index 3359a2e99..ce9c96cd2 100644 --- a/src/directory.h +++ b/src/directory.h @@ -131,7 +131,7 @@ void directory_sort(struct directory *directory); bool -directory_walk(struct directory *directory, +directory_walk(const struct directory *directory, bool recursive, const struct db_visitor *visitor, void *ctx, GError **error_r);