command: add command "listfiles"

Lists files and directories.  Supports storage plugins.
This commit is contained in:
Max Kellermann
2014-02-28 19:02:23 +01:00
parent 797bbeabeb
commit 96afa8bd2b
15 changed files with 337 additions and 55 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -24,6 +24,9 @@
class Client;
CommandResult
handle_listfiles_db(Client &client, const char *uri);
CommandResult
handle_lsinfo2(Client &client, int argc, char *argv[]);

View File

@@ -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

View File

@@ -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[]);

View File

@@ -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,

View File

@@ -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[]);

View File

@@ -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)
{

View File

@@ -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[]);