diff options
Diffstat (limited to 'src/command')
25 files changed, 531 insertions, 456 deletions
diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index 6a4b18198..e0b16d77d 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -35,8 +35,10 @@ #include "protocol/Result.hxx" #include "Partition.hxx" #include "client/Client.hxx" +#include "util/Macros.hxx" #include "util/Tokenizer.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" #ifdef ENABLE_SQLITE #include "StickerCommands.hxx" @@ -60,22 +62,22 @@ struct command { unsigned permission; int min; int max; - CommandResult (*handler)(Client &client, unsigned argc, char **argv); + CommandResult (*handler)(Client &client, ConstBuffer<const char *> args); }; /* don't be fooled, this is the command handler for "commands" command */ static CommandResult -handle_commands(Client &client, unsigned argc, char *argv[]); +handle_commands(Client &client, ConstBuffer<const char *> args); static CommandResult -handle_not_commands(Client &client, unsigned argc, char *argv[]); +handle_not_commands(Client &client, ConstBuffer<const char *> args); /** * The command registry. * * This array must be sorted! */ -static const struct command commands[] = { +static constexpr struct command commands[] = { { "add", PERMISSION_ADD, 1, 1, handle_add }, { "addid", PERMISSION_ADD, 1, 2, handle_addid }, { "addtagid", PERMISSION_ADD, 3, 3, handle_addtagid }, @@ -194,7 +196,7 @@ static const struct command commands[] = { { "volume", PERMISSION_CONTROL, 1, 1, handle_volume }, }; -static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]); +static constexpr unsigned num_commands = ARRAY_SIZE(commands); static bool command_available(gcc_unused const Partition &partition, @@ -210,19 +212,27 @@ command_available(gcc_unused const Partition &partition, return neighbor_commands_available(partition.instance); #endif + if (strcmp(cmd->cmd, "save") == 0 || + strcmp(cmd->cmd, "rm") == 0 || + strcmp(cmd->cmd, "rename") == 0 || + strcmp(cmd->cmd, "playlistdelete") == 0 || + strcmp(cmd->cmd, "playlistmove") == 0 || + strcmp(cmd->cmd, "playlistclear") == 0 || + strcmp(cmd->cmd, "playlistadd") == 0 || + strcmp(cmd->cmd, "listplaylists") == 0) + return playlist_commands_available(); + return true; } /* don't be fooled, this is the command handler for "commands" command */ static CommandResult -handle_commands(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_commands(Client &client, gcc_unused ConstBuffer<const char *> args) { const unsigned permission = client.GetPermission(); - const struct command *cmd; for (unsigned i = 0; i < num_commands; ++i) { - cmd = &commands[i]; + const struct command *cmd = &commands[i]; if (cmd->permission == (permission & cmd->permission) && command_available(client.partition, cmd)) @@ -233,14 +243,12 @@ handle_commands(Client &client, } static CommandResult -handle_not_commands(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_not_commands(Client &client, gcc_unused ConstBuffer<const char *> args) { const unsigned permission = client.GetPermission(); - const struct command *cmd; for (unsigned i = 0; i < num_commands; ++i) { - cmd = &commands[i]; + const struct command *cmd = &commands[i]; if (cmd->permission != (permission & cmd->permission)) client_printf(client, "command: %s\n", cmd->cmd); @@ -266,13 +274,12 @@ static const struct command * command_lookup(const char *name) { unsigned a = 0, b = num_commands, i; - int cmp; /* binary search */ do { i = (a + b) / 2; - cmp = strcmp(name, commands[i].cmd); + const auto cmp = strcmp(name, commands[i].cmd); if (cmp == 0) return &commands[i]; else if (cmp < 0) @@ -286,11 +293,8 @@ command_lookup(const char *name) static bool command_check_request(const struct command *cmd, Client &client, - unsigned permission, unsigned argc, char *argv[]) + unsigned permission, ConstBuffer<const char *> args) { - const unsigned min = cmd->min + 1; - const unsigned max = cmd->max + 1; - if (cmd->permission != (permission & cmd->permission)) { command_error(client, ACK_ERROR_PERMISSION, "you don't have permission for \"%s\"", @@ -298,21 +302,24 @@ command_check_request(const struct command *cmd, Client &client, return false; } - if (min == 0) + const int min = cmd->min; + const int max = cmd->max; + + if (min < 0) return true; - if (min == max && max != argc) { + if (min == max && unsigned(max) != args.size) { command_error(client, ACK_ERROR_ARG, "wrong number of arguments for \"%s\"", - argv[0]); + cmd->cmd); return false; - } else if (argc < min) { + } else if (args.size < unsigned(min)) { command_error(client, ACK_ERROR_ARG, - "too few arguments for \"%s\"", argv[0]); + "too few arguments for \"%s\"", cmd->cmd); return false; - } else if (argc > max && max /* != 0 */ ) { + } else if (max >= 0 && args.size > unsigned(max)) { command_error(client, ACK_ERROR_ARG, - "too many arguments for \"%s\"", argv[0]); + "too many arguments for \"%s\"", cmd->cmd); return false; } else return true; @@ -320,25 +327,20 @@ command_check_request(const struct command *cmd, Client &client, static const struct command * command_checked_lookup(Client &client, unsigned permission, - unsigned argc, char *argv[]) + const char *cmd_name, ConstBuffer<const char *> args) { - const struct command *cmd; - current_command = ""; - if (argc == 0) - return nullptr; - - cmd = command_lookup(argv[0]); + const struct command *cmd = command_lookup(cmd_name); if (cmd == nullptr) { command_error(client, ACK_ERROR_UNKNOWN, - "unknown command \"%s\"", argv[0]); + "unknown command \"%s\"", cmd_name); return nullptr; } current_command = cmd->cmd; - if (!command_check_request(cmd, client, permission, argc, argv)) + if (!command_check_request(cmd, client, permission, args)) return nullptr; return cmd; @@ -348,17 +350,18 @@ CommandResult command_process(Client &client, unsigned num, char *line) { Error error; - char *argv[COMMAND_ARGV_MAX] = { nullptr }; - const struct command *cmd; - CommandResult ret = CommandResult::ERROR; command_list_num = num; /* get the command name (first word on the line) */ + /* we have to set current_command because command_error() + expects it to be set */ Tokenizer tokenizer(line); - argv[0] = tokenizer.NextWord(error); - if (argv[0] == nullptr) { + + const char *const cmd_name = current_command = + tokenizer.NextWord(error); + if (cmd_name == nullptr) { current_command = ""; if (tokenizer.IsEnd()) command_error(client, ACK_ERROR_UNKNOWN, @@ -374,38 +377,41 @@ command_process(Client &client, unsigned num, char *line) return CommandResult::FINISH; } - unsigned argc = 1; + char *argv[COMMAND_ARGV_MAX]; + ConstBuffer<const char *> args(argv, 0); /* now parse the arguments (quoted or unquoted) */ - while (argc < COMMAND_ARGV_MAX && - (argv[argc] = - tokenizer.NextParam(error)) != nullptr) - ++argc; - - /* some error checks; we have to set current_command because - command_error() expects it to be set */ - - current_command = argv[0]; - - if (argc >= COMMAND_ARGV_MAX) { - command_error(client, ACK_ERROR_ARG, "Too many arguments"); - current_command = nullptr; - return CommandResult::ERROR; - } - - if (!tokenizer.IsEnd()) { - command_error(client, ACK_ERROR_ARG, "%s", error.GetMessage()); - current_command = nullptr; - return CommandResult::ERROR; + while (true) { + if (args.size == COMMAND_ARGV_MAX) { + command_error(client, ACK_ERROR_ARG, + "Too many arguments"); + current_command = nullptr; + return CommandResult::ERROR; + } + + char *a = tokenizer.NextParam(error); + if (a == nullptr) { + if (tokenizer.IsEnd()) + break; + + command_error(client, ACK_ERROR_ARG, "%s", error.GetMessage()); + current_command = nullptr; + return CommandResult::ERROR; + } + + argv[args.size++] = a; } /* look up and invoke the command handler */ - cmd = command_checked_lookup(client, client.GetPermission(), - argc, argv); - if (cmd) - ret = cmd->handler(client, argc, argv); + const struct command *cmd = + command_checked_lookup(client, client.GetPermission(), + cmd_name, args); + + CommandResult ret = cmd + ? cmd->handler(client, args) + : CommandResult::ERROR; current_command = nullptr; command_list_num = 0; diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index a3ea8d0ae..2d1e1f69b 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -32,6 +32,7 @@ #include "util/Error.hxx" #include "SongFilter.hxx" #include "protocol/Result.hxx" +#include "protocol/ArgParser.hxx" #include "BulkEdit.hxx" #include <string.h> @@ -49,12 +50,10 @@ handle_listfiles_db(Client &client, const char *uri) } CommandResult -handle_lsinfo2(Client &client, unsigned argc, char *argv[]) +handle_lsinfo2(Client &client, ConstBuffer<const char *> args) { - const char *const uri = argc == 2 - ? argv[1] - /* default is root directory */ - : ""; + /* default is root directory */ + const char *const uri = args.IsEmpty() ? "" : args.front(); const DatabaseSelection selection(uri, false); @@ -66,9 +65,17 @@ handle_lsinfo2(Client &client, unsigned argc, char *argv[]) } static CommandResult -handle_match(Client &client, unsigned argc, char *argv[], bool fold_case) +handle_match(Client &client, ConstBuffer<const char *> args, bool fold_case) { - ConstBuffer<const char *> args(argv + 1, argc - 1); + unsigned window_start = 0, window_end = std::numeric_limits<int>::max(); + if (args.size >= 2 && strcmp(args[args.size - 2], "window") == 0) { + if (!check_range(client, &window_start, &window_end, + args.back())) + return CommandResult::ERROR; + + args.pop_back(); + args.pop_back(); + } SongFilter filter; if (!filter.Parse(args, fold_case)) { @@ -79,28 +86,27 @@ handle_match(Client &client, unsigned argc, char *argv[], bool fold_case) const DatabaseSelection selection("", true, &filter); Error error; - return db_selection_print(client, selection, true, false, error) + return db_selection_print(client, selection, true, false, + window_start, window_end, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_find(Client &client, unsigned argc, char *argv[]) +handle_find(Client &client, ConstBuffer<const char *> args) { - return handle_match(client, argc, argv, false); + return handle_match(client, args, false); } CommandResult -handle_search(Client &client, unsigned argc, char *argv[]) +handle_search(Client &client, ConstBuffer<const char *> args) { - return handle_match(client, argc, argv, true); + return handle_match(client, args, true); } static CommandResult -handle_match_add(Client &client, unsigned argc, char *argv[], bool fold_case) +handle_match_add(Client &client, ConstBuffer<const char *> args, bool fold_case) { - ConstBuffer<const char *> args(argv + 1, argc - 1); - SongFilter filter; if (!filter.Parse(args, fold_case)) { command_error(client, ACK_ERROR_ARG, "incorrect arguments"); @@ -117,21 +123,20 @@ handle_match_add(Client &client, unsigned argc, char *argv[], bool fold_case) } CommandResult -handle_findadd(Client &client, unsigned argc, char *argv[]) +handle_findadd(Client &client, ConstBuffer<const char *> args) { - return handle_match_add(client, argc, argv, false); + return handle_match_add(client, args, false); } CommandResult -handle_searchadd(Client &client, unsigned argc, char *argv[]) +handle_searchadd(Client &client, ConstBuffer<const char *> args) { - return handle_match_add(client, argc, argv, true); + return handle_match_add(client, args, true); } CommandResult -handle_searchaddpl(Client &client, unsigned argc, char *argv[]) +handle_searchaddpl(Client &client, ConstBuffer<const char *> args) { - ConstBuffer<const char *> args(argv + 1, argc - 1); const char *playlist = args.shift(); SongFilter filter; @@ -152,10 +157,8 @@ handle_searchaddpl(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_count(Client &client, unsigned argc, char *argv[]) +handle_count(Client &client, ConstBuffer<const char *> args) { - ConstBuffer<const char *> args(argv + 1, argc - 1); - TagType group = TAG_NUM_OF_ITEM_TYPES; if (args.size >= 2 && strcmp(args[args.size - 2], "group") == 0) { const char *s = args[args.size - 1]; @@ -183,24 +186,21 @@ handle_count(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_listall(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_listall(Client &client, ConstBuffer<const char *> args) { - const char *directory = ""; - - if (argc == 2) - directory = argv[1]; + /* default is root directory */ + const char *const uri = args.IsEmpty() ? "" : args.front(); Error error; - return db_selection_print(client, DatabaseSelection(directory, true), + return db_selection_print(client, DatabaseSelection(uri, true), false, false, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_list(Client &client, unsigned argc, char *argv[]) +handle_list(Client &client, ConstBuffer<const char *> args) { - ConstBuffer<const char *> args(argv + 1, argc - 1); const char *tag_name = args.shift(); unsigned tagType = locate_parse_type(tag_name); @@ -271,15 +271,13 @@ handle_list(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_listallinfo(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_listallinfo(Client &client, ConstBuffer<const char *> args) { - const char *directory = ""; - - if (argc == 2) - directory = argv[1]; + /* default is root directory */ + const char *const uri = args.IsEmpty() ? "" : args.front(); Error error; - return db_selection_print(client, DatabaseSelection(directory, true), + return db_selection_print(client, DatabaseSelection(uri, true), true, false, error) ? CommandResult::OK : print_error(client, error); diff --git a/src/command/DatabaseCommands.hxx b/src/command/DatabaseCommands.hxx index 7abf89e0c..0f6e2700a 100644 --- a/src/command/DatabaseCommands.hxx +++ b/src/command/DatabaseCommands.hxx @@ -23,38 +23,39 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult handle_listfiles_db(Client &client, const char *uri); CommandResult -handle_lsinfo2(Client &client, unsigned argc, char *argv[]); +handle_lsinfo2(Client &client, ConstBuffer<const char *> args); CommandResult -handle_find(Client &client, unsigned argc, char *argv[]); +handle_find(Client &client, ConstBuffer<const char *> args); CommandResult -handle_findadd(Client &client, unsigned argc, char *argv[]); +handle_findadd(Client &client, ConstBuffer<const char *> args); CommandResult -handle_search(Client &client, unsigned argc, char *argv[]); +handle_search(Client &client, ConstBuffer<const char *> args); CommandResult -handle_searchadd(Client &client, unsigned argc, char *argv[]); +handle_searchadd(Client &client, ConstBuffer<const char *> args); CommandResult -handle_searchaddpl(Client &client, unsigned argc, char *argv[]); +handle_searchaddpl(Client &client, ConstBuffer<const char *> args); CommandResult -handle_count(Client &client, unsigned argc, char *argv[]); +handle_count(Client &client, ConstBuffer<const char *> args); CommandResult -handle_listall(Client &client, unsigned argc, char *argv[]); +handle_listall(Client &client, ConstBuffer<const char *> args); CommandResult -handle_list(Client &client, unsigned argc, char *argv[]); +handle_list(Client &client, ConstBuffer<const char *> args); CommandResult -handle_listallinfo(Client &client, unsigned argc, char *argv[]); +handle_listallinfo(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index 1b6a11cf5..acf71eca4 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -25,6 +25,7 @@ #include "protocol/Ack.hxx" #include "protocol/Result.hxx" #include "client/Client.hxx" +#include "util/ConstBuffer.hxx" #include "util/CharUtil.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" @@ -202,11 +203,10 @@ read_file_comments(Client &client, const Path path_fs) } CommandResult -handle_read_comments(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_read_comments(Client &client, ConstBuffer<const char *> args) { - assert(argc == 2); - - const char *const uri = argv[1]; + assert(args.size == 1); + const char *const uri = args.front(); if (memcmp(uri, "file:///", 8) == 0) { /* read comments from arbitrary local file */ diff --git a/src/command/FileCommands.hxx b/src/command/FileCommands.hxx index 62835a82c..b77157e3f 100644 --- a/src/command/FileCommands.hxx +++ b/src/command/FileCommands.hxx @@ -23,11 +23,12 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult handle_listfiles_local(Client &client, const char *path_utf8); CommandResult -handle_read_comments(Client &client, unsigned argc, char *argv[]); +handle_read_comments(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/MessageCommands.cxx b/src/command/MessageCommands.cxx index a86bdf30c..4bf22abcc 100644 --- a/src/command/MessageCommands.cxx +++ b/src/command/MessageCommands.cxx @@ -24,6 +24,7 @@ #include "Instance.hxx" #include "Partition.hxx" #include "protocol/Result.hxx" +#include "util/ConstBuffer.hxx" #include <set> #include <string> @@ -31,11 +32,12 @@ #include <assert.h> CommandResult -handle_subscribe(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_subscribe(Client &client, ConstBuffer<const char *> args) { - assert(argc == 2); + assert(args.size == 1); + const char *const channel_name = args[0]; - switch (client.Subscribe(argv[1])) { + switch (client.Subscribe(channel_name)) { case Client::SubscribeResult::OK: return CommandResult::OK; @@ -61,11 +63,12 @@ handle_subscribe(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_unsubscribe(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_unsubscribe(Client &client, ConstBuffer<const char *> args) { - assert(argc == 2); + assert(args.size == 1); + const char *const channel_name = args[0]; - if (client.Unsubscribe(argv[1])) + if (client.Unsubscribe(channel_name)) return CommandResult::OK; else { command_error(client, ACK_ERROR_NO_EXIST, @@ -75,10 +78,9 @@ handle_unsubscribe(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_channels(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_channels(Client &client, gcc_unused ConstBuffer<const char *> args) { - assert(argc == 1); + assert(args.IsEmpty()); std::set<std::string> channels; for (const auto &c : *client.partition.instance.client_list) @@ -93,9 +95,9 @@ handle_channels(Client &client, CommandResult handle_read_messages(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) + gcc_unused ConstBuffer<const char *> args) { - assert(argc == 1); + assert(args.IsEmpty()); while (!client.messages.empty()) { const ClientMessage &msg = client.messages.front(); @@ -109,19 +111,21 @@ handle_read_messages(Client &client, } CommandResult -handle_send_message(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_send_message(Client &client, ConstBuffer<const char *> args) { - assert(argc == 3); + assert(args.size == 2); - if (!client_message_valid_channel_name(argv[1])) { + const char *const channel_name = args[0]; + const char *const message_text = args[1]; + + if (!client_message_valid_channel_name(channel_name)) { command_error(client, ACK_ERROR_ARG, "invalid channel name"); return CommandResult::ERROR; } bool sent = false; - const ClientMessage msg(argv[1], argv[2]); + const ClientMessage msg(channel_name, message_text); for (auto &c : *client.partition.instance.client_list) if (c.PushMessage(msg)) sent = true; diff --git a/src/command/MessageCommands.hxx b/src/command/MessageCommands.hxx index ac8afe2fb..b10863277 100644 --- a/src/command/MessageCommands.hxx +++ b/src/command/MessageCommands.hxx @@ -23,20 +23,21 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult -handle_subscribe(Client &client, unsigned argc, char *argv[]); +handle_subscribe(Client &client, ConstBuffer<const char *> args); CommandResult -handle_unsubscribe(Client &client, unsigned argc, char *argv[]); +handle_unsubscribe(Client &client, ConstBuffer<const char *> args); CommandResult -handle_channels(Client &client, unsigned argc, char *argv[]); +handle_channels(Client &client, ConstBuffer<const char *> args); CommandResult -handle_read_messages(Client &client, unsigned argc, char *argv[]); +handle_read_messages(Client &client, ConstBuffer<const char *> args); CommandResult -handle_send_message(Client &client, unsigned argc, char *argv[]); +handle_send_message(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/NeighborCommands.cxx b/src/command/NeighborCommands.cxx index 22e8adf9e..3efae7883 100644 --- a/src/command/NeighborCommands.cxx +++ b/src/command/NeighborCommands.cxx @@ -25,6 +25,7 @@ #include "protocol/Result.hxx" #include "neighbor/Glue.hxx" #include "neighbor/Info.hxx" +#include "util/ConstBuffer.hxx" #include <set> #include <string> @@ -38,8 +39,7 @@ neighbor_commands_available(const Instance &instance) } CommandResult -handle_listneighbors(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_listneighbors(Client &client, gcc_unused ConstBuffer<const char *> args) { const NeighborGlue *const neighbors = client.partition.instance.neighbors; diff --git a/src/command/NeighborCommands.hxx b/src/command/NeighborCommands.hxx index 7fb309aeb..e07f26925 100644 --- a/src/command/NeighborCommands.hxx +++ b/src/command/NeighborCommands.hxx @@ -25,12 +25,13 @@ struct Instance; class Client; +template<typename T> struct ConstBuffer; gcc_pure bool neighbor_commands_available(const Instance &instance); CommandResult -handle_listneighbors(Client &client, unsigned argc, char *argv[]); +handle_listneighbors(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index a924f77b5..6328acc4c 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -37,6 +37,7 @@ #include "mixer/Volume.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" #include "fs/AllocatedPath.hxx" #include "Stats.hxx" #include "Permission.hxx" @@ -68,8 +69,7 @@ print_spl_list(Client &client, const PlaylistVector &list) } CommandResult -handle_urlhandlers(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_urlhandlers(Client &client, gcc_unused ConstBuffer<const char *> args) { if (client.IsLocal()) client_puts(client, "handler: file://\n"); @@ -78,31 +78,27 @@ handle_urlhandlers(Client &client, } CommandResult -handle_decoders(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_decoders(Client &client, gcc_unused ConstBuffer<const char *> args) { decoder_list_print(client); return CommandResult::OK; } CommandResult -handle_tagtypes(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_tagtypes(Client &client, gcc_unused ConstBuffer<const char *> args) { tag_print_types(client); return CommandResult::OK; } CommandResult -handle_kill(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_kill(gcc_unused Client &client, gcc_unused ConstBuffer<const char *> args) { return CommandResult::KILL; } CommandResult -handle_close(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_close(gcc_unused Client &client, gcc_unused ConstBuffer<const char *> args) { return CommandResult::FINISH; } @@ -116,12 +112,10 @@ print_tag(TagType type, const char *value, void *ctx) } CommandResult -handle_listfiles(Client &client, unsigned argc, char *argv[]) +handle_listfiles(Client &client, ConstBuffer<const char *> args) { - const char *const uri = argc == 2 - ? argv[1] - /* default is root directory */ - : ""; + /* default is root directory */ + const char *const uri = args.IsEmpty() ? "" : args.front(); if (memcmp(uri, "file:///", 8) == 0) /* list local directory */ @@ -157,12 +151,10 @@ static constexpr tag_handler print_tag_handler = { }; CommandResult -handle_lsinfo(Client &client, unsigned argc, char *argv[]) +handle_lsinfo(Client &client, ConstBuffer<const char *> args) { - const char *const uri = argc == 2 - ? argv[1] - /* default is root directory */ - : ""; + /* default is root directory */ + const char *const uri = args.IsEmpty() ? "" : args.front(); if (memcmp(uri, "file:///", 8) == 0) { /* print information about an arbitrary local file */ @@ -207,7 +199,7 @@ handle_lsinfo(Client &client, unsigned argc, char *argv[]) } #ifdef ENABLE_DATABASE - CommandResult result = handle_lsinfo2(client, argc, argv); + CommandResult result = handle_lsinfo2(client, args); if (result != CommandResult::OK) return result; #endif @@ -265,14 +257,14 @@ handle_update(Client &client, Database &db, #endif static CommandResult -handle_update(Client &client, unsigned argc, char *argv[], bool discard) +handle_update(Client &client, ConstBuffer<const char *> args, bool discard) { #ifdef ENABLE_DATABASE const char *path = ""; - assert(argc <= 2); - if (argc == 2) { - path = argv[1]; + assert(args.size <= 1); + if (!args.IsEmpty()) { + path = args.front(); if (*path == 0 || strcmp(path, "/") == 0) /* backwards compatibility with MPD 0.15 */ @@ -292,8 +284,7 @@ handle_update(Client &client, unsigned argc, char *argv[], bool discard) if (db != nullptr) return handle_update(client, *db, path, discard); #else - (void)argc; - (void)argv; + (void)args; (void)discard; #endif @@ -302,24 +293,24 @@ handle_update(Client &client, unsigned argc, char *argv[], bool discard) } CommandResult -handle_update(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_update(Client &client, gcc_unused ConstBuffer<const char *> args) { - return handle_update(client, argc, argv, false); + return handle_update(client, args, false); } CommandResult -handle_rescan(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_rescan(Client &client, gcc_unused ConstBuffer<const char *> args) { - return handle_update(client, argc, argv, true); + return handle_update(client, args, true); } CommandResult -handle_setvol(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_setvol(Client &client, ConstBuffer<const char *> args) { unsigned level; bool success; - if (!check_unsigned(client, &level, argv[1])) + if (!check_unsigned(client, &level, args.front())) return CommandResult::ERROR; if (level > 100) { @@ -338,10 +329,10 @@ handle_setvol(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_volume(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_volume(Client &client, ConstBuffer<const char *> args) { int relative; - if (!check_int(client, &relative, argv[1])) + if (!check_int(client, &relative, args.front())) return CommandResult::ERROR; if (relative < -100 || relative > 100) { @@ -372,26 +363,24 @@ handle_volume(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_stats(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_stats(Client &client, gcc_unused ConstBuffer<const char *> args) { stats_print(client); return CommandResult::OK; } CommandResult -handle_ping(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_ping(gcc_unused Client &client, gcc_unused ConstBuffer<const char *> args) { return CommandResult::OK; } CommandResult -handle_password(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_password(Client &client, ConstBuffer<const char *> args) { unsigned permission = 0; - if (getPermissionFromPassword(argv[1], &permission) < 0) { + if (getPermissionFromPassword(args.front(), &permission) < 0) { command_error(client, ACK_ERROR_PASSWORD, "incorrect password"); return CommandResult::ERROR; } @@ -402,8 +391,7 @@ handle_password(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_config(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_config(Client &client, gcc_unused ConstBuffer<const char *> args) { if (!client.IsLocal()) { command_error(client, ACK_ERROR_PERMISSION, @@ -423,17 +411,16 @@ handle_config(Client &client, } CommandResult -handle_idle(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_idle(Client &client, ConstBuffer<const char *> args) { unsigned flags = 0; - for (unsigned i = 1; i < argc; ++i) { - unsigned event = idle_parse_name(argv[i]); + for (const char *i : args) { + unsigned event = idle_parse_name(i); if (event == 0) { command_error(client, ACK_ERROR_ARG, "Unrecognized idle event: %s", - argv[i]); + i); return CommandResult::ERROR; } diff --git a/src/command/OtherCommands.hxx b/src/command/OtherCommands.hxx index 7cfa35dfb..a0076954e 100644 --- a/src/command/OtherCommands.hxx +++ b/src/command/OtherCommands.hxx @@ -23,53 +23,54 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult -handle_urlhandlers(Client &client, unsigned argc, char *argv[]); +handle_urlhandlers(Client &client, ConstBuffer<const char *> args); CommandResult -handle_decoders(Client &client, unsigned argc, char *argv[]); +handle_decoders(Client &client, ConstBuffer<const char *> args); CommandResult -handle_tagtypes(Client &client, unsigned argc, char *argv[]); +handle_tagtypes(Client &client, ConstBuffer<const char *> args); CommandResult -handle_kill(Client &client, unsigned argc, char *argv[]); +handle_kill(Client &client, ConstBuffer<const char *> args); CommandResult -handle_close(Client &client, unsigned argc, char *argv[]); +handle_close(Client &client, ConstBuffer<const char *> args); CommandResult -handle_listfiles(Client &client, unsigned argc, char *argv[]); +handle_listfiles(Client &client, ConstBuffer<const char *> args); CommandResult -handle_lsinfo(Client &client, unsigned argc, char *argv[]); +handle_lsinfo(Client &client, ConstBuffer<const char *> args); CommandResult -handle_update(Client &client, unsigned argc, char *argv[]); +handle_update(Client &client, ConstBuffer<const char *> args); CommandResult -handle_rescan(Client &client, unsigned argc, char *argv[]); +handle_rescan(Client &client, ConstBuffer<const char *> args); CommandResult -handle_setvol(Client &client, unsigned argc, char *argv[]); +handle_setvol(Client &client, ConstBuffer<const char *> args); CommandResult -handle_volume(Client &client, unsigned argc, char *argv[]); +handle_volume(Client &client, ConstBuffer<const char *> args); CommandResult -handle_stats(Client &client, unsigned argc, char *argv[]); +handle_stats(Client &client, ConstBuffer<const char *> args); CommandResult -handle_ping(Client &client, unsigned argc, char *argv[]); +handle_ping(Client &client, ConstBuffer<const char *> args); CommandResult -handle_password(Client &client, unsigned argc, char *argv[]); +handle_password(Client &client, ConstBuffer<const char *> args); CommandResult -handle_config(Client &client, unsigned argc, char *argv[]); +handle_config(Client &client, ConstBuffer<const char *> args); CommandResult -handle_idle(Client &client, unsigned argc, char *argv[]); +handle_idle(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/OutputCommands.cxx b/src/command/OutputCommands.cxx index c69a0dd65..5b0894310 100644 --- a/src/command/OutputCommands.cxx +++ b/src/command/OutputCommands.cxx @@ -25,12 +25,15 @@ #include "protocol/ArgParser.hxx" #include "client/Client.hxx" #include "Partition.hxx" +#include "util/ConstBuffer.hxx" CommandResult -handle_enableoutput(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_enableoutput(Client &client, ConstBuffer<const char *> args) { + assert(args.size == 1); + unsigned device; - if (!check_unsigned(client, &device, argv[1])) + if (!check_unsigned(client, &device, args.front())) return CommandResult::ERROR; if (!audio_output_enable_index(client.partition.outputs, device)) { @@ -43,10 +46,12 @@ handle_enableoutput(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_disableoutput(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_disableoutput(Client &client, ConstBuffer<const char *> args) { + assert(args.size == 1); + unsigned device; - if (!check_unsigned(client, &device, argv[1])) + if (!check_unsigned(client, &device, args.front())) return CommandResult::ERROR; if (!audio_output_disable_index(client.partition.outputs, device)) { @@ -59,10 +64,12 @@ handle_disableoutput(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_toggleoutput(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_toggleoutput(Client &client, ConstBuffer<const char *> args) { + assert(args.size == 1); + unsigned device; - if (!check_unsigned(client, &device, argv[1])) + if (!check_unsigned(client, &device, args.front())) return CommandResult::ERROR; if (!audio_output_toggle_index(client.partition.outputs, device)) { @@ -75,9 +82,10 @@ handle_toggleoutput(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_devices(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_devices(Client &client, gcc_unused ConstBuffer<const char *> args) { + assert(args.IsEmpty()); + printAudioDevices(client, client.partition.outputs); return CommandResult::OK; diff --git a/src/command/OutputCommands.hxx b/src/command/OutputCommands.hxx index 8d6be0511..d550791ac 100644 --- a/src/command/OutputCommands.hxx +++ b/src/command/OutputCommands.hxx @@ -23,17 +23,18 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult -handle_enableoutput(Client &client, unsigned argc, char *argv[]); +handle_enableoutput(Client &client, ConstBuffer<const char *> args); CommandResult -handle_disableoutput(Client &client, unsigned argc, char *argv[]); +handle_disableoutput(Client &client, ConstBuffer<const char *> args); CommandResult -handle_toggleoutput(Client &client, unsigned argc, char *argv[]); +handle_toggleoutput(Client &client, ConstBuffer<const char *> args); CommandResult -handle_devices(Client &client, unsigned argc, char *argv[]); +handle_devices(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx index cd7f42289..c24088f9a 100644 --- a/src/command/PlayerCommands.cxx +++ b/src/command/PlayerCommands.cxx @@ -30,6 +30,7 @@ #include "protocol/ArgParser.hxx" #include "AudioFormat.hxx" #include "ReplayGainConfig.hxx" +#include "util/ConstBuffer.hxx" #ifdef ENABLE_DATABASE #include "db/update/Service.hxx" @@ -56,22 +57,22 @@ #define COMMAND_STATUS_UPDATING_DB "updating_db" CommandResult -handle_play(Client &client, unsigned argc, char *argv[]) +handle_play(Client &client, ConstBuffer<const char *> args) { int song = -1; - if (argc == 2 && !check_int(client, &song, argv[1])) + if (!args.IsEmpty() && !check_int(client, &song, args.front())) return CommandResult::ERROR; PlaylistResult result = client.partition.PlayPosition(song); return print_playlist_result(client, result); } CommandResult -handle_playid(Client &client, unsigned argc, char *argv[]) +handle_playid(Client &client, ConstBuffer<const char *> args) { int id = -1; - if (argc == 2 && !check_int(client, &id, argv[1])) + if (!args.IsEmpty() && !check_int(client, &id, args.front())) return CommandResult::ERROR; PlaylistResult result = client.partition.PlayId(id); @@ -79,28 +80,25 @@ handle_playid(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_stop(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_stop(Client &client, gcc_unused ConstBuffer<const char *> args) { client.partition.Stop(); return CommandResult::OK; } CommandResult -handle_currentsong(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_currentsong(Client &client, gcc_unused ConstBuffer<const char *> args) { playlist_print_current(client, client.playlist); return CommandResult::OK; } CommandResult -handle_pause(Client &client, - unsigned argc, char *argv[]) +handle_pause(Client &client, ConstBuffer<const char *> args) { - if (argc == 2) { + if (!args.IsEmpty()) { bool pause_flag; - if (!check_bool(client, &pause_flag, argv[1])) + if (!check_bool(client, &pause_flag, args.front())) return CommandResult::ERROR; client.player_control.SetPause(pause_flag); @@ -111,8 +109,7 @@ handle_pause(Client &client, } CommandResult -handle_status(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_status(Client &client, gcc_unused ConstBuffer<const char *> args) { const char *state = nullptr; int song; @@ -182,6 +179,10 @@ handle_status(Client &client, player_status.elapsed_time.ToDoubleS(), player_status.bit_rate); + if (!player_status.total_time.IsNegative()) + client_printf(client, "duration: %1.3f\n", + player_status.total_time.ToDoubleS()); + if (player_status.audio_format.IsDefined()) { struct audio_format_string af_string; @@ -222,8 +223,7 @@ handle_status(Client &client, } CommandResult -handle_next(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_next(Client &client, gcc_unused ConstBuffer<const char *> args) { playlist &playlist = client.playlist; @@ -239,18 +239,17 @@ handle_next(Client &client, } CommandResult -handle_previous(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_previous(Client &client, gcc_unused ConstBuffer<const char *> args) { client.partition.PlayPrevious(); return CommandResult::OK; } CommandResult -handle_repeat(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_repeat(Client &client, ConstBuffer<const char *> args) { bool status; - if (!check_bool(client, &status, argv[1])) + if (!check_bool(client, &status, args.front())) return CommandResult::ERROR; client.partition.SetRepeat(status); @@ -258,10 +257,10 @@ handle_repeat(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_single(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_single(Client &client, ConstBuffer<const char *> args) { bool status; - if (!check_bool(client, &status, argv[1])) + if (!check_bool(client, &status, args.front())) return CommandResult::ERROR; client.partition.SetSingle(status); @@ -269,10 +268,10 @@ handle_single(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_consume(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_consume(Client &client, ConstBuffer<const char *> args) { bool status; - if (!check_bool(client, &status, argv[1])) + if (!check_bool(client, &status, args.front())) return CommandResult::ERROR; client.partition.SetConsume(status); @@ -280,10 +279,10 @@ handle_consume(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_random(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_random(Client &client, ConstBuffer<const char *> args) { bool status; - if (!check_bool(client, &status, argv[1])) + if (!check_bool(client, &status, args.front())) return CommandResult::ERROR; client.partition.SetRandom(status); @@ -292,22 +291,21 @@ handle_random(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_clearerror(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_clearerror(gcc_unused Client &client, gcc_unused ConstBuffer<const char *> args) { client.player_control.ClearError(); return CommandResult::OK; } CommandResult -handle_seek(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_seek(Client &client, ConstBuffer<const char *> args) { unsigned song; SongTime seek_time; - if (!check_unsigned(client, &song, argv[1])) + if (!check_unsigned(client, &song, args[0])) return CommandResult::ERROR; - if (!ParseCommandArg(client, seek_time, argv[2])) + if (!ParseCommandArg(client, seek_time, args[1])) return CommandResult::ERROR; PlaylistResult result = @@ -316,14 +314,14 @@ handle_seek(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_seekid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_seekid(Client &client, ConstBuffer<const char *> args) { unsigned id; SongTime seek_time; - if (!check_unsigned(client, &id, argv[1])) + if (!check_unsigned(client, &id, args[0])) return CommandResult::ERROR; - if (!ParseCommandArg(client, seek_time, argv[2])) + if (!ParseCommandArg(client, seek_time, args[1])) return CommandResult::ERROR; PlaylistResult result = @@ -332,9 +330,9 @@ handle_seekid(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_seekcur(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_seekcur(Client &client, ConstBuffer<const char *> args) { - const char *p = argv[1]; + const char *p = args.front(); bool relative = *p == '+' || *p == '-'; SignedSongTime seek_time; if (!ParseCommandArg(client, seek_time, p)) @@ -346,11 +344,11 @@ handle_seekcur(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_crossfade(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_crossfade(Client &client, ConstBuffer<const char *> args) { unsigned xfade_time; - if (!check_unsigned(client, &xfade_time, argv[1])) + if (!check_unsigned(client, &xfade_time, args.front())) return CommandResult::ERROR; client.player_control.SetCrossFade(xfade_time); @@ -358,11 +356,11 @@ handle_crossfade(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_mixrampdb(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_mixrampdb(Client &client, ConstBuffer<const char *> args) { float db; - if (!check_float(client, &db, argv[1])) + if (!check_float(client, &db, args.front())) return CommandResult::ERROR; client.player_control.SetMixRampDb(db); @@ -370,11 +368,11 @@ handle_mixrampdb(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_mixrampdelay(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_mixrampdelay(Client &client, ConstBuffer<const char *> args) { float delay_secs; - if (!check_float(client, &delay_secs, argv[1])) + if (!check_float(client, &delay_secs, args.front())) return CommandResult::ERROR; client.player_control.SetMixRampDelay(delay_secs); @@ -382,10 +380,9 @@ handle_mixrampdelay(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_replay_gain_mode(Client &client, - gcc_unused unsigned argc, char *argv[]) +handle_replay_gain_mode(Client &client, ConstBuffer<const char *> args) { - if (!replay_gain_set_mode_string(argv[1])) { + if (!replay_gain_set_mode_string(args.front())) { command_error(client, ACK_ERROR_ARG, "Unrecognized replay gain mode"); return CommandResult::ERROR; @@ -396,8 +393,7 @@ handle_replay_gain_mode(Client &client, } CommandResult -handle_replay_gain_status(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_replay_gain_status(Client &client, gcc_unused ConstBuffer<const char *> args) { client_printf(client, "replay_gain_mode: %s\n", replay_gain_get_mode_string()); diff --git a/src/command/PlayerCommands.hxx b/src/command/PlayerCommands.hxx index da7083f1e..492a4aced 100644 --- a/src/command/PlayerCommands.hxx +++ b/src/command/PlayerCommands.hxx @@ -23,68 +23,69 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult -handle_play(Client &client, unsigned argc, char *argv[]); +handle_play(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playid(Client &client, unsigned argc, char *argv[]); +handle_playid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_stop(Client &client, unsigned argc, char *argv[]); +handle_stop(Client &client, ConstBuffer<const char *> args); CommandResult -handle_currentsong(Client &client, unsigned argc, char *argv[]); +handle_currentsong(Client &client, ConstBuffer<const char *> args); CommandResult -handle_pause(Client &client, unsigned argc, char *argv[]); +handle_pause(Client &client, ConstBuffer<const char *> args); CommandResult -handle_status(Client &client, unsigned argc, char *argv[]); +handle_status(Client &client, ConstBuffer<const char *> args); CommandResult -handle_next(Client &client, unsigned argc, char *argv[]); +handle_next(Client &client, ConstBuffer<const char *> args); CommandResult -handle_previous(Client &client, unsigned argc, char *avg[]); +handle_previous(Client &client, ConstBuffer<const char *> args); CommandResult -handle_repeat(Client &client, unsigned argc, char *argv[]); +handle_repeat(Client &client, ConstBuffer<const char *> args); CommandResult -handle_single(Client &client, unsigned argc, char *argv[]); +handle_single(Client &client, ConstBuffer<const char *> args); CommandResult -handle_consume(Client &client, unsigned argc, char *argv[]); +handle_consume(Client &client, ConstBuffer<const char *> args); CommandResult -handle_random(Client &client, unsigned argc, char *argv[]); +handle_random(Client &client, ConstBuffer<const char *> args); CommandResult -handle_clearerror(Client &client, unsigned argc, char *argv[]); +handle_clearerror(Client &client, ConstBuffer<const char *> args); CommandResult -handle_seek(Client &client, unsigned argc, char *argv[]); +handle_seek(Client &client, ConstBuffer<const char *> args); CommandResult -handle_seekid(Client &client, unsigned argc, char *argv[]); +handle_seekid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_seekcur(Client &client, unsigned argc, char *argv[]); +handle_seekcur(Client &client, ConstBuffer<const char *> args); CommandResult -handle_crossfade(Client &client, unsigned argc, char *argv[]); +handle_crossfade(Client &client, ConstBuffer<const char *> args); CommandResult -handle_mixrampdb(Client &client, unsigned argc, char *argv[]); +handle_mixrampdb(Client &client, ConstBuffer<const char *> args); CommandResult -handle_mixrampdelay(Client &client, unsigned argc, char *argv[]); +handle_mixrampdelay(Client &client, ConstBuffer<const char *> args); CommandResult -handle_replay_gain_mode(Client &client, unsigned argc, char *argv[]); +handle_replay_gain_mode(Client &client, ConstBuffer<const char *> args); CommandResult -handle_replay_gain_status(Client &client, unsigned argc, char *argv[]); +handle_replay_gain_status(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index c2b18064c..4abc88031 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -35,8 +35,17 @@ #include "protocol/ArgParser.hxx" #include "protocol/Result.hxx" #include "ls.hxx" +#include "Mapper.hxx" +#include "fs/AllocatedPath.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" + +bool +playlist_commands_available() +{ + return !map_spl_path().IsNull(); +} static void print_spl_list(Client &client, const PlaylistVector &list) @@ -50,28 +59,28 @@ print_spl_list(Client &client, const PlaylistVector &list) } CommandResult -handle_save(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_save(Client &client, ConstBuffer<const char *> args) { - PlaylistResult result = spl_save_playlist(argv[1], client.playlist); + PlaylistResult result = spl_save_playlist(args.front(), client.playlist); return print_playlist_result(client, result); } CommandResult -handle_load(Client &client, unsigned argc, char *argv[]) +handle_load(Client &client, ConstBuffer<const char *> args) { unsigned start_index, end_index; - if (argc < 3) { + if (args.size < 2) { start_index = 0; end_index = unsigned(-1); - } else if (!check_range(client, &start_index, &end_index, argv[2])) + } else if (!check_range(client, &start_index, &end_index, args[1])) return CommandResult::ERROR; const ScopeBulkEdit bulk_edit(client.partition); Error error; const SongLoader loader(client); - if (!playlist_open_into_queue(argv[1], + if (!playlist_open_into_queue(args.front(), start_index, end_index, client.playlist, client.player_control, loader, error)) @@ -81,94 +90,104 @@ handle_load(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_listplaylist(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_listplaylist(Client &client, ConstBuffer<const char *> args) { - if (playlist_file_print(client, argv[1], false)) + const char *const name = args.front(); + + if (playlist_file_print(client, name, false)) return CommandResult::OK; Error error; - return spl_print(client, argv[1], false, error) + return spl_print(client, name, false, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_listplaylistinfo(Client &client, - gcc_unused unsigned argc, char *argv[]) +handle_listplaylistinfo(Client &client, ConstBuffer<const char *> args) { - if (playlist_file_print(client, argv[1], true)) + const char *const name = args.front(); + + if (playlist_file_print(client, name, true)) return CommandResult::OK; Error error; - return spl_print(client, argv[1], true, error) + return spl_print(client, name, true, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_rm(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_rm(Client &client, ConstBuffer<const char *> args) { + const char *const name = args.front(); + Error error; - return spl_delete(argv[1], error) + return spl_delete(name, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_rename(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_rename(Client &client, ConstBuffer<const char *> args) { + const char *const old_name = args[0]; + const char *const new_name = args[1]; + Error error; - return spl_rename(argv[1], argv[2], error) + return spl_rename(old_name, new_name, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_playlistdelete(Client &client, - gcc_unused unsigned argc, char *argv[]) { - char *playlist = argv[1]; +handle_playlistdelete(Client &client, ConstBuffer<const char *> args) +{ + const char *const name = args[0]; unsigned from; - if (!check_unsigned(client, &from, argv[2])) + if (!check_unsigned(client, &from, args[1])) return CommandResult::ERROR; Error error; - return spl_remove_index(playlist, from, error) + return spl_remove_index(name, from, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_playlistmove(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_playlistmove(Client &client, ConstBuffer<const char *> args) { - char *playlist = argv[1]; + const char *const name = args.front(); unsigned from, to; - if (!check_unsigned(client, &from, argv[2])) + if (!check_unsigned(client, &from, args[1])) return CommandResult::ERROR; - if (!check_unsigned(client, &to, argv[3])) + if (!check_unsigned(client, &to, args[2])) return CommandResult::ERROR; Error error; - return spl_move_index(playlist, from, to, error) + return spl_move_index(name, from, to, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_playlistclear(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_playlistclear(Client &client, ConstBuffer<const char *> args) { + const char *const name = args.front(); + Error error; - return spl_clear(argv[1], error) + return spl_clear(name, error) ? CommandResult::OK : print_error(client, error); } CommandResult -handle_playlistadd(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_playlistadd(Client &client, ConstBuffer<const char *> args) { - char *playlist = argv[1]; - char *uri = argv[2]; + const char *const playlist = args[0]; + const char *const uri = args[1]; bool success; Error error; @@ -199,8 +218,7 @@ handle_playlistadd(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_listplaylists(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_listplaylists(Client &client, gcc_unused ConstBuffer<const char *> args) { Error error; const auto list = ListPlaylistFiles(error); diff --git a/src/command/PlaylistCommands.hxx b/src/command/PlaylistCommands.hxx index fba4e1318..91721899c 100644 --- a/src/command/PlaylistCommands.hxx +++ b/src/command/PlaylistCommands.hxx @@ -21,40 +21,46 @@ #define MPD_PLAYLIST_COMMANDS_HXX #include "CommandResult.hxx" +#include "Compiler.h" class Client; +template<typename T> struct ConstBuffer; + +gcc_const +bool +playlist_commands_available(); CommandResult -handle_save(Client &client, unsigned argc, char *argv[]); +handle_save(Client &client, ConstBuffer<const char *> args); CommandResult -handle_load(Client &client, unsigned argc, char *argv[]); +handle_load(Client &client, ConstBuffer<const char *> args); CommandResult -handle_listplaylist(Client &client, unsigned argc, char *argv[]); +handle_listplaylist(Client &client, ConstBuffer<const char *> args); CommandResult -handle_listplaylistinfo(Client &client, unsigned argc, char *argv[]); +handle_listplaylistinfo(Client &client, ConstBuffer<const char *> args); CommandResult -handle_rm(Client &client, unsigned argc, char *argv[]); +handle_rm(Client &client, ConstBuffer<const char *> args); CommandResult -handle_rename(Client &client, unsigned argc, char *argv[]); +handle_rename(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlistdelete(Client &client, unsigned argc, char *argv[]); +handle_playlistdelete(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlistmove(Client &client, unsigned argc, char *argv[]); +handle_playlistmove(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlistclear(Client &client, unsigned argc, char *argv[]); +handle_playlistclear(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlistadd(Client &client, unsigned argc, char *argv[]); +handle_playlistadd(Client &client, ConstBuffer<const char *> args); CommandResult -handle_listplaylists(Client &client, unsigned argc, char *argv[]); +handle_listplaylists(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx index d0b789eb1..e94c7bc8b 100644 --- a/src/command/QueueCommands.cxx +++ b/src/command/QueueCommands.cxx @@ -59,9 +59,9 @@ translate_uri(Client &client, const char *uri) } CommandResult -handle_add(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_add(Client &client, ConstBuffer<const char *> args) { - const char *uri = argv[1]; + const char *uri = args.front(); if (memcmp(uri, "/", 2) == 0) /* this URI is malformed, but some clients are buggy and use "add /" to add the whole database, which @@ -99,9 +99,9 @@ handle_add(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_addid(Client &client, unsigned argc, char *argv[]) +handle_addid(Client &client, ConstBuffer<const char *> args) { - const char *const uri = translate_uri(client, argv[1]); + const char *const uri = translate_uri(client, args.front()); if (uri == nullptr) return CommandResult::ERROR; @@ -111,9 +111,9 @@ handle_addid(Client &client, unsigned argc, char *argv[]) if (added_id == 0) return print_error(client, error); - if (argc == 3) { + if (args.size == 2) { unsigned to; - if (!check_unsigned(client, &to, argv[2])) + if (!check_unsigned(client, &to, args[1])) return CommandResult::ERROR; PlaylistResult result = client.partition.MoveId(added_id, to); if (result != PlaylistResult::SUCCESS) { @@ -160,14 +160,14 @@ parse_time_range(const char *p, SongTime &start_r, SongTime &end_r) } CommandResult -handle_rangeid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_rangeid(Client &client, ConstBuffer<const char *> args) { unsigned id; - if (!check_unsigned(client, &id, argv[1])) + if (!check_unsigned(client, &id, args.front())) return CommandResult::ERROR; SongTime start, end; - if (!parse_time_range(argv[2], start, end)) { + if (!parse_time_range(args[1], start, end)) { command_error(client, ACK_ERROR_ARG, "Bad range"); return CommandResult::ERROR; } @@ -182,11 +182,11 @@ handle_rangeid(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_delete(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_delete(Client &client, ConstBuffer<const char *> args) { unsigned start, end; - if (!check_range(client, &start, &end, argv[1])) + if (!check_range(client, &start, &end, args.front())) return CommandResult::ERROR; PlaylistResult result = client.partition.DeleteRange(start, end); @@ -194,11 +194,11 @@ handle_delete(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_deleteid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_deleteid(Client &client, ConstBuffer<const char *> args) { unsigned id; - if (!check_unsigned(client, &id, argv[1])) + if (!check_unsigned(client, &id, args.front())) return CommandResult::ERROR; PlaylistResult result = client.partition.DeleteId(id); @@ -206,19 +206,17 @@ handle_deleteid(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_playlist(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_playlist(Client &client, gcc_unused ConstBuffer<const char *> args) { playlist_print_uris(client, client.playlist); return CommandResult::OK; } CommandResult -handle_shuffle(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_shuffle(gcc_unused Client &client, ConstBuffer<const char *> args) { unsigned start = 0, end = client.playlist.queue.GetLength(); - if (argc == 2 && !check_range(client, &start, &end, argv[1])) + if (args.size == 1 && !check_range(client, &start, &end, args.front())) return CommandResult::ERROR; client.partition.Shuffle(start, end); @@ -226,19 +224,18 @@ handle_shuffle(gcc_unused Client &client, } CommandResult -handle_clear(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_clear(gcc_unused Client &client, gcc_unused ConstBuffer<const char *> args) { client.partition.ClearQueue(); return CommandResult::OK; } CommandResult -handle_plchanges(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_plchanges(Client &client, ConstBuffer<const char *> args) { uint32_t version; - if (!check_uint32(client, &version, argv[1])) + if (!check_uint32(client, &version, args.front())) return CommandResult::ERROR; playlist_print_changes_info(client, client.playlist, version); @@ -246,11 +243,11 @@ handle_plchanges(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_plchangesposid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_plchangesposid(Client &client, ConstBuffer<const char *> args) { uint32_t version; - if (!check_uint32(client, &version, argv[1])) + if (!check_uint32(client, &version, args.front())) return CommandResult::ERROR; playlist_print_changes_position(client, client.playlist, version); @@ -258,12 +255,12 @@ handle_plchangesposid(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_playlistinfo(Client &client, unsigned argc, char *argv[]) +handle_playlistinfo(Client &client, ConstBuffer<const char *> args) { unsigned start = 0, end = std::numeric_limits<unsigned>::max(); bool ret; - if (argc == 2 && !check_range(client, &start, &end, argv[1])) + if (args.size == 1 && !check_range(client, &start, &end, args.front())) return CommandResult::ERROR; ret = playlist_print_info(client, client.playlist, start, end); @@ -275,11 +272,11 @@ handle_playlistinfo(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_playlistid(Client &client, unsigned argc, char *argv[]) +handle_playlistid(Client &client, ConstBuffer<const char *> args) { - if (argc >= 2) { + if (!args.IsEmpty()) { unsigned id; - if (!check_unsigned(client, &id, argv[1])) + if (!check_unsigned(client, &id, args.front())) return CommandResult::ERROR; bool ret = playlist_print_id(client, client.playlist, id); @@ -295,11 +292,9 @@ handle_playlistid(Client &client, unsigned argc, char *argv[]) } static CommandResult -handle_playlist_match(Client &client, unsigned argc, char *argv[], +handle_playlist_match(Client &client, ConstBuffer<const char *> args, bool fold_case) { - ConstBuffer<const char *> args(argv + 1, argc - 1); - SongFilter filter; if (!filter.Parse(args, fold_case)) { command_error(client, ACK_ERROR_ARG, "incorrect arguments"); @@ -311,35 +306,35 @@ handle_playlist_match(Client &client, unsigned argc, char *argv[], } CommandResult -handle_playlistfind(Client &client, unsigned argc, char *argv[]) +handle_playlistfind(Client &client, ConstBuffer<const char *> args) { - return handle_playlist_match(client, argc, argv, false); + return handle_playlist_match(client, args, false); } CommandResult -handle_playlistsearch(Client &client, unsigned argc, char *argv[]) +handle_playlistsearch(Client &client, ConstBuffer<const char *> args) { - return handle_playlist_match(client, argc, argv, true); + return handle_playlist_match(client, args, true); } CommandResult -handle_prio(Client &client, unsigned argc, char *argv[]) +handle_prio(Client &client, ConstBuffer<const char *> args) { + const char *const priority_string = args.shift(); unsigned priority; - if (!check_unsigned(client, &priority, argv[1])) + if (!check_unsigned(client, &priority, priority_string)) return CommandResult::ERROR; if (priority > 0xff) { command_error(client, ACK_ERROR_ARG, - "Priority out of range: %s", argv[1]); + "Priority out of range: %s", priority_string); return CommandResult::ERROR; } - for (unsigned i = 2; i < argc; ++i) { + for (const char *i : args) { unsigned start_position, end_position; - if (!check_range(client, &start_position, &end_position, - argv[i])) + if (!check_range(client, &start_position, &end_position, i)) return CommandResult::ERROR; PlaylistResult result = @@ -354,22 +349,23 @@ handle_prio(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_prioid(Client &client, unsigned argc, char *argv[]) +handle_prioid(Client &client, ConstBuffer<const char *> args) { + const char *const priority_string = args.shift(); unsigned priority; - if (!check_unsigned(client, &priority, argv[1])) + if (!check_unsigned(client, &priority, priority_string)) return CommandResult::ERROR; if (priority > 0xff) { command_error(client, ACK_ERROR_ARG, - "Priority out of range: %s", argv[1]); + "Priority out of range: %s", priority_string); return CommandResult::ERROR; } - for (unsigned i = 2; i < argc; ++i) { + for (const char *i : args) { unsigned song_id; - if (!check_unsigned(client, &song_id, argv[i])) + if (!check_unsigned(client, &song_id, i)) return CommandResult::ERROR; PlaylistResult result = @@ -382,14 +378,14 @@ handle_prioid(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_move(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_move(Client &client, ConstBuffer<const char *> args) { unsigned start, end; int to; - if (!check_range(client, &start, &end, argv[1])) + if (!check_range(client, &start, &end, args[0])) return CommandResult::ERROR; - if (!check_int(client, &to, argv[2])) + if (!check_int(client, &to, args[1])) return CommandResult::ERROR; PlaylistResult result = @@ -398,27 +394,27 @@ handle_move(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_moveid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_moveid(Client &client, ConstBuffer<const char *> args) { unsigned id; int to; - if (!check_unsigned(client, &id, argv[1])) + if (!check_unsigned(client, &id, args[0])) return CommandResult::ERROR; - if (!check_int(client, &to, argv[2])) + if (!check_int(client, &to, args[1])) return CommandResult::ERROR; PlaylistResult result = client.partition.MoveId(id, to); return print_playlist_result(client, result); } CommandResult -handle_swap(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_swap(Client &client, ConstBuffer<const char *> args) { unsigned song1, song2; - if (!check_unsigned(client, &song1, argv[1])) + if (!check_unsigned(client, &song1, args[0])) return CommandResult::ERROR; - if (!check_unsigned(client, &song2, argv[2])) + if (!check_unsigned(client, &song2, args[1])) return CommandResult::ERROR; PlaylistResult result = @@ -427,13 +423,13 @@ handle_swap(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_swapid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_swapid(Client &client, ConstBuffer<const char *> args) { unsigned id1, id2; - if (!check_unsigned(client, &id1, argv[1])) + if (!check_unsigned(client, &id1, args[0])) return CommandResult::ERROR; - if (!check_unsigned(client, &id2, argv[2])) + if (!check_unsigned(client, &id2, args[1])) return CommandResult::ERROR; PlaylistResult result = client.partition.SwapIds(id1, id2); diff --git a/src/command/QueueCommands.hxx b/src/command/QueueCommands.hxx index f98f7bad2..5193e8b65 100644 --- a/src/command/QueueCommands.hxx +++ b/src/command/QueueCommands.hxx @@ -23,65 +23,66 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult -handle_add(Client &client, unsigned argc, char *argv[]); +handle_add(Client &client, ConstBuffer<const char *> args); CommandResult -handle_addid(Client &client, unsigned argc, char *argv[]); +handle_addid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_rangeid(Client &client, unsigned argc, char *argv[]); +handle_rangeid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_delete(Client &client, unsigned argc, char *argv[]); +handle_delete(Client &client, ConstBuffer<const char *> args); CommandResult -handle_deleteid(Client &client, unsigned argc, char *argv[]); +handle_deleteid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlist(Client &client, unsigned argc, char *argv[]); +handle_playlist(Client &client, ConstBuffer<const char *> args); CommandResult -handle_shuffle(Client &client, unsigned argc, char *argv[]); +handle_shuffle(Client &client, ConstBuffer<const char *> args); CommandResult -handle_clear(Client &client, unsigned argc, char *argv[]); +handle_clear(Client &client, ConstBuffer<const char *> args); CommandResult -handle_plchanges(Client &client, unsigned argc, char *argv[]); +handle_plchanges(Client &client, ConstBuffer<const char *> args); CommandResult -handle_plchangesposid(Client &client, unsigned argc, char *argv[]); +handle_plchangesposid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlistinfo(Client &client, unsigned argc, char *argv[]); +handle_playlistinfo(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlistid(Client &client, unsigned argc, char *argv[]); +handle_playlistid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlistfind(Client &client, unsigned argc, char *argv[]); +handle_playlistfind(Client &client, ConstBuffer<const char *> args); CommandResult -handle_playlistsearch(Client &client, unsigned argc, char *argv[]); +handle_playlistsearch(Client &client, ConstBuffer<const char *> args); CommandResult -handle_prio(Client &client, unsigned argc, char *argv[]); +handle_prio(Client &client, ConstBuffer<const char *> args); CommandResult -handle_prioid(Client &client, unsigned argc, char *argv[]); +handle_prioid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_move(Client &client, unsigned argc, char *argv[]); +handle_move(Client &client, ConstBuffer<const char *> args); CommandResult -handle_moveid(Client &client, unsigned argc, char *argv[]); +handle_moveid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_swap(Client &client, unsigned argc, char *argv[]); +handle_swap(Client &client, ConstBuffer<const char *> args); CommandResult -handle_swapid(Client &client, unsigned argc, char *argv[]); +handle_swapid(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/StickerCommands.cxx b/src/command/StickerCommands.cxx index 37506d51b..fce53e162 100644 --- a/src/command/StickerCommands.cxx +++ b/src/command/StickerCommands.cxx @@ -31,6 +31,7 @@ #include "Partition.hxx" #include "Instance.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" #include <string.h> @@ -51,53 +52,64 @@ sticker_song_find_print_cb(const LightSong &song, const char *value, } static CommandResult -handle_sticker_song(Client &client, unsigned argc, char *argv[]) +handle_sticker_song(Client &client, ConstBuffer<const char *> args) { Error error; const Database *db = client.GetDatabase(error); if (db == nullptr) return print_error(client, error); + const char *const cmd = args.front(); + /* get song song_id key */ - if (argc == 5 && strcmp(argv[1], "get") == 0) { - const LightSong *song = db->GetSong(argv[3], error); + if (args.size == 4 && strcmp(cmd, "get") == 0) { + const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) return print_error(client, error); - const auto value = sticker_song_get_value(*song, argv[4]); + const auto value = sticker_song_get_value(*song, args[3], + error); db->ReturnSong(song); if (value.empty()) { + if (error.IsDefined()) + return print_error(client, error); + command_error(client, ACK_ERROR_NO_EXIST, "no such sticker"); return CommandResult::ERROR; } - sticker_print_value(client, argv[4], value.c_str()); + sticker_print_value(client, args[3], value.c_str()); return CommandResult::OK; /* list song song_id */ - } else if (argc == 4 && strcmp(argv[1], "list") == 0) { - const LightSong *song = db->GetSong(argv[3], error); + } else if (args.size == 3 && strcmp(cmd, "list") == 0) { + const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) return print_error(client, error); - sticker *sticker = sticker_song_get(*song); + sticker *sticker = sticker_song_get(*song, error); db->ReturnSong(song); if (sticker) { sticker_print(client, *sticker); sticker_free(sticker); - } + } else if (error.IsDefined()) + return print_error(client, error); return CommandResult::OK; /* set song song_id id key */ - } else if (argc == 6 && strcmp(argv[1], "set") == 0) { - const LightSong *song = db->GetSong(argv[3], error); + } else if (args.size == 5 && strcmp(cmd, "set") == 0) { + const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) return print_error(client, error); - bool ret = sticker_song_set_value(*song, argv[4], argv[5]); + bool ret = sticker_song_set_value(*song, args[3], args[4], + error); db->ReturnSong(song); if (!ret) { + if (error.IsDefined()) + return print_error(client, error); + command_error(client, ACK_ERROR_SYSTEM, "failed to set sticker value"); return CommandResult::ERROR; @@ -105,17 +117,20 @@ handle_sticker_song(Client &client, unsigned argc, char *argv[]) return CommandResult::OK; /* delete song song_id [key] */ - } else if ((argc == 4 || argc == 5) && - strcmp(argv[1], "delete") == 0) { - const LightSong *song = db->GetSong(argv[3], error); + } else if ((args.size == 3 || args.size == 4) && + strcmp(cmd, "delete") == 0) { + const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) return print_error(client, error); - bool ret = argc == 4 - ? sticker_song_delete(*song) - : sticker_song_delete_value(*song, argv[4]); + bool ret = args.size == 3 + ? sticker_song_delete(*song, error) + : sticker_song_delete_value(*song, args[3], error); db->ReturnSong(song); if (!ret) { + if (error.IsDefined()) + return print_error(client, error); + command_error(client, ACK_ERROR_SYSTEM, "no such sticker"); return CommandResult::ERROR; @@ -123,20 +138,48 @@ handle_sticker_song(Client &client, unsigned argc, char *argv[]) return CommandResult::OK; /* find song dir key */ - } else if (argc == 5 && strcmp(argv[1], "find") == 0) { + } else if ((args.size == 4 || args.size == 6) && + strcmp(cmd, "find") == 0) { /* "sticker find song a/directory name" */ - const char *const base_uri = argv[3]; + const char *const base_uri = args[2]; + + StickerOperator op = StickerOperator::EXISTS; + const char *value = nullptr; + + if (args.size == 6) { + /* match the value */ + + const char *op_s = args[4]; + value = args[5]; + + if (strcmp(op_s, "=") == 0) + op = StickerOperator::EQUALS; + else if (strcmp(op_s, "<") == 0) + op = StickerOperator::LESS_THAN; + else if (strcmp(op_s, ">") == 0) + op = StickerOperator::GREATER_THAN; + else { + command_error(client, ACK_ERROR_ARG, + "bad operator"); + return CommandResult::ERROR; + } + } bool success; struct sticker_song_find_data data = { client, - argv[4], + args[3], }; success = sticker_song_find(*db, base_uri, data.name, - sticker_song_find_print_cb, &data); + op, value, + sticker_song_find_print_cb, &data, + error); if (!success) { + if (error.IsDefined()) + return print_error(client, error); + command_error(client, ACK_ERROR_SYSTEM, "failed to set search sticker database"); return CommandResult::ERROR; @@ -150,9 +193,9 @@ handle_sticker_song(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_sticker(Client &client, unsigned argc, char *argv[]) +handle_sticker(Client &client, ConstBuffer<const char *> args) { - assert(argc >= 4); + assert(args.size >= 3); if (!sticker_enabled()) { command_error(client, ACK_ERROR_UNKNOWN, @@ -160,8 +203,8 @@ handle_sticker(Client &client, unsigned argc, char *argv[]) return CommandResult::ERROR; } - if (strcmp(argv[2], "song") == 0) - return handle_sticker_song(client, argc, argv); + if (strcmp(args[1], "song") == 0) + return handle_sticker_song(client, args); else { command_error(client, ACK_ERROR_ARG, "unknown sticker domain"); diff --git a/src/command/StickerCommands.hxx b/src/command/StickerCommands.hxx index cf46cd034..0e8d765fa 100644 --- a/src/command/StickerCommands.hxx +++ b/src/command/StickerCommands.hxx @@ -23,8 +23,9 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult -handle_sticker(Client &client, unsigned argc, char *argv[]); +handle_sticker(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/StorageCommands.cxx b/src/command/StorageCommands.cxx index ee51c573e..510e9f1b1 100644 --- a/src/command/StorageCommands.cxx +++ b/src/command/StorageCommands.cxx @@ -25,6 +25,7 @@ #include "protocol/Result.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" #include "fs/Traits.hxx" #include "client/Client.hxx" #include "Partition.hxx" @@ -167,7 +168,7 @@ print_storage_uri(Client &client, const Storage &storage) } CommandResult -handle_listmounts(Client &client, gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_listmounts(Client &client, gcc_unused ConstBuffer<const char *> args) { Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { @@ -189,7 +190,7 @@ handle_listmounts(Client &client, gcc_unused unsigned argc, gcc_unused char *arg } CommandResult -handle_mount(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_mount(Client &client, ConstBuffer<const char *> args) { Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { @@ -199,8 +200,8 @@ handle_mount(Client &client, gcc_unused unsigned argc, char *argv[]) CompositeStorage &composite = *(CompositeStorage *)_composite; - const char *const local_uri = argv[1]; - const char *const remote_uri = argv[2]; + const char *const local_uri = args[0]; + const char *const remote_uri = args[1]; if (*local_uri == 0) { command_error(client, ACK_ERROR_ARG, "Bad mount point"); @@ -252,7 +253,7 @@ handle_mount(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_unmount(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_unmount(Client &client, ConstBuffer<const char *> args) { Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { @@ -262,7 +263,7 @@ handle_unmount(Client &client, gcc_unused unsigned argc, char *argv[]) CompositeStorage &composite = *(CompositeStorage *)_composite; - const char *const local_uri = argv[1]; + const char *const local_uri = args.front(); if (*local_uri == 0) { command_error(client, ACK_ERROR_ARG, "Bad mount point"); diff --git a/src/command/StorageCommands.hxx b/src/command/StorageCommands.hxx index a3636d54a..ce3622c4b 100644 --- a/src/command/StorageCommands.hxx +++ b/src/command/StorageCommands.hxx @@ -24,6 +24,7 @@ class Client; class Storage; +template<typename T> struct ConstBuffer; CommandResult handle_listfiles_storage(Client &client, Storage &storage, const char *uri); @@ -32,12 +33,12 @@ CommandResult handle_listfiles_storage(Client &client, const char *uri); CommandResult -handle_listmounts(Client &client, unsigned argc, char *argv[]); +handle_listmounts(Client &client, ConstBuffer<const char *> args); CommandResult -handle_mount(Client &client, unsigned argc, char *argv[]); +handle_mount(Client &client, ConstBuffer<const char *> args); CommandResult -handle_unmount(Client &client, unsigned argc, char *argv[]); +handle_unmount(Client &client, ConstBuffer<const char *> args); #endif diff --git a/src/command/TagCommands.cxx b/src/command/TagCommands.cxx index 2d537671c..6f821f451 100644 --- a/src/command/TagCommands.cxx +++ b/src/command/TagCommands.cxx @@ -25,15 +25,16 @@ #include "protocol/Result.hxx" #include "tag/Tag.hxx" #include "Partition.hxx" +#include "util/ConstBuffer.hxx" CommandResult -handle_addtagid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_addtagid(Client &client, ConstBuffer<const char *> args) { unsigned song_id; - if (!check_unsigned(client, &song_id, argv[1])) + if (!check_unsigned(client, &song_id, args.front())) return CommandResult::ERROR; - const char *const tag_name = argv[2]; + const char *const tag_name = args[1]; const TagType tag_type = tag_name_parse_i(tag_name); if (tag_type == TAG_NUM_OF_ITEM_TYPES) { command_error(client, ACK_ERROR_ARG, @@ -41,7 +42,7 @@ handle_addtagid(Client &client, gcc_unused unsigned argc, char *argv[]) return CommandResult::ERROR; } - const char *const value = argv[3]; + const char *const value = args[2]; Error error; if (!client.partition.playlist.AddSongIdTag(song_id, tag_type, value, @@ -52,15 +53,15 @@ handle_addtagid(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_cleartagid(Client &client, unsigned argc, char *argv[]) +handle_cleartagid(Client &client, ConstBuffer<const char *> args) { unsigned song_id; - if (!check_unsigned(client, &song_id, argv[1])) + if (!check_unsigned(client, &song_id, args.front())) return CommandResult::ERROR; TagType tag_type = TAG_NUM_OF_ITEM_TYPES; - if (argc >= 3) { - const char *const tag_name = argv[2]; + if (args.size >= 2) { + const char *const tag_name = args[1]; tag_type = tag_name_parse_i(tag_name); if (tag_type == TAG_NUM_OF_ITEM_TYPES) { command_error(client, ACK_ERROR_ARG, diff --git a/src/command/TagCommands.hxx b/src/command/TagCommands.hxx index 748838e68..bc813b151 100644 --- a/src/command/TagCommands.hxx +++ b/src/command/TagCommands.hxx @@ -23,11 +23,12 @@ #include "CommandResult.hxx" class Client; +template<typename T> struct ConstBuffer; CommandResult -handle_addtagid(Client &client, unsigned argc, char *argv[]); +handle_addtagid(Client &client, ConstBuffer<const char *> args); CommandResult -handle_cleartagid(Client &client, unsigned argc, char *argv[]); +handle_cleartagid(Client &client, ConstBuffer<const char *> args); #endif |