command: add command "listfiles"
Lists files and directories. Supports storage plugins.
This commit is contained in:
@@ -107,6 +107,9 @@ static const struct command commands[] = {
|
||||
{ "list", PERMISSION_READ, 1, -1, handle_list },
|
||||
{ "listall", PERMISSION_READ, 0, 1, handle_listall },
|
||||
{ "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 },
|
||||
#endif
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
|
@@ -31,6 +31,18 @@
|
||||
#include "SongFilter.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
|
||||
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);
|
||||
|
||||
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 CommandResult::OK;
|
||||
@@ -60,7 +72,7 @@ handle_match(Client &client, int argc, char *argv[], bool fold_case)
|
||||
const DatabaseSelection selection("", true, &filter);
|
||||
|
||||
Error error;
|
||||
return db_selection_print(client, selection, true, error)
|
||||
return db_selection_print(client, selection, true, false, error)
|
||||
? CommandResult::OK
|
||||
: print_error(client, error);
|
||||
}
|
||||
|
@@ -24,6 +24,9 @@
|
||||
|
||||
class Client;
|
||||
|
||||
CommandResult
|
||||
handle_listfiles_db(Client &client, const char *uri);
|
||||
|
||||
CommandResult
|
||||
handle_lsinfo2(Client &client, int argc, char *argv[]);
|
||||
|
||||
|
@@ -17,6 +17,8 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS /* for PRIu64 */
|
||||
|
||||
#include "config.h"
|
||||
#include "FileCommands.hxx"
|
||||
#include "CommandError.hxx"
|
||||
@@ -33,9 +35,79 @@
|
||||
#include "TagFile.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "TimePrint.hxx"
|
||||
#include "ls.hxx"
|
||||
|
||||
#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
|
||||
static bool
|
||||
|
@@ -24,6 +24,9 @@
|
||||
|
||||
class Client;
|
||||
|
||||
CommandResult
|
||||
handle_listfiles_local(Client &client, const char *path_utf8);
|
||||
|
||||
CommandResult
|
||||
handle_read_comments(Client &client, int argc, char *argv[]);
|
||||
|
||||
|
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "OtherCommands.hxx"
|
||||
#include "FileCommands.hxx"
|
||||
#include "StorageCommands.hxx"
|
||||
#include "CommandError.hxx"
|
||||
#include "db/Uri.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
@@ -112,6 +114,41 @@ print_tag(TagType type, const char *value, void *ctx)
|
||||
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 = {
|
||||
nullptr,
|
||||
print_tag,
|
||||
|
@@ -39,6 +39,9 @@ handle_kill(Client &client, int argc, char *argv[]);
|
||||
CommandResult
|
||||
handle_close(Client &client, int argc, char *argv[]);
|
||||
|
||||
CommandResult
|
||||
handle_listfiles(Client &client, int argc, char *argv[]);
|
||||
|
||||
CommandResult
|
||||
handle_lsinfo(Client &client, int argc, char *argv[]);
|
||||
|
||||
|
@@ -17,6 +17,8 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS /* for PRIu64 */
|
||||
|
||||
#include "config.h"
|
||||
#include "StorageCommands.hxx"
|
||||
#include "CommandError.hxx"
|
||||
@@ -29,10 +31,103 @@
|
||||
#include "Instance.hxx"
|
||||
#include "storage/Registry.hxx"
|
||||
#include "storage/CompositeStorage.hxx"
|
||||
#include "storage/FileInfo.hxx"
|
||||
#include "db/plugins/simple/SimpleDatabasePlugin.hxx"
|
||||
#include "db/update/Service.hxx"
|
||||
#include "TimePrint.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
|
||||
print_storage_uri(Client &client, const Storage &storage)
|
||||
{
|
||||
|
@@ -23,6 +23,13 @@
|
||||
#include "CommandResult.hxx"
|
||||
|
||||
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
|
||||
handle_listmounts(Client &client, int argc, char *argv[]);
|
||||
|
Reference in New Issue
Block a user