diff --git a/src/core/download.cc b/src/core/download.cc index 1ea0bc50..8ecd029e 100644 --- a/src/core/download.cc +++ b/src/core/download.cc @@ -40,6 +40,7 @@ #include #include #include +#include #include "download.h" @@ -128,7 +129,7 @@ Download::string_to_connection_type(const std::string& name) { else if (name == "seed") return torrent::Download::CONNECTION_SEED; else - throw std::runtime_error("Invalid connection type selected: \"" + name + "\""); + throw torrent::input_error("Unknown peer connection type selected: \"" + name + "\""); } const char* diff --git a/src/core/manager.cc b/src/core/manager.cc index 0d2eea03..0d6f503b 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -82,11 +82,11 @@ Manager::Manager() : void Manager::initialize_first() { if ((m_pollManager = PollManagerEPoll::create(sysconf(_SC_OPEN_MAX))) != NULL) - m_logImportant.push_front("Using 'epoll' based polling"); + m_logImportant.push_front("Using 'epoll' based polling."); else if ((m_pollManager = PollManagerSelect::create(sysconf(_SC_OPEN_MAX))) != NULL) - m_logImportant.push_front("Using 'select' based polling"); + m_logImportant.push_front("Using 'select' based polling."); else - throw std::runtime_error("Could not create any PollManager"); + throw std::runtime_error("Could not create any PollManager."); // Need to initialize this before parseing options. torrent::initialize(m_pollManager->get_torrent_poll()); diff --git a/src/main.cc b/src/main.cc index d9174b91..9061bb9c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -100,67 +100,57 @@ is_resized() { int parse_options(Control* c, OptionHandler* optionHandler, int argc, char** argv) { - OptionParser optionParser; + try { + OptionParser optionParser; - // Converted. - optionParser.insert_flag('h', sigc::ptr_fun(&print_help)); + // Converted. + optionParser.insert_flag('h', sigc::ptr_fun(&print_help)); - optionParser.insert_option('b', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "bind")); - optionParser.insert_option('d', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "directory")); - optionParser.insert_option('i', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "ip")); - optionParser.insert_option('p', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "port_range")); - optionParser.insert_option('s', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "session")); + optionParser.insert_option('b', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "bind")); + optionParser.insert_option('d', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "directory")); + optionParser.insert_option('i', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "ip")); + optionParser.insert_option('p', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "port_range")); + optionParser.insert_option('s', sigc::bind<0>(sigc::mem_fun(*optionHandler, &OptionHandler::process), "session")); - optionParser.insert_option_list('o', sigc::mem_fun(*optionHandler, &OptionHandler::process)); + optionParser.insert_option_list('o', sigc::mem_fun(*optionHandler, &OptionHandler::process)); - return optionParser.process(argc, argv); + return optionParser.process(argc, argv); + + } catch (torrent::input_error& e) { + throw std::runtime_error("Failed to parse command line option: " + std::string(e.what())); + } } void initialize_option_handler(Control* c, OptionHandler* optionHandler) { - optionHandler->insert("max_peers", new OptionHandlerInt(c, &apply_download_max_peers, &validate_download_peers)); - optionHandler->insert("min_peers", new OptionHandlerInt(c, &apply_download_min_peers, &validate_download_peers)); - optionHandler->insert("max_uploads", new OptionHandlerInt(c, &apply_download_max_uploads, &validate_download_peers)); + optionHandler->insert("max_peers", new OptionHandlerInt(c, &apply_download_max_peers)); + optionHandler->insert("min_peers", new OptionHandlerInt(c, &apply_download_min_peers)); + optionHandler->insert("max_uploads", new OptionHandlerInt(c, &apply_download_max_uploads)); - optionHandler->insert("download_rate", new OptionHandlerInt(c, &apply_global_download_rate, &validate_rate)); - optionHandler->insert("upload_rate", new OptionHandlerInt(c, &apply_global_upload_rate, &validate_rate)); + optionHandler->insert("download_rate", new OptionHandlerInt(c, &apply_global_download_rate)); + optionHandler->insert("upload_rate", new OptionHandlerInt(c, &apply_global_upload_rate)); - optionHandler->insert("bind", new OptionHandlerString(c, &apply_bind, &validate_ip)); - optionHandler->insert("ip", new OptionHandlerString(c, &apply_ip, &validate_ip)); - optionHandler->insert("port_range", new OptionHandlerString(c, &apply_port_range, &validate_port_range)); - optionHandler->insert("port_random", new OptionHandlerString(c, &apply_port_random, &validate_yes_no)); + optionHandler->insert("bind", new OptionHandlerString(c, &apply_bind)); + optionHandler->insert("ip", new OptionHandlerString(c, &apply_ip)); + optionHandler->insert("port_range", new OptionHandlerString(c, &apply_port_range)); + optionHandler->insert("port_random", new OptionHandlerString(c, &apply_port_random)); - optionHandler->insert("check_hash", new OptionHandlerString(c, &apply_check_hash, &validate_yes_no)); - optionHandler->insert("directory", new OptionHandlerString(c, &apply_download_directory, &validate_directory)); + optionHandler->insert("check_hash", new OptionHandlerString(c, &apply_check_hash)); + optionHandler->insert("directory", new OptionHandlerString(c, &apply_download_directory)); - optionHandler->insert("hash_read_ahead", new OptionHandlerInt(c, &apply_hash_read_ahead, &validate_hash_read_ahead)); - optionHandler->insert("hash_interval", new OptionHandlerInt(c, &apply_hash_interval, &validate_hash_interval)); - optionHandler->insert("hash_max_tries", new OptionHandlerInt(c, &apply_hash_max_tries, &validate_hash_max_tries)); - optionHandler->insert("max_open_files", new OptionHandlerInt(c, &apply_max_open_files, &validate_fd)); - optionHandler->insert("max_open_sockets", new OptionHandlerInt(c, &apply_max_open_sockets, &validate_fd)); + optionHandler->insert("hash_read_ahead", new OptionHandlerInt(c, &apply_hash_read_ahead)); + optionHandler->insert("hash_interval", new OptionHandlerInt(c, &apply_hash_interval)); + optionHandler->insert("hash_max_tries", new OptionHandlerInt(c, &apply_hash_max_tries)); + optionHandler->insert("max_open_files", new OptionHandlerInt(c, &apply_max_open_files)); + optionHandler->insert("max_open_sockets", new OptionHandlerInt(c, &apply_max_open_sockets)); - optionHandler->insert("connection_leech", new OptionHandlerString(c, &apply_connection_leech, &validate_non_empty)); - optionHandler->insert("connection_seed", new OptionHandlerString(c, &apply_connection_seed, &validate_non_empty)); + optionHandler->insert("connection_leech", new OptionHandlerString(c, &apply_connection_leech)); + optionHandler->insert("connection_seed", new OptionHandlerString(c, &apply_connection_seed)); - optionHandler->insert("session", new OptionHandlerString(c, &apply_session_directory, &validate_directory)); - optionHandler->insert("encoding_list", new OptionHandlerString(c, &apply_encoding_list, &validate_non_empty)); - optionHandler->insert("tracker_dump", new OptionHandlerString(c, &apply_tracker_dump, &validate_yes_no)); - optionHandler->insert("use_udp_trackers", new OptionHandlerString(c, &apply_use_udp_trackers, &validate_yes_no)); -} - -void -load_option_file(const std::string& filename, OptionHandler* optionHandler, bool require = false) { - std::fstream f(filename.c_str(), std::ios::in); - - if (!f.is_open()) { - std::cout << "Could not open option file \"" << filename << "\"" << std::endl; - return; - } - - OptionFile optionFile; - - optionFile.slot_option(sigc::mem_fun(*optionHandler, &OptionHandler::process)); - optionFile.process(&f); + optionHandler->insert("session", new OptionHandlerString(c, &apply_session_directory)); + optionHandler->insert("encoding_list", new OptionHandlerString(c, &apply_encoding_list)); + optionHandler->insert("tracker_dump", new OptionHandlerString(c, &apply_tracker_dump)); + optionHandler->insert("use_udp_trackers", new OptionHandlerString(c, &apply_use_udp_trackers)); } void @@ -198,13 +188,16 @@ main(int argc, char** argv) { utils::Timer::update(); OptionHandler optionHandler; - Control uiControl; + Control uiControl; srandom(utils::Timer::cache().usec()); srand48(utils::Timer::cache().usec()); initialize_option_handler(&uiControl, &optionHandler); + OptionFile optionFile; + optionFile.slot_option(sigc::mem_fun(optionHandler, &OptionHandler::process)); + try { SignalHandler::set_ignore(SIGPIPE); @@ -215,8 +208,8 @@ main(int argc, char** argv) { uiControl.core()->initialize_first(); - if (getenv("HOME")) - load_option_file(getenv("HOME") + std::string("/.rtorrent.rc"), &optionHandler); + if (getenv("HOME") && !optionFile.process_file(getenv("HOME") + std::string("/.rtorrent.rc"))) + uiControl.core()->get_log_important().push_front("Could not load \"~/.rtorrent.rc\"."); int firstArg = parse_options(&uiControl, &optionHandler, argc, argv); @@ -250,13 +243,13 @@ main(int argc, char** argv) { } catch (torrent::base_error& e) { display::Canvas::cleanup(); - std::cout << "Caught exception from libtorrent: \"" << e.what() << '"' << std::endl; + std::cout << "Caught exception from libtorrent: " << e.what() << std::endl; return -1; } catch (std::exception& e) { display::Canvas::cleanup(); - std::cout << "Caught exception: \"" << e.what() << '"' << std::endl; + std::cout << e.what() << std::endl; return -1; } @@ -288,8 +281,6 @@ do_panic(int signum) { if (signum == SIGBUS) std::cout << "A bus error propably means you ran out of diskspace." << std::endl; - - std::cout << "TO AVOID CORRUPT DOWNLOADS, RUN \"touch\" ON ALL DOWNLOADED FILES OR INITATE HASH RECHECK WITH ^R ON ALL TORRENTS." << std::endl; exit(-1); } diff --git a/src/option_file.cc b/src/option_file.cc index 57966740..f275af6a 100644 --- a/src/option_file.cc +++ b/src/option_file.cc @@ -38,18 +38,35 @@ #include #include +#include +#include #include "option_file.h" -void -OptionFile::process(std::istream* stream) { - char buf[max_size_line]; +bool +OptionFile::process_file(const std::string& filename) { + std::fstream file(filename.c_str(), std::ios::in); - while (stream->good()) { - stream->getline(buf, max_size_line); + if (!file.good()) + return false; - parse_line(buf); + int lineNumber = 0; + char buffer[max_size_line]; + + try { + + while (file.getline(buffer, max_size_line).good()) { + lineNumber++; + parse_line(buffer); + } + + } catch (torrent::input_error& e) { + snprintf(buffer, max_size_line, "Error in option file: %s:%i: %s", filename.c_str(), lineNumber, e.what()); + + throw std::runtime_error(buffer); } + + return true; } void @@ -69,7 +86,7 @@ OptionFile::parse_line(const char* line) { if ((result = std::sscanf(line, "%63s = \"%511[^\"]s", key, opt)) != 2 && (result = std::sscanf(line, "%63s = %511s", key, opt)) != 2 && result == 1) - throw std::runtime_error("Error parseing option file."); + throw torrent::input_error("Error parseing option file."); if (opt[0] == '"' && opt[1] == '"') opt[0] = '\0'; diff --git a/src/option_file.h b/src/option_file.h index bf7c11a8..e5122883 100644 --- a/src/option_file.h +++ b/src/option_file.h @@ -37,25 +37,27 @@ #ifndef RTORRENT_OPTION_FILE_H #define RTORRENT_OPTION_FILE_H -#include #include #include class OptionFile { public: - static const int max_size_key = 64; - static const int max_size_opt = 512; + static const int max_size_key = 128; + static const int max_size_opt = 1024; static const int max_size_line = max_size_key + max_size_opt + 64; typedef sigc::slot2 SlotStringPair; - void slot_option(const SlotStringPair& s) { m_slotOption = s; } + // Returns false when the file doesn't exist or cannot be opened. + bool process_file(const std::string& filename); - void process(std::istream* stream); + void slot_option(const SlotStringPair& s) { m_slotOption = s; } private: void parse_line(const char* line); + static char* fill_buffer(int fd, char* buffer, char* first, char* last); + SlotStringPair m_slotOption; }; diff --git a/src/option_handler.cc b/src/option_handler.cc index b634df13..cfc89d1e 100644 --- a/src/option_handler.cc +++ b/src/option_handler.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include "option_handler.h" @@ -78,7 +79,7 @@ OptionHandler::process(const std::string& key, const std::string& arg) const { const_iterator itr = find(key); if (itr == end()) - throw std::runtime_error("Could not find option key matching \"" + key + "\""); + throw torrent::input_error("Could not find option key \"" + key + "\"."); itr->second->process(key, arg); } diff --git a/src/option_handler.h b/src/option_handler.h index 870a2c7e..cda7e5e0 100644 --- a/src/option_handler.h +++ b/src/option_handler.h @@ -77,6 +77,7 @@ public: void clear(); + // The caller must catch torrent::input_error in case of bad input. void process(const std::string& key, const std::string& arg) const; }; diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index 8d77b599..da533a93 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -37,6 +37,7 @@ #include "config.h" #include +#include #include #include @@ -49,67 +50,19 @@ void receive_tracker_dump(std::istream* s); -bool -validate_ip(const std::string& arg) { - struct in_addr addr; - - return inet_aton(arg.c_str(), &addr); +void +OptionHandlerInt::process(const std::string& key, const std::string& arg) { + int a; + + if (std::sscanf(arg.c_str(), "%i", &a) != 1) + throw torrent::input_error("Invalid argument for \"" + key + "\": \"" + arg + "\""); + + m_apply(m_control, a); } -// We consider an empty string to be valid as this allows us to -// disable options. -bool -validate_directory(const std::string& arg) { - //return arg.empty() || utils::Directory(arg).is_valid(); - return true; -} - -bool -validate_port_range(const std::string& arg) { - int a, b; - - return std::sscanf(arg.c_str(), "%i-%i", &a, &b) == 2 && - a <= b && a > 0 && b < (1 << 16); -} - -bool -validate_yes_no(const std::string& arg) { - return arg == "yes" || arg == "no"; -} - -bool -validate_non_empty(const std::string& arg) { - return !arg.empty(); -} - -bool -validate_download_peers(int arg) { - return arg > 0 && arg < (1 << 16); -} - -bool -validate_rate(int arg) { - return arg >= 0 && arg < (1 << 20); -} - -bool -validate_hash_read_ahead(int arg) { - return arg >= 1 && arg < 64; -} - -bool -validate_hash_interval(int arg) { - return arg >= 1 && arg < 1000; -} - -bool -validate_hash_max_tries(int arg) { - return arg >= 1 && arg < 20; -} - -bool -validate_fd(int arg) { - return arg >= 1 && arg < (1 << 16); +void +OptionHandlerString::process(const std::string& key, const std::string& arg) { + m_apply(m_control, arg); } void @@ -137,22 +90,24 @@ apply_download_directory(Control* m, const std::string& arg) { void apply_connection_leech(Control* m, const std::string& arg) { + core::Download::string_to_connection_type(arg); m->core()->get_download_list().slot_map_insert()["1_connection_leech"] = sigc::bind(sigc::mem_fun(&core::Download::set_connection_leech), arg); } void apply_connection_seed(Control* m, const std::string& arg) { + core::Download::string_to_connection_type(arg); m->core()->get_download_list().slot_map_insert()["1_connection_seed"] = sigc::bind(sigc::mem_fun(&core::Download::set_connection_seed), arg); } void apply_global_download_rate(Control* m, int arg) { - m->ui()->receive_down_throttle(arg); + m->ui()->set_down_throttle(arg); } void apply_global_upload_rate(Control* m, int arg) { - m->ui()->receive_up_throttle(arg); + m->ui()->set_up_throttle(arg); } void diff --git a/src/option_handler_rules.h b/src/option_handler_rules.h index bcdba935..e8eb9c9f 100644 --- a/src/option_handler_rules.h +++ b/src/option_handler_rules.h @@ -48,19 +48,6 @@ class Control; // Not pretty, but it is simple and easy to modify. -bool validate_ip(const std::string& arg); -bool validate_directory(const std::string& arg); -bool validate_port_range(const std::string& arg); -bool validate_yes_no(const std::string& arg); -bool validate_non_empty(const std::string& arg); - -bool validate_download_peers(int arg); -bool validate_rate(int arg); -bool validate_hash_read_ahead(int arg); -bool validate_hash_interval(int arg); -bool validate_hash_max_tries(int arg); -bool validate_fd(int arg); - void apply_download_min_peers(Control* m, int arg); void apply_download_max_peers(Control* m, int arg); void apply_download_max_uploads(Control* m, int arg); @@ -91,47 +78,30 @@ void apply_encoding_list(Control* m, const std::string& arg); class OptionHandlerInt : public OptionHandlerBase { public: - typedef bool (*Validate)(int); typedef void (*Apply)(Control*, int); - OptionHandlerInt(Control* c, Apply a, Validate v) : - m_control(c), m_apply(a), m_validate(v) {} + OptionHandlerInt(Control* c, Apply a) : + m_control(c), m_apply(a) {} - virtual void process(const std::string& key, const std::string& arg) { - int a; - - if (std::sscanf(arg.c_str(), "%i", &a) != 1 || - !m_validate(a)) - throw std::runtime_error("Invalid argument for \"" + key + "\": \"" + arg + "\""); - - m_apply(m_control, a); - } + virtual void process(const std::string& key, const std::string& arg); private: Control* m_control; Apply m_apply; - Validate m_validate; }; class OptionHandlerString : public OptionHandlerBase { public: - typedef bool (*Validate)(const std::string&); typedef void (*Apply)(Control*, const std::string&); - OptionHandlerString(Control* c, Apply a, Validate v) : - m_control(c), m_apply(a), m_validate(v) {} + OptionHandlerString(Control* c, Apply a) : + m_control(c), m_apply(a) {} - virtual void process(const std::string& key, const std::string& arg) { - if (!m_validate(arg)) - throw std::runtime_error("Invalid argument for \"" + key + "\": \"" + arg + "\""); - - m_apply(m_control, arg); - } + virtual void process(const std::string& key, const std::string& arg); private: Control* m_control; Apply m_apply; - Validate m_validate; }; #endif diff --git a/src/ui/root.cc b/src/ui/root.cc index eaec5c82..2a86ab6b 100644 --- a/src/ui/root.cc +++ b/src/ui/root.cc @@ -94,49 +94,57 @@ void Root::setup_keys() { m_control->input()->push_back(&m_bindings); - m_bindings['a'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_up_throttle), 1); - m_bindings['z'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_up_throttle), -1); - m_bindings['s'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_up_throttle), 5); - m_bindings['x'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_up_throttle), -5); - m_bindings['d'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_up_throttle), 50); - m_bindings['c'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_up_throttle), -50); + m_bindings['a'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 1); + m_bindings['z'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -1); + m_bindings['s'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 5); + m_bindings['x'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -5); + m_bindings['d'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 50); + m_bindings['c'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -50); - m_bindings['A'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_down_throttle), 1); - m_bindings['Z'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_down_throttle), -1); - m_bindings['S'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_down_throttle), 5); - m_bindings['X'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_down_throttle), -5); - m_bindings['D'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_down_throttle), 50); - m_bindings['C'] = sigc::bind(sigc::mem_fun(*this, &Root::receive_down_throttle), -50); + m_bindings['A'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 1); + m_bindings['Z'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -1); + m_bindings['S'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 5); + m_bindings['X'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -5); + m_bindings['D'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 50); + m_bindings['C'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -50); m_bindings[KEY_RESIZE] = sigc::mem_fun(*m_control->display(), &display::Manager::adjust_layout); m_bindings['\x11'] = sigc::mem_fun(*m_control, &Control::receive_shutdown); } void -Root::receive_down_throttle(int t) { +Root::set_down_throttle(unsigned int throttle) { if (m_windowStatusbar != NULL) m_windowStatusbar->mark_dirty(); - torrent::set_down_throttle(std::max(torrent::down_throttle() + t * 1024, 0)); + torrent::set_down_throttle(throttle * 1024); } void -Root::receive_up_throttle(int t) { +Root::set_up_throttle(unsigned int throttle) { if (m_windowStatusbar != NULL) m_windowStatusbar->mark_dirty(); - uint32_t throttle = std::max(torrent::up_throttle() + t * 1024, 0); - - torrent::set_up_throttle(throttle); + torrent::set_up_throttle(throttle * 1024); if (throttle == 0) torrent::set_max_unchoked(0); - else if (throttle <= 10 << 10) - torrent::set_max_unchoked(1 + throttle / (1 << 10)); + else if (throttle <= 10) + torrent::set_max_unchoked(1 + throttle / 1); else - torrent::set_max_unchoked(10 + throttle / (5 << 10)); + torrent::set_max_unchoked(10 + throttle / 5); +} + +void +Root::adjust_down_throttle(int throttle) { + set_down_throttle(std::max(torrent::down_throttle() / 1024 + throttle, 0)); +} + +void +Root::adjust_up_throttle(int throttle) { + set_up_throttle(std::max(torrent::up_throttle() / 1024 + throttle, 0)); } } diff --git a/src/ui/root.h b/src/ui/root.h index 1e784c04..64da4cb0 100644 --- a/src/ui/root.h +++ b/src/ui/root.h @@ -60,8 +60,11 @@ public: WStatusbar* window_statusbar() { return m_windowStatusbar; } - void receive_down_throttle(int t); - void receive_up_throttle(int t); + void set_down_throttle(unsigned int throttle); + void set_up_throttle(unsigned int throttle); + + void adjust_down_throttle(int throttle); + void adjust_up_throttle(int throttle); private: void setup_keys();