mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-15 06:32:31 +00:00
* Allow inlining functions in commands with parantheses, e.g:
print = (convert.time,(system.time)) schedule2 = test_print_2,2,2,((print,"current ",((convert.time,((system.time)))))) Multiple parantheses need to be used in schedule to ensure the command doesn't get evaluated at the time of calling schedule. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@1191 e378c898-3ddf-0310-93e7-cc216c733640
This commit is contained in:
+50
-1
@@ -57,6 +57,55 @@ system_method_generate_command(torrent::Object::list_const_iterator first, torre
|
||||
return command;
|
||||
}
|
||||
|
||||
void
|
||||
system_method_generate_command2(torrent::Object* object, torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last) {
|
||||
if (first == last) {
|
||||
// TODO: Use empty object.
|
||||
*object = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (first->is_string()) {
|
||||
std::string command;
|
||||
|
||||
while (first != last) {
|
||||
if (!command.empty())
|
||||
command += " ;";
|
||||
|
||||
command += (first++)->as_string();
|
||||
}
|
||||
|
||||
*object = command;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (first + 1 == last) {
|
||||
if (!first->is_dict_key())
|
||||
throw torrent::input_error("New command of wrong type.");
|
||||
|
||||
*object = *first;
|
||||
|
||||
uint32_t flags = object->flags() & torrent::Object::mask_function;
|
||||
object->unset_flags(torrent::Object::mask_function);
|
||||
object->set_flags((flags >> 1) & torrent::Object::mask_function);
|
||||
|
||||
} else {
|
||||
*object = torrent::Object::create_list();
|
||||
|
||||
while (first != last) {
|
||||
if (!first->is_dict_key())
|
||||
throw torrent::input_error("New command of wrong type.");
|
||||
|
||||
object->as_list().push_back(*first++);
|
||||
|
||||
uint32_t flags = object->as_list().back().flags() & torrent::Object::mask_function;
|
||||
object->as_list().back().unset_flags(torrent::Object::mask_function);
|
||||
object->as_list().back().set_flags((flags >> 1) & torrent::Object::mask_function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// torrent::Object
|
||||
// system_method_insert_function(const torrent::Object::list_type& args, int flags) {
|
||||
|
||||
@@ -86,7 +135,7 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
||||
value = itrArgs != args.end() ? rpc::convert_to_string(*itrArgs) : "";
|
||||
break;
|
||||
case rpc::object_storage::flag_function_type:
|
||||
value = itrArgs != args.end() ? system_method_generate_command(itrArgs, args.end()) : "";
|
||||
system_method_generate_command2(&value, itrArgs, args.end());
|
||||
break;
|
||||
case rpc::object_storage::flag_multi_type:
|
||||
break;
|
||||
|
||||
@@ -186,9 +186,8 @@ apply_schedule(const torrent::Object::list_type& args) {
|
||||
const std::string& arg1 = (itr++)->as_string();
|
||||
const std::string& arg2 = (itr++)->as_string();
|
||||
const std::string& arg3 = (itr++)->as_string();
|
||||
const std::string& arg4 = (itr++)->as_string();
|
||||
|
||||
control->command_scheduler()->parse(arg1, arg2, arg3, arg4);
|
||||
control->command_scheduler()->parse(arg1, arg2, arg3, *itr);
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
+10
-9
@@ -212,9 +212,10 @@ main(int argc, char** argv) {
|
||||
// "method.insert = test.value2,value,6\n"
|
||||
|
||||
// "method.insert = test.string,string,6\n"
|
||||
// "method.insert = test.bool,bool,true\n"
|
||||
// "method.insert = test.bool,bool,true\n"
|
||||
|
||||
"method.insert = test.method.simple,simple,\"print=simple_test_,$argument.0=\"\n"
|
||||
// "method.insert.simple = test.method.simple,((print,simple_test_,$argument.0=))\n"
|
||||
// "method.insert.simple = test.method.double,((print,simple_test_,$argument.0=)),\"print=simple_test_,$argument.1=\"\n"
|
||||
|
||||
"method.insert = event.download.inserted,multi\n"
|
||||
"method.insert = event.download.inserted_new,multi\n"
|
||||
@@ -239,8 +240,8 @@ main(int argc, char** argv) {
|
||||
"method.set_key = event.download.erased, !_download_list, ui.unfocus_download=\n"
|
||||
"method.set_key = event.download.erased, ~_delete_tied, d.delete_tied=\n"
|
||||
|
||||
"method.insert = group.insert_persistent_view,simple|const,"
|
||||
"view.add=$argument.0=,view.persistent=$argument.0=,\"group.insert=$argument.0=,$argument.0=\"\n"
|
||||
"method.insert.c_simple = group.insert_persistent_view,"
|
||||
"((view.add,((argument.0)))),((view.persistent,((argument.0)))),((group.insert,((argument.0)),((argument.0))))\n"
|
||||
|
||||
// Allow setting 'group2.view' as constant, so that we can't
|
||||
// modify the value. And look into the possibility of making
|
||||
@@ -302,12 +303,12 @@ main(int argc, char** argv) {
|
||||
"view.sort_new = seeding,less=d.state_changed=\n"
|
||||
"view.sort_current = seeding,less=d.state_changed=\n"
|
||||
|
||||
"schedule2 = view.main,10,10,\"view.sort=main,20\"\n"
|
||||
"schedule2 = view.name,10,10,\"view.sort=name,20\"\n"
|
||||
"schedule2 = view.main,10,10,((view.sort,main,20))\n"
|
||||
"schedule2 = view.name,10,10,((view.sort,name,20))\n"
|
||||
|
||||
"schedule2 = session_save,1200,1200,session.save=\n"
|
||||
"schedule2 = low_diskspace,5,60,close_low_diskspace=500M\n"
|
||||
"schedule2 = prune_file_status,3600,86400,system.file_status_cache.prune=\n"
|
||||
"schedule2 = session_save,1200,1200,((session.save))\n"
|
||||
"schedule2 = low_diskspace,5,60,((close_low_diskspace,500M))\n"
|
||||
"schedule2 = prune_file_status,3600,86400,((system.file_status_cache.prune))\n"
|
||||
|
||||
"protocol.encryption.set=allow_incoming,prefer_plaintext,enable_retry\n"
|
||||
);
|
||||
|
||||
@@ -97,7 +97,22 @@ CommandScheduler::call_item(value_type item) {
|
||||
// removed.
|
||||
|
||||
try {
|
||||
rpc::parse_command_multiple_std(item->command());
|
||||
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.
|
||||
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());
|
||||
}
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
if (m_slotErrorMessage.is_valid())
|
||||
@@ -119,13 +134,19 @@ CommandScheduler::call_item(value_type item) {
|
||||
}
|
||||
|
||||
void
|
||||
CommandScheduler::parse(const std::string& key, const std::string& bufAbsolute, const std::string& bufInterval, const std::string& command) {
|
||||
CommandScheduler::parse(const std::string& key,
|
||||
const std::string& bufAbsolute,
|
||||
const std::string& bufInterval,
|
||||
const torrent::Object& command) {
|
||||
if (!command.is_string() && !command.is_dict_key())
|
||||
throw torrent::bencode_error("Invalid type passed to command scheduler.");
|
||||
|
||||
uint32_t absolute = parse_absolute(bufAbsolute.c_str());
|
||||
uint32_t interval = parse_interval(bufInterval.c_str());
|
||||
|
||||
CommandSchedulerItem* item = *insert(key);
|
||||
|
||||
item->set_command(command);
|
||||
item->command() = command;
|
||||
item->set_interval(interval);
|
||||
|
||||
item->enable((cachedTime + rak::timer::from_seconds(absolute)).round_seconds());
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
#include <inttypes.h>
|
||||
#include <rak/functional_fun.h>
|
||||
|
||||
namespace torrent {
|
||||
class Object;
|
||||
}
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class CommandSchedulerItem;
|
||||
@@ -71,7 +75,8 @@ public:
|
||||
void erase(iterator itr);
|
||||
void erase_str(const std::string& key) { erase(find(key)); }
|
||||
|
||||
void parse(const std::string& key, const std::string& bufAbsolute, const std::string& bufInterval, const std::string& command);
|
||||
void parse(const std::string& key, const std::string& bufAbsolute,
|
||||
const std::string& bufInterval, const torrent::Object& command);
|
||||
|
||||
static uint32_t parse_absolute(const char* str);
|
||||
static uint32_t parse_interval(const char* str);
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#include <torrent/object.h>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class CommandSchedulerItem {
|
||||
@@ -54,9 +56,7 @@ public:
|
||||
void disable();
|
||||
|
||||
const std::string& key() const { return m_key; }
|
||||
|
||||
const std::string& command() const { return m_command; }
|
||||
void set_command(const std::string& s) { m_command = s; }
|
||||
torrent::Object& command() { return m_command; }
|
||||
|
||||
// 'interval()' should in the future return some more dynamic values.
|
||||
uint32_t interval() const { return m_interval; }
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
void operator = (const CommandSchedulerItem&);
|
||||
|
||||
std::string m_key;
|
||||
std::string m_command;
|
||||
torrent::Object m_command;
|
||||
|
||||
uint32_t m_interval;
|
||||
rak::timer m_timeScheduled;
|
||||
|
||||
@@ -62,13 +62,14 @@ object_storage::insert(const char* key_data, uint32_t key_size, const torrent::O
|
||||
// Check for size > key_size.
|
||||
// Check for empty string.
|
||||
|
||||
bool use_raw = false;
|
||||
torrent::Object object;
|
||||
|
||||
switch (flags & mask_type) {
|
||||
case flag_bool_type: object = !!convert_to_value(rawObject); break;
|
||||
case flag_value_type: object = convert_to_value(rawObject); break;
|
||||
case flag_string_type: object = convert_to_string(rawObject); break;
|
||||
case flag_function_type: object = convert_to_string(rawObject); break;
|
||||
case flag_function_type: use_raw = true; break;
|
||||
case flag_multi_type: object = torrent::Object::create_map(); break;
|
||||
}
|
||||
|
||||
@@ -81,7 +82,7 @@ object_storage::insert(const char* key_data, uint32_t key_size, const torrent::O
|
||||
throw torrent::input_error("Key already exists in object_storage.");
|
||||
|
||||
result.first->second.flags = flags;
|
||||
result.first->second.object = object;
|
||||
result.first->second.object = use_raw ? rawObject : object;
|
||||
|
||||
return result.first;
|
||||
}
|
||||
@@ -147,7 +148,7 @@ 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_str(itr->second.object.as_string(), target, object);
|
||||
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);
|
||||
default:
|
||||
|
||||
@@ -181,6 +181,43 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (*
|
||||
|
||||
return ++first;
|
||||
|
||||
} else if (*first == '(') {
|
||||
int32_t depth = 1;
|
||||
|
||||
while (first + 1 != last && *(first + 1) == '(') {
|
||||
first++;
|
||||
depth++;
|
||||
}
|
||||
|
||||
if (depth > 3)
|
||||
throw torrent::input_error("Max 3 parantheses per object allowed.");
|
||||
|
||||
*dest = torrent::Object::create_dict_key();
|
||||
dest->set_flags(torrent::Object::flag_function << (depth - 1));
|
||||
|
||||
first = parse_string(first + 1, last, &dest->as_dict_key(), &parse_is_delim_func);
|
||||
first = parse_skip_wspace(first, last);
|
||||
|
||||
if (first == last || !parse_is_delim_func(*first))
|
||||
throw torrent::input_error("Could not find closing ')'.");
|
||||
|
||||
if (*first == ',') {
|
||||
// This will always create a list even for single argument functions...
|
||||
dest->as_dict_obj() = torrent::Object::create_list();
|
||||
first = parse_list(first + 1, last, &dest->as_dict_obj(), &parse_is_delim_func);
|
||||
first = parse_skip_wspace(first, last);
|
||||
}
|
||||
|
||||
while (depth != 0 && first != last && *first == ')') {
|
||||
first++;
|
||||
depth--;
|
||||
}
|
||||
|
||||
if (depth != 0)
|
||||
throw torrent::input_error("Parantheses mismatch.");
|
||||
|
||||
return first;
|
||||
|
||||
} else {
|
||||
*dest = std::string();
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ inline bool parse_is_delim_list(const char c) { return parse_is_seperator(c)
|
||||
inline bool parse_is_delim_command(const char c) { return parse_is_seperator(c) || c == ';' || std::isspace(c); }
|
||||
// inline bool parse_is_delim_block(const char c) { return c == ';' || c == '}'; }
|
||||
inline bool parse_is_delim_block(const char c) { return parse_is_seperator(c) || c == '}'; }
|
||||
inline bool parse_is_delim_func(const char c) { return parse_is_seperator(c) || c == ')'; }
|
||||
|
||||
const char* parse_skip_wspace(const char* first);
|
||||
const char* parse_skip_wspace(const char* first, const char* last);
|
||||
|
||||
@@ -93,7 +93,19 @@ parse_command_execute(target_type target, torrent::Object* object) {
|
||||
parse_command_execute(target, &*itr);
|
||||
}
|
||||
|
||||
} else if (*object->as_string().c_str() == '$') {
|
||||
} else if (object->is_dict_key()) {
|
||||
parse_command_execute(target, &object->as_dict_obj());
|
||||
|
||||
if (object->flags() & torrent::Object::flag_function) {
|
||||
*object = rpc::commands.call_command(object->as_dict_key().c_str(), object->as_dict_obj(), target);
|
||||
|
||||
} else {
|
||||
uint32_t flags = object->flags() & torrent::Object::mask_function;
|
||||
object->unset_flags(torrent::Object::mask_function);
|
||||
object->set_flags((flags >> 1) & torrent::Object::mask_function);
|
||||
}
|
||||
|
||||
} else if (object->is_string() && *object->as_string().c_str() == '$') {
|
||||
const std::string& str = object->as_string();
|
||||
|
||||
*object = parse_command(target, str.c_str() + 1, str.c_str() + str.size()).first;
|
||||
@@ -252,6 +264,48 @@ command_function_call(const torrent::raw_string& cmd, target_type target, const
|
||||
}
|
||||
}
|
||||
|
||||
const torrent::Object
|
||||
command_function_call_object(const torrent::Object& 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;
|
||||
|
||||
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_cmd = cmd;
|
||||
|
||||
rpc::parse_command_execute(target, &tmp_cmd);
|
||||
result = rpc::commands.call_command(tmp_cmd.as_dict_key().c_str(), tmp_cmd.as_dict_obj());
|
||||
}
|
||||
|
||||
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_multi_call(const torrent::Object::map_type& cmd, target_type target, const torrent::Object& args) {
|
||||
rpc::command_base::stack_type stack;
|
||||
|
||||
@@ -62,6 +62,8 @@ typedef std::pair<torrent::Object, const char*> parse_command_type;
|
||||
parse_command_type parse_command(target_type target, const char* first, const char* last);
|
||||
torrent::Object parse_command_multiple(target_type target, const char* first, const char* last);
|
||||
|
||||
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);
|
||||
|
||||
@@ -130,6 +132,8 @@ call_command_d_range(const char* key, core::Download* download, torrent::Object:
|
||||
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
|
||||
|
||||
@@ -56,6 +56,8 @@ ThreadWorker::ThreadWorker() {
|
||||
}
|
||||
|
||||
ThreadWorker::~ThreadWorker() {
|
||||
if (m_safe.scgi)
|
||||
m_safe.scgi->deactivate();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user