Commit 96afa8bd authored by Max Kellermann's avatar Max Kellermann

command: add command "listfiles"

Lists files and directories. Supports storage plugins.
parent 797bbeab
ver 0.19 (not yet released) ver 0.19 (not yet released)
* protocol * protocol
- new commands "addtagid", "cleartagid" - new commands "addtagid", "cleartagid", "listfiles"
- "lsinfo" and "readcomments" allowed for remote files - "lsinfo" and "readcomments" allowed for remote files
- "listneighbors" lists file servers on the local network - "listneighbors" lists file servers on the local network
- "playlistadd" supports file:/// - "playlistadd" supports file:///
......
...@@ -1600,6 +1600,31 @@ OK ...@@ -1600,6 +1600,31 @@ OK
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="command_listfiles">
<term>
<cmdsynopsis>
<command>listfiles</command>
<arg><replaceable>URI</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Lists the contents of the directory
<varname>URI</varname>, including files are not
recognized by MPD. <varname>URI</varname> can be a path
relative to the music directory or an URI understood by
one of the storage plugins. The response contains at
least one line for each directory entry with the prefix
"file: " or "directory: ", and may be followed by file
attributes such as "Last-Modified" and "size".
</para>
<para>
For example, "smb://SERVER" returns a list of all shares
on the given SMB/CIFS server; "nfs://servername/path"
obtains a directory listing from the NFS server.
</para>
</listitem>
</varlistentry>
<varlistentry id="command_lsinfo"> <varlistentry id="command_lsinfo">
<term> <term>
<cmdsynopsis> <cmdsynopsis>
......
...@@ -30,44 +30,50 @@ ...@@ -30,44 +30,50 @@
#define SONG_FILE "file: " #define SONG_FILE "file: "
static void static void
song_print_uri(Client &client, const char *uri) song_print_uri(Client &client, const char *uri, bool base)
{ {
std::string allocated;
if (base) {
uri = PathTraitsUTF8::GetBase(uri);
} else {
#ifdef ENABLE_DATABASE #ifdef ENABLE_DATABASE
const Storage *storage = client.GetStorage(); const Storage *storage = client.GetStorage();
if (storage != nullptr) { if (storage != nullptr) {
const char *suffix = storage->MapToRelativeUTF8(uri); const char *suffix = storage->MapToRelativeUTF8(uri);
if (suffix != nullptr) if (suffix != nullptr)
uri = suffix; uri = suffix;
} }
#endif #endif
const std::string allocated = uri_remove_auth(uri); allocated = uri_remove_auth(uri);
if (!allocated.empty()) if (!allocated.empty())
uri = allocated.c_str(); uri = allocated.c_str();
}
client_printf(client, "%s%s\n", SONG_FILE, uri); client_printf(client, "%s%s\n", SONG_FILE, uri);
} }
void void
song_print_uri(Client &client, const LightSong &song) song_print_uri(Client &client, const LightSong &song, bool base)
{ {
if (song.directory != nullptr) { if (!base && song.directory != nullptr) {
client_printf(client, "%s%s/%s\n", SONG_FILE, client_printf(client, "%s%s/%s\n", SONG_FILE,
song.directory, song.uri); song.directory, song.uri);
} else } else
song_print_uri(client, song.uri); song_print_uri(client, song.uri, base);
} }
void void
song_print_uri(Client &client, const DetachedSong &song) song_print_uri(Client &client, const DetachedSong &song, bool base)
{ {
song_print_uri(client, song.GetURI()); song_print_uri(client, song.GetURI(), base);
} }
void void
song_print_info(Client &client, const LightSong &song) song_print_info(Client &client, const LightSong &song, bool base)
{ {
song_print_uri(client, song); song_print_uri(client, song, base);
if (song.end_ms > 0) if (song.end_ms > 0)
client_printf(client, "Range: %u.%03u-%u.%03u\n", client_printf(client, "Range: %u.%03u-%u.%03u\n",
...@@ -87,9 +93,9 @@ song_print_info(Client &client, const LightSong &song) ...@@ -87,9 +93,9 @@ song_print_info(Client &client, const LightSong &song)
} }
void void
song_print_info(Client &client, const DetachedSong &song) song_print_info(Client &client, const DetachedSong &song, bool base)
{ {
song_print_uri(client, song); song_print_uri(client, song, base);
const unsigned start_ms = song.GetStartMS(); const unsigned start_ms = song.GetStartMS();
const unsigned end_ms = song.GetEndMS(); const unsigned end_ms = song.GetEndMS();
......
...@@ -25,15 +25,15 @@ class DetachedSong; ...@@ -25,15 +25,15 @@ class DetachedSong;
class Client; class Client;
void void
song_print_info(Client &client, const DetachedSong &song); song_print_info(Client &client, const DetachedSong &song, bool base=false);
void void
song_print_info(Client &client, const LightSong &song); song_print_info(Client &client, const LightSong &song, bool base=false);
void void
song_print_uri(Client &client, const LightSong &song); song_print_uri(Client &client, const LightSong &song, bool base=false);
void void
song_print_uri(Client &client, const DetachedSong &song); song_print_uri(Client &client, const DetachedSong &song, bool base=false);
#endif #endif
...@@ -107,6 +107,9 @@ static const struct command commands[] = { ...@@ -107,6 +107,9 @@ static const struct command commands[] = {
{ "list", PERMISSION_READ, 1, -1, handle_list }, { "list", PERMISSION_READ, 1, -1, handle_list },
{ "listall", PERMISSION_READ, 0, 1, handle_listall }, { "listall", PERMISSION_READ, 0, 1, handle_listall },
{ "listallinfo", PERMISSION_READ, 0, 1, handle_listallinfo }, { "listallinfo", PERMISSION_READ, 0, 1, handle_listallinfo },
#endif
{ "listfiles", PERMISSION_READ, 0, 1, handle_listfiles },
#ifdef ENABLE_DATABASE
{ "listmounts", PERMISSION_READ, 0, 0, handle_listmounts }, { "listmounts", PERMISSION_READ, 0, 0, handle_listmounts },
#endif #endif
#ifdef ENABLE_NEIGHBOR_PLUGINS #ifdef ENABLE_NEIGHBOR_PLUGINS
......
...@@ -32,6 +32,18 @@ ...@@ -32,6 +32,18 @@
#include "protocol/Result.hxx" #include "protocol/Result.hxx"
CommandResult CommandResult
handle_listfiles_db(Client &client, const char *uri)
{
const DatabaseSelection selection(uri, false);
Error error;
if (!db_selection_print(client, selection, false, true, error))
return print_error(client, error);
return CommandResult::OK;
}
CommandResult
handle_lsinfo2(Client &client, int argc, char *argv[]) handle_lsinfo2(Client &client, int argc, char *argv[])
{ {
const char *const uri = argc == 2 const char *const uri = argc == 2
...@@ -42,7 +54,7 @@ handle_lsinfo2(Client &client, int argc, char *argv[]) ...@@ -42,7 +54,7 @@ handle_lsinfo2(Client &client, int argc, char *argv[])
const DatabaseSelection selection(uri, false); const DatabaseSelection selection(uri, false);
Error error; Error error;
if (!db_selection_print(client, selection, true, error)) if (!db_selection_print(client, selection, true, false, error))
return print_error(client, error); return print_error(client, error);
return CommandResult::OK; return CommandResult::OK;
...@@ -60,7 +72,7 @@ handle_match(Client &client, int argc, char *argv[], bool fold_case) ...@@ -60,7 +72,7 @@ handle_match(Client &client, int argc, char *argv[], bool fold_case)
const DatabaseSelection selection("", true, &filter); const DatabaseSelection selection("", true, &filter);
Error error; Error error;
return db_selection_print(client, selection, true, error) return db_selection_print(client, selection, true, false, error)
? CommandResult::OK ? CommandResult::OK
: print_error(client, error); : print_error(client, error);
} }
......
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
class Client; class Client;
CommandResult CommandResult
handle_listfiles_db(Client &client, const char *uri);
CommandResult
handle_lsinfo2(Client &client, int argc, char *argv[]); handle_lsinfo2(Client &client, int argc, char *argv[]);
CommandResult CommandResult
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#define __STDC_FORMAT_MACROS /* for PRIu64 */
#include "config.h" #include "config.h"
#include "FileCommands.hxx" #include "FileCommands.hxx"
#include "CommandError.hxx" #include "CommandError.hxx"
...@@ -33,9 +35,79 @@ ...@@ -33,9 +35,79 @@
#include "TagFile.hxx" #include "TagFile.hxx"
#include "storage/StorageInterface.hxx" #include "storage/StorageInterface.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "fs/FileSystem.hxx"
#include "TimePrint.hxx"
#include "ls.hxx" #include "ls.hxx"
#include <assert.h> #include <assert.h>
#include <sys/stat.h>
#include <inttypes.h> /* for PRIu64 */
gcc_pure
static bool
SkipNameFS(const char *name_fs)
{
return name_fs[0] == '.' &&
(name_fs[1] == 0 ||
(name_fs[1] == '.' && name_fs[2] == 0));
}
gcc_pure
static bool
skip_path(const char *name_fs)
{
return strchr(name_fs, '\n') != nullptr;
}
CommandResult
handle_listfiles_local(Client &client, const char *path_utf8)
{
const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
if (path_fs.IsNull()) {
command_error(client, ACK_ERROR_NO_EXIST,
"unsupported file name");
return CommandResult::ERROR;
}
Error error;
if (!client.AllowFile(path_fs, error))
return print_error(client, error);
DirectoryReader reader(path_fs);
if (reader.HasFailed()) {
error.FormatErrno("Failed to open '%s'", path_utf8);
return print_error(client, error);
}
while (reader.ReadEntry()) {
const Path name_fs = reader.GetEntry();
if (SkipNameFS(name_fs.c_str()) || skip_path(name_fs.c_str()))
continue;
std::string name_utf8 = name_fs.ToUTF8();
if (name_utf8.empty())
continue;
const AllocatedPath full_fs =
AllocatedPath::Build(path_fs, name_fs);
struct stat st;
if (!StatFile(full_fs, st, false))
continue;
if (S_ISREG(st.st_mode)) {
client_printf(client, "file: %s\n"
"size: %" PRIu64 "\n",
name_utf8.c_str(),
uint64_t(st.st_size));
} else if (S_ISDIR(st.st_mode))
client_printf(client, "directory: %s\n",
name_utf8.c_str());
time_print(client, "Last-Modified", st.st_mtime);
}
return CommandResult::OK;
}
gcc_pure gcc_pure
static bool static bool
......
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
class Client; class Client;
CommandResult CommandResult
handle_listfiles_local(Client &client, const char *path_utf8);
CommandResult
handle_read_comments(Client &client, int argc, char *argv[]); handle_read_comments(Client &client, int argc, char *argv[]);
#endif #endif
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "config.h" #include "config.h"
#include "OtherCommands.hxx" #include "OtherCommands.hxx"
#include "FileCommands.hxx"
#include "StorageCommands.hxx"
#include "CommandError.hxx" #include "CommandError.hxx"
#include "db/Uri.hxx" #include "db/Uri.hxx"
#include "storage/StorageInterface.hxx" #include "storage/StorageInterface.hxx"
...@@ -112,6 +114,41 @@ print_tag(TagType type, const char *value, void *ctx) ...@@ -112,6 +114,41 @@ print_tag(TagType type, const char *value, void *ctx)
tag_print(client, type, value); tag_print(client, type, value);
} }
CommandResult
handle_listfiles(Client &client, int argc, char *argv[])
{
const char *const uri = argc == 2
? argv[1]
/* default is root directory */
: "";
if (memcmp(uri, "file:///", 8) == 0)
/* list local directory */
return handle_listfiles_local(client, uri + 7);
#ifdef ENABLE_DATABASE
if (uri_has_scheme(uri))
/* use storage plugin to list remote directory */
return handle_listfiles_storage(client, uri);
/* must be a path relative to the configured
music_directory */
if (client.partition.instance.storage != nullptr)
/* if we have a storage instance, obtain a list of
files from it */
return handle_listfiles_storage(client,
*client.partition.instance.storage,
uri);
/* fall back to entries from database if we have no storage */
return handle_listfiles_db(client, uri);
#else
command_error(client, ACK_ERROR_NO_EXIST, "No database");
return CommandResult::ERROR;
#endif
}
static constexpr tag_handler print_tag_handler = { static constexpr tag_handler print_tag_handler = {
nullptr, nullptr,
print_tag, print_tag,
......
...@@ -40,6 +40,9 @@ CommandResult ...@@ -40,6 +40,9 @@ CommandResult
handle_close(Client &client, int argc, char *argv[]); handle_close(Client &client, int argc, char *argv[]);
CommandResult CommandResult
handle_listfiles(Client &client, int argc, char *argv[]);
CommandResult
handle_lsinfo(Client &client, int argc, char *argv[]); handle_lsinfo(Client &client, int argc, char *argv[]);
CommandResult CommandResult
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#define __STDC_FORMAT_MACROS /* for PRIu64 */
#include "config.h" #include "config.h"
#include "StorageCommands.hxx" #include "StorageCommands.hxx"
#include "CommandError.hxx" #include "CommandError.hxx"
...@@ -29,10 +31,103 @@ ...@@ -29,10 +31,103 @@
#include "Instance.hxx" #include "Instance.hxx"
#include "storage/Registry.hxx" #include "storage/Registry.hxx"
#include "storage/CompositeStorage.hxx" #include "storage/CompositeStorage.hxx"
#include "storage/FileInfo.hxx"
#include "db/plugins/simple/SimpleDatabasePlugin.hxx" #include "db/plugins/simple/SimpleDatabasePlugin.hxx"
#include "db/update/Service.hxx" #include "db/update/Service.hxx"
#include "TimePrint.hxx"
#include "Idle.hxx" #include "Idle.hxx"
#include <inttypes.h> /* for PRIu64 */
gcc_pure
static bool
skip_path(const char *name_utf8)
{
return strchr(name_utf8, '\n') != nullptr;
}
static bool
handle_listfiles_storage(Client &client, StorageDirectoryReader &reader,
Error &error)
{
const char *name_utf8;
while ((name_utf8 = reader.Read()) != nullptr) {
if (skip_path(name_utf8))
continue;
FileInfo info;
if (!reader.GetInfo(false, info, error))
continue;
switch (info.type) {
case FileInfo::Type::OTHER:
/* ignore */
continue;
case FileInfo::Type::REGULAR:
client_printf(client, "file: %s\n"
"size: %" PRIu64 "\n",
name_utf8,
info.size);
break;
case FileInfo::Type::DIRECTORY:
client_printf(client, "directory: %s\n", name_utf8);
break;
}
if (info.mtime != 0)
time_print(client, "Last-Modified", info.mtime);
}
return true;
}
static bool
handle_listfiles_storage(Client &client, Storage &storage, const char *uri,
Error &error)
{
auto reader = storage.OpenDirectory(uri, error);
if (reader == nullptr)
return false;
bool success = handle_listfiles_storage(client, *reader, error);
delete reader;
return success;
}
CommandResult
handle_listfiles_storage(Client &client, Storage &storage, const char *uri)
{
Error error;
if (!handle_listfiles_storage(client, storage, uri, error))
return print_error(client, error);
return CommandResult::OK;
}
CommandResult
handle_listfiles_storage(Client &client, const char *uri)
{
Error error;
Storage *storage = CreateStorageURI(uri, error);
if (storage == nullptr) {
if (error.IsDefined())
return print_error(client, error);
command_error(client, ACK_ERROR_ARG,
"Unrecognized storage URI");
return CommandResult::ERROR;
}
bool success = handle_listfiles_storage(client, *storage, "", error);
delete storage;
if (!success)
return print_error(client, error);
return CommandResult::OK;
}
static void static void
print_storage_uri(Client &client, const Storage &storage) print_storage_uri(Client &client, const Storage &storage)
{ {
......
...@@ -23,6 +23,13 @@ ...@@ -23,6 +23,13 @@
#include "CommandResult.hxx" #include "CommandResult.hxx"
class Client; class Client;
class Storage;
CommandResult
handle_listfiles_storage(Client &client, Storage &storage, const char *uri);
CommandResult
handle_listfiles_storage(Client &client, const char *uri);
CommandResult CommandResult
handle_listmounts(Client &client, int argc, char *argv[]); handle_listmounts(Client &client, int argc, char *argv[]);
......
...@@ -29,29 +29,39 @@ ...@@ -29,29 +29,39 @@
#include "LightDirectory.hxx" #include "LightDirectory.hxx"
#include "PlaylistInfo.hxx" #include "PlaylistInfo.hxx"
#include "Interface.hxx" #include "Interface.hxx"
#include "fs/Traits.hxx"
#include <functional> #include <functional>
static const char *
ApplyBaseFlag(const char *uri, bool base)
{
if (base)
uri = PathTraitsUTF8::GetBase(uri);
return uri;
}
static void static void
PrintDirectoryURI(Client &client, const LightDirectory &directory) PrintDirectoryURI(Client &client, bool base, const LightDirectory &directory)
{ {
client_printf(client, "directory: %s\n", directory.GetPath()); client_printf(client, "directory: %s\n",
ApplyBaseFlag(directory.GetPath(), base));
} }
static bool static bool
PrintDirectoryBrief(Client &client, const LightDirectory &directory) PrintDirectoryBrief(Client &client, bool base, const LightDirectory &directory)
{ {
if (!directory.IsRoot()) if (!directory.IsRoot())
PrintDirectoryURI(client, directory); PrintDirectoryURI(client, base, directory);
return true; return true;
} }
static bool static bool
PrintDirectoryFull(Client &client, const LightDirectory &directory) PrintDirectoryFull(Client &client, bool base, const LightDirectory &directory)
{ {
if (!directory.IsRoot()) { if (!directory.IsRoot()) {
PrintDirectoryURI(client, directory); PrintDirectoryURI(client, base, directory);
if (directory.mtime > 0) if (directory.mtime > 0)
time_print(client, "Last-Modified", directory.mtime); time_print(client, "Last-Modified", directory.mtime);
...@@ -61,23 +71,24 @@ PrintDirectoryFull(Client &client, const LightDirectory &directory) ...@@ -61,23 +71,24 @@ PrintDirectoryFull(Client &client, const LightDirectory &directory)
} }
static void static void
print_playlist_in_directory(Client &client, print_playlist_in_directory(Client &client, bool base,
const char *directory, const char *directory,
const char *name_utf8) const char *name_utf8)
{ {
if (directory == nullptr) if (base || directory == nullptr)
client_printf(client, "playlist: %s\n", name_utf8); client_printf(client, "playlist: %s\n",
ApplyBaseFlag(name_utf8, base));
else else
client_printf(client, "playlist: %s/%s\n", client_printf(client, "playlist: %s/%s\n",
directory, name_utf8); directory, name_utf8);
} }
static void static void
print_playlist_in_directory(Client &client, print_playlist_in_directory(Client &client, bool base,
const LightDirectory *directory, const LightDirectory *directory,
const char *name_utf8) const char *name_utf8)
{ {
if (directory == nullptr || directory->IsRoot()) if (base || directory == nullptr || directory->IsRoot())
client_printf(client, "playlist: %s\n", name_utf8); client_printf(client, "playlist: %s\n", name_utf8);
else else
client_printf(client, "playlist: %s/%s\n", client_printf(client, "playlist: %s/%s\n",
...@@ -85,44 +96,48 @@ print_playlist_in_directory(Client &client, ...@@ -85,44 +96,48 @@ print_playlist_in_directory(Client &client,
} }
static bool static bool
PrintSongBrief(Client &client, const LightSong &song) PrintSongBrief(Client &client, bool base, const LightSong &song)
{ {
song_print_uri(client, song); song_print_uri(client, song, base);
if (song.tag->has_playlist) if (song.tag->has_playlist)
/* this song file has an embedded CUE sheet */ /* this song file has an embedded CUE sheet */
print_playlist_in_directory(client, song.directory, song.uri); print_playlist_in_directory(client, base,
song.directory, song.uri);
return true; return true;
} }
static bool static bool
PrintSongFull(Client &client, const LightSong &song) PrintSongFull(Client &client, bool base, const LightSong &song)
{ {
song_print_info(client, song); song_print_info(client, song, base);
if (song.tag->has_playlist) if (song.tag->has_playlist)
/* this song file has an embedded CUE sheet */ /* this song file has an embedded CUE sheet */
print_playlist_in_directory(client, song.directory, song.uri); print_playlist_in_directory(client, base,
song.directory, song.uri);
return true; return true;
} }
static bool static bool
PrintPlaylistBrief(Client &client, PrintPlaylistBrief(Client &client, bool base,
const PlaylistInfo &playlist, const PlaylistInfo &playlist,
const LightDirectory &directory) const LightDirectory &directory)
{ {
print_playlist_in_directory(client, &directory, playlist.name.c_str()); print_playlist_in_directory(client, base,
&directory, playlist.name.c_str());
return true; return true;
} }
static bool static bool
PrintPlaylistFull(Client &client, PrintPlaylistFull(Client &client, bool base,
const PlaylistInfo &playlist, const PlaylistInfo &playlist,
const LightDirectory &directory) const LightDirectory &directory)
{ {
print_playlist_in_directory(client, &directory, playlist.name.c_str()); print_playlist_in_directory(client, base,
&directory, playlist.name.c_str());
if (playlist.mtime > 0) if (playlist.mtime > 0)
time_print(client, "Last-Modified", playlist.mtime); time_print(client, "Last-Modified", playlist.mtime);
...@@ -132,7 +147,7 @@ PrintPlaylistFull(Client &client, ...@@ -132,7 +147,7 @@ PrintPlaylistFull(Client &client,
bool bool
db_selection_print(Client &client, const DatabaseSelection &selection, db_selection_print(Client &client, const DatabaseSelection &selection,
bool full, Error &error) bool full, bool base, Error &error)
{ {
const Database *db = client.GetDatabase(error); const Database *db = client.GetDatabase(error);
if (db == nullptr) if (db == nullptr)
...@@ -141,13 +156,13 @@ db_selection_print(Client &client, const DatabaseSelection &selection, ...@@ -141,13 +156,13 @@ db_selection_print(Client &client, const DatabaseSelection &selection,
using namespace std::placeholders; using namespace std::placeholders;
const auto d = selection.filter == nullptr const auto d = selection.filter == nullptr
? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief, ? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief,
std::ref(client), _1) std::ref(client), base, _1)
: VisitDirectory(); : VisitDirectory();
const auto s = std::bind(full ? PrintSongFull : PrintSongBrief, const auto s = std::bind(full ? PrintSongFull : PrintSongBrief,
std::ref(client), _1); std::ref(client), base, _1);
const auto p = selection.filter == nullptr const auto p = selection.filter == nullptr
? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief, ? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
std::ref(client), _1, _2) std::ref(client), base, _1, _2)
: VisitPlaylist(); : VisitPlaylist();
return db->Visit(selection, d, s, p, error); return db->Visit(selection, d, s, p, error);
...@@ -202,7 +217,7 @@ bool ...@@ -202,7 +217,7 @@ bool
printAllIn(Client &client, const char *uri_utf8, Error &error) printAllIn(Client &client, const char *uri_utf8, Error &error)
{ {
const DatabaseSelection selection(uri_utf8, true); const DatabaseSelection selection(uri_utf8, true);
return db_selection_print(client, selection, false, error); return db_selection_print(client, selection, false, false, error);
} }
bool bool
...@@ -210,7 +225,7 @@ printInfoForAllIn(Client &client, const char *uri_utf8, ...@@ -210,7 +225,7 @@ printInfoForAllIn(Client &client, const char *uri_utf8,
Error &error) Error &error)
{ {
const DatabaseSelection selection(uri_utf8, true); const DatabaseSelection selection(uri_utf8, true);
return db_selection_print(client, selection, true, error); return db_selection_print(client, selection, true, false, error);
} }
static bool static bool
......
...@@ -29,10 +29,11 @@ class Error; ...@@ -29,10 +29,11 @@ class Error;
/** /**
* @param full print attributes/tags * @param full print attributes/tags
* @param base print only base name of songs/directories?
*/ */
bool bool
db_selection_print(Client &client, const DatabaseSelection &selection, db_selection_print(Client &client, const DatabaseSelection &selection,
bool full, Error &error); bool full, bool base, Error &error);
gcc_nonnull(2) gcc_nonnull(2)
bool bool
......
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