diff --git a/scripts/common.m4 b/scripts/common.m4 index 92c2210f..e5f80106 100644 --- a/scripts/common.m4 +++ b/scripts/common.m4 @@ -5,7 +5,7 @@ AC_DEFUN([TORRENT_CHECK_CXXFLAGS], [ if test -n "$CXXFLAGS"; then AC_MSG_RESULT([user-defined "$CXXFLAGS"]) else - CXXFLAGS="-O3 -Wall" + CXXFLAGS="-O2 -Wall" AC_MSG_RESULT([default "$CXXFLAGS"]) fi ]) diff --git a/src/control.h b/src/control.h index 58e320e6..17436e27 100644 --- a/src/control.h +++ b/src/control.h @@ -38,10 +38,10 @@ #define RTORRENT_CONTROL_H #include +#include +#include #include -#include "globals.h" - namespace ui { class Root; } diff --git a/src/core/manager.cc b/src/core/manager.cc index 364d015c..ccafede6 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -50,6 +50,9 @@ #include #include +#include "utils/variable_map.h" + +#include "globals.h" #include "curl_get.h" #include "download.h" #include "download_factory.h" @@ -85,10 +88,8 @@ delete_tied(Download* d) { Manager::Manager() : m_pollManager(NULL), - m_portRandom(false), m_portFirst(6890), - m_portLast(6999), - m_checkHash(true) { + m_portLast(6999) { } void @@ -245,7 +246,7 @@ Manager::check_hash(Download* d) { void Manager::receive_download_done(Download* d) { - if (m_checkHash) { + if (control->variables()->get("check_hash").as_string() == "yes") { // Start the hash checking, send completed to tracker after // finishing. prepare_hash_check(d); @@ -263,7 +264,7 @@ Manager::listen_open() { if (m_portFirst > m_portLast) throw torrent::input_error("Invalid port range for listening"); - if (m_portRandom) { + if (control->variables()->get("port_random").as_string() == "yes") { int boundary = m_portFirst + random() % (m_portLast - m_portFirst + 1); if (!torrent::listen_open(boundary, m_portLast) && @@ -273,6 +274,7 @@ Manager::listen_open() { } else { if (!torrent::listen_open(m_portFirst, m_portLast)) throw torrent::input_error("Could not open/bind a port for listening."); + } } diff --git a/src/core/manager.h b/src/core/manager.h index 53ed56dc..ce0cc40d 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -69,11 +69,8 @@ public: Log& get_log_important() { return m_logImportant; } Log& get_log_complete() { return m_logComplete; } - void set_port_random(bool v) { m_portRandom = v; } void set_port_range(int a, int b) { m_portFirst = a; m_portLast = b; } - void set_check_hash(bool state) { m_checkHash = state; } - // Really should find a more descriptive name. void initialize_first(); void initialize_second(); @@ -121,11 +118,8 @@ private: Log m_logImportant; Log m_logComplete; - bool m_portRandom; int m_portFirst; int m_portLast; - - bool m_checkHash; }; } diff --git a/src/display/utils.cc b/src/display/utils.cc index 8f1d4cc7..7f4e6887 100644 --- a/src/display/utils.cc +++ b/src/display/utils.cc @@ -121,30 +121,31 @@ print_download_info(char* buf, unsigned int length, core::Download* d) { (double)d->get_download().down_rate()->rate() / (1 << 10), (double)d->get_download().up_rate()->total() / (1 << 20)), 0); - //buf += std::max(0, snprintf(buf, length, " Left: ")); - buf = print_download_time_left(buf, length, d); + buf = print_download_time_left(buf, last - buf, d); return buf; } char* print_download_status(char* buf, unsigned int length, core::Download* d) { + char* last = buf + length; + if (!d->get_download().is_active()) - buf += std::max(snprintf(buf, length, "Inactive: "), 0); + buf += std::max(snprintf(buf, last - buf, "Inactive: "), 0); if (d->get_download().is_hash_checking()) - buf += std::max(snprintf(buf, length, "Checking hash [%2i%%]", + buf += std::max(snprintf(buf, last - buf, "Checking hash [%2i%%]", (d->get_download().chunks_hashed() * 100) / d->get_download().chunks_total()), 0); else if (d->get_download().is_tracker_busy() && d->get_download().tracker_focus() < d->get_download().size_trackers()) - buf += std::max(snprintf(buf, length, "Tracker[%i:%i]: Connecting to %s", + buf += std::max(snprintf(buf, last - buf, "Tracker[%i:%i]: Connecting to %s", d->get_download().tracker(d->get_download().tracker_focus()).group(), d->get_download().tracker_focus(), d->get_download().tracker(d->get_download().tracker_focus()).url().c_str()), 0); else if (!d->get_message().empty()) - buf += std::max(snprintf(buf, length, "%s", d->get_message().c_str()), 0); + buf += std::max(snprintf(buf, last - buf, "%s", d->get_message().c_str()), 0); else buf[0] = '\0'; diff --git a/src/display/window_file_list.cc b/src/display/window_file_list.cc index f2698612..501583dc 100644 --- a/src/display/window_file_list.cc +++ b/src/display/window_file_list.cc @@ -120,7 +120,7 @@ WindowFileList::redraw() { m_canvas->print(84, pos, "%i - %i", e.chunk_begin(), - e.chunk_end()); + e.chunk_begin() != e.chunk_end() ? (e.chunk_end() - 1) : e.chunk_end()); ++range.first; ++pos; diff --git a/src/globals.cc b/src/globals.cc index 9d9c2c4a..d5c93b02 100644 --- a/src/globals.cc +++ b/src/globals.cc @@ -40,3 +40,5 @@ rak::priority_queue_default taskScheduler; rak::timer cachedTime; + +Control* control = NULL; diff --git a/src/globals.h b/src/globals.h index 25825283..526e0a0e 100644 --- a/src/globals.h +++ b/src/globals.h @@ -40,7 +40,11 @@ #include #include +#include "control.h" + extern rak::priority_queue_default taskScheduler; extern rak::timer cachedTime; +extern Control* control; + #endif diff --git a/src/main.cc b/src/main.cc index dc19839f..bdb411db 100644 --- a/src/main.cc +++ b/src/main.cc @@ -159,12 +159,12 @@ main(int argc, char** argv) { cachedTime = rak::timer::current(); - Control control; + control = new Control; srandom(cachedTime.usec()); srand48(cachedTime.usec()); - initialize_option_handler(&control); + initialize_option_handler(control); SignalHandler::set_ignore(SIGPIPE); SignalHandler::set_handler(SIGINT, sigc::mem_fun(control, &Control::receive_shutdown)); @@ -172,17 +172,17 @@ main(int argc, char** argv) { SignalHandler::set_handler(SIGBUS, sigc::bind(sigc::ptr_fun(&do_panic), SIGBUS)); SignalHandler::set_handler(SIGFPE, sigc::bind(sigc::ptr_fun(&do_panic), SIGFPE)); - control.core()->initialize_first(); + control->core()->initialize_first(); OptionFile optionFile; - optionFile.slot_option(sigc::mem_fun(control.variables(), &utils::VariableMap::set)); + optionFile.slot_option(sigc::mem_fun(control->variables(), &utils::VariableMap::process_command)); if (getenv("HOME") && !optionFile.process_file(getenv("HOME") + std::string("/.rtorrent.rc"))) - control.core()->get_log_important().push_front("Could not load \"~/.rtorrent.rc\"."); + control->core()->get_log_important().push_front("Could not load \"~/.rtorrent.rc\"."); - int firstArg = parse_options(&control, control.variables(), argc, argv); + int firstArg = parse_options(control, control->variables(), argc, argv); - control.initialize(); + control->initialize(); // Just to make sure we did all the stuff on the queue before // loading any torrents. @@ -192,38 +192,42 @@ main(int argc, char** argv) { // Load session torrents and perform scheduled tasks to ensure // session torrents are loaded before arg torrents. - load_session_torrents(&control); + load_session_torrents(control); rak::priority_queue_perform(&taskScheduler, cachedTime); - load_arg_torrents(&control, argv + firstArg, argv + argc); + load_arg_torrents(control, argv + firstArg, argv + argc); - control.display()->adjust_layout(); + control->display()->adjust_layout(); - while (!control.is_shutdown_completed()) { - control.inc_tick(); + while (!control->is_shutdown_completed()) { + control->inc_tick(); cachedTime = rak::timer::current(); rak::priority_queue_perform(&taskScheduler, cachedTime); // Do shutdown check before poll, not after. - control.core()->get_poll_manager()->poll(client_next_timeout()); + control->core()->get_poll_manager()->poll(client_next_timeout()); } - control.cleanup(); + control->cleanup(); } catch (torrent::base_error& e) { display::Canvas::cleanup(); + delete control; std::cout << "Caught exception: " << e.what() << std::endl; return -1; } catch (std::exception& e) { display::Canvas::cleanup(); + delete control; std::cout << e.what() << std::endl; return -1; } + delete control; + return 0; } diff --git a/src/option_file.cc b/src/option_file.cc index 1b6ffe6b..5f1257ff 100644 --- a/src/option_file.cc +++ b/src/option_file.cc @@ -57,7 +57,7 @@ OptionFile::process_file(const std::string& filename) { while (file.getline(buffer, max_size_line).good()) { lineNumber++; - parse_line(buffer); + m_slotOption(buffer); } } catch (torrent::input_error& e) { @@ -69,28 +69,28 @@ OptionFile::process_file(const std::string& filename) { return true; } -void -OptionFile::parse_line(const char* line) { +// void +// OptionFile::parse_line(const char* line) { //const char* last = std::find(line, line + max_size_line, '\0'); - if (line[0] == '#') - return; +// if (line[0] == '#') +// return; - int result; - char key[64]; - char opt[512]; +// int result; +// char key[64]; +// char opt[512]; - opt[0] = '\0'; +// opt[0] = '\0'; - // Check for empty lines, and options within "abc". - if ((result = std::sscanf(line, "%63s = \"%511[^\"]", key, opt)) != 2 && - (result = std::sscanf(line, "%63s = %511s", key, opt)) != 2 && - result == 1) - throw torrent::input_error("Error parseing option file."); +// // Check for empty lines, and options within "abc". +// if ((result = std::sscanf(line, "%63s = \"%511[^\"]", key, opt)) != 2 && +// (result = std::sscanf(line, "%63s = %511s", key, opt)) != 2 && +// result == 1) +// throw torrent::input_error("Error parseing option file."); - if (opt[0] == '"' && opt[1] == '"') - opt[0] = '\0'; +// if (opt[0] == '"' && opt[1] == '"') +// opt[0] = '\0'; - if (result >= 1) - m_slotOption(key, opt); -} +// if (result >= 1) +// m_slotOption(key, opt); +// } diff --git a/src/option_file.h b/src/option_file.h index 08f46729..7d04f86f 100644 --- a/src/option_file.h +++ b/src/option_file.h @@ -46,7 +46,7 @@ public: static const int max_size_opt = 1024; static const int max_size_line = max_size_key + max_size_opt + 64; - typedef sigc::slot2 SlotStringPair; + typedef sigc::slot1 SlotStringPair; // Returns false when the file doesn't exist or cannot be opened. bool process_file(const std::string& filename); diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index 46abf5c5..a8872f1d 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -55,6 +55,7 @@ #include "utils/variable_generic.h" #include "utils/variable_map.h" +#include "globals.h" #include "control.h" #include "option_handler_rules.h" #include "command_scheduler.h" @@ -178,11 +179,6 @@ apply_port_range(Control* m, const std::string& arg) { m->core()->set_port_range(a, b); } -void -apply_port_random(Control* m, const std::string& arg) { - m->core()->set_port_random(arg == "yes"); -} - void apply_tracker_dump(Control* m, const std::string& arg) { if (arg == "yes") @@ -199,14 +195,6 @@ apply_use_udp_trackers(Control* m, const std::string& arg) { m->core()->get_download_list().slot_map_insert()["1_use_udp_trackers"] = sigc::bind(sigc::mem_fun(&core::Download::enable_udp_trackers), false); } -void -apply_check_hash(Control* m, const std::string& arg) { - if (arg == "yes") - m->core()->set_check_hash(true); - else - m->core()->set_check_hash(false); -} - void apply_http_proxy(Control* m, const std::string& arg) { m->core()->get_poll_manager()->get_http_stack()->set_http_proxy(arg); @@ -299,44 +287,49 @@ apply_schedule_remove(Control* m, const std::string& arg) { void initialize_option_handler(Control* c) { - c->variables()->insert("bind", new utils::VariableSlotString<>(NULL, rak::mem_fn(c->core(), &core::Manager::bind))); + utils::VariableMap* variables = control->variables(); - c->variables()->insert("ip", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_ip, c))); - c->variables()->insert("port_range", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_port_range, c))); - c->variables()->insert("port_random", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_port_random, c))); + // Cleaned up. + variables->insert("check_hash", new utils::VariableValue("yes")); + variables->insert("port_random", new utils::VariableValue("yes")); + variables->insert("session", new utils::VariableSlotString<>(NULL, rak::mem_fn(&control->core()->get_download_store(), &core::DownloadStore::use))); - c->variables()->insert("check_hash", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_check_hash, c))); - c->variables()->insert("directory", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_download_directory, c))); + // Old. + variables->insert("bind", new utils::VariableSlotString<>(NULL, rak::mem_fn(control->core(), &core::Manager::bind))); - c->variables()->insert("max_peers", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_download_max_peers, c), "%i")); - c->variables()->insert("min_peers", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_download_min_peers, c), "%i")); - c->variables()->insert("max_uploads", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_download_max_uploads, c), "%i")); + variables->insert("ip", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_ip, c))); + variables->insert("port_range", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_port_range, c))); - c->variables()->insert("download_rate", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_global_download_rate, c), "%i")); - c->variables()->insert("upload_rate", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_global_upload_rate, c), "%i")); + variables->insert("directory", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_download_directory, c))); - c->variables()->insert("hash_read_ahead", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_hash_read_ahead, c), "%i")); - c->variables()->insert("hash_interval", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_hash_interval, c), "%i")); - c->variables()->insert("hash_max_tries", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_hash_max_tries, c), "%i")); - c->variables()->insert("max_open_files", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_max_open_files, c), "%i")); - c->variables()->insert("max_open_sockets", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_max_open_sockets, c), "%i")); + variables->insert("max_peers", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_download_max_peers, c), "%i")); + variables->insert("min_peers", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_download_min_peers, c), "%i")); + variables->insert("max_uploads", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_download_max_uploads, c), "%i")); - c->variables()->insert("umask", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_umask, c), "%o")); + variables->insert("download_rate", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_global_download_rate, c), "%i")); + variables->insert("upload_rate", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_global_upload_rate, c), "%i")); - c->variables()->insert("connection_leech", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_connection_leech, c))); - c->variables()->insert("connection_seed", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_connection_seed, c))); + variables->insert("hash_read_ahead", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_hash_read_ahead, c), "%i")); + variables->insert("hash_interval", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_hash_interval, c), "%i")); + variables->insert("hash_max_tries", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_hash_max_tries, c), "%i")); + variables->insert("max_open_files", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_max_open_files, c), "%i")); + variables->insert("max_open_sockets", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_max_open_sockets, c), "%i")); - c->variables()->insert("load", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_load, c))); - c->variables()->insert("load_start", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_load_start, c))); - c->variables()->insert("stop_untied", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_stop_untied, c))); - c->variables()->insert("remove_untied", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_remove_untied, c))); + variables->insert("umask", new utils::VariableSlotValue(NULL, rak::bind_ptr_fn(&apply_umask, c), "%o")); - c->variables()->insert("session", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_session_directory, c))); - c->variables()->insert("encoding_list", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_encoding_list, c))); - c->variables()->insert("tracker_dump", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_tracker_dump, c))); - c->variables()->insert("use_udp_trackers", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_use_udp_trackers, c))); + variables->insert("connection_leech", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_connection_leech, c))); + variables->insert("connection_seed", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_connection_seed, c))); - c->variables()->insert("http_proxy", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_http_proxy, c))); - c->variables()->insert("schedule", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_schedule, c))); - c->variables()->insert("schedule_remove", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_schedule_remove, c))); + variables->insert("load", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_load, c))); + variables->insert("load_start", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_load_start, c))); + variables->insert("stop_untied", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_stop_untied, c))); + variables->insert("remove_untied", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_remove_untied, c))); + + variables->insert("encoding_list", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_encoding_list, c))); + variables->insert("tracker_dump", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_tracker_dump, c))); + variables->insert("use_udp_trackers", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_use_udp_trackers, c))); + + variables->insert("http_proxy", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_http_proxy, c))); + variables->insert("schedule", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_schedule, c))); + variables->insert("schedule_remove", new utils::VariableSlotString<>(NULL, rak::bind_ptr_fn(&apply_schedule_remove, c))); } diff --git a/src/utils/variable_generic.cc b/src/utils/variable_generic.cc index f417e70d..469164ae 100644 --- a/src/utils/variable_generic.cc +++ b/src/utils/variable_generic.cc @@ -40,17 +40,17 @@ namespace utils { -// VariableString::~VariableString() { -// } +VariableValue::~VariableValue() { +} -// std::string -// VariableString::get() { -// return m_variable; -// } +const torrent::Bencode& +VariableValue::get() { + return m_variable; +} -// void -// VariableString::set(const std::string& arg) { -// m_variable = arg; -// } +void +VariableValue::set(const torrent::Bencode& arg) { + m_variable = arg; +} } diff --git a/src/utils/variable_generic.h b/src/utils/variable_generic.h index 74c49ccf..040b9656 100644 --- a/src/utils/variable_generic.h +++ b/src/utils/variable_generic.h @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -48,19 +49,18 @@ namespace utils { -// class VariableS : public Variable { -// public: -// VariableString(const std::string& v = "") : m_variable(v) {} -// virtual ~VariableString(); +class VariableValue : public Variable { +public: + VariableValue(const torrent::Bencode& v = torrent::Bencode()) : m_variable(v) {} + virtual ~VariableValue(); -// virtual const torrent::Bencode& get(); -// virtual void set(const torrent::Bencode& arg); + virtual const torrent::Bencode& get(); + virtual void set(const torrent::Bencode& arg); -// private: -// std::string m_variable; -// }; +private: + torrent::Bencode m_variable; +}; -// VariableSlot? template class VariableSlotString : public Variable { public: @@ -84,10 +84,16 @@ public: } virtual void set(const torrent::Bencode& arg) { - if (!arg.is_string()) + switch (arg.get_type()) { + case torrent::Bencode::TYPE_STRING: + m_slotSet(arg.as_string()); + break; + case torrent::Bencode::TYPE_NONE: + m_slotSet(""); + break; + default: throw torrent::internal_error("VariableSlotString::set(...) got wrong type."); - - m_slotSet(arg.as_string()); + } } private: @@ -103,15 +109,19 @@ private: template class VariableSlotValue : public Variable { public: - typedef rak::function0 SlotGet; - typedef rak::function1 SlotSet; + typedef rak::function0 SlotGet; + typedef rak::function1 SlotSet; + typedef std::pair Range; VariableSlotValue(typename SlotGet::base_type* slotGet, typename SlotSet::base_type* slotSet, - const char* pattern) { + const char* pattern, + Range range = Range(std::numeric_limits::min(), + std::numeric_limits::max())) { m_slotGet.set(slotGet); m_slotSet.set(slotSet); m_pattern = pattern; + m_range = range; } virtual ~VariableSlotValue() {} @@ -148,6 +158,7 @@ private: SlotSet m_slotSet; const char* m_pattern; + Range m_range; // Store the cache here to avoid unnessesary copying and such. This // should not result in any unresonable memory usage since few diff --git a/src/utils/variable_map.cc b/src/utils/variable_map.cc index 6ecd285a..0589a26e 100644 --- a/src/utils/variable_map.cc +++ b/src/utils/variable_map.cc @@ -82,16 +82,83 @@ VariableMap::set(const std::string& key, const torrent::Bencode& arg) { itr->second->set(arg); } +std::string::const_iterator +parse_name(std::string::const_iterator first, std::string::const_iterator 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; +} + +std::string::const_iterator +parse_unknown(std::string::const_iterator first, std::string::const_iterator last, torrent::Bencode* dest) { + if (*first == '"') { + std::string::const_iterator next = std::find_if(++first, last, std::bind2nd(std::equal_to(), '"')); + + if (first == last || + first == next || + next == last) + throw torrent::input_error("Could not find closing '\"'."); + + *dest = std::string(first, next); + return ++next; + + } else { + // Add rak::or and check for ','. + std::string::const_iterator next = std::find_if(first, last, std::ptr_fun(&std::isspace)); + + *dest = std::string(first, next); + return next; + } +} + +std::string::const_iterator +parse_args(std::string::const_iterator first, std::string::const_iterator last, torrent::Bencode::List* dest) { + first = std::find_if(first, last, std::not1(std::ptr_fun(&std::isspace))); + + while (first != last) { + dest->push_back(torrent::Bencode()); + + first = parse_unknown(first, last, &dest->back()); + first = std::find_if(first, last, std::not1(std::ptr_fun(&std::isspace))); + + if (first != last && *first != ',') + throw torrent::input_error("A string with blanks must be quoted."); + } + + return first; +} + void VariableMap::process_command(const std::string& command) { - std::string::size_type pos = command.find('='); + std::string::const_iterator pos = command.begin(); + pos = std::find_if(pos, command.end(), std::not1(std::ptr_fun(&std::isspace))); - if (pos == std::string::npos) - throw torrent::input_error("Option handler could not find '=' in command."); + if (pos == command.end() || *pos == '#') + return; - // Do sscanf, check for integer. Later move and make it smarter. + // Replace with parse_unknown? + std::string key; + pos = parse_name(pos, command.end(), &key); + pos = std::find_if(pos, command.end(), std::not1(std::ptr_fun(&std::isspace))); + + if (pos == command.end() || *pos != '=') + throw torrent::input_error("Could not find '='."); - set(command.substr(0, pos), torrent::Bencode(command.substr(pos + 1, std::string::npos))); + torrent::Bencode args(torrent::Bencode::TYPE_LIST); + parse_args(pos + 1, command.end(), &args.as_list()); + + if (args.as_list().empty()) + set(key, torrent::Bencode()); + + else if (++args.as_list().begin() == args.as_list().end()) + set(key, *args.as_list().begin()); + + else + set(key, args); } }