diff --git a/src/db/plugins/simple/Directory.cxx b/src/db/plugins/simple/Directory.cxx index 05de51963..a81021b47 100644 --- a/src/db/plugins/simple/Directory.cxx +++ b/src/db/plugins/simple/Directory.cxx @@ -120,38 +120,49 @@ Directory::PruneEmpty() } } -Directory * +Directory::LookupResult Directory::LookupDirectory(const char *uri) { assert(holding_db_lock()); assert(uri != nullptr); if (isRootDirectory(uri)) - return this; + return { this, nullptr }; char *duplicated = xstrdup(uri), *name = duplicated; Directory *d = this; - while (1) { + while (true) { char *slash = strchr(name, '/'); - if (slash == name) { - d = nullptr; + if (slash == name) break; - } if (slash != nullptr) *slash = '\0'; - d = d->FindChild(name); - if (d == nullptr || slash == nullptr) + Directory *tmp = d->FindChild(name); + if (tmp == nullptr) + /* not found */ break; + d = tmp; + + if (slash == nullptr) { + /* found everything */ + name = nullptr; + break; + } + name = slash + 1; } free(duplicated); - return d; + const char *rest = name == nullptr + ? nullptr + : uri + (name - duplicated); + + return { d, rest }; } void @@ -197,26 +208,16 @@ Directory::LookupSong(const char *uri) assert(holding_db_lock()); assert(uri != nullptr); - char *duplicated = xstrdup(uri); - char *base = strrchr(duplicated, '/'); + auto r = LookupDirectory(uri); + if (r.uri == nullptr) + /* it's a directory */ + return nullptr; - Directory *d = this; - if (base != nullptr) { - *base++ = 0; - d = d->LookupDirectory(duplicated); - if (d == nullptr) { - free(duplicated); - return nullptr; - } - } else - base = duplicated; - - Song *song = d->FindSong(base); - assert(song == nullptr || song->parent == d); - - free(duplicated); - return song; + if (strchr(r.uri, '/') != nullptr) + /* refers to a URI "below" the actual song */ + return nullptr; + return r.directory->FindSong(r.uri); } static int diff --git a/src/db/plugins/simple/Directory.hxx b/src/db/plugins/simple/Directory.hxx index 716afecea..ab03092f7 100644 --- a/src/db/plugins/simple/Directory.hxx +++ b/src/db/plugins/simple/Directory.hxx @@ -149,6 +149,21 @@ public: return child; } + struct LookupResult { + /** + * The last directory that was found. If the given + * URI could not be resolved at all, then this is the + * root directory. + */ + Directory *directory; + + /** + * The remaining URI part (without leading slash) or + * nullptr if the given URI was consumed completely. + */ + const char *uri; + }; + /** * Looks up a directory by its relative URI. * @@ -156,7 +171,7 @@ public: * @return the Directory, or nullptr if none was found */ gcc_pure - Directory *LookupDirectory(const char *uri); + LookupResult LookupDirectory(const char *uri); gcc_pure bool IsEmpty() const { diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx index 9911caa66..8ba6eef92 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx @@ -244,28 +244,33 @@ SimpleDatabase::Visit(const DatabaseSelection &selection, { ScopeDatabaseLock protect; - const Directory *directory = root->LookupDirectory(selection.uri.c_str()); - if (directory == nullptr) { + auto r = root->LookupDirectory(selection.uri.c_str()); + if (r.uri == nullptr) { + /* it's a directory */ + + if (selection.recursive && visit_directory && + !visit_directory(r.directory->Export(), error)) + return false; + + return r.directory->Walk(selection.recursive, selection.filter, + visit_directory, visit_song, + visit_playlist, + error); + } + + if (strchr(r.uri, '/') == nullptr) { if (visit_song) { - Song *song = root->LookupSong(selection.uri.c_str()); + Song *song = r.directory->FindSong(r.uri); if (song != nullptr) { const LightSong song2 = song->Export(); return !selection.Match(song2) || visit_song(song2, error); } } - - error.Set(db_domain, DB_NOT_FOUND, "No such directory"); - return false; } - if (selection.recursive && visit_directory && - !visit_directory(directory->Export(), error)) - return false; - - return directory->Walk(selection.recursive, selection.filter, - visit_directory, visit_song, visit_playlist, - error); + error.Set(db_domain, DB_NOT_FOUND, "No such directory"); + return false; } bool