diff --git a/TODO b/TODO index 9869e133..452c9b04 100644 --- a/TODO +++ b/TODO @@ -32,4 +32,6 @@ Bug: Using ^s on a bad torrent does not catch the exception. Show torrent creation date? -Recheck hash key. \ No newline at end of file +Recheck hash key. + +BUG: Doesn't show the last file in the file list. diff --git a/configure.ac b/configure.ac index b04c9afd..d38bd745 100644 --- a/configure.ac +++ b/configure.ac @@ -4,35 +4,16 @@ AM_INIT_AUTOMAKE AM_CONFIG_HEADER(config.h) sinclude(scripts/checks.m4) +sinclude(scripts/common.m4) -CXXFLAGS="-O3" - -AC_ARG_ENABLE(debug, - [ --enable-debug enable debug information [default=yes]], - [ - if test "$enableval" = "yes"; then - CXXFLAGS="$CXXFLAGS -Wall -g -DDEBUG" - else - CXXFLAGS="$CXXFLAGS -Wall -DNDEBUG" - fi - ],[ - CXXFLAGS="$CXXFLAGS -Wall -g -DDEBUG" - ] -) +TORRENT_CHECK_CXXFLAGS() +TORRENT_ENABLE_DEBUG() +TORRENT_ENABLE_WERROR() AC_PROG_CXX AC_PROG_RANLIB AC_SYS_LARGEFILE -AC_ARG_ENABLE(werror, - [ --enable-werror enable the -Werror flag [default=no]], - [ - if test "$enableval" = "yes"; then - CXXFLAGS="$CXXFLAGS -Werror" - fi - ] -) - AC_SEARCH_LIBS(wbkgdset, ncurses curses,,echo "*** The ncurses library is required!";exit 1) TORRENT_CHECK_EXECINFO() diff --git a/scripts/checks.m4 b/scripts/checks.m4 index 2630611c..982858bf 100644 --- a/scripts/checks.m4 +++ b/scripts/checks.m4 @@ -18,8 +18,8 @@ AC_DEFUN([TORRENT_CHECK_CURL], [ my_cv_curl_vers="$ver" AC_MSG_RESULT([$my_cv_curl_vers]) - CURL_CFLAGS=`curl-config --cflags` - CURL_LIBS=`curl-config --libs` + CURL_CFLAGS="`curl-config --cflags`" + CURL_LIBS="`curl-config --libs`" else AC_MSG_RESULT(FAILED) AC_MSG_ERROR([$ver is too old. Need version $check or higher.]) @@ -60,74 +60,3 @@ AC_DEFUN([TORRENT_CHECK_OPENSSL], [ AC_MSG_ERROR(Could not find openssl's crypto library, try --with-openssl=PATH)) ]) ]) - - -AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [ - - AC_LANG_PUSH(C++) - AC_MSG_CHECKING(signedness of mincore parameter) - - AC_COMPILE_IFELSE( - [[#include - #include - void f() { mincore((void*)0, 0, (unsigned char*)0); } - ]], - [ - AC_DEFINE(USE_MINCORE_UNSIGNED, 1, use unsigned char* in mincore) - AC_MSG_RESULT(unsigned) - ], - [ - AC_COMPILE_IFELSE( - [[#include - #include - void f() { mincore((void*)0, 0, (char*)0); } - ]], - [ - AC_DEFINE(USE_MINCORE_UNSIGNED, 0, use char* in mincore) - AC_MSG_RESULT(signed) - ], - [ - AC_MSG_ERROR([mincore signedness test failed]) - ]) - ]) - - AC_LANG_POP(C++) -]) - - -AC_DEFUN([TORRENT_OTFD], [ - - AC_LANG_PUSH(C++) - AC_MSG_CHECKING(for proper overloaded template function disambiguation) - - AC_COMPILE_IFELSE( - [[template void f(T&) {} - template void f(T*) {} - int main() { int *i = 0; f(*i); f(i); } - ]], - [ - AC_MSG_RESULT(yes) - ], [ - AC_MSG_RESULT(no) - AC_MSG_ERROR([your compiler does not properly handle overloaded template function disambiguation]) - ]) - - AC_LANG_POP(C++) -]) - - -AC_DEFUN([TORRENT_CHECK_EXECINFO], [ - - AC_MSG_CHECKING(for execinfo.h) - - AC_COMPILE_IFELSE( - [[#include - int main() { backtrace((void**)0, 0); backtrace_symbols((char**)0, 0); return 0;} - ]], - [ - AC_MSG_RESULT(yes) - AC_DEFINE(USE_EXECINFO, 1, Use execinfo.h) - ], [ - AC_MSG_RESULT(no) - ]) -]) diff --git a/scripts/common.m4 b/scripts/common.m4 new file mode 100644 index 00000000..2495b2e7 --- /dev/null +++ b/scripts/common.m4 @@ -0,0 +1,109 @@ +AC_DEFUN([TORRENT_CHECK_CXXFLAGS], [ + + AC_MSG_CHECKING([for user-defined CXXFLAGS]) + + if test !$CXXFLAGS; then + CXXFLAGS="-O3 -Wall" + AC_MSG_RESULT([default "$CXXFLAGS"]) + else + AC_MSG_RESULT([user-defined "$CXXFLAGS"]) + fi +]) + + +AC_DEFUN([TORRENT_ENABLE_DEBUG], [ + + AC_ARG_ENABLE(debug, + [ --enable-debug enable debug information [default=yes]], + [ + if test "$enableval" = "yes"; then + CXXFLAGS="$CXXFLAGS -g -DDEBUG" + else + CXXFLAGS="$CXXFLAGS -DNDEBUG" + fi + ],[ + CXXFLAGS="$CXXFLAGS -g -DDEBUG" + ]) +]) + + +AC_DEFUN([TORRENT_ENABLE_WERROR], [ + + AC_ARG_ENABLE(werror, + [ --enable-werror enable the -Werror flag [default=no]], + [ + if test "$enableval" = "yes"; then + CXXFLAGS="$CXXFLAGS -Werror" + fi + ]) +]) + + +AC_DEFUN([TORRENT_OTFD], [ + + AC_LANG_PUSH(C++) + AC_MSG_CHECKING(for proper overloaded template function disambiguation) + + AC_COMPILE_IFELSE( + [[template void f(T&) {} + template void f(T*) {} + int main() { int *i = 0; f(*i); f(i); } + ]], + [ + AC_MSG_RESULT(yes) + ], [ + AC_MSG_RESULT(no) + AC_MSG_ERROR([your compiler does not properly handle overloaded template function disambiguation]) + ]) + + AC_LANG_POP(C++) +]) + + +AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [ + + AC_LANG_PUSH(C++) + AC_MSG_CHECKING(signedness of mincore parameter) + + AC_COMPILE_IFELSE( + [[#include + #include + void f() { mincore((void*)0, 0, (unsigned char*)0); } + ]], + [ + AC_DEFINE(USE_MINCORE_UNSIGNED, 1, use unsigned char* in mincore) + AC_MSG_RESULT(unsigned) + ], + [ + AC_COMPILE_IFELSE( + [[#include + #include + void f() { mincore((void*)0, 0, (char*)0); } + ]], + [ + AC_DEFINE(USE_MINCORE_UNSIGNED, 0, use char* in mincore) + AC_MSG_RESULT(signed) + ], + [ + AC_MSG_ERROR([mincore signedness test failed]) + ]) + ]) + + AC_LANG_POP(C++) +]) + +AC_DEFUN([TORRENT_CHECK_EXECINFO], [ + + AC_MSG_CHECKING(for execinfo.h) + + AC_COMPILE_IFELSE( + [[#include + int main() { backtrace((void**)0, 0); backtrace_symbols((char**)0, 0); return 0;} + ]], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(USE_EXECINFO, 1, Use execinfo.h) + ], [ + AC_MSG_RESULT(no) + ]) +]) diff --git a/src/core/hash_queue.cc b/src/core/hash_queue.cc index fc89d5f6..32689d16 100644 --- a/src/core/hash_queue.cc +++ b/src/core/hash_queue.cc @@ -34,12 +34,10 @@ namespace core { void HashQueue::insert(Download* d, Slot s) { - if (d->get_download().is_hash_checking()) + if (d->get_download().is_hash_checking() || + find(d) != end()) return; - if (std::find_if(begin(), end(), rak::equal(d, std::mem_fun(&HashQueueNode::get_download))) != end()) - throw std::logic_error("core::HashQueue::insert(...) received a Download that is already queued"); - if (d->get_download().is_hash_checked()) { s(); return; @@ -47,21 +45,23 @@ HashQueue::insert(Download* d, Slot s) { iterator itr = Base::insert(end(), new HashQueueNode(d, s)); - (*itr)->set_connection(d->get_download().signal_hash_done(sigc::bind(sigc::mem_fun(*this, &HashQueue::receive_hash_done), itr))); + (*itr)->set_connection(d->get_download().signal_hash_done(sigc::bind(sigc::mem_fun(*this, &HashQueue::receive_hash_done), + (*itr)->get_download()))); fill_queue(); } void HashQueue::remove(Download* d) { - iterator itr = std::find_if(begin(), end(), rak::equal(d, std::mem_fun(&HashQueueNode::get_download))); + iterator itr = find(d); if (itr == end()) return; - if ((*itr)->get_download()->get_download().is_hash_checking()) - // What do we do if we're already checking? - ; + // We don't do anything if we're already checking, just disconnect. +// if ((*itr)->get_download()->get_download().is_hash_checking()) { +// // What do we do if we're already checking? +// } delete *itr; Base::erase(itr); @@ -69,13 +69,24 @@ HashQueue::remove(Download* d) { fill_queue(); } +HashQueue::iterator +HashQueue::find(Download* d) { + return std::find_if(begin(), end(), rak::equal(d, std::mem_fun(&HashQueueNode::get_download))); +} + void -HashQueue::receive_hash_done(Base::iterator itr) { +HashQueue::receive_hash_done(Download* d) { + iterator itr = find(d); + + if (itr == end()) + return; + Slot s = (*itr)->get_slot(); delete *itr; Base::erase(itr); + // Can we call this before the delete? s(); fill_queue(); } diff --git a/src/core/hash_queue.h b/src/core/hash_queue.h index 735b0bdb..7348f3c9 100644 --- a/src/core/hash_queue.h +++ b/src/core/hash_queue.h @@ -54,14 +54,16 @@ public: using Base::empty; using Base::size; - // Should it be safe to try inserting already present/checked downloads? void insert(Download* d, Slot s); - // It's safe to try to remove downloads not in the queue. + // It's safe to try to remove downloads not in the queue. The hash + // checking is not stopped if it has already started. void remove(Download* d); + iterator find(Download* d); + private: - void receive_hash_done(Base::iterator itr); + void receive_hash_done(Download* d); void fill_queue(); }; @@ -69,7 +71,9 @@ private: class HashQueueNode { public: HashQueueNode(Download* d, HashQueue::Slot s) : m_download(d), m_slot(s) {} - ~HashQueueNode() { m_connection.disconnect(); } + ~HashQueueNode() { disconnect(); } + + void disconnect() { m_connection.disconnect(); } Download* get_download() { return m_download; } HashQueue::Slot get_slot() { return m_slot; } diff --git a/src/core/manager.cc b/src/core/manager.cc index 66e9fabd..8176b798 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -80,6 +80,7 @@ Manager::erase(DownloadList::iterator itr) { if (!(*itr)->get_download().is_open()) throw std::logic_error("core::Manager::erase(...) called on an closed download"); + m_hashQueue.remove(*itr); m_downloadStore.remove(*itr); return m_downloadList.erase(itr); @@ -87,35 +88,36 @@ Manager::erase(DownloadList::iterator itr) { void Manager::start(Download* d) { - if (d->get_download().is_active()) - return; + try { + if (d->get_download().is_active()) + return; - if (!d->get_download().is_open()) - d->open(); + if (!d->get_download().is_open()) + d->open(); - if (d->get_download().is_hash_checked()) - d->start(); - else - // This can cause infinit loops. - m_hashQueue.insert(d, sigc::mem_fun(d, &Download::start)); + if (d->get_download().is_hash_checked()) + d->start(); + else + // This can cause infinit loops. + m_hashQueue.insert(d, sigc::mem_fun(d, &Download::start)); + + } catch (torrent::local_error& e) { + m_logImportant.push_front(e.what()); + m_logComplete.push_front(e.what()); + } } void Manager::stop(Download* d) { - m_hashQueue.remove(d); - - d->stop(); - - if (d->get_download().is_hash_checked()) - d->get_download().hash_save(); - - m_downloadStore.save(d); -} - -void -Manager::start_safe(Download* d) { try { - start(d); + m_hashQueue.remove(d); + d->stop(); + + if (d->get_download().is_hash_checked()) + d->get_download().hash_save(); + + m_downloadStore.save(d); + } catch (torrent::local_error& e) { m_logImportant.push_front(e.what()); m_logComplete.push_front(e.what()); @@ -124,6 +126,7 @@ Manager::start_safe(Download* d) { void Manager::set_dns(const std::string& dns) { + // TODO: Switch with the inet version of this thingie. unsigned int a, b, c, d; if (std::sscanf(dns.c_str(), "%i.%i.%i.%i", &a, &b, &c, &d) != 4 || @@ -136,18 +139,6 @@ Manager::set_dns(const std::string& dns) { m_dns = str.str(); } -void -Manager::receive_http_done(CurlGet* http) { - try { - create_final(http->get_stream()); - - } catch (torrent::local_error& e) { - // What to do? Keep in list for now. - m_logImportant.push_front(e.what()); - m_logComplete.push_front(e.what()); - } -} - void Manager::receive_http_failed(std::string msg) { m_logImportant.push_front("Http download error: \"" + msg + "\""); @@ -156,10 +147,29 @@ Manager::receive_http_failed(std::string msg) { void Manager::create_file(const std::string& uri) { + std::fstream f(uri.c_str(), std::ios::in); + + create_final(&f); +} + +void +Manager::create_http(const std::string& uri) { + core::HttpQueue::iterator itr = m_httpQueue.insert(uri); + + (*itr)->signal_done().slots().push_front(sigc::bind(sigc::mem_fun(*this, &core::Manager::create_final), + (*itr)->get_stream())); + (*itr)->signal_failed().slots().push_front(sigc::mem_fun(*this, &core::Manager::receive_http_failed)); +} + +void +Manager::create_final(std::istream* s) { try { - std::fstream f(uri.c_str(), std::ios::in); - - create_final(&f); + iterator itr = m_downloadList.insert(s); + + setup_download(*itr); + start(*itr); + + m_downloadStore.save(*itr); } catch (torrent::local_error& e) { // What to do? Keep in list for now. @@ -169,32 +179,20 @@ Manager::create_file(const std::string& uri) { } void -Manager::create_http(const std::string& uri) { - core::HttpQueue::iterator itr = m_httpQueue.insert(uri); - - (*itr)->signal_done().slots().push_front(sigc::bind(sigc::mem_fun(*this, &core::Manager::receive_http_done), *itr)); - (*itr)->signal_failed().slots().push_front(sigc::mem_fun(*this, &core::Manager::receive_http_failed)); -} - -Manager::iterator -Manager::create_final(std::istream* s) { - iterator itr = m_downloadList.insert(s); - - (*itr)->get_download().set_ip(m_dns); +Manager::setup_download(Download* d) { + d->get_download().set_ip(m_dns); if (!m_defaultRoot.empty()) - (*itr)->get_download().set_root_dir(m_defaultRoot + ((*itr)->get_download().get_entry_size() > 1 ? (*itr)->get_download().get_name() : "")); - - start(*itr); - m_downloadStore.save(*itr); + d->get_download().set_root_dir(m_defaultRoot + + (d->get_download().get_entry_size() > 1 ? + d->get_download().get_name() : + "")); if (m_debugTracker >= 0) - (*itr)->get_download().signal_tracker_dump(sigc::mem_fun(*this, &Manager::receive_debug_tracker)); + d->get_download().signal_tracker_dump(sigc::mem_fun(*this, &Manager::receive_debug_tracker)); // If we want to monitor network stuff. - (*itr)->get_download().signal_network_log(sigc::mem_fun(m_logComplete, &Log::push_front)); - - return itr; + d->get_download().signal_network_log(sigc::mem_fun(m_logComplete, &Log::push_front)); } void diff --git a/src/core/manager.h b/src/core/manager.h index 2253cf57..76426582 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -64,8 +64,6 @@ public: void start(Download* d); void stop(Download* d); - void start_safe(Download* d); - const std::string& get_dns() { return m_dns; } void set_dns(const std::string& dns); @@ -77,13 +75,14 @@ public: void debug_tracker() { m_debugTracker = 0; } private: - void receive_http_done(CurlGet* http); void receive_http_failed(std::string msg); void create_file(const std::string& uri); void create_http(const std::string& uri); - iterator create_final(std::istream* s); + void create_final(std::istream* s); + + void setup_download(Download* itr); void receive_debug_tracker(std::istream* s); diff --git a/src/display/window_file_list.cc b/src/display/window_file_list.cc index 23a53cdc..80c14dcf 100644 --- a/src/display/window_file_list.cc +++ b/src/display/window_file_list.cc @@ -42,6 +42,10 @@ WindowFileList::redraw() { m_nextDraw = utils::Timer::cache().round_seconds() + 10 * 1000000; m_canvas->erase(); + if (m_download->get_download().get_entry_size() == 0 || + m_canvas->get_height() < 2) + return; + int pos = 0; m_canvas->print( 2, pos, "File"); @@ -51,18 +55,13 @@ WindowFileList::redraw() { ++pos; - if (m_download->get_download().get_entry_size() == 0) - return; - if (*m_focus >= m_download->get_download().get_entry_size()) throw std::logic_error("WindowFileList::redraw() called on an object with a bad focus value"); - typedef std::pair Range; - Range range = rak::advance_bidirectional(0, *m_focus, m_download->get_download().get_entry_size(), - m_canvas->get_height()); + m_canvas->get_height() - pos); while (range.first != range.second) { torrent::Entry e = m_download->get_download().get_entry(range.first); diff --git a/src/display/window_file_list.h b/src/display/window_file_list.h index 74c34bc1..f1be91e8 100644 --- a/src/display/window_file_list.h +++ b/src/display/window_file_list.h @@ -39,6 +39,8 @@ namespace display { class WindowFileList : public Window { public: + typedef std::pair Range; + WindowFileList(core::Download* d, unsigned int* focus); virtual void redraw(); diff --git a/src/main.cc b/src/main.cc index b7742b7d..2dce8ebe 100644 --- a/src/main.cc +++ b/src/main.cc @@ -145,6 +145,7 @@ main(int argc, char** argv) { try { + SignalHandler::set_ignore(SIGPIPE); SignalHandler::set_handler(SIGINT, sigc::bind(sigc::mem_fun(uiRoot, &ui::Root::set_shutdown_received), true)); SignalHandler::set_handler(SIGSEGV, sigc::bind(sigc::ptr_fun(&do_panic), SIGSEGV)); SignalHandler::set_handler(SIGBUS, sigc::bind(sigc::ptr_fun(&do_panic), SIGBUS)); diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 7a429e4d..8c36eaeb 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -187,7 +187,7 @@ DownloadList::receive_start_download() { if (m_downloadList.get_focus() == m_downloadList.end()) return; - m_control->get_core().start_safe(*m_downloadList.get_focus()); + m_control->get_core().start(*m_downloadList.get_focus()); } void