From 47d36c0690d07aeced942c967c3f60d85a9b13a7 Mon Sep 17 00:00:00 2001 From: SineSwiper Date: Sun, 25 Mar 2012 16:54:02 -0400 Subject: [PATCH 01/11] Add new version of rtorrent_fast_resume.pl Add torrent_data script to peek at torrent contents --- doc/rtorrent_fast_resume.pl | 128 ++++++++++++++++++++++++++++++++++++ doc/torrent_data | 23 +++++++ 2 files changed, 151 insertions(+) create mode 100644 doc/rtorrent_fast_resume.pl create mode 100644 doc/torrent_data diff --git a/doc/rtorrent_fast_resume.pl b/doc/rtorrent_fast_resume.pl new file mode 100644 index 00000000..e01849f5 --- /dev/null +++ b/doc/rtorrent_fast_resume.pl @@ -0,0 +1,128 @@ +#!/usr/bin/perl + +# Perl script to add rTorrent fast resume data to torrent files. +# +# Usage: +# rtorrent_fast_resume.pl [base-directory] < plain.torrent > with_fast_resume.torrent +# -OR- +# rtorrent_fast_resume.pl [base-directory] plain.torrent [with_fast_resume.torrent] + +use strict; +use warnings; +use Convert::Bencode_XS qw(bencode bdecode); +use File::Spec; # core module +use POSIX; # core module + +$/ = undef; +$| = 1; + +# Process ARGV +my $d = $ARGV[0]; +if ($d and not -d $d) { + if (-f $d and -s $d and not $ARGV[2]) { # missing directory, but has file + $ARGV[2] = $ARGV[1]; + $ARGV[1] = $ARGV[0]; + $d = ''; + } + else { die "$d is not a directory\n"; } +} +$d ||= "."; +$d .= "/" unless $d =~ m#/$#; + +my ($in, $out, $msg); +my ($in_file, $out_file) = ('', ''); +if ($ARGV[1]) { + $in_file = $ARGV[1]; + open($in, ($ARGV[2] ? '<' : '+<'), $in_file) || die "Cannot open $in_file for input!\n"; + unless ($ARGV[2]) { + $out = $in; + $out_file = $in_file; + } +} +else { $in = *STDIN; } +if ($ARGV[2]) { + $out_file = $ARGV[2]; + open($out, '>', $out_file) || die "Cannot open $out_file for output!\n"; + $msg = *STDOUT; +} +elsif (!$ARGV[1]) { $out = *STDOUT; } +$msg //= *STDERR; + +print {$msg} "Total input torrent size: ".sprintf('%.2f', (-s $in_file) / 1024)." KB\n" if $in_file; +print {$msg} "Decoding ".($in_file || 'torrent from standard input')."..."; +my $t = bdecode(scalar <$in>); + +die "No info key.\n" unless ref $t eq "HASH" and exists $t->{info}; +my $psize = $t->{info}{"piece length"} or die "No piece length key.\n"; +print {$msg} "done\n\n"; + +my @files; +my $tsize = 0; +if (exists $t->{info}{files}) { + print {$msg} "Multi file torrent: $t->{info}{name}\n"; + for (@{$t->{info}{files}}) { + push @files, join "/", $t->{info}{name},@{$_->{path}}; + $tsize += $_->{length}; + } +} +else { + print {$msg} "Single file torrent: $t->{info}{name}\n"; + @files = ($t->{info}{name}); + $tsize = $t->{info}{length}; +} +my $chunks = int(($tsize + $psize - 1) / $psize); +print {$msg} "Total size: ".sprintf('%.2f', $tsize / 1024**2)." MB; $chunks chunks; ", scalar @files, " files.\n\n"; + +die "Inconsistent piece information!\n" if $chunks*20 != length $t->{info}{pieces}; + +print {$msg} "Adding fast resume information..."; +# flags => 1+16+ + +my $pmod = 0; +$t->{libtorrent_resume}{bitfield} = $chunks; +foreach my $f (0..$#files) { + die "$d$files[$f] not found.\n" unless -e "$d$files[$f]"; + my $mtime = (stat "$d$files[$f]")[9]; + + # Compute number of chunks per file + my $fsize = $t->{info}{files}[$f]{length}; + my $fchunks = ($pmod ? 1 : 0); + if ($pmod >= $fsize) { ($fsize, $pmod ) = (0, $pmod-$fsize); } + else { ($pmod, $fsize) = (0, $fsize-$pmod); } + $fchunks += ceil($fsize / $psize); + $pmod ||= $psize - ($fsize % $psize); + + $t->{libtorrent_resume}{files}[$f] = { + priority => 0, # Don't download; we already have the file, so don't clobber it! + mtime => $mtime, + completed => $fchunks, + }; +}; +$t->{libtorrent_resume}{'uncertain_pieces.timestamp'} = time; + +# Some extra information to re-enforce the fact that this is a finished torrent +$d .= $t->{info}{name}; +$t->{rtorrent} = { + state => 1, # started + state_changed => time, + state_counter => 1, + chunks_wanted => 0, + chunks_done => $chunks, + complete => 1, + hashing => 0, # Not hashing + directory => File::Spec->file_name_is_absolute($d) ? $d : File::Spec->rel2abs($d), + ((tied_to_file => File::Spec->file_name_is_absolute($out_file) ? $out_file : File::Spec->rel2abs($out_file)) x!! $out_file), + 'timestamp.finished' => 0, + 'timestamp.started' => time, +}; + +print {$msg} "done\n"; + +print {$msg} "Encoding ".($out_file || 'torrent from standard output')."..."; +seek($out, 0, 0); # just in case files are the same +print {$out} bencode($t); + +close($in); +close($out); +print {$msg} "done\n"; +exit; diff --git a/doc/torrent_data b/doc/torrent_data new file mode 100644 index 00000000..9207ae87 --- /dev/null +++ b/doc/torrent_data @@ -0,0 +1,23 @@ +#!/usr/bin/perl + +# Perl script to read torrent data + +use strict; +use warnings; +use Convert::Bencode_XS qw(bdecode); +use Data::Dumper; + +$/ = undef; +$| = 1; + +my $in; +my $in_file = $ARGV[0]; +if ($in_file) { open($in, '<', $in_file) || die "Cannot open $in_file for input!\n"; } +else { $in = *STDIN; } + +print "Total input torrent size: ".sprintf('%.2f', (-s $in_file) / 1024)." KB\n" if $in_file; +print "Decoding ".($in_file || 'torrent from standard input')."..."; +my $t = bdecode(scalar <$in>); +print "done\n\n"; + +print Data::Dumper->new([$t], ['*Torrent'])->Indent(1)->Useqq(1)->Quotekeys(0)->Sortkeys(1)->Dump; From cf1c44ef5b94aac6c7b63c03a70139cf873c05f7 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Mon, 16 Apr 2012 03:13:14 +0900 Subject: [PATCH 02/11] Removed sigc++ dependency from ConnectionManager. --- src/core/manager.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/manager.cc b/src/core/manager.cc index 8693e95e..df0c489e 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -135,7 +135,7 @@ Manager::get_throttle(const std::string& name) { void Manager::set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles) { m_addressThrottles.set_merge(begin, end, throttles); - torrent::connection_manager()->set_address_throttle(sigc::mem_fun(control->core(), &core::Manager::get_address_throttle)); + torrent::connection_manager()->address_throttle() = tr1::bind(&core::Manager::get_address_throttle, control->core(), tr1::placeholders::_1); } torrent::ThrottlePair @@ -150,6 +150,13 @@ Manager::initialize_second() { m_httpQueue->slot_factory(sigc::mem_fun(m_httpStack, &CurlStack::new_object)); CurlStack::global_init(); + + torrent::connection_manager()->signal_handshake_log().push_back(tr1::bind(&Manager::handshake_log, + this, + tr1::placeholders::_1, + tr1::placeholders::_2, + tr1::placeholders::_3, + tr1::placeholders::_4)); } void From b79dea94b2f537eda620ed48207369c076fcd11f Mon Sep 17 00:00:00 2001 From: rakshasa Date: Tue, 1 May 2012 00:29:50 +0900 Subject: [PATCH 03/11] Wait for disowned http requests to finish to ensure stopped event gets sent to tracker on client shutdown. --- src/control.cc | 12 +++++++++++- src/control.h | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/control.cc b/src/control.cc index 29603594..e1893883 100644 --- a/src/control.cc +++ b/src/control.cc @@ -44,6 +44,7 @@ #include "core/download_store.h" #include "core/view_manager.h" #include "core/dht_manager.h" +#include "core/http_queue.h" #include "display/canvas.h" #include "display/window.h" @@ -146,7 +147,16 @@ Control::cleanup_exception() { bool Control::is_shutdown_completed() { - return m_shutdownQuick && !worker_thread->is_active() && torrent::is_inactive(); + if (!m_shutdownQuick || worker_thread->is_active()) + return false; + + // Tracker requests can be disowned, so wait for these to + // finish. The edge case of torrent http downloads may delay + // shutdown. + if (!core()->http_stack()->empty() || !core()->http_queue()->empty()) + return false; + + return torrent::is_inactive(); } void diff --git a/src/control.h b/src/control.h index 29f3bcbd..681b6750 100644 --- a/src/control.h +++ b/src/control.h @@ -90,7 +90,6 @@ public: core::ViewManager* view_manager() { return m_viewManager; } core::DhtManager* dht_manager() { return m_dhtManager; } - ui::Root* ui() { return m_ui; } display::Manager* display() { return m_display; } input::Manager* input() { return m_input; } From 76ed90f5148e88177bc9fcf6db710b8913e6bfaf Mon Sep 17 00:00:00 2001 From: rakshasa Date: Fri, 4 May 2012 05:09:21 +0900 Subject: [PATCH 04/11] Removed commented out settings for views. --- src/main.cc | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main.cc b/src/main.cc index 0e6f281a..905f0ed1 100644 --- a/src/main.cc +++ b/src/main.cc @@ -319,35 +319,25 @@ main(int argc, char** argv) { "view.add = complete\n" "view.filter = complete,((d.complete))\n" "view.filter_on = complete,event.download.hash_done,event.download.hash_failed,event.download.hash_final_failed,event.download.finished\n" - // "view.sort_new = complete,((less,((d.state_changed))))\n" - // "view.sort_current = complete,((less,((d.state_changed))))\n" "view.add = incomplete\n" "view.filter = incomplete,((not,((d.complete))))\n" "view.filter_on = incomplete,event.download.hash_done,event.download.hash_failed," "event.download.hash_final_failed,event.download.finished\n" - // "view.sort_new = incomplete,((less,((d.state_changed))))\n" - // "view.sort_current = incomplete,((less,((d.state_changed))))\n" // The hashing view does not include stopped torrents. "view.add = hashing\n" "view.filter = hashing,((d.hashing))\n" "view.filter_on = hashing,event.download.hash_queued,event.download.hash_removed," "event.download.hash_done,event.download.hash_failed,event.download.hash_final_failed,event.download.finished\n" -// "view.sort_new = hashing,less=d.state_changed=\n" -// "view.sort_current = hashing,less=d.state_changed=\n" "view.add = seeding\n" "view.filter = seeding,((and,((d.state)),((d.complete))))\n" - "view.filter_on = seeding,event.download.resumed,event.download.paused,event.download.finished\n" - // "view.sort_new = seeding,((less,((d.state_changed))))\n" - // "view.sort_current = seeding,((less,((d.state_changed))))\n" + "view.filter_on = seeding,event.download.resumed,event.download.paused,event.download.finished\n" "view.add = leeching\n" "view.filter = leeching,((and,((d.state)),((not,((d.complete))))))\n" - "view.filter_on = leeching,event.download.resumed,event.download.paused,event.download.finished\n" - // "view.sort_new = leeching,((less,((d.state_changed))))\n" - // "view.sort_current = leeching,((less,((d.state_changed))))\n" + "view.filter_on = leeching,event.download.resumed,event.download.paused,event.download.finished\n" "schedule2 = view.main,10,10,((view.sort,main,20))\n" "schedule2 = view.name,10,10,((view.sort,name,20))\n" From 8d0f9b330022c97dc32b3ca1c72818d92607ffbb Mon Sep 17 00:00:00 2001 From: rakshasa Date: Fri, 4 May 2012 05:17:11 +0900 Subject: [PATCH 05/11] Removed old hanshake log signal. --- src/core/manager.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/core/manager.cc b/src/core/manager.cc index df0c489e..d27ec91e 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -150,13 +150,6 @@ Manager::initialize_second() { m_httpQueue->slot_factory(sigc::mem_fun(m_httpStack, &CurlStack::new_object)); CurlStack::global_init(); - - torrent::connection_manager()->signal_handshake_log().push_back(tr1::bind(&Manager::handshake_log, - this, - tr1::placeholders::_1, - tr1::placeholders::_2, - tr1::placeholders::_3, - tr1::placeholders::_4)); } void From fb75510c0f4f87706b1b017fa4355c389a3d530a Mon Sep 17 00:00:00 2001 From: rakshasa Date: Fri, 4 May 2012 21:31:12 +0900 Subject: [PATCH 06/11] Allow functions and lists of functions to be passed to view.event_{added,removed}. --- src/command_ui.cc | 36 +++++++----------------------------- src/core/view.cc | 20 +++++++++++--------- src/core/view.h | 12 ++++++------ src/core/view_manager.h | 4 ++-- src/main.cc | 10 +++++----- src/rpc/command_scheduler.cc | 19 +------------------ src/rpc/parse_commands.cc | 33 +++++++++++++++++++++++++++++++++ src/rpc/parse_commands.h | 12 ++++++++++++ 8 files changed, 77 insertions(+), 69 deletions(-) diff --git a/src/command_ui.cc b/src/command_ui.cc index a5d778e7..d5a6a5c8 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -53,8 +53,7 @@ #include "control.h" #include "command_helpers.h" -typedef void (core::ViewManager::*view_cfilter_slot)(const std::string&, const torrent::Object&); -typedef void (core::ViewManager::*view_event_slot)(const std::string&, const std::string&); +typedef void (core::ViewManager::*view_event_slot)(const std::string&, const torrent::Object&); torrent::Object apply_view_filter_on(const torrent::Object::list_type& args) { @@ -76,33 +75,12 @@ apply_view_filter_on(const torrent::Object::list_type& args) { return torrent::Object(); } -torrent::Object -apply_view_cfilter(view_cfilter_slot viewFilterSlot, const torrent::Object::list_type& args) { - if (args.size() != 2) - throw torrent::input_error("Too few arguments."); - - const std::string& name = args.front().as_string(); - - if (name.empty()) - throw torrent::input_error("First argument must be a string."); - - (control->view_manager()->*viewFilterSlot)(name, args.back()); - - return torrent::Object(); -} - torrent::Object apply_view_event(view_event_slot viewFilterSlot, const torrent::Object::list_type& args) { if (args.size() != 2) - throw torrent::input_error("Too few arguments."); - - const std::string& name = args.front().as_string(); - - if (name.empty()) - throw torrent::input_error("First argument must be a string."); - - (control->view_manager()->*viewFilterSlot)(name, args.back().as_string()); + throw torrent::input_error("Wrong argument count."); + (control->view_manager()->*viewFilterSlot)(args.front().as_string(), args.back()); return torrent::Object(); } @@ -472,7 +450,7 @@ torrent::Object cmd_view_persistent(const torrent::Object::string_type& args) { core::View* view = *control->view_manager()->find_throw(args); - if (!view->get_filter().is_empty() || !view->get_event_added().empty() || !view->get_event_removed().empty()) + if (!view->get_filter().is_empty() || !view->event_added().is_empty() || !view->event_removed().is_empty()) throw torrent::input_error("Cannot set modified views as persitent."); view->set_filter("d.views.has=" + args); @@ -546,12 +524,12 @@ initialize_command_ui() { CMD2_ANY_L ("view.list", tr1::bind(&apply_view_list)); CMD2_ANY_LIST("view.set", tr1::bind(&apply_view_set, tr1::placeholders::_2)); - CMD2_ANY_LIST("view.filter", tr1::bind(&apply_view_cfilter, &core::ViewManager::set_filter, tr1::placeholders::_2)); + CMD2_ANY_LIST("view.filter", tr1::bind(&apply_view_event, &core::ViewManager::set_filter, tr1::placeholders::_2)); CMD2_ANY_LIST("view.filter_on", tr1::bind(&apply_view_filter_on, tr1::placeholders::_2)); CMD2_ANY_LIST("view.sort", tr1::bind(&apply_view_sort, tr1::placeholders::_2)); - CMD2_ANY_LIST("view.sort_new", tr1::bind(&apply_view_cfilter, &core::ViewManager::set_sort_new, tr1::placeholders::_2)); - CMD2_ANY_LIST("view.sort_current", tr1::bind(&apply_view_cfilter, &core::ViewManager::set_sort_current, tr1::placeholders::_2)); + CMD2_ANY_LIST("view.sort_new", tr1::bind(&apply_view_event, &core::ViewManager::set_sort_new, tr1::placeholders::_2)); + CMD2_ANY_LIST("view.sort_current", tr1::bind(&apply_view_event, &core::ViewManager::set_sort_current, tr1::placeholders::_2)); CMD2_ANY_LIST("view.event_added", tr1::bind(&apply_view_event, &core::ViewManager::set_event_added, tr1::placeholders::_2)); CMD2_ANY_LIST("view.event_removed", tr1::bind(&apply_view_event, &core::ViewManager::set_event_removed, tr1::placeholders::_2)); diff --git a/src/core/view.cc b/src/core/view.cc index 8f637396..d1969449 100644 --- a/src/core/view.cc +++ b/src/core/view.cc @@ -184,7 +184,7 @@ View::erase(Download* download) { } else { erase_internal(itr); - rpc::parse_command_multiple_d_nothrow(download, m_eventRemoved); + rpc::call_object_nothrow(m_event_removed, rpc::make_target(download)); } } @@ -200,7 +200,7 @@ View::set_visible(Download* download) { base_type::erase(itr); insert_visible(download); - rpc::parse_command_multiple_d_nothrow(download, m_eventAdded); + rpc::call_object_nothrow(m_event_added, rpc::make_target(download)); } void @@ -218,7 +218,7 @@ View::set_not_visible(Download* download) { base_type::erase(itr); base_type::push_back(download); - rpc::parse_command_multiple_d_nothrow(download, m_eventRemoved); + rpc::call_object_nothrow(m_event_removed, rpc::make_target(download)); } void @@ -275,11 +275,13 @@ View::filter() { // done by using a base_type* member variable, and making sure we // set the elements to NULL as we trigger commands on them. Or // perhaps always clear them, thus not throwing anything. - if (!m_eventRemoved.empty()) - std::for_each(changed.begin(), splitChanged, rak::bind2nd(std::ptr_fun(&rpc::parse_command_multiple_d_nothrow), m_eventRemoved)); + if (!m_event_removed.is_empty()) + std::for_each(changed.begin(), splitChanged, + tr1::bind(&rpc::call_object_d_nothrow, m_event_removed, tr1::placeholders::_1)); - if (!m_eventAdded.empty()) - std::for_each(splitChanged, changed.end(), rak::bind2nd(std::ptr_fun(&rpc::parse_command_multiple_d_nothrow), m_eventAdded)); + if (!m_event_added.is_empty()) + std::for_each(changed.begin(), splitChanged, + tr1::bind(&rpc::call_object_d_nothrow, m_event_added, tr1::placeholders::_1)); emit_changed(); } @@ -297,7 +299,7 @@ View::filter_download(core::Download* download) { erase_internal(itr); insert_visible(download); - rpc::parse_command_multiple_d_nothrow(download, m_eventAdded); + rpc::call_object_nothrow(m_event_added, rpc::make_target(download)); } else { // This makes sure the download is sorted even if it is @@ -315,7 +317,7 @@ View::filter_download(core::Download* download) { erase_internal(itr); base_type::push_back(download); - rpc::parse_command_multiple_d_nothrow(download, m_eventRemoved); + rpc::call_object_nothrow(m_event_removed, rpc::make_target(download)); } emit_changed(); diff --git a/src/core/view.h b/src/core/view.h index 2488ba75..98d366d3 100644 --- a/src/core/view.h +++ b/src/core/view.h @@ -127,10 +127,10 @@ public: void clear_filter_on(); - const std::string& get_event_added() const { return m_eventAdded; } - const std::string& get_event_removed() const { return m_eventRemoved; } - void set_event_added(const std::string& cmd) { m_eventAdded = cmd; } - void set_event_removed(const std::string& cmd) { m_eventRemoved = cmd; } + const torrent::Object& event_added() const { return m_event_added; } + const torrent::Object& event_removed() const { return m_event_removed; } + void set_event_added(const torrent::Object& cmd) { m_event_added = cmd; } + void set_event_removed(const torrent::Object& cmd) { m_event_removed = cmd; } // The time of the last change to the view, semantics of this is // user-dependent. Used by f.ex. ViewManager to decide if it should @@ -171,8 +171,8 @@ private: torrent::Object m_filter; - std::string m_eventAdded; - std::string m_eventRemoved; + torrent::Object m_event_added; + torrent::Object m_event_removed; rak::timer m_lastChanged; diff --git a/src/core/view_manager.h b/src/core/view_manager.h index 1f6c5da7..e2ef1ab2 100644 --- a/src/core/view_manager.h +++ b/src/core/view_manager.h @@ -95,8 +95,8 @@ public: void set_filter(const std::string& name, const torrent::Object& cmd); void set_filter_on(const std::string& name, const filter_args& args); - void set_event_added(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_event_added(cmd); } - void set_event_removed(const std::string& name, const std::string& cmd) { (*find_throw(name))->set_event_removed(cmd); } + void set_event_added(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_event_added(cmd); } + void set_event_removed(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_event_removed(cmd); } private: DownloadList* m_list; diff --git a/src/main.cc b/src/main.cc index 905f0ed1..a4b288f4 100644 --- a/src/main.cc +++ b/src/main.cc @@ -293,7 +293,7 @@ main(int argc, char** argv) { "group.insert = seeding,seeding\n" - "session.name.set = \"$cat=$system.hostname=,:,$system.pid=\"\n" + "session.name.set = (cat,(system.hostname),:,(system.pid))\n" // Currently not doing any sorting on main. "view.add = main\n" @@ -308,13 +308,13 @@ main(int argc, char** argv) { "view.add = started\n" "view.filter = started,((false))\n" - "view.event_added = started,\"view.set_not_visible=stopped ;d.state.set=1 ;scheduler.simple.added=\"\n" - "view.event_removed = started,\"view.set_visible=stopped ;scheduler.simple.removed=\"\n" + "view.event_added = started,{(view.set_not_visible,stopped),(d.state.set,1),(scheduler.simple.added)}\n" + "view.event_removed = started,{(view.set_visible,stopped),(scheduler.simple.removed)}\n" "view.add = stopped\n" "view.filter = stopped,((false))\n" - "view.event_added = stopped,\"d.state.set=0 ;view.set_not_visible=started\"\n" - "view.event_removed = stopped,view.set_visible=started\n" + "view.event_added = stopped,{(d.state.set,0),(view.set_not_visible,started)}\n" + "view.event_removed = stopped,((view.set_visible,started))\n" "view.add = complete\n" "view.filter = complete,((d.complete))\n" diff --git a/src/rpc/command_scheduler.cc b/src/rpc/command_scheduler.cc index 4968e8da..779cc4ac 100644 --- a/src/rpc/command_scheduler.cc +++ b/src/rpc/command_scheduler.cc @@ -97,24 +97,7 @@ CommandScheduler::call_item(value_type item) { // removed. try { - if (item->command().is_string()) { - rpc::parse_command_multiple_std(item->command().as_string()); - - } else if (item->command().is_dict_key()) { - // This can/should be optimized... - torrent::Object tmp_command = item->command(); - - // Unquote the root function object so 'parse_command_execute' - // doesn't end up calling it. - // - // TODO: Only call this if mask_function is set? - uint32_t flags = tmp_command.flags() & torrent::Object::mask_function; - tmp_command.unset_flags(torrent::Object::mask_function); - tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function); - - rpc::parse_command_execute(rpc::make_target(), &tmp_command); - rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj()); - } + rpc::call_object(item->command()); } catch (torrent::input_error& e) { if (m_slotErrorMessage.is_valid()) diff --git a/src/rpc/parse_commands.cc b/src/rpc/parse_commands.cc index 7eba5961..d49e5604 100644 --- a/src/rpc/parse_commands.cc +++ b/src/rpc/parse_commands.cc @@ -239,6 +239,39 @@ parse_command_file(const std::string& path) { return true; } +void +call_object(const torrent::Object& command, target_type target) { + switch (command.type()) { + case torrent::Object::TYPE_STRING: + parse_command_multiple(target, command.as_string().c_str(), command.as_string().c_str() + command.as_string().size()); + break; + + case torrent::Object::TYPE_LIST: + for (torrent::Object::list_const_iterator itr = command.as_list().begin(), last = command.as_list().end(); itr != last; itr++) + call_object(*itr, target); + break; + + case torrent::Object::TYPE_DICT_KEY: + { + // This can/should be optimized... + torrent::Object tmp_command = command; + + // Unquote the root function object so 'parse_command_execute' + // doesn't end up calling it. + // + // TODO: Only call this if mask_function is set? + uint32_t flags = tmp_command.flags() & torrent::Object::mask_function; + tmp_command.unset_flags(torrent::Object::mask_function); + tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function); + + parse_command_execute(make_target(), &tmp_command); + commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(), target); + } + default: + break; + } +} + // // // diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h index bf3893e6..d4bd83a1 100644 --- a/src/rpc/parse_commands.h +++ b/src/rpc/parse_commands.h @@ -124,6 +124,18 @@ call_command_d_range(const char* key, core::Download* download, torrent::Object: return commands.call_command_d(key, download, rawArgs); } +void call_object(const torrent::Object& command, target_type target = make_target()); + +inline void +call_object_nothrow(const torrent::Object& command, target_type target = make_target()) { + try { call_object(command, target); } catch (torrent::input_error& e) {} +} + +inline void +call_object_d_nothrow(const torrent::Object& command, core::Download* download) { + try { call_object(command, make_target(download)); } catch (torrent::input_error& e) {} +} + // // // From d2d09d97f8c9b780a0f3b20416d5af9ed2638ddc Mon Sep 17 00:00:00 2001 From: rakshasa Date: Fri, 4 May 2012 22:33:26 +0900 Subject: [PATCH 07/11] Replace old command call functions with the new one taking torrent::Objects. --- src/command_helpers.h | 2 +- src/rpc/object_storage.cc | 3 +- src/rpc/parse_commands.cc | 131 +++++++------------------------------- src/rpc/parse_commands.h | 17 ++--- 4 files changed, 31 insertions(+), 122 deletions(-) diff --git a/src/command_helpers.h b/src/command_helpers.h index 6c2974e2..022a1b43 100644 --- a/src/command_helpers.h +++ b/src/command_helpers.h @@ -135,7 +135,7 @@ void initialize_commands(); torrent::raw_string::from_c_str(key), tr1::placeholders::_2)); #define CMD2_FUNC_SINGLE(key, cmds) \ - CMD2_ANY(key, tr1::bind(&rpc::command_function_call, torrent::raw_string::from_c_str(cmds), \ + CMD2_ANY(key, tr1::bind(&rpc::command_function_call_object, torrent::Object(torrent::raw_string::from_c_str(cmds)), \ tr1::placeholders::_1, tr1::placeholders::_2)); #define CMD2_REDIRECT(from_key, to_key) \ diff --git a/src/rpc/object_storage.cc b/src/rpc/object_storage.cc index 9a7bfa1c..eebc149a 100644 --- a/src/rpc/object_storage.cc +++ b/src/rpc/object_storage.cc @@ -181,9 +181,8 @@ object_storage::call_function(const torrent::raw_string& key, target_type target switch (itr->second.flags & mask_type) { case flag_function_type: - return command_function_call_object(itr->second.object, target, object); case flag_multi_type: - return command_function_multi_call(itr->second.object.as_map(), target, object); + return command_function_call_object(itr->second.object, target, object); default: throw torrent::input_error("Key not found or wrong type."); } diff --git a/src/rpc/parse_commands.cc b/src/rpc/parse_commands.cc index d49e5604..4ae1935e 100644 --- a/src/rpc/parse_commands.cc +++ b/src/rpc/parse_commands.cc @@ -239,20 +239,32 @@ parse_command_file(const std::string& path) { return true; } -void +torrent::Object call_object(const torrent::Object& command, target_type target) { switch (command.type()) { + case torrent::Object::TYPE_RAW_STRING: + return parse_command_multiple(target, command.as_raw_string().begin(), command.as_raw_string().end()); case torrent::Object::TYPE_STRING: - parse_command_multiple(target, command.as_string().c_str(), command.as_string().c_str() + command.as_string().size()); - break; + return parse_command_multiple(target, command.as_string().c_str(), command.as_string().c_str() + command.as_string().size()); case torrent::Object::TYPE_LIST: - for (torrent::Object::list_const_iterator itr = command.as_list().begin(), last = command.as_list().end(); itr != last; itr++) - call_object(*itr, target); - break; + { + torrent::Object result; + for (torrent::Object::list_const_iterator itr = command.as_list().begin(), last = command.as_list().end(); itr != last; itr++) + result = call_object(*itr, target); + + return result; + } + case torrent::Object::TYPE_MAP: + { + for (torrent::Object::map_const_iterator itr = command.as_map().begin(), last = command.as_map().end(); itr != last; itr++) + call_object(itr->second, target); + + return torrent::Object(); + } case torrent::Object::TYPE_DICT_KEY: - { + { // This can/should be optimized... torrent::Object tmp_command = command; @@ -265,10 +277,10 @@ call_object(const torrent::Object& command, target_type target) { tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function); parse_command_execute(make_target(), &tmp_command); - commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(), target); - } + return commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(), target); + } default: - break; + return torrent::Object(); } } @@ -276,31 +288,6 @@ call_object(const torrent::Object& command, target_type target) { // // -// Temp until it can be moved somewhere better... -const torrent::Object -command_function_call(const torrent::raw_string& cmd, target_type target, const torrent::Object& args) { - rpc::command_base::stack_type stack; - torrent::Object* last_stack; - - if (args.is_list()) - last_stack = rpc::command_base::push_stack(args.as_list(), &stack); - else if (args.type() != torrent::Object::TYPE_NONE) - last_stack = rpc::command_base::push_stack(&args, &args + 1, &stack); - else - last_stack = rpc::command_base::push_stack(NULL, NULL, &stack); - - try { - torrent::Object result = parse_command_multiple(target, cmd.begin(), cmd.end()); - - rpc::command_base::pop_stack(&stack, last_stack); - return result; - - } catch (torrent::bencode_error& e) { - rpc::command_base::pop_stack(&stack, last_stack); - throw e; - } -} - const torrent::Object command_function_call_object(const torrent::Object& cmd, target_type target, const torrent::Object& args) { rpc::command_base::stack_type stack; @@ -314,34 +301,7 @@ command_function_call_object(const torrent::Object& cmd, target_type target, con last_stack = rpc::command_base::push_stack(NULL, NULL, &stack); try { - torrent::Object result; - - if (cmd.is_string()) { - result = parse_command_multiple(target, cmd.as_string().c_str(), cmd.as_string().c_str() + cmd.as_string().size()); - - } else if (cmd.is_list()){ - for (torrent::Object::list_const_iterator first = cmd.as_list().begin(), last = cmd.as_list().end(); first != last; first++) { - torrent::Object tmp_cmd = *first; - - rpc::parse_command_execute(target, &tmp_cmd); - result = rpc::commands.call_command(tmp_cmd.as_dict_key().c_str(), tmp_cmd.as_dict_obj()); - } - - } else { - torrent::Object tmp_command = cmd; - - // Unquote the root function object so 'parse_command_execute' - // doesn't end up calling it. - // - // TODO: Only call this if mask_function is set? - uint32_t flags = tmp_command.flags() & torrent::Object::mask_function; - tmp_command.unset_flags(torrent::Object::mask_function); - tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function); - - rpc::parse_command_execute(target, &tmp_command); - rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(), target); - } - + torrent::Object result = call_object(cmd, target); rpc::command_base::pop_stack(&stack, last_stack); return result; @@ -351,49 +311,4 @@ command_function_call_object(const torrent::Object& cmd, target_type target, con } } -const torrent::Object -command_function_multi_call(const torrent::Object::map_type& cmd, target_type target, const torrent::Object& args) { - rpc::command_base::stack_type stack; - torrent::Object* last_stack; - - if (args.is_list()) - last_stack = rpc::command_base::push_stack(args.as_list(), &stack); - else if (args.type() != torrent::Object::TYPE_NONE) - last_stack = rpc::command_base::push_stack(&args, &args + 1, &stack); - else - last_stack = rpc::command_base::push_stack(NULL, NULL, &stack); - - try { - for (torrent::Object::map_const_iterator itr = cmd.begin(), last = cmd.end(); itr != last; itr++) { - if (itr->second.is_dict_key()) { - // This can/should be optimized... - torrent::Object tmp_command = itr->second; - - // Unquote the root function object so 'parse_command_execute' - // doesn't end up calling it. - // - // TODO: Only call this if mask_function is set? - uint32_t flags = tmp_command.flags() & torrent::Object::mask_function; - tmp_command.unset_flags(torrent::Object::mask_function); - tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function); - - rpc::parse_command_execute(target, &tmp_command); - rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(), target); - continue; - } - - const std::string& cmd_str = itr->second.as_string(); - parse_command_multiple(target, cmd_str.c_str(), cmd_str.c_str() + cmd_str.size()); - } - - } catch (torrent::bencode_error& e) { - rpc::command_base::pop_stack(&stack, last_stack); - throw e; - } - - rpc::command_base::pop_stack(&stack, last_stack); - return torrent::Object(); -} - - } diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h index d4bd83a1..ad5d4033 100644 --- a/src/rpc/parse_commands.h +++ b/src/rpc/parse_commands.h @@ -124,33 +124,28 @@ call_command_d_range(const char* key, core::Download* download, torrent::Object: return commands.call_command_d(key, download, rawArgs); } -void call_object(const torrent::Object& command, target_type target = make_target()); +torrent::Object call_object(const torrent::Object& command, target_type target = make_target()); -inline void +inline torrent::Object call_object_nothrow(const torrent::Object& command, target_type target = make_target()) { - try { call_object(command, target); } catch (torrent::input_error& e) {} + try { return call_object(command, target); } catch (torrent::input_error& e) { return torrent::Object(); } } -inline void +inline torrent::Object call_object_d_nothrow(const torrent::Object& command, core::Download* download) { - try { call_object(command, make_target(download)); } catch (torrent::input_error& e) {} + try { return call_object(command, make_target(download)); } catch (torrent::input_error& e) { return torrent::Object(); } } // // // -// Temp until it can be moved somewhere better... -const torrent::Object -command_function_call(const torrent::raw_string& cmd, target_type target, const torrent::Object& args); const torrent::Object command_function_call_object(const torrent::Object& cmd, target_type target, const torrent::Object& args); -const torrent::Object -command_function_multi_call(const torrent::Object::map_type& cmd, target_type target, const torrent::Object& args); inline const torrent::Object command_function_call_str(const std::string& cmd, target_type target, const torrent::Object& args) { - return command_function_call(torrent::raw_string::from_string(cmd), target, args); + return command_function_call_object(torrent::Object(cmd), target, args); } } From 004862850d628e2599c15d66f6b51498e4d7a29e Mon Sep 17 00:00:00 2001 From: rakshasa Date: Fri, 4 May 2012 22:56:58 +0900 Subject: [PATCH 08/11] Allow command function lists for dynamically created functions. --- src/command_dynamic.cc | 2 +- src/main.cc | 4 ++-- src/rpc/object_storage.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/command_dynamic.cc b/src/command_dynamic.cc index 1f605b78..852651b1 100644 --- a/src/command_dynamic.cc +++ b/src/command_dynamic.cc @@ -401,7 +401,7 @@ system_method_set_key(const torrent::Object::list_type& args) { return torrent::Object(); } - if (itrArgs->is_dict_key()) + if (itrArgs->is_dict_key() || itrArgs->is_list()) control->object_storage()->set_str_multi_key_obj(key.c_str(), cmd_key, *itrArgs); else control->object_storage()->set_str_multi_key(key, cmd_key, system_method_generate_command(itrArgs, args.end())); diff --git a/src/main.cc b/src/main.cc index a4b288f4..4f2a9184 100644 --- a/src/main.cc +++ b/src/main.cc @@ -266,8 +266,8 @@ main(int argc, char** argv) { "method.set_key = event.download.inserted, 1_connect_logs, ((d.initialize_logs))\n" "method.set_key = event.download.inserted, 1_send_scrape, ((d.tracker.send_scrape,30))\n" - "method.set_key = event.download.inserted_new, 1_prepare, \"branch=d.state=,view.set_visible=started,view.set_visible=stopped ;d.save_full_session=\"\n" - "method.set_key = event.download.inserted_session, 1_prepare, \"branch=d.state=,view.set_visible=started,view.set_visible=stopped\"\n" + "method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) ),(d.save_full_session)}\n" + "method.set_key = event.download.inserted_session, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n" "method.set_key = event.download.inserted, 1_prioritize_toc, \"branch=file.prioritize_toc=,{\\\"f.multicall=(file.prioritize_toc.first),f.prioritize_first.enable=\\\",\\\"f.multicall=(file.prioritize_toc.last),f.prioritize_last.enable=\\\",d.update_priorities=}\"\n" diff --git a/src/rpc/object_storage.cc b/src/rpc/object_storage.cc index eebc149a..1da057a3 100644 --- a/src/rpc/object_storage.cc +++ b/src/rpc/object_storage.cc @@ -218,7 +218,7 @@ object_storage::erase_multi_key(const torrent::raw_string& key, const std::strin void object_storage::set_multi_key_obj(const torrent::raw_string& key, const std::string& cmd_key, const torrent::Object& object) { - if (!object.is_string() && !object.is_dict_key()) + if (!object.is_string() && !object.is_dict_key() && !object.is_list()) throw torrent::input_error("Object is wrong type."); local_iterator itr = find_local_mutable(key, flag_multi_type); From 2ca49e34858ec773d031617249de836deff973a2 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Mon, 7 May 2012 01:53:39 +0900 Subject: [PATCH 09/11] Removed unused parse command function. --- src/rpc/parse_commands.cc | 5 ----- src/rpc/parse_commands.h | 3 --- 2 files changed, 8 deletions(-) diff --git a/src/rpc/parse_commands.cc b/src/rpc/parse_commands.cc index 4ae1935e..59f9abe2 100644 --- a/src/rpc/parse_commands.cc +++ b/src/rpc/parse_commands.cc @@ -181,11 +181,6 @@ parse_command_multiple(target_type target, const char* first, const char* last) return result.first; } -parse_command_type -parse_command_object(target_type target, const torrent::Object& object) { - return parse_command(target, object.as_string().c_str(), object.as_string().c_str() + object.as_string().size()); -} - bool parse_command_file(const std::string& path) { std::fstream file(rak::path_expand(path).c_str(), std::ios::in); diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h index ad5d4033..2df33723 100644 --- a/src/rpc/parse_commands.h +++ b/src/rpc/parse_commands.h @@ -64,9 +64,6 @@ torrent::Object parse_command_multiple(target_type target, const char* fi void parse_command_execute(target_type target, torrent::Object* object); -// Make this take care of lists too. -parse_command_type parse_command_object(target_type target, const torrent::Object& object); - inline torrent::Object parse_command_single(target_type target, const char* first) { return parse_command(target, first, first + std::strlen(first)).first; } inline torrent::Object parse_command_multiple(target_type target, const char* first) { return parse_command_multiple(target, first, first + std::strlen(first)); } From 8f5f383e3c80d6b42f2621b380a842dc9f0b2c72 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Mon, 7 May 2012 02:19:12 +0900 Subject: [PATCH 10/11] For POSIX threads config script try none first to avoid adding flags when not needed. --- scripts/ax_pthread.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ax_pthread.m4 b/scripts/ax_pthread.m4 index e20a388c..27f2533c 100644 --- a/scripts/ax_pthread.m4 +++ b/scripts/ax_pthread.m4 @@ -123,7 +123,7 @@ fi # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. -ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" +ax_pthread_flags="none pthreads -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: @@ -160,7 +160,7 @@ case "${host_cpu}-${host_os}" in ;; *-darwin*) - ax_pthread_flags="-pthread $ax_pthread_flags" + ax_pthread_flags="none -pthread $ax_pthread_flags" ;; esac From d0f6bb440a038fd01550e9b5b4952d5946dba12f Mon Sep 17 00:00:00 2001 From: rakshasa Date: Mon, 7 May 2012 02:25:37 +0900 Subject: [PATCH 11/11] Fixed clang compilation issues. --- src/command_local.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command_local.cc b/src/command_local.cc index d88b8c0a..7df4ec7f 100644 --- a/src/command_local.cc +++ b/src/command_local.cc @@ -263,10 +263,10 @@ file_print_list(torrent::Object::list_const_iterator first, torrent::Object::lis while (first != last) { switch (first->type()) { case torrent::Object::TYPE_STRING: - fprintf(output, " %s" + !(flags & file_print_use_space), first->as_string().c_str()); + fprintf(output, (const char*)" %s" + !(flags & file_print_use_space), first->as_string().c_str()); break; case torrent::Object::TYPE_VALUE: - fprintf(output, " %lli" + !(flags & file_print_use_space), first->as_value()); + fprintf(output, (const char*)" %lli" + !(flags & file_print_use_space), first->as_value()); break; case torrent::Object::TYPE_LIST: file_print_list(first->as_list().begin(), first->as_list().end(), output, 0);