command: add command "listfiles"
Lists files and directories. Supports storage plugins.
This commit is contained in:
parent
797bbeabeb
commit
96afa8bd2b
2
NEWS
2
NEWS
@ -1,6 +1,6 @@
|
|||||||
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
|
|||||||
</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,8 +30,13 @@
|
|||||||
#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) {
|
||||||
@ -41,33 +46,34 @@ song_print_uri(Client &client, const char *uri)
|
|||||||
}
|
}
|
||||||
#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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|||||||
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[] = {
|
|||||||
{ "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
|
||||||
|
@ -31,6 +31,18 @@
|
|||||||
#include "SongFilter.hxx"
|
#include "SongFilter.hxx"
|
||||||
#include "protocol/Result.hxx"
|
#include "protocol/Result.hxx"
|
||||||
|
|
||||||
|
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
|
CommandResult
|
||||||
handle_lsinfo2(Client &client, int argc, char *argv[])
|
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)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
|
|
||||||
|
CommandResult
|
||||||
|
handle_listfiles_db(Client &client, const char *uri);
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_lsinfo2(Client &client, int argc, char *argv[]);
|
handle_lsinfo2(Client &client, int argc, char *argv[]);
|
||||||
|
|
||||||
|
@ -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 @@
|
|||||||
#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
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
|
|
||||||
|
CommandResult
|
||||||
|
handle_listfiles_local(Client &client, const char *path_utf8);
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_read_comments(Client &client, int argc, char *argv[]);
|
handle_read_comments(Client &client, int argc, char *argv[]);
|
||||||
|
|
||||||
|
@ -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)
|
|||||||
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,
|
||||||
|
@ -39,6 +39,9 @@ handle_kill(Client &client, int argc, char *argv[]);
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_close(Client &client, int argc, char *argv[]);
|
handle_close(Client &client, int argc, char *argv[]);
|
||||||
|
|
||||||
|
CommandResult
|
||||||
|
handle_listfiles(Client &client, int argc, char *argv[]);
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_lsinfo(Client &client, int argc, char *argv[]);
|
handle_lsinfo(Client &client, int argc, char *argv[]);
|
||||||
|
|
||||||
|
@ -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 @@
|
|||||||
#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 @@
|
|||||||
#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 @@
|
|||||||
#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 void
|
static const char *
|
||||||
PrintDirectoryURI(Client &client, const LightDirectory &directory)
|
ApplyBaseFlag(const char *uri, bool base)
|
||||||
{
|
{
|
||||||
client_printf(client, "directory: %s\n", directory.GetPath());
|
if (base)
|
||||||
|
uri = PathTraitsUTF8::GetBase(uri);
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintDirectoryURI(Client &client, bool base, const LightDirectory &directory)
|
||||||
|
{
|
||||||
|
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
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
|
|||||||
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,
|
|||||||
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;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @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
|
||||||
|
Loading…
Reference in New Issue
Block a user