diff options
author | Max Kellermann <max@duempel.org> | 2014-02-28 19:02:23 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2014-03-01 06:25:57 +0100 |
commit | 96afa8bd2ba44d6669949db5fce4fee5f826b753 (patch) | |
tree | a484d32c412b2cb716cc149a42f702e8c3013f48 /src/command | |
parent | 797bbeabeb212ee3d818acdb19d85e2d8642f5ed (diff) |
command: add command "listfiles"
Lists files and directories. Supports storage plugins.
Diffstat (limited to 'src/command')
-rw-r--r-- | src/command/AllCommands.cxx | 3 | ||||
-rw-r--r-- | src/command/DatabaseCommands.cxx | 16 | ||||
-rw-r--r-- | src/command/DatabaseCommands.hxx | 3 | ||||
-rw-r--r-- | src/command/FileCommands.cxx | 72 | ||||
-rw-r--r-- | src/command/FileCommands.hxx | 3 | ||||
-rw-r--r-- | src/command/OtherCommands.cxx | 37 | ||||
-rw-r--r-- | src/command/OtherCommands.hxx | 3 | ||||
-rw-r--r-- | src/command/StorageCommands.cxx | 95 | ||||
-rw-r--r-- | src/command/StorageCommands.hxx | 7 |
9 files changed, 237 insertions, 2 deletions
diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index 488c27daa..ab4de823b 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -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 diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index 9d00698c6..96ea357bc 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -32,6 +32,18 @@ #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[]) { const char *const uri = argc == 2 @@ -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); } diff --git a/src/command/DatabaseCommands.hxx b/src/command/DatabaseCommands.hxx index 8678f19c8..7a9c68ffe 100644 --- a/src/command/DatabaseCommands.hxx +++ b/src/command/DatabaseCommands.hxx @@ -25,6 +25,9 @@ class Client; CommandResult +handle_listfiles_db(Client &client, const char *uri); + +CommandResult handle_lsinfo2(Client &client, int argc, char *argv[]); CommandResult diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index e5cc9690f..f7ca28b50 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -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 diff --git a/src/command/FileCommands.hxx b/src/command/FileCommands.hxx index 8858b62c9..51467a009 100644 --- a/src/command/FileCommands.hxx +++ b/src/command/FileCommands.hxx @@ -25,6 +25,9 @@ class Client; CommandResult +handle_listfiles_local(Client &client, const char *path_utf8); + +CommandResult handle_read_comments(Client &client, int argc, char *argv[]); #endif diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index 6415e84df..eac26735b 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -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, diff --git a/src/command/OtherCommands.hxx b/src/command/OtherCommands.hxx index 4f54303bd..f487e9605 100644 --- a/src/command/OtherCommands.hxx +++ b/src/command/OtherCommands.hxx @@ -40,6 +40,9 @@ 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[]); CommandResult diff --git a/src/command/StorageCommands.cxx b/src/command/StorageCommands.cxx index f0698f04b..ffee8cb74 100644 --- a/src/command/StorageCommands.cxx +++ b/src/command/StorageCommands.cxx @@ -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) { diff --git a/src/command/StorageCommands.hxx b/src/command/StorageCommands.hxx index 82470a0e2..905cd636e 100644 --- a/src/command/StorageCommands.hxx +++ b/src/command/StorageCommands.hxx @@ -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[]); |