diff options
author | xsmile | 2018-06-04 15:20:31 +0200 |
---|---|---|
committer | xsmile | 2018-06-04 15:20:31 +0200 |
commit | d5f9faa36269d5ce2144d93ab4622f3d67e102fb (patch) | |
tree | ee3300867bac5924804e985b1105a1425580ecb8 /command_pyroscope.cc | |
parent | 8a6e454d1f806fd9e7ca74d922b24e76e7a5e79e (diff) | |
download | aur-d5f9faa36269d5ce2144d93ab4622f3d67e102fb.tar.gz |
update
Diffstat (limited to 'command_pyroscope.cc')
-rw-r--r-- | command_pyroscope.cc | 458 |
1 files changed, 406 insertions, 52 deletions
diff --git a/command_pyroscope.cc b/command_pyroscope.cc index 480158e09294..98e854329701 100644 --- a/command_pyroscope.cc +++ b/command_pyroscope.cc @@ -21,15 +21,15 @@ #include <cstdio> #include <climits> #include <ctime> +#include <cwchar> +#include <set> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <rak/path.h> +#include <rak/algorithm.h> #include <rak/functional.h> #include <rak/functional_fun.h> -#if RT_HEX_VERSION < 0x000904 - #include <sigc++/adaptors/bind.h> -#endif #include "core/download.h" #include "core/manager.h" @@ -46,12 +46,13 @@ #include "control.h" #include "command_helpers.h" -#if (RT_HEX_VERSION >= 0x000901) - #define _cxxstd_ tr1 -#else - #define _cxxstd_ std -#endif +// In 0.9.x this changed to 'tr1' (dropping sigc::bind), see https://stackoverflow.com/a/4682954/2748717 +// "C++ Technical Report 1" was later added to "C++11", using tr1 makes stuff compile on older GCC +#define _cxxstd_ tr1 + +// List of system capabilities for `system.has` command +static std::set<std::string> system_capabilities; // handle for message log file namespace core { @@ -180,7 +181,7 @@ torrent::Tracker* get_active_tracker(torrent::Download* item) { torrent::TrackerList* tl = item->tracker_list(); torrent::Tracker* tracker = 0; - for (int trkidx = 0; trkidx < tl->size(); trkidx++) { + 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) { @@ -224,6 +225,29 @@ std::string get_active_tracker_domain(torrent::Download* item) { } +// return various scrape information of the "main" tracker for this download item +int64_t get_active_tracker_scrape_info(const int operation, torrent::Download* item) { + int64_t scrape_num = 0; + torrent::Tracker* tracker = get_active_tracker(item); + + if (tracker) { + switch (operation) { + case 1: + scrape_num = tracker->scrape_downloaded(); + break; + case 2: + scrape_num = tracker->scrape_complete(); + break; + case 3: + scrape_num = tracker->scrape_incomplete(); + break; + } + } + + return scrape_num; +} + + /* @DOC `compare = <order>, <sort_key>=[, ...]` @@ -355,12 +379,8 @@ torrent::Object apply_ui_bind_key(rpc::target_type target, const torrent::Object switch (displayType) { case ui::DownloadList::DISPLAY_DOWNLOAD_LIST: display->bindings()[key] = -#if RT_HEX_VERSION < 0x000904 - sigc::bind(sigc::mem_fun(*(ui::ElementDownloadList*)display, &ui::ElementDownloadList::receive_command), -#else _cxxstd_::bind(&ui::ElementDownloadList::receive_command, (ui::ElementDownloadList*)display, -#endif - bound_commands[displayType][key].c_str()); + bound_commands[displayType][key].c_str()); break; default: return torrent::Object(); @@ -471,6 +491,46 @@ torrent::Object cmd_log_messages(const torrent::Object::string_type& arg) { } +torrent::Object cmd_import_return(rpc::target_type target, const torrent::Object& args) { + // Handled in src/rpc/parse_commands.cc::parse_command_file via patch + throw torrent::input_error("import.return"); +} + + +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()) + throw torrent::bencode_error("d.custom.if_z: Missing key argument."); + const std::string& key = (itr++)->as_string(); + if (key.empty()) + throw torrent::bencode_error("d.custom.if_z: Empty key argument."); + if (itr == args.end()) + 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); + } catch (torrent::bencode_error& e) { + return itr->as_string(); + } +} + + +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."); + + torrent::Object result = keys_only ? torrent::Object::create_list() : torrent::Object::create_map(); + torrent::Object::map_type& entries = download->bencode()->get_key("rtorrent").get_key("custom").as_map(); + + for (torrent::Object::map_type::const_iterator itr = entries.begin(), last = entries.end(); itr != last; itr++) { + if (keys_only) result.as_list().push_back(itr->first); + else result.as_map()[itr->first] = itr->second; + } + + return result; +} + + torrent::Object d_multicall_filtered(const torrent::Object::list_type& args) { if (args.size() < 2) @@ -534,6 +594,99 @@ 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!"); + } + torrent::Object::list_const_iterator itr = args.begin(); + return itr->as_string(); +} + + +static int64_t string_get_value_arg(const char* name, torrent::Object::list_const_iterator& itr) { + int64_t result = 0; + if (itr->is_string()) { + char* junk = 0; + result = strtol(itr->as_string().c_str(), &junk, 10); + if (*junk) { + throw torrent::input_error("string." + std::string(name) + ": " + "junk at end of value: " + itr->as_string()); + } + } else { + result = itr->as_value(); + } + + ++itr; + return result; +} + + +torrent::Object cmd_string_len(rpc::target_type target, const torrent::Object::list_type& args) { + std::mbstate_t mbs = std::mbstate_t(); + std::string text = string_get_first_arg("len", args); + const char* pos = text.c_str(); + int glyphs = 0, bytes = 0, skip; + + while (*pos && (skip = std::mbrlen(pos, text.length() - bytes, &mbs)) > 0) { + pos += skip; + bytes += skip; + ++glyphs; + } + + return (int64_t) glyphs; +} + + +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); + + torrent::Object::list_const_iterator itr = args.begin() + 1; + int64_t glyphs = 0, count = text.length(); + std::string fallback; + if (itr != args.end()) glyphs = string_get_value_arg("substr(pos)", itr); + if (itr != args.end()) count = string_get_value_arg("substr(count)", itr); + if (itr != args.end()) fallback = (itr++)->as_string(); + + if (count < 0) { + throw torrent::input_error("string.substr: Invalid negative count!"); + } + + std::mbstate_t mbs = std::mbstate_t(); + const char* pos = text.c_str(); + int bytes = 0, skip; + + if (glyphs < 0) { + std::string::size_type offsets[text.length() + 1]; + int64_t idx = 0; + while (*pos && (skip = std::mbrlen(pos, text.length() - bytes, &mbs)) > 0) { + offsets[idx++] = bytes; + pos += skip; + bytes += skip; + } + offsets[idx] = bytes; + + int64_t begidx = std::max(idx + glyphs, 0L); + int64_t endidx = std::min(idx, begidx + count); + return text.substr(offsets[begidx], offsets[endidx] - offsets[begidx]); + } + + while (glyphs-- > 0 && *pos && (skip = std::mbrlen(pos, text.length() - bytes, &mbs)) > 0) { + pos += skip; + bytes += skip; + } + if (!*pos) return fallback; + + int bytes_pos = bytes, bytes_count = 0; + while (count-- > 0 && *pos && (skip = std::mbrlen(pos, text.length() - bytes, &mbs)) > 0) { + pos += skip; + bytes += skip; + bytes_count += skip; + } + + return text.substr(bytes_pos, bytes_count); +} + + torrent::Object::value_type apply_string_contains(bool ignore_case, const torrent::Object::list_type& args) { if (args.size() < 2) { throw torrent::input_error("string.contains[_i] takes at least two arguments!"); @@ -603,6 +756,70 @@ torrent::Object cmd_string_replace(rpc::target_type target, const torrent::Objec } +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!"); + } + + torrent::Object::list_const_iterator itr = args.begin(); + torrent::Object::list_type array = (itr++)->as_list(); + torrent::Object::value_type index = (itr++)->as_value(); + + if (array.empty()) { + throw torrent::input_error("array.at: array is empty!"); + } + if (index < 0 || int(array.size()) <= index) { + throw torrent::input_error("array.at: index out of bounds!"); + } + + return array.at(index); +} + + +void add_capability(const char* name) { + system_capabilities.insert(name); +} + + +torrent::Object cmd_system_has(const torrent::Object::string_type& arg) { + if (arg.empty()) { + throw torrent::input_error("Passed empty string to 'system.has'!"); + } + + bool result = (system_capabilities.count(arg) != 0); + if (!result && '=' == arg.at(arg.size()-1)) { + result = rpc::commands.has(arg.substr(0, arg.size()-1)); + } + return (int64_t) result; +} + + +torrent::Object cmd_system_has_list() { + torrent::Object result = torrent::Object::create_list(); + torrent::Object::list_type& resultList = result.as_list(); + + for (std::set<std::string>::const_iterator itr = system_capabilities.begin(); itr != system_capabilities.end(); itr++) { + resultList.push_back(*itr); + } + + return result; +} + + +torrent::Object cmd_system_has_methods(bool filter_public) { + torrent::Object result = torrent::Object::create_list(); + torrent::Object::list_type& resultList = result.as_list(); + + for (rpc::CommandMap::const_iterator itr = rpc::commands.begin(), last = rpc::commands.end(); itr != last; itr++) { + if (bool(itr->second.m_flags & rpc::CommandMap::flag_public_xmlrpc) == filter_public) { + resultList.push_back(itr->first); + } + } + + 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!"); @@ -630,40 +847,130 @@ torrent::Object cmd_value(rpc::target_type target, const torrent::Object::list_t } -// Backports from 0.9.2 -#if (API_VERSION < 3) -template <typename InputIterator, typename OutputIterator> OutputIterator -pyro_transform_hex(InputIterator first, InputIterator last, OutputIterator dest) { - const char* hex = "0123456789abcdef"; - while (first != last) { - *(dest++) = (*first >> 4)[hex]; - *(dest++) = (*first & 15)[hex]; +torrent::Object cmd_d_tracker_domain(core::Download* download) { + return get_active_tracker_domain(download->download()); +} - ++first; - } - return dest; +torrent::Object cmd_d_tracker_scrape_info(const int operation, core::Download* download) { + return get_active_tracker_scrape_info(operation, download->download()); } -torrent::Object d_chunks_seen(core::Download* download) { - const uint8_t* seen = download->download()->chunks_seen(); +// MATH FUNCTIONS - if (seen == NULL) - return std::string(); +inline std::vector<int64_t> as_vector(const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in as_vector."); - uint32_t size = download->download()->file_list()->size_chunks(); - std::string result; - result.resize(size * 2); - pyro_transform_hex((const char*)seen, (const char*)seen + size, result.begin()); + std::vector<int64_t> result; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + if (itr->is_value()) { + result.push_back(itr->as_value()); + } else if (itr->is_string()) { + result.push_back(rpc::convert_to_value(itr->as_string())); + } else if (itr->is_list()) { + std::vector<int64_t> subResult = as_vector(itr->as_list()); + result.insert(result.end(), subResult.begin(), subResult.end()); + } else { + throw torrent::input_error("Wrong type supplied to as_vector."); + } + } return result; } -#endif -torrent::Object cmd_d_tracker_domain(core::Download* download) { - return get_active_tracker_domain(download->download()); +int64_t apply_math_basic(const char* name, const std::function<int64_t(int64_t,int64_t)> op, + const torrent::Object::list_type& args) { + int64_t val = 0, rhs = 0; + bool divides = !strcmp(name, "math.div") || !strcmp(name, "math.mod"); + + if (args.size() == 0) + throw torrent::input_error(std::string(name) + ": No arguments provided!"); + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + if (itr->is_value()) { + rhs = itr->as_value(); + } else if (itr->is_string()) { + rhs = rpc::convert_to_value(itr->as_string()); + } else if (itr->is_list()) { + rhs = apply_math_basic(name, op, itr->as_list()); + } else { + throw torrent::input_error(std::string(name) + ": Wrong argument type"); + } + + if (divides && !rhs && itr != args.begin()) + throw torrent::input_error(std::string(name) + ": Division by zero!"); + val = itr == args.begin() ? rhs : op(val, rhs); + } + + return val; +} + + +int64_t apply_arith_basic(const std::function<int64_t(int64_t,int64_t)> op, + const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_basic."); + + int64_t val = 0; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + if (itr->is_value()) { + val = itr == args.begin() ? itr->as_value() + : (op(val, itr->as_value()) ? val : itr->as_value()); + } else if (itr->is_string()) { + int64_t cval = rpc::convert_to_value(itr->as_string()); + val = itr == args.begin() ? cval : (op(val, cval) ? val : cval); + } else if (itr->is_list()) { + int64_t fval = apply_arith_basic(op, itr->as_list()); + val = itr == args.begin() ? fval : (op(val, fval) ? val : fval); + } else { + throw torrent::input_error("Wrong type supplied to apply_arith_basic."); + } + } + + return val; +} + + +int64_t apply_arith_count(const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_count."); + + int64_t val = 0; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + switch (itr->type()) { + case torrent::Object::TYPE_VALUE: + case torrent::Object::TYPE_STRING: + val++; + break; + case torrent::Object::TYPE_LIST: + val += apply_arith_count(itr->as_list()); + break; + default: + throw torrent::input_error("Wrong type supplied to apply_arith_count."); + } + } + + return val; +} + +int64_t apply_arith_other(const char* op, const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_other."); + + if (strcmp(op, "average") == 0) { + return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args)); + } else if (strcmp(op, "median") == 0) { + std::vector<int64_t> result = as_vector(args); + return (int64_t)rak::median(result.begin(), result.end()); + } else { + throw torrent::input_error("Wrong operation supplied to apply_arith_other."); + } } @@ -688,16 +995,14 @@ torrent::Object cmd_ui_current_view() { void initialize_command_pyroscope() { -// Backports from 0.9.2 -#if (API_VERSION < 3) - // https://github.com/rakshasa/rtorrent/commit/b28f2ea8070 - // https://github.com/rakshasa/rtorrent/commit/020de10f38210a07a567aeebbe385a4faaf4b517 - CMD2_DL("d.chunks_seen", _cxxstd_::bind(&d_chunks_seen, _cxxstd_::placeholders::_1)); - - // https://github.com/rakshasa/rtorrent/commit/5bed4f01ad - CMD2_TRACKER("t.is_usable", _cxxstd_::bind(&torrent::Tracker::is_usable, _cxxstd_::placeholders::_1)); - CMD2_TRACKER("t.is_busy", _cxxstd_::bind(&torrent::Tracker::is_busy, _cxxstd_::placeholders::_1)); -#endif + /* + *_ANY – no arguments (signature `cmd_*()`) + *_ANY_P – the 'P' means 'private' + *_STRING – takes (one?) string argument + *_LIST – takes any number of arguments + *_DL, *_DL_LIST – function gets a `core::Download*` as first parameter + *_VAR_VALUE – define a value, with getter and setter, and a default + */ #if RT_HEX_VERSION <= 0x000906 // these are merged into 0.9.7+ mainline! (well, maybe, PRs are ignored) @@ -707,20 +1012,69 @@ void initialize_command_pyroscope() { CMD2_ANY_LIST("d.multicall.filtered", _cxxstd_::bind(&d_multicall_filtered, _cxxstd_::placeholders::_2)); #endif - CMD2_ANY("throttle.names", _cxxstd_::bind(&cmd_throttle_names)); + // string.* group + CMD2_ANY_LIST("string.len", &cmd_string_len); + 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); + + // array.* group + CMD2_ANY_LIST("array.at", &cmd_array_at); + + // math.* group + CMD2_ANY_LIST("math.add", std::bind(&apply_math_basic, "math.add", std::plus<int64_t>(), std::placeholders::_2)); + CMD2_ANY_LIST("math.sub", std::bind(&apply_math_basic, "math.sub", std::minus<int64_t>(), std::placeholders::_2)); + CMD2_ANY_LIST("math.mul", std::bind(&apply_math_basic, "math.mul", std::multiplies<int64_t>(), std::placeholders::_2)); + CMD2_ANY_LIST("math.div", std::bind(&apply_math_basic, "math.div", std::divides<int64_t>(), std::placeholders::_2)); + CMD2_ANY_LIST("math.mod", std::bind(&apply_math_basic, "math.mod", std::modulus<int64_t>(), std::placeholders::_2)); + CMD2_ANY_LIST("math.min", std::bind(&apply_arith_basic, std::less<int64_t>(), std::placeholders::_2)); + CMD2_ANY_LIST("math.max", std::bind(&apply_arith_basic, std::greater<int64_t>(), std::placeholders::_2)); + CMD2_ANY_LIST("math.cnt", std::bind(&apply_arith_count, std::placeholders::_2)); + CMD2_ANY_LIST("math.avg", std::bind(&apply_arith_other, "average", std::placeholders::_2)); + CMD2_ANY_LIST("math.med", std::bind(&apply_arith_other, "median", std::placeholders::_2)); + + // ui.focus.* – quick paging + CMD2_ANY("ui.focus.home", _cxxstd_::bind(&cmd_ui_focus_home)); + CMD2_ANY("ui.focus.end", _cxxstd_::bind(&cmd_ui_focus_end)); + CMD2_ANY("ui.focus.pgup", _cxxstd_::bind(&cmd_ui_focus_pgup)); + CMD2_ANY("ui.focus.pgdn", _cxxstd_::bind(&cmd_ui_focus_pgdn)); + CMD2_VAR_VALUE("ui.focus.page_size", 50); + + // system.has.* + CMD2_ANY_STRING("system.has", _cxxstd_::bind(&cmd_system_has, _cxxstd_::placeholders::_2)); + 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)); + + // 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.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)); + + // Misc commands CMD2_ANY_LIST("value", &cmd_value); CMD2_ANY_LIST("compare", &apply_compare); CMD2_ANY("ui.bind_key", &apply_ui_bind_key); CMD2_VAR_VALUE("ui.bind_key.verbose", 1); + CMD2_ANY("throttle.names", _cxxstd_::bind(&cmd_throttle_names)); CMD2_DL("d.tracker_domain", _cxxstd_::bind(&cmd_d_tracker_domain, _cxxstd_::placeholders::_1)); + CMD2_DL("d.tracker_scrape.downloaded", _cxxstd_::bind(&cmd_d_tracker_scrape_info, 1, _cxxstd_::placeholders::_1)); + CMD2_DL("d.tracker_scrape.complete", _cxxstd_::bind(&cmd_d_tracker_scrape_info, 2, _cxxstd_::placeholders::_1)); + CMD2_DL("d.tracker_scrape.incomplete", _cxxstd_::bind(&cmd_d_tracker_scrape_info, 3, _cxxstd_::placeholders::_1)); + CMD2_ANY_STRING("log.messages", _cxxstd_::bind(&cmd_log_messages, _cxxstd_::placeholders::_2)); - CMD2_ANY("ui.focus.home", _cxxstd_::bind(&cmd_ui_focus_home)); - CMD2_ANY("ui.focus.end", _cxxstd_::bind(&cmd_ui_focus_end)); - CMD2_ANY("ui.focus.pgup", _cxxstd_::bind(&cmd_ui_focus_pgup)); - CMD2_ANY("ui.focus.pgdn", _cxxstd_::bind(&cmd_ui_focus_pgdn)); - CMD2_VAR_VALUE("ui.focus.page_size", 50); + CMD2_ANY_P("import.return", &cmd_import_return); + CMD2_DL("d.is_meta", _cxxstd_::bind(&torrent::DownloadInfo::is_meta_download, + _cxxstd_::bind(&core::Download::info, _cxxstd_::placeholders::_1))); + + // List capabilities of this build + add_capability("system.has"); // self + add_capability("rtorrent-ps"); // obvious + add_capability("colors"); // not monochrome + add_capability("canvas_v2"); // new PS 1.1 canvas with fully dynamic columns } |