diff --git a/src/command_events.cc b/src/command_events.cc index 7160b697..9994e3e8 100644 --- a/src/command_events.cc +++ b/src/command_events.cc @@ -47,7 +47,8 @@ #include "core/manager.h" #include "rpc/command_slot.h" #include "rpc/command_variable.h" -#include "utils/parse.h" +#include "rpc/parse.h" +#include "rpc/parse_commands.h" #include "globals.h" #include "control.h" @@ -69,7 +70,7 @@ apply_on_state_change(core::DownloadList::slot_map* slotMap, const torrent::Obje if (args.back().as_string().empty()) slotMap->erase(key); else - (*slotMap)[key] = sigc::bind(sigc::mem_fun(control->download_variables(), &utils::VariableMap::process_d_std_single), + (*slotMap)[key] = sigc::bind(sigc::bind<0>(&utils::parse_command_d_single_std, control->download_variables()), utils::convert_list_to_command(++args.begin(), args.end())); return torrent::Object(); @@ -194,8 +195,8 @@ void apply_load_verbose(const std::string& arg) { control->core()->try_cre void apply_load_start(const std::string& arg) { control->core()->try_create_download_expand(arg, true, false, true); } void apply_load_start_verbose(const std::string& arg) { control->core()->try_create_download_expand(arg, true, true, true); } -void apply_import(const std::string& path) { if (!control->variable()->process_file(path.c_str())) throw torrent::input_error("Could not open option file: " + path); } -void apply_try_import(const std::string& path) { if (!control->variable()->process_file(path.c_str())) control->core()->push_log("Could not read resource file: " + path); } +void apply_import(const std::string& path) { if (!utils::parse_command_file(control->variable(), path)) throw torrent::input_error("Could not open option file: " + path); } +void apply_try_import(const std::string& path) { if (!utils::parse_command_file(control->variable(), path)) control->core()->push_log("Could not read resource file: " + path); } void apply_close_low_diskspace(int64_t arg) { diff --git a/src/command_network.cc b/src/command_network.cc index 00035e25..7db82bd9 100644 --- a/src/command_network.cc +++ b/src/command_network.cc @@ -53,7 +53,7 @@ #include "ui/root.h" #include "rpc/command_slot.h" #include "rpc/command_variable.h" -#include "utils/parse.h" +#include "rpc/parse.h" #include "utils/variable_map.h" #include "globals.h" diff --git a/src/command_ui.cc b/src/command_ui.cc index 4bdab1c7..3d075c1d 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -45,7 +45,7 @@ #include "core/view_manager.h" #include "rpc/command_slot.h" #include "rpc/command_variable.h" -#include "utils/parse.h" +#include "rpc/parse.h" #include "globals.h" #include "control.h" diff --git a/src/control.cc b/src/control.cc index 87eb08f9..e22adc09 100644 --- a/src/control.cc +++ b/src/control.cc @@ -51,6 +51,7 @@ #include "input/manager.h" #include "input/input_event.h" #include "rpc/fast_cgi.h" +#include "rpc/parse_commands.h" #include "rpc/scgi.h" #include "rpc/xmlrpc.h" #include "ui/root.h" @@ -87,7 +88,7 @@ Control::Control() : m_taskShutdown.set_slot(rak::mem_fn(this, &Control::handle_shutdown)); - m_commandScheduler->set_slot_command(rak::mem_fn(m_variables, &utils::VariableMap::process_std_single)); + m_commandScheduler->set_slot_command(rak::bind_ptr_fn(&utils::parse_command_single_std, m_variables)); m_commandScheduler->set_slot_error_message(rak::mem_fn(m_core, &core::Manager::push_log)); } diff --git a/src/main.cc b/src/main.cc index 599b4da0..0d88880d 100644 --- a/src/main.cc +++ b/src/main.cc @@ -58,6 +58,7 @@ #include "display/manager.h" #include "input/bindings.h" +#include "rpc/parse_commands.h" #include "utils/directory.h" #include "utils/variable_map.h" @@ -87,7 +88,7 @@ parse_options(Control* c, int argc, char** argv) { optionParser.insert_option('p', sigc::bind<0>(sigc::mem_fun(c->variable(), &utils::VariableMap::call_command_set_string), "port_range")); optionParser.insert_option('s', sigc::bind<0>(sigc::mem_fun(c->variable(), &utils::VariableMap::call_command_set_string), "session")); - optionParser.insert_option('O', sigc::mem_fun(c->variable(), &utils::VariableMap::process_std_single)); + optionParser.insert_option('O', sigc::bind<0>(&utils::parse_command_single_std, c->variable())); optionParser.insert_option_list('o', sigc::mem_fun(c->variable(), &utils::VariableMap::call_command_set_std_string)); return optionParser.process(argc, argv); @@ -165,8 +166,7 @@ main(int argc, char** argv) { // torrent::ConnectionManager* are valid etc. initialize_commands(); - control->variable()->process_multiple - ( + utils::parse_command_multiple(control->variable(), // Currently not doing any sorting on main. "view_add = main\n" @@ -227,12 +227,12 @@ main(int argc, char** argv) { "view_sort_current = scheduler,state_changed\n" // "schedule = scheduler,10,10,download_scheduler=\n" - ); + ); if (OptionParser::has_flag('n', argc, argv)) control->core()->push_log("Ignoring ~/.rtorrent.rc."); else - control->variable()->process_single("try_import = ~/.rtorrent.rc"); + utils::parse_command_single(control->variable(), "try_import = ~/.rtorrent.rc"); int firstArg = parse_options(control, argc, argv); diff --git a/src/rpc/Makefile.am b/src/rpc/Makefile.am index 7ea589a6..1bb81112 100644 --- a/src/rpc/Makefile.am +++ b/src/rpc/Makefile.am @@ -10,6 +10,10 @@ libsub_rpc_a_SOURCES = \ command_download_slot.h \ fast_cgi.cc \ fast_cgi.h \ + parse.cc \ + parse.h \ + parse_commands.cc \ + parse_commands.h \ scgi.cc \ scgi.h \ scgi_task.cc \ diff --git a/src/rpc/command_download_slot.cc b/src/rpc/command_download_slot.cc index 0c68b4f4..2d4e0c18 100644 --- a/src/rpc/command_download_slot.cc +++ b/src/rpc/command_download_slot.cc @@ -37,7 +37,7 @@ #include "config.h" #include "core/download.h" -#include "utils/parse.h" +#include "rpc/parse.h" #include "command_download_slot.h" diff --git a/src/rpc/command_slot.cc b/src/rpc/command_slot.cc index fe06b7b9..1d69b99d 100644 --- a/src/rpc/command_slot.cc +++ b/src/rpc/command_slot.cc @@ -36,7 +36,7 @@ #include "config.h" -#include "utils/parse.h" +#include "rpc/parse.h" #include "command_slot.h" diff --git a/src/rpc/command_variable.cc b/src/rpc/command_variable.cc index 88bd5c84..c8406fd2 100644 --- a/src/rpc/command_variable.cc +++ b/src/rpc/command_variable.cc @@ -36,7 +36,7 @@ #include "config.h" -#include "utils/parse.h" +#include "rpc/parse.h" #include "command_variable.h" diff --git a/src/utils/parse.cc b/src/rpc/parse.cc similarity index 100% rename from src/utils/parse.cc rename to src/rpc/parse.cc diff --git a/src/utils/parse.h b/src/rpc/parse.h similarity index 98% rename from src/utils/parse.h rename to src/rpc/parse.h index ff43fee5..7315f6fe 100644 --- a/src/utils/parse.h +++ b/src/rpc/parse.h @@ -34,6 +34,9 @@ // Skomakerveien 33 // 3185 Skoppum, NORWAY +#ifndef RTORRENT_RPC_PARSE_H +#define RTORRENT_RPC_PARSE_H + #include #include @@ -81,3 +84,5 @@ convert_to_single_argument(const torrent::Object& args) { } } + +#endif diff --git a/src/rpc/parse_commands.cc b/src/rpc/parse_commands.cc new file mode 100644 index 00000000..dd098189 --- /dev/null +++ b/src/rpc/parse_commands.cc @@ -0,0 +1,176 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "parse.h" +#include "parse_commands.h" +#include "utils/variable_map.h" + +namespace utils { + +struct variable_map_is_space : std::unary_function { + bool operator () (char c) const { + return std::isspace(c); + } +}; + +struct variable_map_is_newline : std::unary_function { + bool operator () (char c) const { + return c == '\n' || c == '\0'; + } +}; + +// Use a static length buffer for dest. +const char* +parse_command_name(const char* first, const char* last, std::string* dest) { + if (first == last || !std::isalpha(*first)) + throw torrent::input_error("Invalid start of name."); + + for ( ; first != last && (std::isalnum(*first) || *first == '_'); ++first) + dest->push_back(*first); + + return first; +} + +const char* +parse_command_single(VariableMap* varMap, const char* first) { + return parse_command_single(varMap, first, first + std::strlen(first)); +} + +const char* +parse_command_single(VariableMap* varMap, const char* first, const char* last) { + first = std::find_if(first, last, std::not1(variable_map_is_space())); + + if (first == last || *first == '#') + return last; + + // Avoid using a string here? + std::string key; + first = parse_command_name(first, last, &key); + first = std::find_if(first, last, std::not1(variable_map_is_space())); + + if (first == last || *first != '=') + throw torrent::input_error("Could not find '='."); + + torrent::Object args; + parse_whole_list(first + 1, last, &args); + + varMap->call_command(key.c_str(), args); + + return last; +} + +const char* +parse_command_d_single(VariableMap* varMap, core::Download* download, const char* first, const char* last) { + first = std::find_if(first, last, std::not1(variable_map_is_space())); + + if (first == last || *first == '#') + return last; + + std::string key; + first = parse_command_name(first, last, &key); + first = std::find_if(first, last, std::not1(variable_map_is_space())); + + if (first == last || *first != '=') + throw torrent::input_error("Could not find '='."); + + torrent::Object args; + parse_whole_list(first + 1, last, &args); + + varMap->call_command_d(key.c_str(), download, args); + + return last; +} + +void +parse_command_multiple(VariableMap* varMap, const char* first) { + try { + while (first != '\0') { + const char* last = first; + + while (*last != '\n' && *last != '\0') last++; + + // Should we check the return value? Probably not necessary as + // parse_args throws on unquoted multi-word input. + parse_command_single(varMap, first, last); + + if (*last == '\0') + return; + + first = last + 1; + } + + } catch (torrent::input_error& e) { + throw torrent::input_error(std::string("Error parsing multi-line option: ") + e.what()); + } +} + +bool +parse_command_file(VariableMap* varMap, const std::string& path) { + std::fstream file(rak::path_expand(path).c_str(), std::ios::in); + + if (!file.is_open()) + return false; + + int lineNumber = 0; + char buffer[2048]; + + try { + + while (file.getline(buffer, 2048).good()) { + lineNumber++; + // Would be nice to make this zero-copy. + parse_command_single(varMap, buffer, buffer + std::strlen(buffer)); + } + + } catch (torrent::input_error& e) { + snprintf(buffer, 2048, "Error in option file: %s:%i: %s", path.c_str(), lineNumber, e.what()); + + throw torrent::input_error(buffer); + } + + return true; +} + +} diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h new file mode 100644 index 00000000..61fa1301 --- /dev/null +++ b/src/rpc/parse_commands.h @@ -0,0 +1,72 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#ifndef RTORRENT_RPC_PARSE_COMMANDS_H +#define RTORRENT_RPC_PARSE_COMMANDS_H + +#include + +namespace core { + class Download; +} + +namespace utils { + +class VariableMap; + +const char* parse_command_name(const char* first, const char* last, std::string* dest); + +const char* parse_command_single(VariableMap* varMap, const char* first); +const char* parse_command_single(VariableMap* varMap, const char* first, const char* last); + +const char* parse_command_d_single(VariableMap* varMap, core::Download* download, const char* first, const char* last); + +void parse_command_multiple(VariableMap* varMap, const char* first); +bool parse_command_file(VariableMap* varMap, const std::string& path); + +inline void +parse_command_single_std(VariableMap* varMap, const std::string& cmd) { + parse_command_single(varMap, cmd.c_str(), cmd.c_str() + cmd.size()); +} + +inline void +parse_command_d_single_std(VariableMap* varMap, core::Download* download, const std::string& cmd) { + parse_command_d_single(varMap, download, cmd.c_str(), cmd.c_str() + cmd.size()); +} + +} + +#endif diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index ec5ac1c0..d366b19e 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -57,6 +57,7 @@ #include "display/window_title.h" #include "utils/variable_map.h" +#include "rpc/parse_commands.h" #include "control.h" #include "download.h" @@ -290,7 +291,7 @@ DownloadList::receive_exit_input(Input type) { break; case INPUT_COMMAND: - control->variable()->process_std_single(input->str()); + utils::parse_command_single_std(control->variable(), input->str()); break; default: diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index 77fad1f8..0e111859 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -6,8 +6,6 @@ libsub_utils_a_SOURCES = \ list_focus.h \ lockfile.cc \ lockfile.h \ - parse.cc \ - parse.h \ socket_fd.cc \ socket_fd.h \ variable_map.cc \ diff --git a/src/utils/variable_map.cc b/src/utils/variable_map.cc index f1b622b1..55f3869a 100644 --- a/src/utils/variable_map.cc +++ b/src/utils/variable_map.cc @@ -45,7 +45,6 @@ #include #include -#include "parse.h" #include "rpc/command.h" #include "variable_map.h" @@ -83,133 +82,6 @@ VariableMap::insert(key_type key, const variable_map_data_type src) { src.m_flags | flag_dont_delete, src.m_parm, src.m_doc))); } -struct variable_map_is_space : std::unary_function { - bool operator () (char c) const { - return std::isspace(c); - } -}; - -struct variable_map_is_newline : std::unary_function { - bool operator () (char c) const { - return c == '\n' || c == '\0'; - } -}; - -// Use a static length buffer for dest. -const char* -parse_name(const char* first, const char* last, std::string* dest) { - if (first == last || !std::isalpha(*first)) - throw torrent::input_error("Invalid start of name."); - - for ( ; first != last && (std::isalnum(*first) || *first == '_'); ++first) - dest->push_back(*first); - - return first; -} - -const char* -VariableMap::process_single(const char* first) { - // This could be optimized, but dunno if there's any point in doing - // it. - return process_single(first, first + std::strlen(first)); -} - -const char* -VariableMap::process_single(const char* first, const char* last) { - first = std::find_if(first, last, std::not1(variable_map_is_space())); - - if (first == last || *first == '#') - return last; - - std::string key; - first = parse_name(first, last, &key); - first = std::find_if(first, last, std::not1(variable_map_is_space())); - - if (first == last || *first != '=') - throw torrent::input_error("Could not find '='."); - - mapped_type args; - parse_whole_list(first + 1, last, &args); - - call_command(key.c_str(), args); - - return last; -} - -const char* -VariableMap::process_d_single(core::Download* download, const char* first, const char* last) { - first = std::find_if(first, last, std::not1(variable_map_is_space())); - - if (first == last || *first == '#') - return last; - - std::string key; - first = parse_name(first, last, &key); - first = std::find_if(first, last, std::not1(variable_map_is_space())); - - if (first == last || *first != '=') - throw torrent::input_error("Could not find '='."); - - mapped_type args; - parse_whole_list(first + 1, last, &args); - - call_command_d(key.c_str(), download, args); - - return last; -} - -// Consider what the command scheduler should do. - -void -VariableMap::process_multiple(const char* first) { - try { - while (first != '\0') { - const char* last = first; - - while (*last != '\n' && *last != '\0') last++; - - // Should we check the return value? Probably not necessary as - // parse_args throws on unquoted multi-word input. - process_single(first, last); - - if (*last == '\0') - return; - - first = last + 1; - } - - } catch (torrent::input_error& e) { - throw torrent::input_error(std::string("Error parsing multi-line option: ") + e.what()); - } -} - -bool -VariableMap::process_file(key_type path) { - std::fstream file(rak::path_expand(path).c_str(), std::ios::in); - - if (!file.is_open()) - return false; - - int lineNumber = 0; - char buffer[max_size_line]; - - try { - - while (file.getline(buffer, max_size_line).good()) { - lineNumber++; - // Would be nice to make this zero-copy. - process_single(buffer, buffer + std::strlen(buffer)); - } - - } catch (torrent::input_error& e) { - snprintf(buffer, max_size_line, "Error in option file: %s:%i: %s", path, lineNumber, e.what()); - - throw torrent::input_error(buffer); - } - - return true; -} - const VariableMap::mapped_type VariableMap::call_command(key_type key, const mapped_type& arg) { const_iterator itr = base_type::find(key); diff --git a/src/utils/variable_map.h b/src/utils/variable_map.h index fad4ed35..c0760085 100644 --- a/src/utils/variable_map.h +++ b/src/utils/variable_map.h @@ -70,7 +70,7 @@ struct variable_map_data_type { const char* parm, const char* doc) : m_variable(variable), m_genericSlot(genericSlot), m_downloadSlot(downloadSlot), m_flags(flags), m_parm(parm), m_doc(doc) {} - Command* m_variable; + Command* m_variable; generic_slot m_genericSlot; download_slot m_downloadSlot; @@ -111,27 +111,11 @@ public: bool has(const char* key) const { return base_type::find(key) != base_type::end(); } bool has(const std::string& key) const { return has(key.c_str()); } - // Allow NULL slot as a temporary compatibility hack. - void insert(key_type key, Command* variable, generic_slot genericSlot, download_slot downloadSlot, int flags, const char* parm, const char* doc); void insert(key_type key, const variable_map_data_type src); - const char* process_single(const char* first); - const char* process_single(const char* first, const char* last); - void process_std_single(const std::string& cmd) { process_single(cmd.c_str(), cmd.c_str() + cmd.size()); } - - const char* process_d_single(core::Download* download, const char* first, const char* last); - void process_d_std_single(core::Download* download, const std::string& cmd) { process_d_single(download, cmd.c_str(), cmd.c_str() + cmd.size()); } - - void process_multiple(const char* first); - - void process_stream(std::istream* str); - bool process_file(key_type path); - - // The new API, which is atm just a wrapper over the old and - // requires seperate calls to get and set. These will be merged. const mapped_type call_command(key_type key, const mapped_type& arg); const mapped_type call_command_void(key_type key) { return call_command(key, torrent::Object()); } const std::string call_command_string(key_type key) { return call_command(key, torrent::Object()).as_string(); }