diff --git a/doc/rtorrent.1.xml b/doc/rtorrent.1.xml index 0b50a4ea..68ba17dd 100644 --- a/doc/rtorrent.1.xml +++ b/doc/rtorrent.1.xml @@ -438,9 +438,8 @@ Set the default download directory. Defaults to "./". Session management will be enabled and the torrent files for all open downloads will be stored in this directory. Only one instance of -rtorrent should be used with each session directory, though at the -moment no locking is done. An empty string will disable the session -directory. +rtorrent can be used per session directory. An empty string will +disable the session directory. diff --git a/src/command_download.cc b/src/command_download.cc index 865351cd..2d30d551 100644 --- a/src/command_download.cc +++ b/src/command_download.cc @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -220,6 +221,62 @@ retrieve_d_hash(core::Download* download) { return torrent::Object(rak::transform_hex(hashString->begin(), hashString->end())); } +torrent::Object +f_multicall(core::Download* download, const torrent::Object& rawArgs) { + const torrent::Object::list_type& args = rawArgs.as_list(); + + if (args.empty()) + throw torrent::input_error("Too few arguments."); + + // We ignore the first arg for now, but it will be used for + // selecting what files to include. + + // Add some pre-parsing of the commands, so we don't spend time + // parsing and searching command map for every single call. + torrent::Object resultRaw(torrent::Object::TYPE_LIST); + torrent::Object::list_type& result = resultRaw.as_list(); + + for (torrent::FileList::const_iterator itr = download->file_list()->begin(), last = download->file_list()->end(); itr != last; itr++) { + torrent::Object::list_type& row = result.insert(result.end(), torrent::Object(torrent::Object::TYPE_LIST))->as_list(); + + for (torrent::Object::list_type::const_iterator cItr = ++args.begin(), cLast = args.end(); cItr != args.end(); cItr++) { + const std::string& cmd = cItr->as_string(); + row.push_back(rpc::parse_command(rpc::make_target(*itr), cmd.c_str(), cmd.c_str() + cmd.size()).first); + } + } + + return resultRaw; +} + +torrent::Object +t_multicall(core::Download* download, const torrent::Object& rawArgs) { + const torrent::Object::list_type& args = rawArgs.as_list(); + + if (args.empty()) + throw torrent::input_error("Too few arguments."); + + // We ignore the first arg for now, but it will be used for + // selecting what files to include. + + // Add some pre-parsing of the commands, so we don't spend time + // parsing and searching command map for every single call. + torrent::Object resultRaw(torrent::Object::TYPE_LIST); + torrent::Object::list_type& result = resultRaw.as_list(); + + for (int itr = 0, last = download->tracker_list()->size(); itr != last; itr++) { + torrent::Object::list_type& row = result.insert(result.end(), torrent::Object(torrent::Object::TYPE_LIST))->as_list(); + + for (torrent::Object::list_type::const_iterator cItr = ++args.begin(), cLast = args.end(); cItr != args.end(); cItr++) { + const std::string& cmd = cItr->as_string(); + torrent::Tracker t = download->tracker_list()->get(itr); + + row.push_back(rpc::parse_command(rpc::make_target(&t), cmd.c_str(), cmd.c_str() + cmd.size()).first); + } + } + + return resultRaw; +} + #define ADD_CD_SLOT(key, function, slot, parm, doc) \ commandDownloadSlotsItr->set_slot(slot); \ rpc::commands.insert_download(key, commandDownloadSlotsItr++, &rpc::CommandDownloadSlot::function, rpc::CommandMap::flag_dont_delete, parm, doc); @@ -383,4 +440,7 @@ initialize_command_download() { ADD_CD_STRING_BI("directory", std::mem_fun(&core::Download::set_root_directory), rak::on(std::mem_fun(&core::Download::file_list), std::mem_fun(&torrent::FileList::root_dir))); ADD_CD_VALUE_BI("priority", std::mem_fun(&core::Download::set_priority), std::mem_fun(&core::Download::priority)); ADD_CD_STRING_UNI("priority_str", std::ptr_fun(&retrieve_d_priority_str)); + + ADD_CD_SLOT_PUBLIC("f.multicall", call_list, rak::ptr_fn(&f_multicall), "i:", "") + ADD_CD_SLOT_PUBLIC("t.multicall", call_list, rak::ptr_fn(&t_multicall), "i:", "") } diff --git a/src/command_events.cc b/src/command_events.cc index 646a0a36..2aadddc5 100644 --- a/src/command_events.cc +++ b/src/command_events.cc @@ -268,7 +268,7 @@ apply_download_list(const torrent::Object& rawArgs) { } torrent::Object -apply_call_download(const torrent::Object& rawArgs) { +d_multicall(const torrent::Object& rawArgs) { const torrent::Object::list_type& args = rawArgs.as_list(); if (args.empty()) @@ -306,7 +306,7 @@ apply_call_download(const torrent::Object& rawArgs) { for (torrent::Object::list_type::const_iterator cItr = ++args.begin(), cLast = args.end(); cItr != args.end(); cItr++) { const std::string& cmd = cItr->as_string(); - row.push_back(rpc::parse_command_d_single(*vItr, cmd.c_str(), cmd.c_str() + cmd.size())); + row.push_back(rpc::parse_command(rpc::make_target(*vItr), cmd.c_str(), cmd.c_str() + cmd.size()).first); } } @@ -354,5 +354,6 @@ initialize_command_events() { ADD_COMMAND_VALUE_UN("close_low_diskspace", std::ptr_fun(&apply_close_low_diskspace)); ADD_COMMAND_LIST("download_list", rak::ptr_fn(&apply_download_list)); - ADD_COMMAND_LIST("call_download", rak::ptr_fn(&apply_call_download)); + ADD_COMMAND_LIST("d.multicall", rak::ptr_fn(&d_multicall)); + ADD_COMMAND_COPY("call_download", call_list, "i:", ""); } diff --git a/src/main.cc b/src/main.cc index e3f8f944..f1942039 100644 --- a/src/main.cc +++ b/src/main.cc @@ -165,7 +165,7 @@ main(int argc, char** argv) { // torrent::ConnectionManager* are valid etc. initialize_commands(); - rpc::parse_command_multiple( + rpc::parse_command_multiple(rpc::make_target(), // "set_name = $cat={$get_hostname=,:,$get_pid=}\n" "set_name = \"$cat=$get_hostname=,:,$get_pid=\"\n" @@ -236,7 +236,7 @@ main(int argc, char** argv) { if (OptionParser::has_flag('n', argc, argv)) control->core()->push_log("Ignoring ~/.rtorrent.rc."); else - rpc::parse_command_single("try_import = ~/.rtorrent.rc"); + rpc::parse_command_single(rpc::make_target(), "try_import = ~/.rtorrent.rc"); int firstArg = parse_options(control, argc, argv); diff --git a/src/rpc/command_download_slot.cc b/src/rpc/command_download_slot.cc index d9d4ba05..66dc6551 100644 --- a/src/rpc/command_download_slot.cc +++ b/src/rpc/command_download_slot.cc @@ -60,6 +60,7 @@ CommandDownloadSlot::call_list(Command* rawCommand, core::Download* download, co case torrent::Object::TYPE_VALUE: case torrent::Object::TYPE_STRING: +// case torrent::Object::TYPE_NONE: { torrent::Object tmpList(torrent::Object::TYPE_LIST); tmpList.as_list().push_back(rawArgs); diff --git a/src/rpc/command_map.h b/src/rpc/command_map.h index 947f9812..cd9edd24 100644 --- a/src/rpc/command_map.h +++ b/src/rpc/command_map.h @@ -138,7 +138,7 @@ public: void insert(key_type key, const command_map_data_type src); - const mapped_type call_command(key_type key, const mapped_type& arg, target_type target = target_type((int)target_generic, NULL)); + const mapped_type call_command (key_type key, const mapped_type& arg, target_type target = target_type((int)target_generic, NULL)); const mapped_type call_command_d(key_type key, core::Download* download, const mapped_type& arg) { return call_command(key, arg, target_type((int)target_download, download)); } const mapped_type call_command_f(key_type key, torrent::File* file, const mapped_type& arg) { return call_command(key, arg, target_type((int)target_file, file)); } const mapped_type call_command_p(key_type key, torrent::Peer* peer, const mapped_type& arg) { return call_command(key, arg, target_type((int)target_peer, peer)); } diff --git a/src/rpc/command_slot.cc b/src/rpc/command_slot.cc index 34eccc75..2c10ba67 100644 --- a/src/rpc/command_slot.cc +++ b/src/rpc/command_slot.cc @@ -59,6 +59,7 @@ CommandSlot::call_list(Command* rawCommand, const torrent::Object& rawArgs) { case torrent::Object::TYPE_VALUE: case torrent::Object::TYPE_STRING: + case torrent::Object::TYPE_NONE: { torrent::Object tmpList(torrent::Object::TYPE_LIST); tmpList.as_list().push_back(rawArgs); diff --git a/src/rpc/parse_commands.cc b/src/rpc/parse_commands.cc index 7c5c553f..7757ed1f 100644 --- a/src/rpc/parse_commands.cc +++ b/src/rpc/parse_commands.cc @@ -82,22 +82,22 @@ parse_count_escaped(const char* first, const char* last) { // // Find a better name. void -parse_command_execute(core::Download* download, torrent::Object* object) { +parse_command_execute(CommandMap::target_type target, torrent::Object* object) { if (object->is_list()) { for (torrent::Object::list_type::iterator itr = object->as_list().begin(), last = object->as_list().end(); itr != last; itr++) - parse_command_execute(download, &*itr); + parse_command_execute(target, &*itr); } else if (*object->as_string().c_str() == '$') { const std::string& str = object->as_string(); - *object = parse_command_d_single(download, str.c_str() + 1, str.c_str() + str.size()); + *object = parse_command(target, str.c_str() + 1, str.c_str() + str.size()).first; } } // Set 'download' to NULL to call the generic functions, thus reusing // the code below for both cases. -std::pair -parse_command(core::Download* download, const char* first, const char* last) { +parse_command_type +parse_command(CommandMap::target_type target, const char* first, const char* last) { first = std::find_if(first, last, std::not1(command_map_is_space())); if (first == last || *first == '#') @@ -127,32 +127,22 @@ parse_command(core::Download* download, const char* first, const char* last) { // Replace any strings starting with '$' with the result of the // following command. - parse_command_execute(download, &args); + parse_command_execute(target, &args); - return std::make_pair(commands.call_command_d(key.c_str(), download, args), first); + return std::make_pair(commands.call_command(key.c_str(), args, target), first); } void -parse_command_single(const char* first) { - parse_command(NULL, first, first + std::strlen(first)); -} - -void -parse_command_multiple(core::Download* download, const char* first, const char* last) { +parse_command_multiple(CommandMap::target_type target, const char* first, const char* last) { while (first != last) { // Should we check the return value? Probably not necessary as // parse_args throws on unquoted multi-word input. - std::pair result = parse_command(download, first, last); + parse_command_type result = parse_command(target, first, last); first = result.second; } } -void -parse_command_d_multiple(core::Download* download, const char* first) { - parse_command_multiple(download, first, first + std::strlen(first)); -} - bool parse_command_file(const std::string& path) { std::fstream file(rak::path_expand(path).c_str(), std::ios::in); @@ -185,7 +175,7 @@ parse_command_file(const std::string& path) { } // Would be nice to make this zero-copy. - parse_command(NULL, buffer, buffer + getCount); + parse_command(make_target(), buffer, buffer + getCount); getCount = 0; } diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h index caf65b92..0ddcf95e 100644 --- a/src/rpc/parse_commands.h +++ b/src/rpc/parse_commands.h @@ -38,6 +38,7 @@ #define RTORRENT_RPC_PARSE_COMMANDS_H #include +#include #include "command_map.h" #include "exec_file.h" @@ -54,36 +55,43 @@ extern CommandMap commands; extern XmlRpc xmlrpc; extern ExecFile execFile; +inline CommandMap::target_type make_target() { return CommandMap::target_type((int)CommandMap::target_generic, NULL); } +inline CommandMap::target_type make_target(core::Download* target) { return CommandMap::target_type((int)CommandMap::target_download, target); } +inline CommandMap::target_type make_target(torrent::File* target) { return CommandMap::target_type((int)CommandMap::target_file, target); } +inline CommandMap::target_type make_target(torrent::Peer* target) { return CommandMap::target_type((int)CommandMap::target_peer, target); } +inline CommandMap::target_type make_target(torrent::Tracker* target) { return CommandMap::target_type((int)CommandMap::target_tracker, target); } + +typedef std::pair parse_command_type; + // The generic parse command function, used by the rest. At some point // the 'download' parameter should be replaced by a more generic one. -std::pair parse_command(core::Download* download, const char* first, const char* last); +parse_command_type parse_command(CommandMap::target_type target, const char* first, const char* last); +void parse_command_multiple(CommandMap::target_type target, const char* first, const char* last); -void parse_command_single(const char* first); -inline torrent::Object parse_command_single(const char* first, const char* last) { return parse_command(NULL, first, last).first; } -inline torrent::Object parse_command_d_single(core::Download* download, const char* first, const char* last) { return parse_command(download, first, last).first; } - -void parse_command_multiple(core::Download* download, const char* first, const char* last); - -void parse_command_d_multiple(core::Download* download, const char* first); -inline void parse_command_d_multiple_std(core::Download* download, const std::string& cmd) { parse_command_multiple(download, cmd.c_str(), cmd.c_str() + cmd.size()); } -inline void parse_command_multiple(const char* first) { parse_command_d_multiple(NULL, first); } +inline void parse_command_single(CommandMap::target_type target, const char* first) { parse_command(target, first, first + std::strlen(first)); } +inline void parse_command_multiple(CommandMap::target_type target, const char* first) { parse_command_multiple(target, first, first + std::strlen(first)); } bool parse_command_file(const std::string& path); const char* parse_command_name(const char* first, const char* last, std::string* dest); inline void parse_command_single_std(const std::string& cmd) { - parse_command_single(cmd.c_str(), cmd.c_str() + cmd.size()); + parse_command(make_target(), cmd.c_str(), cmd.c_str() + cmd.size()); } inline void parse_command_d_single_std(core::Download* download, const std::string& cmd) { - parse_command_d_single(download, cmd.c_str(), cmd.c_str() + cmd.size()); + parse_command(make_target(download), cmd.c_str(), cmd.c_str() + cmd.size()); } inline void parse_command_multiple_std(const std::string& cmd) { - parse_command_multiple(NULL, cmd.c_str(), cmd.c_str() + cmd.size()); + parse_command_multiple(make_target(), cmd.c_str(), cmd.c_str() + cmd.size()); +} + +inline void +parse_command_d_multiple_std(core::Download* download, const std::string& cmd) { + parse_command_multiple(make_target(download), cmd.c_str(), cmd.c_str() + cmd.size()); } inline torrent::Object call_command(const char* key, const torrent::Object& obj) { return commands.call_command(key, obj); } diff --git a/src/rpc/xmlrpc.cc b/src/rpc/xmlrpc.cc index 18c5093d..41b6d110 100644 --- a/src/rpc/xmlrpc.cc +++ b/src/rpc/xmlrpc.cc @@ -275,7 +275,7 @@ xmlrpc_to_object_target(xmlrpc_env* env, xmlrpc_value* value, int callType, void torrent::Object result; - if (current + 1 > last) { + if (current + 1 < last) { result = torrent::Object(torrent::Object::TYPE_LIST); torrent::Object::list_type& listRef = result.as_list();