summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorxsmile2018-07-02 22:51:10 +0200
committerxsmile2018-07-02 22:51:10 +0200
commite9b4bfa9b8b176e8b291fe8115e59242e302a6a2 (patch)
tree8d6e021191cf5cf695f4d3beec9cc142f0d043c4
parentd1e56fd0fdfbe3d8d7c8c2bfc20b8f800ceb66e5 (diff)
downloadaur-e9b4bfa9b8b176e8b291fe8115e59242e302a6a2.tar.gz
update
-rw-r--r--PKGBUILD12
-rw-r--r--command_pyroscope.cc345
-rw-r--r--ps-event-view_all.patch30
-rw-r--r--ps-fix-log-xmlrpc-close_all.patch22
-rw-r--r--ui_pyroscope.cc111
-rw-r--r--ui_pyroscope.h7
6 files changed, 470 insertions, 57 deletions
diff --git a/PKGBUILD b/PKGBUILD
index 636ab2f9f3bc..900f76d8095a 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -3,7 +3,7 @@
_pkgname=rtorrent
pkgname=rtorrent-ps
_pkgver=0.9.6
-pkgver=1.0.r273.g8b4cb6e
+pkgver=1.1.r15.g56057fc
pkgrel=1
pkgdesc='Extended rTorrent distribution with UI enhancements, colorization, and some added features'
url='https://github.com/pyroscope/rtorrent-ps'
@@ -17,6 +17,7 @@ source=("https://github.com/rakshasa/$_pkgname/archive/$_pkgver.tar.gz"
'command_pyroscope.cc'
'ps-event-view_all.patch'
'ps-fix-double-slash-319_all.patch'
+ 'ps-fix-log-xmlrpc-close_all.patch'
'ps-fix-sort-started-stopped-views_all.patch'
'ps-fix-throttle-args_all.patch'
'ps-handle-sighup-578_all.patch'
@@ -39,9 +40,10 @@ source=("https://github.com/rakshasa/$_pkgname/archive/$_pkgver.tar.gz"
'ui_pyroscope.patch')
md5sums=('b8b4009f95f8543244ae1d23b1810d7c'
'b49903d3fa25a66c72db69570dfe8b47'
- '313581839dd8bcbd14b4759789adcada'
- '56701bca42cc9b309637bf3f918ede12'
+ 'e7f737e4b2c4db9659faf2609a17732e'
+ 'fbe511a1dfe89fe0510a077e61ae6ec7'
'22fae392c6e281dc438b39a5019e7e1b'
+ '1b4f82e6123c5baa0cd07056e368064e'
'3fd739c0d5a9442f0cdec9ed5a720eaa'
'ab490d1d1df9c27f3cf624966f7f03f6'
'2137e16f8b881170fb92fb7a6c276193'
@@ -59,8 +61,8 @@ md5sums=('b8b4009f95f8543244ae1d23b1810d7c'
'26faff00b306b6ef276a7d9e6d964994'
'bd04a0699b80c8042e1cf63a7e0e4222'
'd0a956f0eb4b53b66d83df2a8a4d16dc'
- '4f51b9e66d63e24570d7f4c88fb6bf1d'
- '2a71f0c478e8fe7bce464ac85c4bec44'
+ '88c3520c6ba51a31ea29903b5ee6c484'
+ 'a572a0fd087c89d8f50f16544bb1ec69'
'0a2bbaf74c7160ba33876dcc2f050f14')
prepare() {
diff --git a/command_pyroscope.cc b/command_pyroscope.cc
index e432cffe6473..80fa52b54f0b 100644
--- a/command_pyroscope.cc
+++ b/command_pyroscope.cc
@@ -180,16 +180,19 @@ torrent::Object apply_random(rpc::target_type target, const torrent::Object::lis
torrent::Tracker* get_active_tracker(torrent::Download* item) {
torrent::TrackerList* tl = item->tracker_list();
torrent::Tracker* tracker = 0;
+ torrent::Tracker* fallback = 0;
for (size_t trkidx = 0; trkidx < tl->size(); trkidx++) {
tracker = tl->at(trkidx);
- if (tracker->is_usable() && tracker->type() == torrent::Tracker::TRACKER_HTTP
- && tracker->scrape_complete() + tracker->scrape_incomplete() > 0) {
- break;
+ if (tracker->is_usable() && tracker->type() == torrent::Tracker::TRACKER_HTTP) {
+ if (!fallback) fallback = tracker;
+ if (tracker->scrape_complete() || tracker->scrape_incomplete()) {
+ break;
+ }
}
tracker = 0;
}
- if (!tracker && tl->size()) tracker = tl->at(0);
+ if (!tracker && tl->size()) tracker = fallback ? fallback : tl->at(0);
return tracker;
}
@@ -497,6 +500,11 @@ torrent::Object cmd_import_return(rpc::target_type target, const torrent::Object
}
+torrent::Object cmd_do(rpc::target_type target, const torrent::Object& args) {
+ return rpc::call_object(args, target);
+}
+
+
torrent::Object retrieve_d_custom_if_z(core::Download* download, const torrent::Object::list_type& args) {
torrent::Object::list_const_iterator itr = args.begin();
if (itr == args.end())
@@ -508,13 +516,53 @@ torrent::Object retrieve_d_custom_if_z(core::Download* download, const torrent::
throw torrent::bencode_error("d.custom.if_z: Missing default argument.");
try {
- return download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key);
+ const std::string& val = download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key);
+ return val.empty() ? itr->as_string() : val;
} catch (torrent::bencode_error& e) {
return itr->as_string();
}
}
+torrent::Object cmd_d_custom_set_if_z(core::Download* download, const torrent::Object::list_type& args) {
+ torrent::Object::list_const_iterator itr = args.begin();
+ if (itr == args.end())
+ throw torrent::bencode_error("d.custom.set_if_z: Missing key argument.");
+ const std::string& key = (itr++)->as_string();
+ if (key.empty())
+ throw torrent::bencode_error("d.custom.set_if_z: Empty key argument.");
+ if (itr == args.end())
+ throw torrent::bencode_error("d.custom.set_if_z: Missing value argument.");
+
+ bool set_it = false;
+ try {
+ const std::string& val = download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key);
+ set_it = val.empty();
+ } catch (torrent::bencode_error& e) {
+ set_it = true;
+ }
+ if (set_it)
+ download->bencode()->get_key("rtorrent").
+ insert_preserve_copy("custom", torrent::Object::create_map()).first->second.
+ insert_key(key, itr->as_string());
+
+ return torrent::Object();
+}
+
+
+torrent::Object cmd_d_custom_erase(core::Download* download, const torrent::Object::list_type& args) {
+ for (torrent::Object::list_type::const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
+ const std::string& key = itr->as_string();
+ if (key.empty())
+ throw torrent::bencode_error("d.custom.erase: Empty key argument.");
+
+ download->bencode()->get_key("rtorrent").get_key("custom").erase_key(key);
+ }
+
+ return torrent::Object();
+}
+
+
torrent::Object retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::Object::list_type& args) {
if (args.begin() != args.end())
throw torrent::bencode_error("d.custom.keys/items takes no arguments.");
@@ -531,6 +579,44 @@ torrent::Object retrieve_d_custom_map(core::Download* download, bool keys_only,
}
+torrent::Object cmd_d_custom_toggle(core::Download* download, const std::string& key) {
+ bool result = true;
+ try {
+ const std::string& strval = download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key);
+ if (!strval.empty()) {
+ char* junk = 0;
+ long number = strtol(strval.c_str(), &junk, 10);
+ while (std::isspace(*junk)) ++junk;
+ result = !*junk && number == 0;
+ }
+ } catch (torrent::bencode_error& e) {
+ // true
+ }
+
+ download->bencode()->get_key("rtorrent").
+ insert_preserve_copy("custom", torrent::Object::create_map()).first->second.
+ insert_key(key, result ? "1" : "0");
+ return (int64_t) (result ? 1 : 0);
+}
+
+
+torrent::Object retrieve_d_custom_as_value(core::Download* download, const std::string& key) {
+ try {
+ const std::string& strval = download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key);
+ if (strval.empty())
+ return (int64_t) 0;
+
+ char* junk = 0;
+ long result = strtol(strval.c_str(), &junk, 10);
+ if (*junk)
+ throw torrent::input_error("d.custom.as_value(" + key + "): junk at end of '" + strval + "'!");
+ return (int64_t) result;
+ } catch (torrent::bencode_error& e) {
+ return (int64_t) 0;
+ }
+}
+
+
torrent::Object
d_multicall_filtered(const torrent::Object::list_type& args) {
if (args.size() < 2)
@@ -539,12 +625,7 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
// Find the given view
core::ViewManager* viewManager = control->view_manager();
- core::ViewManager::iterator viewItr;
-
- if (!arg->as_string().empty())
- viewItr = viewManager->find(arg->as_string());
- else
- viewItr = viewManager->find("default");
+ core::ViewManager::iterator viewItr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
if (viewItr == viewManager->end())
throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
@@ -594,15 +675,39 @@ torrent::Object cmd_throttle_names() {
}
-static const std::string& string_get_first_arg(const char* name, const torrent::Object::list_type& args) {
- if (args.size() < 1) {
- throw torrent::input_error("string." + std::string(name) + " needs a string argument!");
+// Get length of an UTF8-encoded std::string
+size_t u8_length(const std::string& text) {
+ // Take total length and subtract number of non-leading multi-bytes
+ return text.length() - count_if(text.begin(), text.end(),
+ [](char c)->bool { return (c & 0xC0) == 0x80; });
+}
+
+
+// Chop off an UTF-8 string
+std::string u8_chop(const std::string& text, size_t glyphs) {
+ std::mbstate_t mbs = std::mbstate_t();
+ size_t bytes = 0, skip;
+ const char* pos = text.c_str();
+
+ while (*pos && glyphs-- > 0 && (skip = std::mbrlen(pos, text.length() - bytes, &mbs)) > 0) {
+ pos += skip;
+ bytes += skip;
}
+
+ return bytes < text.length() ? text.substr(0, bytes) : text;
+}
+
+
+static const std::string& string_get_first_arg(const char* name, const torrent::Object::list_type& args) {
torrent::Object::list_const_iterator itr = args.begin();
+ if (args.size() < 1 || !itr->is_string()) {
+ throw torrent::input_error("string." + std::string(name) + " needs a string argument.0!");
+ }
return itr->as_string();
}
+// get a numeric arg from a string or value, advancing the passed iterator
static int64_t string_get_value_arg(const char* name, torrent::Object::list_const_iterator& itr) {
int64_t result = 0;
if (itr->is_string()) {
@@ -637,6 +742,147 @@ torrent::Object cmd_string_len(rpc::target_type target, const torrent::Object::l
}
+torrent::Object cmd_string_join(rpc::target_type target, const torrent::Object::list_type& args) {
+ std::string delim = string_get_first_arg("join", args);
+ std::string result;
+ torrent::Object::list_const_iterator first = args.begin() + 1, last = args.end();
+
+ for (torrent::Object::list_const_iterator itr = first; itr != last; ++itr) {
+ if (itr != first) result += delim;
+ rpc::print_object_std(&result, &*itr, 0);
+ }
+
+ return result;
+}
+
+
+torrent::Object cmd_string_strip(int where, const torrent::Object::list_type& args) {
+ std::string text = string_get_first_arg("[lr]strip", args);
+ torrent::Object::list_const_iterator first = args.begin() + 1, last = args.end();
+
+ if (args.size() == 1) {
+ // Strip whitespace
+ if (where <= 0) {
+ text.erase(text.begin(),
+ std::find_if(text.begin(), text.end(),
+ std::not1(std::ptr_fun<int, int>(std::isspace))));
+ }
+ if (where >= 0) {
+ text.erase(std::find_if(text.rbegin(), text.rend(),
+ std::not1(std::ptr_fun<int, int>(std::isspace))).base(),
+ text.end());
+ }
+ } else {
+ size_t lpos = 0, rpos = text.length();
+ bool changed;
+ do {
+ changed = false;
+ for (torrent::Object::list_const_iterator itr = first; itr != last; ++itr) {
+ const std::string& strippable = itr->as_string();
+ if (strippable.empty()) continue;
+
+ bool found;
+ do {
+ found = false;
+
+ if (where <= 0) {
+ if (0 == strncmp(text.c_str() + lpos, strippable.c_str(), strippable.length())) {
+ lpos += strippable.length();
+ changed = found = true;
+ }
+ }
+ if (where >= 0 && lpos <= rpos - strippable.length()) {
+ if (0 == strncmp(text.c_str() + rpos - strippable.length(), strippable.c_str(), strippable.length())) {
+ rpos -= strippable.length();
+ changed = found = true;
+ }
+ }
+ } while (found && lpos < rpos);
+ }
+ } while (changed && lpos < rpos);
+ text = lpos < rpos ? text.substr(lpos, rpos - lpos) : "";
+ }
+
+ return text;
+}
+
+
+torrent::Object cmd_string_pad(bool at_end, const torrent::Object::list_type& args) {
+ std::string text;
+ if (args.size() > 0 && args.begin()->is_value()) {
+ char buf[65];
+ snprintf(buf, sizeof(buf), "%ld", (long)args.begin()->as_value());
+ text = buf;
+ } else {
+ text = string_get_first_arg("[lr]pad", args);
+ }
+
+ torrent::Object::list_const_iterator itr = args.begin() + 1;
+ int64_t pad_len = 0;
+ std::string filler;
+ if (itr != args.end()) pad_len = string_get_value_arg("[lr]pad(pad_len)", itr);
+ if (itr != args.end()) filler = (itr++)->as_string();
+ if (pad_len < 0)
+ throw torrent::input_error("string.[lr]pad: Invalid negative padding length!");
+ if (filler.empty()) filler = " ";
+ size_t text_len = u8_length(text), filler_len = u8_length(filler);
+
+ if (size_t(pad_len) > text_len) {
+ std::string pad;
+ size_t count = size_t(pad_len) - text_len;
+
+ if (filler.length() == 1) { // optimize the common case
+ pad.insert(0, count, filler.at(0));
+ } else while (count > 0) {
+ if (count >= filler_len) {
+ pad += filler;
+ count -= filler_len;
+ } else {
+ pad += u8_chop(filler, count);
+ count = 0;
+ }
+ }
+
+ return at_end ? text + pad : pad + text;
+ }
+
+ return text;
+}
+
+
+torrent::Object cmd_string_split(rpc::target_type target, const torrent::Object::list_type& args) {
+ const std::string text = string_get_first_arg("split", args);
+ if (args.size() != 2 || !args.rbegin()->is_string()) {
+ throw torrent::input_error("string.split needs a string argument.1!");
+ }
+ const std::string delim = args.rbegin()->as_string();
+ torrent::Object result = torrent::Object::create_list();
+ torrent::Object::list_type& resultList = result.as_list();
+
+ if (delim.length()) {
+ size_t pos = 0, next = 0;
+
+ while ((next = text.find(delim, pos)) != std::string::npos) {
+ resultList.push_back(text.substr(pos, next - pos));
+ pos = next + delim.length();
+ }
+ resultList.push_back(text.substr(pos));
+ } else {
+ std::mbstate_t mbs = std::mbstate_t();
+ const char* cpos = text.c_str();
+ int bytes = 0, skip;
+
+ while (*cpos && (skip = std::mbrlen(cpos, text.length() - bytes, &mbs)) > 0) {
+ resultList.push_back(std::string(cpos, skip));
+ cpos += skip;
+ bytes += skip;
+ }
+ }
+
+ return result;
+}
+
+
torrent::Object cmd_string_substr(rpc::target_type target, const torrent::Object::list_type& args) {
const std::string text = string_get_first_arg("substr", args);
@@ -756,6 +1002,36 @@ torrent::Object cmd_string_replace(rpc::target_type target, const torrent::Objec
}
+torrent::Object cmd_string_compare(int mode, const torrent::Object::list_type& args) {
+ const char* opnames[] = {"equals", "startswith", "endswith"};
+ if (args.size() < 2) {
+ throw torrent::input_error("string." + std::string(opnames[mode]) + " takes at least two arguments!");
+ }
+
+ std::string value = string_get_first_arg(opnames[mode], args);
+ torrent::Object::list_const_iterator first = args.begin() + 1, last = args.end();
+
+ for (torrent::Object::list_const_iterator itr = first; itr != last; ++itr) {
+ const std::string& cmp = itr->as_string();
+ switch (mode) {
+ case 0:
+ if (value == cmp) return (int64_t) 1;
+ break;
+ case 1:
+ if (value.substr(0, cmp.length()) == cmp) return (int64_t) 1;
+ break;
+ case 2:
+ if (value.length() >= cmp.length() && value.substr(value.length() - cmp.length()) == cmp) return (int64_t) 1;
+ break;
+ default:
+ throw torrent::input_error("string comparison: internal error (unknown mode)");
+ }
+ }
+
+ return (int64_t) 0;
+}
+
+
torrent::Object cmd_array_at(rpc::target_type target, const torrent::Object::list_type& args) {
if (args.size() != 2) {
throw torrent::input_error("array.at takes at exactly two arguments!");
@@ -820,6 +1096,20 @@ torrent::Object cmd_system_has_methods(bool filter_public) {
}
+torrent::Object cmd_system_client_version_as_value() {
+ int64_t result = 0;
+ const char* pos = PACKAGE_VERSION;
+
+ while (*pos) {
+ result = 100 * result + strtol(pos, (char**)&pos, 10);
+ if (*pos && *pos != '.')
+ throw torrent::input_error("INTERNAL ERROR: Bad version " PACKAGE_VERSION);
+ if (*pos) ++pos;
+ }
+ return result;
+}
+
+
torrent::Object cmd_value(rpc::target_type target, const torrent::Object::list_type& args) {
if (args.size() < 1) {
throw torrent::input_error("'value' takes at least a number argument!");
@@ -1005,20 +1295,34 @@ void initialize_command_pyroscope() {
*/
#if RT_HEX_VERSION <= 0x000906
- // these are merged into 0.9.7+ mainline! (well, maybe, PRs are ignored)
+ // these are merged into 0.9.7+ mainline!
CMD2_ANY_STRING("system.env", _cxxstd_::bind(&cmd_system_env, _cxxstd_::placeholders::_2));
CMD2_ANY("ui.current_view", _cxxstd_::bind(&cmd_ui_current_view));
+#endif
+
+#if RT_HEX_VERSION <= 0x000907
+ // these are merged into 0.9.8+ mainline! (well, maybe, PRs are mostly ignored)
CMD2_ANY_LIST("system.random", &apply_random);
CMD2_ANY_LIST("d.multicall.filtered", _cxxstd_::bind(&d_multicall_filtered, _cxxstd_::placeholders::_2));
#endif
// string.* group
CMD2_ANY_LIST("string.len", &cmd_string_len);
+ CMD2_ANY_LIST("string.join", &cmd_string_join);
+ CMD2_ANY_LIST("string.split", &cmd_string_split);
CMD2_ANY_LIST("string.substr", &cmd_string_substr);
CMD2_ANY_LIST("string.contains", &cmd_string_contains);
CMD2_ANY_LIST("string.contains_i", &cmd_string_contains_i);
CMD2_ANY_LIST("string.map", &cmd_string_map);
CMD2_ANY_LIST("string.replace", &cmd_string_replace);
+ CMD2_ANY_LIST("string.equals", std::bind(&cmd_string_compare, 0, std::placeholders::_2));
+ CMD2_ANY_LIST("string.startswith", std::bind(&cmd_string_compare, 1, std::placeholders::_2));
+ CMD2_ANY_LIST("string.endswith", std::bind(&cmd_string_compare, 2, std::placeholders::_2));
+ CMD2_ANY_LIST("string.strip", std::bind(&cmd_string_strip, 0, std::placeholders::_2));
+ CMD2_ANY_LIST("string.lstrip", std::bind(&cmd_string_strip, -1, std::placeholders::_2));
+ CMD2_ANY_LIST("string.rstrip", std::bind(&cmd_string_strip, 1, std::placeholders::_2));
+ CMD2_ANY_LIST("string.lpad", std::bind(&cmd_string_pad, false, std::placeholders::_2));
+ CMD2_ANY_LIST("string.rpad", std::bind(&cmd_string_pad, true, std::placeholders::_2));
// array.* group
CMD2_ANY_LIST("array.at", &cmd_array_at);
@@ -1047,14 +1351,23 @@ void initialize_command_pyroscope() {
CMD2_ANY("system.has.list", _cxxstd_::bind(&cmd_system_has_list));
CMD2_ANY("system.has.private_methods", _cxxstd_::bind(&cmd_system_has_methods, false));
CMD2_ANY("system.has.public_methods", _cxxstd_::bind(&cmd_system_has_methods, true));
+ CMD2_ANY("system.client_version.as_value", _cxxstd_::bind(&cmd_system_client_version_as_value));
// d.custom.* extensions
CMD2_DL_LIST("d.custom.if_z", _cxxstd_::bind(&retrieve_d_custom_if_z,
_cxxstd_::placeholders::_1, _cxxstd_::placeholders::_2));
+ CMD2_DL_LIST("d.custom.set_if_z", _cxxstd_::bind(&cmd_d_custom_set_if_z,
+ _cxxstd_::placeholders::_1, _cxxstd_::placeholders::_2));
+ CMD2_DL_LIST("d.custom.erase", _cxxstd_::bind(&cmd_d_custom_erase,
+ _cxxstd_::placeholders::_1, _cxxstd_::placeholders::_2));
CMD2_DL_LIST("d.custom.keys", _cxxstd_::bind(&retrieve_d_custom_map,
_cxxstd_::placeholders::_1, true, _cxxstd_::placeholders::_2));
CMD2_DL_LIST("d.custom.items", _cxxstd_::bind(&retrieve_d_custom_map,
_cxxstd_::placeholders::_1, false, _cxxstd_::placeholders::_2));
+ CMD2_DL_STRING("d.custom.toggle", _cxxstd_::bind(&cmd_d_custom_toggle,
+ _cxxstd_::placeholders::_1, _cxxstd_::placeholders::_2));
+ CMD2_DL_STRING("d.custom.as_value", _cxxstd_::bind(&retrieve_d_custom_as_value,
+ _cxxstd_::placeholders::_1, _cxxstd_::placeholders::_2));
// Misc commands
CMD2_ANY_LIST("value", &cmd_value);
@@ -1069,6 +1382,7 @@ void initialize_command_pyroscope() {
CMD2_ANY_STRING("log.messages", _cxxstd_::bind(&cmd_log_messages, _cxxstd_::placeholders::_2));
CMD2_ANY_P("import.return", &cmd_import_return);
+ CMD2_ANY("do", _cxxstd_::bind(&cmd_do, _cxxstd_::placeholders::_1, _cxxstd_::placeholders::_2));
CMD2_DL("d.is_meta", _cxxstd_::bind(&torrent::DownloadInfo::is_meta_download,
_cxxstd_::bind(&core::Download::info, _cxxstd_::placeholders::_1)));
@@ -1078,4 +1392,5 @@ void initialize_command_pyroscope() {
add_capability("colors"); // not monochrome
add_capability("canvas_v2"); // new PS 1.1 canvas with fully dynamic columns
add_capability("collapsed-views"); // pre-collapsed views
+ add_capability("fixed-log-xmlrpc-close");
}
diff --git a/ps-event-view_all.patch b/ps-event-view_all.patch
index 6d38109c667d..1981d08c03ea 100644
--- a/ps-event-view_all.patch
+++ b/ps-event-view_all.patch
@@ -1,21 +1,31 @@
---- orig-096/src/main.cc 2015-09-03 21:03:30.000000000 +0200
-+++ rtorrent-0.9.6/src/main.cc 2017-04-09 14:47:56.000000000 +0200
-@@ -248,2 +249,5 @@
+--- a/src/main.cc
++++ b/src/main.cc
+@@ -249,6 +249,9 @@ main(int argc, char** argv) {
+
+ rpc::parse_command_multiple
(rpc::make_target(),
+ "method.insert = event.view.hide,multi|rlookup|static\n"
+ "method.insert = event.view.show,multi|rlookup|static\n"
+
"method.insert = event.download.inserted,multi|rlookup|static\n"
---- orig-096/src/ui/element_download_list.cc 2015-09-03 21:03:30.000000000 +0200
-+++ rtorrent-0.9.6/src/ui/element_download_list.cc 2017-04-09 14:51:32.000000000 +0200
-@@ -219,5 +219,10 @@
+ "method.insert = event.download.inserted_new,multi|rlookup|static\n"
+ "method.insert = event.download.inserted_session,multi|rlookup|static\n"
+diff --git a/src/ui/element_download_list.cc b/src/ui/element_download_list.cc
+index 3f34bb9..90a769a 100644
+--- a/src/ui/element_download_list.cc
++++ b/src/ui/element_download_list.cc
+@@ -220,7 +220,14 @@ ElementDownloadList::receive_change_view(const std::string& name) {
+ return;
}
+ std::string old_name = view() ? view()->name() : "";
-+ rpc::commands.call_catch("event.view.hide", rpc::make_target(), name,
-+ "View hide event action failed: ");
++ if (!old_name.empty())
++ rpc::commands.call_catch("event.view.hide", rpc::make_target(), name,
++ "View hide event action failed: ");
set_view(*itr);
-+ rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name,
-+ "View show event action failed: ");
++ if (!old_name.empty())
++ rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name,
++ "View show event action failed: ");
}
+ void
diff --git a/ps-fix-log-xmlrpc-close_all.patch b/ps-fix-log-xmlrpc-close_all.patch
new file mode 100644
index 000000000000..1f27201b458d
--- /dev/null
+++ b/ps-fix-log-xmlrpc-close_all.patch
@@ -0,0 +1,22 @@
+--- a/src/thread_worker.cc
++++ b/src/thread_worker.cc
+@@ -113,14 +113,15 @@ ThreadWorker::change_xmlrpc_log() {
+ if (scgi() == NULL)
+ return;
+
+- if (scgi()->log_fd() != -1)
++ if (scgi()->log_fd() != -1) {
+ ::close(scgi()->log_fd());
+-
+- if (m_xmlrpcLog.empty()) {
++ scgi()->set_log_fd(-1);
+ control->core()->push_log("Closed XMLRPC log.");
+- return;
+ }
+
++ if (m_xmlrpcLog.empty())
++ return;
++
+ scgi()->set_log_fd(open(rak::path_expand(m_xmlrpcLog).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644));
+
+ if (scgi()->log_fd() == -1) {
diff --git a/ui_pyroscope.cc b/ui_pyroscope.cc
index a535f8fee300..06db8c6725c7 100644
--- a/ui_pyroscope.cc
+++ b/ui_pyroscope.cc
@@ -1,6 +1,8 @@
/*
⋅ ⋅⋅ ” ’ ♯ ☢ ☍ ⌘ ✰ ⣿ ⚡ ☯ ⚑ ↺ ⤴ ⤵ ∆ ⌚ ≀∇ ✇ ⚠ ◔ ⚡ ↯ ¿ ⨂ ✖ ⇣ ⇡ ⠁ ⠉ ⠋ ⠛ ⠟ ⠿ ⡿ ⣿ ☹ ➀ ➁ ➂ ➃ ➄ ➅ ➆ ➇ ➈ ➉ ▹ ╍ ▪ ⚯ ⚒ ◌ ⇅ ↡ ↟ ⊛ ♺
+⎆ ㋛ ㋡
+
⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳
@@ -141,21 +143,6 @@ static std::string network_history_up_str;
static std::string network_history_down_str;
-// Chop off an UTF-8 string
-std::string u8_chop(const std::string& text, size_t glyphs) {
- std::mbstate_t mbs = std::mbstate_t();
- size_t bytes = 0, skip;
- const char* pos = text.c_str();
-
- while (*pos && glyphs-- > 0 && (skip = std::mbrlen(pos, text.length() - bytes, &mbs)) > 0) {
- pos += skip;
- bytes += skip;
- }
-
- return bytes < text.length() ? text.substr(0, bytes) : text;
-}
-
-
// get custom field contaioning a long (time_t)
unsigned long get_custom_long(core::Download* d, const char* name) {
try {
@@ -416,7 +403,7 @@ int64_t cmd_d_message_alert(core::Download* d) {
alert = ps::ALERT_REQUEST;
else if (d->message().find("not registered") != std::string::npos
|| d->message().find("torrent cannot be found") != std::string::npos
- || d->message().find("unregistered") != std::string::npos)
+ || d->message().find("nregistered") != std::string::npos)
alert = ps::ALERT_GONE;
else if (d->message().find("not authorized") != std::string::npos
|| d->message().find("blocked from") != std::string::npos
@@ -424,12 +411,32 @@ int64_t cmd_d_message_alert(core::Download* d) {
|| d->message().find("limit exceeded") != std::string::npos
|| d->message().find("active torrents are enough") != std::string::npos)
alert = ps::ALERT_PERMS;
+ else if (d->message().find("tracker is down") != std::string::npos)
+ alert = ps::ALERT_DOWN;
+ else if (d->message().find("n't resolve host name") != std::string::npos)
+ alert = ps::ALERT_DNS;
}
return alert;
}
+std::string get_active_tracker_alias(torrent::Download* item) {
+ std::string url = get_active_tracker_domain(item);
+ if (!url.empty()) {
+ std::string alias = tracker_aliases[url];
+ if (!alias.empty()) url = alias;
+ }
+
+ return url;
+}
+
+
+torrent::Object cmd_d_tracker_alias(core::Download* download) {
+ return get_active_tracker_alias(download->download());
+}
+
+
static void decorate_download_title(Window* window, display::Canvas* canvas, core::View* view,
int pos, Range& range, int x_title) {
int offset = row_offset(view, range);
@@ -451,10 +458,8 @@ static void decorate_download_title(Window* window, display::Canvas* canvas, cor
// show label for active tracker (a/k/a in focus tracker)
if (int(canvas->width()) <= x_title + NAME_RESERVED_WIDTH + 3) return;
- std::string url = get_active_tracker_domain((*range.first)->download());
+ std::string url = get_active_tracker_alias((*range.first)->download());
if (url.empty()) return;
- std::string alias = tracker_aliases[url];
- if (!alias.empty()) url = alias;
// shorten label if too long
int max_len = std::min(TRACKER_LABEL_WIDTH,
@@ -575,6 +580,27 @@ void ui_pyroscope_download_list_redraw_item(Window* window, display::Canvas* can
}
+torrent::Object ui_column_spec(rpc::target_type target, const torrent::Object::list_type& args) {
+ if (args.size() != 1) {
+ throw torrent::input_error("ui.column.spec takes exactly one argument!");
+ }
+ int64_t colidx_wanted = parse_value_arg(*args.begin());
+ std::string spec;
+
+ const torrent::Object::map_type& column_defs = control->object_storage()->get_str("ui.column.render").as_map();
+ torrent::Object::map_const_iterator cols_itr, last_col = column_defs.end();
+
+ for (cols_itr = column_defs.begin(); cols_itr != last_col; ++cols_itr) {
+ char* header_pos = 0;
+ int64_t colidx = strtol(cols_itr->first.c_str(), &header_pos, 10);
+ if (header_pos[0] == ':' && colidx == colidx_wanted)
+ spec = cols_itr->first;
+ }
+
+ return spec;
+}
+
+
torrent::Object ui_column_hide(rpc::target_type target, const torrent::Object::list_type& args) {
for(torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; ++itr) {
int64_t colidx = parse_value_arg(*itr);
@@ -617,6 +643,24 @@ torrent::Object ui_column_hidden_list() {
}
+torrent::Object ui_column_sacrificial_list() {
+ torrent::Object result = torrent::Object::create_list();
+ torrent::Object::list_type& resultList = result.as_list();
+
+ const torrent::Object::map_type& column_defs = control->object_storage()->get_str("ui.column.render").as_map();
+ torrent::Object::map_const_iterator cols_itr, last_col = column_defs.end();
+
+ for (cols_itr = column_defs.begin(); cols_itr != last_col; ++cols_itr) {
+ char* header_pos = 0;
+ int64_t colidx = strtol(cols_itr->first.c_str(), &header_pos, 10);
+ if (header_pos[0] == ':' && header_pos[1] == '?')
+ resultList.push_back(colidx);
+ }
+
+ return result;
+}
+
+
// Render columns from `column_defs`, return total length
int render_columns(bool headers, bool narrow, rpc::target_type target, core::Download* item,
display::Canvas* canvas, int column, int pos, int offset,
@@ -1032,16 +1076,20 @@ void initialize_command_ui_pyroscope() {
CMD2_ANY_LIST("trackers.alias.set_key", &cmd_trackers_alias_set_key);
CMD2_ANY("trackers.alias.items", _cxxstd_::bind(&cmd_trackers_alias_items, _cxxstd_::placeholders::_1));
+ CMD2_DL("d.tracker_alias", _cxxstd_::bind(&display::cmd_d_tracker_alias, _cxxstd_::placeholders::_1));
CMD2_DL("d.message.alert", _cxxstd_::bind(&display::cmd_d_message_alert, _cxxstd_::placeholders::_1));
CMD2_ANY ("ui.canvas_color", _cxxstd_::bind(&display::ui_canvas_color_get));
CMD2_ANY_STRING ("ui.canvas_color.set", _cxxstd_::bind(&display::ui_canvas_color_set, _cxxstd_::placeholders::_2));
+ CMD2_ANY_LIST("ui.column.spec", &display::ui_column_spec);
CMD2_ANY_LIST("ui.column.hide", &display::ui_column_hide);
CMD2_ANY_LIST("ui.column.show", &display::ui_column_show);
CMD2_ANY_LIST("ui.column.is_hidden", &display::ui_column_is_hidden);
CMD2_ANY("ui.column.hidden.list", _cxxstd_::bind(&display::ui_column_hidden_list));
+ CMD2_ANY("ui.column.sacrificial.list", _cxxstd_::bind(&display::ui_column_sacrificial_list));
+ CMD2_VAR_VALUE("ui.column.sacrificed", 0);
PS_VARIABLE_COLOR("ui.color.progress0", "red");
PS_VARIABLE_COLOR("ui.color.progress20", "bold bright red");
@@ -1101,8 +1149,18 @@ void initialize_command_ui_pyroscope() {
// Multi-method to store column definitions
"method.insert = ui.column.render, multi|rlookup|static\n"
+ // Toggle sacrificial columns manually (bound to '/' key)
+ "method.insert = ui.column.sacrificed.toggle, simple, \""
+ "branch = (ui.column.sacrificed), ((ui.column.sacrificed.set, 0)), ((ui.column.sacrificed.set, 1)) ; "
+ "branch = (ui.column.sacrificed),"
+ " \\\"ui.column.show = (ui.column.sacrificial.list)\\\","
+ " \\\"ui.column.hide = (ui.column.sacrificial.list)\\\" ; "
+ "ui.current_view.set = (ui.current_view)\"\n"
+ "schedule2 = column_sacrificed_toggle, 0, 0, ((ui.bind_key,download_list,/,ui.column.sacrificed.toggle=))\n"
+
// Bind '*' to toggle between collapsed and expanded display
- "schedule2 = collapsed_view_toggle, 0, 0, ((ui.bind_key,download_list,*,view.collapsed.toggle=))\n"
+ "schedule2 = collapsed_view_toggle, 0, 0, ((ui.bind_key, download_list, *, \""
+ "view.collapsed.toggle= ; ui.current_view.set = (ui.current_view)\"))\n"
// Collapse built-in views
"view.collapsed.toggle = main\n"
@@ -1155,7 +1213,8 @@ void initialize_command_ui_pyroscope() {
// Status flags (❢ ☢ ☍ ⌘)
"method.set_key = ui.column.render, \"100:3C95/2:❢ \","
- " ((array.at, {\" \", \"♺ \", \"⚠ \", \"◔ \", \"⚡ \", \"↯ \", \"¿?\", \"⨂ \"}, ((d.message.alert)) ))\n"
+ " ((array.at, {\" \", \"♺ \", \"⚠ \", \"◔ \", \"⚡ \", \"↯ \", \"¿?\","
+ " \"⨂ \", \"⋫ \", \"☡ \"}, ((d.message.alert)) ))\n"
"method.set_key = ui.column.render, \"110:2C92/2:☢ \","
" ((string.map, ((cat, ((d.is_open)), ((d.is_active)))), {00, \"▪ \"}, {01, \"▪ \"}, {10, \"╍ \"}, {11, \"▹ \"}))\n"
"method.set_key = ui.column.render, \"120:?2:☍ \","
@@ -1169,7 +1228,7 @@ void initialize_command_ui_pyroscope() {
"method.set_key = ui.column.render, \"420:?3C14/3: ⤵ \", ((convert.magnitude, ((d.tracker_scrape.incomplete)) ))\n"
// Traffic indicator (⚡)
- "method.set_key = ui.column.render, \"500:?2:⚡ \","
+ "method.set_key = ui.column.render, \"500:?2:↕ \","
" ((string.map, ((cat, ((not, ((d.up.rate)) )), ((not, ((d.down.rate)) )) )),"
" {00, \"⇅ \"}, {01, \"↟ \"}, {10, \"↡ \"}, {11, \"  \"} ))\n"
@@ -1179,13 +1238,13 @@ void initialize_command_ui_pyroscope() {
// Up|Leech Time / Down|Completion or Loaded Time
// TODO: Could use "d.timestamp.started" and "d.timestamp.finished" here, but need to check
// when they were introduced, and if they're always set (e.g. what about fast-resumed items?)
- "method.set_key = ui.column.render, \"520:6C96/6:∆⋮ ⌛ \","
+ "method.set_key = ui.column.render, \"520:6C96/6:∆⋮ ⟲ \","
" ((if, ((d.up.rate)),"
" ((convert.human_size, ((d.up.rate)), ((value, 10)) )),"
" ((convert.time_delta, ((value, ((d.custom, tm_completed)) )),"
" ((value, ((d.custom.if_z, tm_started, ((d.custom, tm_loaded)) )) )) ))"
" ))\n"
- "method.set_key = ui.column.render, \"530:6C90/6:∇⋮ ⌚ \","
+ "method.set_key = ui.column.render, \"530:6C90/6:∇⋮ ◷ \","
" ((if, ((d.down.rate)),"
" ((convert.human_size, ((d.down.rate)), ((value, 10)) )),"
" ((convert.time_delta, ((value, ((d.custom.if_z, tm_completed, ((d.custom, tm_loaded)) )) )) ))"
@@ -1204,10 +1263,10 @@ void initialize_command_ui_pyroscope() {
//⠀" ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ "
"method.set_key = ui.column.render, \"920:3C93/3:☯ \","
" ((string.substr, \"☹ ➀ ➁ ➂ ➃ ➄ ➅ ➆ ➇ ➈ ➉ \", ((math.mul, 2, ((math.div, ((d.ratio)), 1000)) )), 2, \"⊛ \"))\n"
- // "☹ ➀ ➁ ➂ ➃ ➄ ➅ ➆ ➇ ➈ ➉ "
+ // "☹ ➀ ➁ ➂ ➃ ➄ ➅ ➆ ➇ ➈ ➉ " "😇 "
// "☹ ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ "
// "☹ ➊ ➋ ➌ ➍ ➎ ➏ ➐ ➑ ➒ ➓ "
- "method.set_key = ui.column.render, \"930:5C15/3C21/2: ✇ \","
+ "method.set_key = ui.column.render, \"930:5C15/3C21/2: ⛁ \","
" ((convert.human_size, ((d.size_bytes)) ))\n"
// Explicitly managed status (✰ = prio; ⚑ = tagged)
diff --git a/ui_pyroscope.h b/ui_pyroscope.h
index cb831c449891..aee5ada5afde 100644
--- a/ui_pyroscope.h
+++ b/ui_pyroscope.h
@@ -17,6 +17,8 @@ enum AlertKind {
ALERT_REQUEST,
ALERT_GONE,
ALERT_PERMS,
+ ALERT_DOWN,
+ ALERT_DNS,
ALERT_MAX
};
@@ -67,6 +69,9 @@ enum ColorKind {
} // namespace
-extern void add_capability(const char* name); // defined in command_pyroscope.cc
+// defined in command_pyroscope.cc (exported here so we only have to patch in one .h)
+extern void add_capability(const char* name);
+extern size_t u8_length(const std::string& text);
+extern std::string u8_chop(const std::string& text, size_t glyphs);
#endif