From 4431fd165225cafd7aa04e5521be84cbb17dff62 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Tue, 3 Jan 2006 21:43:01 +0000 Subject: [PATCH] * Fixed a regression that threw an exception for torrents with empty directories. * When receiving zero length pieces, remove them from the request list. * Send the interested state before any requests as some clients, BitTornado 0.7.14 and uTorrent 0.3.0, disconnect if a request has been received while uninterested. * WITH_POSIX_FALLOCATE configure macro should check against "yes", not "no". * The new priority queue would cause arg torrents to load before session torrents in some cases. Fixed to perform the session torrent tasks before loading arg torrents. * Allow '*' wildcard in filenames when loading torrents. * Added load, load_run, stop_tied and remove_tied settings which can be used to scan a directory for new/removed torrents. These torrents are tied to the lifetime of the torrent file. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@616 e378c898-3ddf-0310-93e7-cc216c733640 --- Makefile.am | 1 + configure.ac | 2 +- rak/priority_queue_default.h | 11 ++ rak/regex.h | 220 ++++++++++++++++++++++++++++++++ rak/string_manip.h | 53 ++++++++ scripts/checks.m4 | 4 +- src/core/download.h | 5 + src/core/download_factory.cc | 42 ++++-- src/core/download_factory.h | 12 ++ src/core/log.cc | 3 + src/core/log.h | 9 ++ src/core/manager.cc | 89 ++++++++++++- src/core/manager.h | 8 +- src/display/window_peer_info.cc | 2 + src/main.cc | 38 ++---- src/option_handler_rules.cc | 56 ++++++++ src/option_handler_rules.h | 5 + src/ui/download_list.cc | 11 +- src/utils/directory.cc | 4 +- src/utils/directory.h | 2 +- 20 files changed, 519 insertions(+), 58 deletions(-) create mode 100644 rak/regex.h diff --git a/Makefile.am b/Makefile.am index 3a1be6ad..720bd272 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST= \ rak/functional_fun.h \ rak/priority_queue.h \ rak/priority_queue_default.h \ + rak/regex.h rak/string_manip.h \ rak/timer.h \ rak/unordered_vector.h \ diff --git a/configure.ac b/configure.ac index 128cc8bf..2719fc7e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(rtorrent, 0.4.1, jaris@ifi.uio.no) +AC_INIT(rtorrent, 0.4.2, jaris@ifi.uio.no) AM_INIT_AUTOMAKE AM_CONFIG_HEADER(config.h) diff --git a/rak/priority_queue_default.h b/rak/priority_queue_default.h index 36dd4e2e..eceede60 100644 --- a/rak/priority_queue_default.h +++ b/rak/priority_queue_default.h @@ -85,6 +85,17 @@ struct priority_compare { typedef std::equal_to priority_equal; typedef priority_queue priority_queue_default; +inline void +priority_queue_perform(priority_queue_default* queue, timer t) { + while (!queue->empty() && queue->top()->time() <= t) { + priority_item* v = queue->top(); + queue->pop(); + + v->clear_time(); + v->call(); + } +} + inline void priority_queue_insert(priority_queue_default* queue, priority_item* item, timer t) { if (t == timer()) diff --git a/rak/regex.h b/rak/regex.h new file mode 100644 index 00000000..23e2197b --- /dev/null +++ b/rak/regex.h @@ -0,0 +1,220 @@ +// rak - Rakshasa's toolbox +// Copyright (C) 2005, 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 + +// This is a hacked up whole string pattern matching. Replace with +// TR1's regex when that becomes widely available. It is intended for +// small strings. + +#ifndef RAK_REGEX_H +#define RAK_REGEX_H + +#include + +#include +#include +#include + +namespace rak { + +class regex : public std::unary_function { +public: + regex() {} + regex(const std::string& p) : m_pattern(p) {} + + const std::string& pattern() const { return m_pattern; } + + bool operator () (const std::string& p) const; + +private: + std::string m_pattern; +}; + +// This isn't optimized, or very clean. A simple hack that should work. +bool +regex::operator () (const std::string& text) const { + if (m_pattern.empty() || + text.empty() || + (m_pattern[0] != '*' && m_pattern[0] != text[0])) + return false; + + // Replace with unordered_vector? + std::list paths; + paths.push_front(0); + + for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) { + + for (std::list::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) { + + unsigned int next = *itrPaths + 1; + + if (m_pattern[*itrPaths] != '*') + itrPaths = paths.erase(itrPaths); + else + itrPaths++; + + // When we reach the end of 'm_pattern', we don't have a whole + // match of 'text'. + if (next == m_pattern.size()) + continue; + + // Push to the back so that '*' will match zero length strings. + if (m_pattern[next] == '*') + paths.push_back(next); + + if (m_pattern[next] == *itrText) + paths.push_front(next); + } + + if (paths.empty()) + return false; + } + + return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end(); +} + +} + +#endif +// rak - Rakshasa's toolbox +// Copyright (C) 2005, 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 + +// This is a hacked up whole string pattern matching. Replace with +// TR1's regex when that becomes widely available. It is intended for +// small strings. + +#ifndef RAK_REGEX_H +#define RAK_REGEX_H + +#include + +#include +#include +#include + +namespace rak { + +class regex : public std::unary_function { +public: + regex() {} + regex(const std::string& p) : m_pattern(p) {} + + const std::string& pattern() const { return m_pattern; } + + bool operator () (const std::string& p) const; + +private: + std::string m_pattern; +}; + +// This isn't optimized, or very clean. A simple hack that should work. +bool +regex::operator () (const std::string& text) const { + if (m_pattern.empty() || + text.empty() || + (m_pattern[0] != '*' && m_pattern[0] != text[0])) + return false; + + // Replace with unordered_vector? + std::list paths; + paths.push_front(0); + + for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) { + + for (std::list::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) { + + unsigned int next = *itrPaths + 1; + + if (m_pattern[*itrPaths] != '*') + itrPaths = paths.erase(itrPaths); + else + itrPaths++; + + // When we reach the end of 'm_pattern', we don't have a whole + // match of 'text'. + if (next == m_pattern.size()) + continue; + + // Push to the back so that '*' will match zero length strings. + if (m_pattern[next] == '*') + paths.push_back(next); + + if (m_pattern[next] == *itrText) + paths.push_front(next); + } + + if (paths.empty()) + return false; + } + + return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end(); +} + +} + +#endif diff --git a/rak/string_manip.h b/rak/string_manip.h index fcd98844..a68bec61 100644 --- a/rak/string_manip.h +++ b/rak/string_manip.h @@ -37,6 +37,7 @@ #ifndef RAK_STRING_MANIP_H #define RAK_STRING_MANIP_H +#include #include namespace rak { @@ -74,6 +75,58 @@ Sequence trim(const Sequence& seq) { return trim_begin(trim_end(seq)); } +// Consider rewritting such that m_seq is replaced by first/last. +template +class split_iterator_t { +public: + typedef typename Sequence::const_iterator const_iterator; + typedef typename Sequence::value_type value_type; + + split_iterator_t() {} + + split_iterator_t(const Sequence& seq, value_type delim) : + m_seq(&seq), + m_delim(delim), + m_pos(seq.begin()), + m_next(std::find(seq.begin(), seq.end(), delim)) { + } + + Sequence operator * () { return Sequence(m_pos, m_next); } + + split_iterator_t& operator ++ () { + m_pos = m_next; + + if (m_pos == m_seq->end()) + return *this; + + m_pos++; + m_next = std::find(m_pos, m_seq->end(), m_delim); + + return *this; + } + + bool operator == (const split_iterator_t& itr) const { return m_pos == m_seq->end(); } + bool operator != (const split_iterator_t& itr) const { return m_pos != m_seq->end(); } + +private: + const Sequence* m_seq; + value_type m_delim; + const_iterator m_pos; + const_iterator m_next; +}; + +template +inline split_iterator_t +split_iterator(const Sequence& seq, typename Sequence::value_type delim) { + return split_iterator_t(seq, delim); +} + +template +inline split_iterator_t +split_iterator(const Sequence& seq) { + return split_iterator_t(); +} + } #endif diff --git a/scripts/checks.m4 b/scripts/checks.m4 index ec7a20b8..b2f48ebe 100644 --- a/scripts/checks.m4 +++ b/scripts/checks.m4 @@ -174,9 +174,9 @@ AC_DEFUN([TORRENT_CHECK_POSIX_FALLOCATE], [ AC_DEFUN([TORRENT_WITH_POSIX_FALLOCATE], [ AC_ARG_WITH(posix-fallocate, - [ --with-fallocate Check for posix_fallocate], + [ --with-posix-fallocate Check for and use posix_fallocate to allocate files.], [ - if test "$withval" = "no"; then + if test "$withval" = "yes"; then TORRENT_CHECK_POSIX_FALLOCATE fi ]) diff --git a/src/core/download.h b/src/core/download.h index b21683b4..679126fc 100644 --- a/src/core/download.h +++ b/src/core/download.h @@ -73,6 +73,9 @@ public: void set_connection_leech(const std::string& name) { m_connectionLeech = string_to_connection_type(name); } void set_connection_seed(const std::string& name) { m_connectionSeed = string_to_connection_type(name); } + const std::string& tied_to_file() const { return m_tiedToFile; } + void set_tied_to_file(const std::string& str) { m_tiedToFile = str; } + void enable_udp_trackers(bool state); // Helper functions for calling functions in torrent::Download @@ -104,6 +107,8 @@ private: ConnType m_connectionLeech; ConnType m_connectionSeed; + std::string m_tiedToFile; + sigc::connection m_connTrackerSucceded; sigc::connection m_connTrackerFailed; sigc::connection m_connStorageError; diff --git a/src/core/download_factory.cc b/src/core/download_factory.cc index a7f66dbe..17276d5d 100644 --- a/src/core/download_factory.cc +++ b/src/core/download_factory.cc @@ -40,6 +40,7 @@ #include #include #include +#include #include "curl_get.h" #include "http_queue.h" @@ -58,7 +59,9 @@ DownloadFactory::DownloadFactory(const std::string& uri, Manager* m) : m_uri(uri), m_session(false), - m_start(false) { + m_start(false), + m_printLog(true), + m_tiedToFile(false) { m_taskLoad.set_slot(rak::mem_fn(this, &DownloadFactory::receive_load)); m_taskCommit.set_slot(rak::mem_fn(this, &DownloadFactory::receive_commit)); @@ -85,7 +88,7 @@ DownloadFactory::commit() { void DownloadFactory::receive_load() { if (m_stream) - throw std::logic_error("DownloadFactory::load() called on an object with m_stream != NULL"); + throw torrent::client_error("DownloadFactory::load() called on an object with m_stream != NULL"); if (std::strncmp(m_uri.c_str(), "http://", 7) == 0) { // Http handling here. @@ -95,6 +98,8 @@ DownloadFactory::receive_load() { (*itr)->signal_done().slots().push_front(sigc::mem_fun(*this, &DownloadFactory::receive_loaded)); (*itr)->signal_failed().slots().push_front(sigc::mem_fun(*this, &DownloadFactory::receive_failed)); + m_tiedToFile = false; + } else { m_stream = new std::fstream(m_uri.c_str(), std::ios::in); @@ -124,9 +129,9 @@ DownloadFactory::receive_commit() { void DownloadFactory::receive_success() { if (m_stream == NULL) - throw std::logic_error("DownloadFactory::receive_success() called on an object with m_stream == NULL"); + throw torrent::client_error("DownloadFactory::receive_success() called on an object with m_stream == NULL"); - Manager::DListItr itr = m_manager->insert(m_stream); + Manager::DListItr itr = m_manager->insert(m_stream, m_printLog); if (itr == m_manager->get_download_list().end()) { // core::Manager should already have added the error message to @@ -135,15 +140,28 @@ DownloadFactory::receive_success() { return; } + torrent::Bencode& bencode = (*itr)->get_bencode(); + if (m_session) { - torrent::Bencode& bencode = (*itr)->get_bencode(); - + // Hmm... this safe? if (bencode.get_key("rtorrent").get_key("state").as_string() == "started") - m_manager->start(*itr); + m_manager->start(*itr, m_printLog); + + if (bencode.get_key("rtorrent").has_key("tied") && + bencode.get_key("rtorrent").get_key("tied").is_string()) + (*itr)->set_tied_to_file(bencode.get_key("rtorrent").get_key("tied").as_string()); } else { + // Remove the settings if this isn't a session torrent. + //bencode.erase_key("rtorrent"); + + if (m_tiedToFile) { + (*itr)->set_tied_to_file(m_uri); + bencode.get_key("rtorrent").insert_key("tied", m_uri); + } + if (m_start) - m_manager->start(*itr); + m_manager->start(*itr, m_printLog); m_manager->get_download_store().save(*itr); } @@ -154,11 +172,13 @@ DownloadFactory::receive_success() { void DownloadFactory::receive_failed(const std::string& msg) { if (m_stream == NULL) - throw std::logic_error("DownloadFactory::receive_success() called on an object with m_stream == NULL"); + throw torrent::client_error("DownloadFactory::receive_success() called on an object with m_stream == NULL"); // Add message to log. - m_manager->get_log_important().push_front(msg + ": \"" + m_uri + "\""); - m_manager->get_log_complete().push_front(msg + ": \"" + m_uri + "\""); + if (m_printLog) { + m_manager->get_log_important().push_front(msg + ": \"" + m_uri + "\""); + m_manager->get_log_complete().push_front(msg + ": \"" + m_uri + "\""); + } m_slotFinished(); } diff --git a/src/core/download_factory.h b/src/core/download_factory.h index 70a37a47..26d6738c 100644 --- a/src/core/download_factory.h +++ b/src/core/download_factory.h @@ -34,6 +34,10 @@ // Skomakerveien 33 // 3185 Skoppum, NORWAY +// The DownloadFactory class assures that loading torrents can be done +// anywhere in the code by queueing the task. The user may change +// settings while, or even after, the torrent is loading. + #ifndef RTORRENT_CORE_DOWNLOAD_FACTORY_H #define RTORRENT_CORE_DOWNLOAD_FACTORY_H @@ -67,6 +71,12 @@ public: bool get_start() const { return m_start; } void set_start(bool v) { m_start = v; } + bool print_log() const { return m_printLog; } + void set_print_log(bool v) { m_printLog = v; } + + bool tied_to_file() const { return m_tiedToFile; } + void set_tied_to_file(bool v) { m_tiedToFile = v; } + void slot_finished(Slot s) { m_slotFinished = s; } private: @@ -85,6 +95,8 @@ private: std::string m_uri; bool m_session; bool m_start; + bool m_printLog; + bool m_tiedToFile; Slot m_slotFinished; rak::priority_item m_taskLoad; diff --git a/src/core/log.cc b/src/core/log.cc index fe7d10ee..868fc19e 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -46,6 +46,9 @@ namespace core { void Log::push_front(const std::string& msg) { + if (!m_enabled) + return; + Base::push_front(Type(cachedTime, msg)); if (size() > 50) diff --git a/src/core/log.h b/src/core/log.h index 63442dc0..ceee4295 100644 --- a/src/core/log.h +++ b/src/core/log.h @@ -64,6 +64,13 @@ public: using Base::empty; using Base::size; + Log() : m_enabled(true) {} + + bool is_enabled() const { return m_enabled; } + + void enable() { m_enabled = true; } + void disable() { m_enabled = false; } + void push_front(const std::string& msg); iterator find_older(rak::timer t); @@ -71,6 +78,8 @@ public: Signal& signal_update() { return m_signalUpdate; } private: + bool m_enabled; + Signal m_signalUpdate; }; diff --git a/src/core/manager.cc b/src/core/manager.cc index d9bbc3ec..e88ce214 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -50,6 +52,7 @@ #include "curl_get.h" #include "download.h" +#include "download_factory.h" #include "manager.h" #include "poll_manager_epoll.h" #include "poll_manager_select.h" @@ -149,13 +152,15 @@ Manager::shutdown(bool force) { } Manager::DListItr -Manager::insert(std::istream* s) { +Manager::insert(std::istream* s, bool printLog) { try { return m_downloadList.insert(s); } catch (torrent::local_error& e) { - m_logImportant.push_front(e.what()); - m_logComplete.push_front(e.what()); + if (printLog) { + m_logImportant.push_front(e.what()); + m_logComplete.push_front(e.what()); + } return m_downloadList.end(); } @@ -173,7 +178,7 @@ Manager::erase(DListItr itr) { } void -Manager::start(Download* d) { +Manager::start(Download* d, bool printLog) { try { d->get_bencode().get_key("rtorrent").get_key("state") = "started"; @@ -190,8 +195,10 @@ Manager::start(Download* d) { m_hashQueue.insert(d, sigc::bind(sigc::mem_fun(m_downloadList, &DownloadList::start), d)); } catch (torrent::local_error& e) { - m_logImportant.push_front(e.what()); - m_logComplete.push_front(e.what()); + if (printLog) { + m_logImportant.push_front(e.what()); + m_logComplete.push_front(e.what()); + } } } @@ -302,4 +309,74 @@ Manager::receive_download_done_hash_checked(Download* d) { d->get_download().tracker_send_completed(); } +void +Manager::try_create_download(const std::string& uri, bool start, bool printLog, bool tied) { + // Adding download. + DownloadFactory* f = new DownloadFactory(uri, this); + + f->set_start(start); + f->set_print_log(printLog); + f->set_tied_to_file(tied); + f->slot_finished(sigc::bind(sigc::ptr_fun(&rak::call_delete_func), f)); + f->load(); + f->commit(); +} + +// Move this somewhere better. +void +path_expand(std::vector* paths, const std::string& pattern) { + std::vector currentCache; + std::vector nextCache; + + rak::split_iterator_t first = rak::split_iterator(pattern, '/'); + rak::split_iterator_t last = rak::split_iterator(pattern); + + if (first == last) + return; + + // Check for initial '/' that indicates the root. + if ((*first).empty()) { + currentCache.push_back(utils::Directory("/")); + ++first; + } else { + currentCache.push_back(utils::Directory("./")); + } + + // Might be an idea to use depth-first search instead. + + for (; first != last; ++first) { + rak::regex r(*first); + + if (r.pattern().empty()) + continue; + + for (std::vector::iterator itr = currentCache.begin(); itr != currentCache.end(); ++itr) { + itr->update(false); + itr->erase(std::remove_if(itr->begin(), itr->end(), std::not1(r)), itr->end()); + + std::transform(itr->begin(), itr->end(), std::back_inserter(nextCache), std::bind1st(std::plus(), itr->get_path() + "/")); + } + + currentCache.clear(); + currentCache.swap(nextCache); + } + + std::transform(currentCache.begin(), currentCache.end(), std::back_inserter(*paths), std::mem_fun_ref(&utils::Directory::get_path)); +} + +void +Manager::try_create_download_expand(const std::string& uri, bool start, bool printLog, bool tied) { + std::vector paths; + paths.reserve(32); + + path_expand(&paths, uri); + + if (!paths.empty()) + for (std::vector::iterator itr = paths.begin(); itr != paths.end(); ++itr) + try_create_download(*itr, start, printLog, tied); + + else + try_create_download(uri, start, printLog, tied); +} + } diff --git a/src/core/manager.h b/src/core/manager.h index eb97de89..92de5609 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -83,16 +83,20 @@ public: void shutdown(bool force); - DListItr insert(std::istream* s); + DListItr insert(std::istream* s, bool printLog = true); DListItr erase(DListItr itr); - void start(Download* d); + void start(Download* d, bool printLog = true); void stop(Download* d); void check_hash(Download* d); void push_log(const std::string& msg) { m_logImportant.push_front(msg); m_logComplete.push_front(msg); } + // Temporary, find a better place for this. + void try_create_download(const std::string& uri, bool start, bool printLog = true, bool tied = false); + void try_create_download_expand(const std::string& uri, bool start, bool printLog = true, bool tied = false); + private: void create_http(const std::string& uri); void create_final(std::istream* s); diff --git a/src/display/window_peer_info.cc b/src/display/window_peer_info.cc index f33f8601..85e02181 100644 --- a/src/display/window_peer_info.cc +++ b/src/display/window_peer_info.cc @@ -83,6 +83,8 @@ WindowPeerInfo::redraw() { core::Download::connection_type_to_string(m_download->get_connection_current()), core::Download::connection_type_to_string(m_download->get_connection_leech()), core::Download::connection_type_to_string(m_download->get_connection_seed())); + m_canvas->print(0, y++, "Tied to file: %s", + m_download->tied_to_file().c_str()); y++; diff --git a/src/main.cc b/src/main.cc index 8a3c3a47..36eb550f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -143,6 +143,11 @@ initialize_option_handler(Control* c, OptionHandler* optionHandler) { optionHandler->insert("connection_leech", new OptionHandlerString(c, &apply_connection_leech)); optionHandler->insert("connection_seed", new OptionHandlerString(c, &apply_connection_seed)); + optionHandler->insert("load", new OptionHandlerString(c, &apply_load)); + optionHandler->insert("load_run", new OptionHandlerString(c, &apply_load_run)); + optionHandler->insert("stop_untied", new OptionHandlerString(c, &apply_stop_untied)); + optionHandler->insert("remove_untied", new OptionHandlerString(c, &apply_remove_untied)); + 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)); @@ -220,15 +225,15 @@ main(int argc, char** argv) { // Just to make sure we did all the stuff on the queue before // loading any torrents. - while (!taskScheduler.empty() && taskScheduler.top()->time() <= cachedTime) { - rak::priority_item* v = taskScheduler.top(); - taskScheduler.pop(); - - v->clear_time(); - v->call(); - } + // + // Remove this? + //rak::priority_queue_perform(&taskScheduler, cachedTime); + // Load session torrents and perform scheduled tasks to ensure + // session torrents are loaded before arg torrents. load_session_torrents(&control); + rak::priority_queue_perform(&taskScheduler, cachedTime); + load_arg_torrents(&control, argv + firstArg, argv + argc); control.display()->adjust_layout(); @@ -237,22 +242,7 @@ main(int argc, char** argv) { countTicks++; cachedTime = rak::timer::current(); - -// std::list workQueue; - -// std::copy(rak::queue_popper(taskScheduler, rak::bind2nd(std::mem_fun(&rak::priority_item::compare), cachedTime)), -// rak::queue_popper(taskScheduler, rak::bind2nd(std::mem_fun(&rak::priority_item::compare), rak::timer())), -// std::back_inserter(workQueue)); -// std::for_each(workQueue.begin(), workQueue.end(), std::mem_fun(&rak::priority_item::clear_time)); -// std::for_each(workQueue.begin(), workQueue.end(), std::mem_fun(&rak::priority_item::call)); - - while (!taskScheduler.empty() && taskScheduler.top()->time() <= cachedTime) { - rak::priority_item* v = taskScheduler.top(); - taskScheduler.pop(); - - v->clear_time(); - v->call(); - } + rak::priority_queue_perform(&taskScheduler, cachedTime); // This needs to be called every second or so. Currently done by // the throttle task in libtorrent. @@ -268,7 +258,7 @@ 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: " << e.what() << std::endl; return -1; } catch (std::exception& e) { diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index a0e41367..0e4d837a 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -37,17 +37,21 @@ #include "config.h" #include +#include #include #include #include #include +#include #include +#include #include #include #include "core/manager.h" #include "ui/root.h" #include "utils/directory.h" +#include "utils/file_stat.h" #include "control.h" #include "option_handler_rules.h" @@ -218,6 +222,58 @@ apply_http_proxy(Control* m, const std::string& arg) { m->core()->get_poll_manager()->get_http_stack()->set_http_proxy(arg); } +void +apply_load(Control* m, const std::string& arg) { + m->core()->try_create_download_expand(arg, false, false, true); +} + +void +apply_load_run(Control* m, const std::string& arg) { + m->core()->try_create_download_expand(arg, true, false, true); +} + +void +apply_stop_untied(Control* m, const std::string& arg) { + core::Manager::DListItr itr = m->core()->get_download_list().begin(); + + while ((itr = std::find_if(itr, m->core()->get_download_list().end(), + rak::on(std::mem_fun(&core::Download::tied_to_file), std::not1(std::mem_fun_ref(&std::string::empty))))) + != m->core()->get_download_list().end()) { + utils::FileStat fs; + + if (fs.update((*itr)->tied_to_file().c_str()) != 0) { + (*itr)->set_tied_to_file(std::string()); + (*itr)->get_bencode().get_key("rtorrent").erase_key("tied"); + + m->core()->stop(*itr); + } + + ++itr; + } +} + +void +apply_remove_untied(Control* m, const std::string& arg) { + core::Manager::DListItr itr = m->core()->get_download_list().begin(); + + while ((itr = std::find_if(itr, m->core()->get_download_list().end(), + rak::on(std::mem_fun(&core::Download::tied_to_file), std::not1(std::mem_fun_ref(&std::string::empty))))) + != m->core()->get_download_list().end()) { + utils::FileStat fs; + + if (fs.update((*itr)->tied_to_file().c_str()) != 0) { + (*itr)->set_tied_to_file(std::string()); + (*itr)->get_bencode().get_key("rtorrent").erase_key("tied"); + + m->core()->stop(*itr); + itr = m->core()->erase(itr); + + } else { + ++itr; + } + } +} + void apply_session_directory(Control* m, const std::string& arg) { m->core()->get_download_store().use(arg); diff --git a/src/option_handler_rules.h b/src/option_handler_rules.h index 46c3a887..41511ccc 100644 --- a/src/option_handler_rules.h +++ b/src/option_handler_rules.h @@ -77,6 +77,11 @@ void apply_check_hash(Control* m, const std::string& arg); void apply_http_proxy(Control* m, const std::string& arg); +void apply_load(Control* m, const std::string& arg); +void apply_load_run(Control* m, const std::string& arg); +void apply_stop_untied(Control* m, const std::string& arg); +void apply_remove_untied(Control* m, const std::string& arg); + void apply_session_directory(Control* m, const std::string& arg); void apply_encoding_list(Control* m, const std::string& arg); diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 16a5e99e..86dbbc10 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -38,12 +38,12 @@ #include #include +#include #include #include #include #include "core/download.h" -#include "core/download_factory.h" #include "core/manager.h" #include "input/bindings.h" @@ -265,16 +265,9 @@ DownloadList::receive_exit_input(bool useDefault) { m_control->ui()->window_statusbar()->set_active(true); m_windowTextInput->set_active(false); - m_control->input()->set_text_input(); - // Adding download. - core::DownloadFactory* f = new core::DownloadFactory(m_windowTextInput->get_input()->str(), m_control->core()); - - f->set_start(useDefault); - f->slot_finished(sigc::bind(sigc::ptr_fun(&rak::call_delete_func), f)); - f->load(); - f->commit(); + m_control->core()->try_create_download_expand(m_windowTextInput->get_input()->str(), useDefault); // Clean up. m_windowTextInput->get_input()->clear(); diff --git a/src/utils/directory.cc b/src/utils/directory.cc index 638a52f9..39941578 100644 --- a/src/utils/directory.cc +++ b/src/utils/directory.cc @@ -57,7 +57,7 @@ Directory::is_valid() const { } bool -Directory::update() { +Directory::update(bool hideDot) { if (m_path.empty()) throw std::logic_error("Directory::update() tried to open an empty path"); @@ -71,7 +71,7 @@ Directory::update() { while ((ent = readdir(d)) != NULL) { std::string de(ent->d_name); - if (!de.empty() && de[0] != '.') + if (!de.empty() && (!hideDot || de[0] != '.')) Base::push_back(ent->d_name); } diff --git a/src/utils/directory.h b/src/utils/directory.h index ce76b8cd..1881a407 100644 --- a/src/utils/directory.h +++ b/src/utils/directory.h @@ -66,7 +66,7 @@ public: bool is_valid() const; - bool update(); + bool update(bool hideDot = true); const std::string& get_path() { return m_path; }