mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Merge branch 'hooks' into 2.4.0
This commit is contained in:
commit
de34095eb5
11 changed files with 381 additions and 139 deletions
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required (VERSION 2.8)
|
cmake_minimum_required (VERSION 2.8)
|
||||||
install (DIRECTORY bash fish vim zsh
|
install (DIRECTORY bash fish vim zsh hooks
|
||||||
DESTINATION ${TASK_DOCDIR}/scripts)
|
DESTINATION ${TASK_DOCDIR}/scripts)
|
||||||
install (DIRECTORY add-ons
|
install (DIRECTORY add-ons
|
||||||
DESTINATION ${TASK_DOCDIR}/scripts
|
DESTINATION ${TASK_DOCDIR}/scripts
|
||||||
|
|
23
scripts/hooks/on-add
Executable file
23
scripts/hooks/on-add
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# - New task JSON.
|
||||||
|
read new_task
|
||||||
|
|
||||||
|
# Processing goes here.
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# - Any line of JSON emitted is added as a new task.
|
||||||
|
# - Any non-JSON emitted is displayed as a message.
|
||||||
|
echo on-add
|
||||||
|
|
||||||
|
# Exit:
|
||||||
|
# - 0 Means accept $new_task if JSON is not emitted.
|
||||||
|
# - 0 Means accept all JSON as new tasks. If UUID matches $new_task, use this
|
||||||
|
# JSON instead of $new_task. If UUID does not match $new_task, then these
|
||||||
|
# are additional tasks.
|
||||||
|
# - 0 Means all non-JSON becomes footnote entries.
|
||||||
|
# - 1 Means all non-JSON becomes error entries.
|
||||||
|
# - 1 Means reject $new_task.
|
||||||
|
exit 0
|
||||||
|
|
22
scripts/hooks/on-exit
Executable file
22
scripts/hooks/on-exit
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# - None
|
||||||
|
|
||||||
|
# Processing goes here.
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# - Any line of JSON emitted is added as a new task.
|
||||||
|
# - Any non-JSON emitted is displayed as a message.
|
||||||
|
echo on-exit
|
||||||
|
|
||||||
|
# Exit:
|
||||||
|
# - 0 Means accept $new_task if JSON is not emitted.
|
||||||
|
# - 0 Means accept all JSON as new tasks. If UUID matches $new_task, use this
|
||||||
|
# JSON instead of $new_task. If UUID does not match $new_task, then these
|
||||||
|
# are additional tasks.
|
||||||
|
# - 0 Means all non-JSON becomes footnote entries.
|
||||||
|
# - 1 Means all non-JSON becomes error entries.
|
||||||
|
# - 1 Means reject $new_task.
|
||||||
|
exit 0
|
||||||
|
|
22
scripts/hooks/on-launch
Executable file
22
scripts/hooks/on-launch
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# - None
|
||||||
|
|
||||||
|
# Processing goes here.
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# - Any line of JSON emitted is added as a new task.
|
||||||
|
# - Any non-JSON emitted is displayed as a message.
|
||||||
|
echo on-launch
|
||||||
|
|
||||||
|
# Exit:
|
||||||
|
# - 0 Means accept $new_task if JSON is not emitted.
|
||||||
|
# - 0 Means accept all JSON as new tasks. If UUID matches $new_task, use this
|
||||||
|
# JSON instead of $new_task. If UUID does not match $new_task, then these
|
||||||
|
# are additional tasks.
|
||||||
|
# - 0 Means all non-JSON becomes footnote entries.
|
||||||
|
# - 1 Means all non-JSON becomes error entries.
|
||||||
|
# - 1 Means reject $new_task.
|
||||||
|
exit 0
|
||||||
|
|
25
scripts/hooks/on-modify
Executable file
25
scripts/hooks/on-modify
Executable file
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# - Original task JSON
|
||||||
|
# - Modified task JSON
|
||||||
|
read original_task
|
||||||
|
read modified_task
|
||||||
|
|
||||||
|
# Processing goes here.
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# - Any line of JSON emitted is added as a new task.
|
||||||
|
# - Any non-JSON emitted is displayed as a message.
|
||||||
|
echo on-modify
|
||||||
|
|
||||||
|
# Exit:
|
||||||
|
# - 0 Means accept $new_task if JSON is not emitted.
|
||||||
|
# - 0 Means accept all JSON as new tasks. If UUID matches $new_task, use this
|
||||||
|
# JSON instead of $new_task. If UUID does not match $new_task, then these
|
||||||
|
# are additional tasks.
|
||||||
|
# - 0 Means all non-JSON becomes footnote entries.
|
||||||
|
# - 1 Means all non-JSON becomes error entries.
|
||||||
|
# - 1 Means reject $new_task.
|
||||||
|
exit 0
|
||||||
|
|
|
@ -566,6 +566,9 @@ void Config::createDefaultData (const std::string& data)
|
||||||
throw std::string ("Error: rc.data.location does not exist - exiting according to rc.exit.on.missing.db setting.");
|
throw std::string ("Error: rc.data.location does not exist - exiting according to rc.exit.on.missing.db setting.");
|
||||||
|
|
||||||
d.create ();
|
d.create ();
|
||||||
|
|
||||||
|
d += "hooks";
|
||||||
|
d.create ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -221,10 +221,9 @@ int Context::initialize (int argc, const char** argv)
|
||||||
// Initialize the database.
|
// Initialize the database.
|
||||||
tdb2.set_location (data_dir);
|
tdb2.set_location (data_dir);
|
||||||
|
|
||||||
// Hook system init, plus post-start event occurring at the first possible
|
// First opportunity to run a hook script.
|
||||||
// moment after hook initialization.
|
|
||||||
hooks.initialize ();
|
hooks.initialize ();
|
||||||
hooks.trigger ("on-launch");
|
hooks.onLaunch ();
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (const std::string& message)
|
catch (const std::string& message)
|
||||||
|
@ -233,6 +232,12 @@ int Context::initialize (int argc, const char** argv)
|
||||||
rc = 2;
|
rc = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
catch (int)
|
||||||
|
{
|
||||||
|
// Hooks can terminate processing by throwing integers.
|
||||||
|
rc = 4;
|
||||||
|
}
|
||||||
|
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
error (STRING_UNKNOWN_ERROR);
|
error (STRING_UNKNOWN_ERROR);
|
||||||
|
@ -297,6 +302,7 @@ int Context::run ()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rc = dispatch (output);
|
rc = dispatch (output);
|
||||||
|
hooks.onExit ();
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
s << "Perf "
|
s << "Perf "
|
||||||
|
@ -317,13 +323,15 @@ int Context::run ()
|
||||||
<< " commit:" << timer_commit.total ()
|
<< " commit:" << timer_commit.total ()
|
||||||
<< " sort:" << timer_sort.total ()
|
<< " sort:" << timer_sort.total ()
|
||||||
<< " render:" << timer_render.total ()
|
<< " render:" << timer_render.total ()
|
||||||
|
<< " hooks:" << timer_hooks.total ()
|
||||||
<< " total:" << (timer_init.total () +
|
<< " total:" << (timer_init.total () +
|
||||||
timer_load.total () +
|
timer_load.total () +
|
||||||
timer_gc.total () +
|
timer_gc.total () +
|
||||||
timer_filter.total () +
|
timer_filter.total () +
|
||||||
timer_commit.total () +
|
timer_commit.total () +
|
||||||
timer_sort.total () +
|
timer_sort.total () +
|
||||||
timer_render.total ())
|
timer_render.total () +
|
||||||
|
timer_hooks.total ())
|
||||||
<< "\n";
|
<< "\n";
|
||||||
debug (s.str ());
|
debug (s.str ());
|
||||||
}
|
}
|
||||||
|
@ -385,7 +393,6 @@ int Context::run ()
|
||||||
else
|
else
|
||||||
std::cerr << *e << "\n";
|
std::cerr << *e << "\n";
|
||||||
|
|
||||||
hooks.trigger ("on-exit");
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,7 @@ public:
|
||||||
Timer timer_commit;
|
Timer timer_commit;
|
||||||
Timer timer_sort;
|
Timer timer_sort;
|
||||||
Timer timer_render;
|
Timer timer_render;
|
||||||
|
Timer timer_hooks;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
357
src/Hooks.cpp
357
src/Hooks.cpp
|
@ -24,69 +24,19 @@
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <iostream> // TODO Remove
|
||||||
#include <cmake.h>
|
#include <cmake.h>
|
||||||
#include <iostream>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <stdio.h>
|
||||||
#include <Context.h>
|
#include <Context.h>
|
||||||
#include <Hooks.h>
|
#include <Hooks.h>
|
||||||
#include <Timer.h>
|
|
||||||
#include <text.h>
|
#include <text.h>
|
||||||
#include <i18n.h>
|
|
||||||
|
|
||||||
extern Context context;
|
extern Context context;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Hook::Hook ()
|
|
||||||
: _event ("")
|
|
||||||
, _file ("")
|
|
||||||
, _function ("")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Hook::Hook (const std::string& e, const std::string& f, const std::string& fn)
|
|
||||||
: _event (e)
|
|
||||||
, _file (f)
|
|
||||||
, _function (fn)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Hook::Hook (const Hook& other)
|
|
||||||
{
|
|
||||||
_event = other._event;
|
|
||||||
_file = other._file;
|
|
||||||
_function = other._function;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Hook& Hook::operator= (const Hook& other)
|
|
||||||
{
|
|
||||||
if (this != &other)
|
|
||||||
{
|
|
||||||
_event = other._event;
|
|
||||||
_file = other._file;
|
|
||||||
_function = other._function;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
Hooks::Hooks ()
|
Hooks::Hooks ()
|
||||||
{
|
{
|
||||||
// New 2.x hooks.
|
|
||||||
_validTaskEvents.push_back ("on-task-add"); // Unimplemented
|
|
||||||
_validTaskEvents.push_back ("on-task-modify"); // Unimplemented
|
|
||||||
_validTaskEvents.push_back ("on-task-complete"); // Unimplemented
|
|
||||||
_validTaskEvents.push_back ("on-task-delete"); // Unimplemented
|
|
||||||
|
|
||||||
_validProgramEvents.push_back ("on-launch");
|
|
||||||
_validProgramEvents.push_back ("on-exit");
|
|
||||||
_validProgramEvents.push_back ("on-file-read"); // Unimplemented
|
|
||||||
_validProgramEvents.push_back ("on-file-write"); // Unimplemented
|
|
||||||
_validProgramEvents.push_back ("on-synch"); // Unimplemented
|
|
||||||
_validProgramEvents.push_back ("on-gc"); // Unimplemented
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -95,86 +45,285 @@ Hooks::~Hooks ()
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Enumerate all hooks, and tell API about the script files it must load in
|
|
||||||
// order to call them. Note that API will perform a deferred read, which means
|
|
||||||
// that if it isn't called, a script will not be loaded.
|
|
||||||
void Hooks::initialize ()
|
void Hooks::initialize ()
|
||||||
{
|
{
|
||||||
// Allow a master switch to turn the whole thing off.
|
// Scan <rc.data.location>/hooks
|
||||||
bool big_red_switch = context.config.getBoolean ("extensions");
|
Directory d (context.config.get ("data.location"));
|
||||||
if (big_red_switch)
|
d += "hooks";
|
||||||
|
if (d.is_directory () &&
|
||||||
|
d.readable ())
|
||||||
{
|
{
|
||||||
Config::const_iterator it;
|
_scripts = d.list ();
|
||||||
for (it = context.config.begin (); it != context.config.end (); ++it)
|
std::sort (_scripts.begin (), _scripts.end ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Occurs when: On launch, after data structures are initiliazed, before
|
||||||
|
// data is loaded.
|
||||||
|
// Data fed to stdin: None
|
||||||
|
// Exit code: 0: Success, proceed
|
||||||
|
// !0: Failure, terminate
|
||||||
|
// Output handled: 0: context.header ()
|
||||||
|
// !0: context.error ()
|
||||||
|
void Hooks::onLaunch ()
|
||||||
|
{
|
||||||
|
context.timer_hooks.start ();
|
||||||
|
|
||||||
|
std::vector <std::string> matchingScripts = scripts ("on-launch");
|
||||||
|
std::vector <std::string>::iterator i;
|
||||||
|
for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i)
|
||||||
|
{
|
||||||
|
std::string output;
|
||||||
|
int status = execute (*i, "", output);
|
||||||
|
|
||||||
|
std::vector <std::string> lines;
|
||||||
|
split (lines, output, '\n');
|
||||||
|
std::vector <std::string>::iterator line;
|
||||||
|
|
||||||
|
if (status == 0)
|
||||||
{
|
{
|
||||||
std::string type;
|
for (line = lines.begin (); line != lines.end (); ++line)
|
||||||
std::string name;
|
|
||||||
std::string value;
|
|
||||||
|
|
||||||
// "<type>.<name>"
|
|
||||||
Nibbler n (it->first);
|
|
||||||
if (n.getUntil ('.', type) &&
|
|
||||||
type == "hook" &&
|
|
||||||
n.skip ('.') &&
|
|
||||||
n.getUntilEOS (name))
|
|
||||||
{
|
{
|
||||||
Nibbler n (it->second);
|
if (line->length () && (*line)[0] == '{')
|
||||||
|
|
||||||
// <path>:<function> [, ...]
|
|
||||||
while (!n.depleted ())
|
|
||||||
{
|
{
|
||||||
std::string file;
|
Task newTask (*line);
|
||||||
std::string function;
|
context.tdb2.add (newTask);
|
||||||
if (n.getUntil (':', file) &&
|
|
||||||
n.skip (':') &&
|
|
||||||
n.getUntil (',', function))
|
|
||||||
{
|
|
||||||
context.debug (std::string ("Event '") + name + "' hooked by " + file + ", function " + function);
|
|
||||||
Hook h (name, Path::expand (file), function);
|
|
||||||
_all.push_back (h);
|
|
||||||
|
|
||||||
(void) n.skip (',');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
; // Was: throw std::string (format ("Malformed hook definition '{1}'.", it->first));
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
context.header (*line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (line = lines.begin (); line != lines.end (); ++line)
|
||||||
|
context.error (*line);
|
||||||
|
|
||||||
|
throw 0; // This is how hooks silently terminate processing.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
context.debug ("Hooks::initialize --> off");
|
context.timer_hooks.stop ();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Program hooks.
|
// Occurs when: On exit, after processing is complete, before output is
|
||||||
bool Hooks::trigger (const std::string& event)
|
// displayed.
|
||||||
|
// Data fed to stdin: None
|
||||||
|
// Exit code: 0: Success
|
||||||
|
// !0: Failure
|
||||||
|
// Output handled: 0: context.footnote ()
|
||||||
|
// !0: context.error ()
|
||||||
|
void Hooks::onExit ()
|
||||||
{
|
{
|
||||||
return false;
|
context.timer_hooks.start ();
|
||||||
|
|
||||||
|
std::vector <std::string> matchingScripts = scripts ("on-exit");
|
||||||
|
std::vector <std::string>::iterator i;
|
||||||
|
for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i)
|
||||||
|
{
|
||||||
|
std::string output;
|
||||||
|
int status = execute (*i, "", output);
|
||||||
|
|
||||||
|
std::vector <std::string> lines;
|
||||||
|
split (lines, output, '\n');
|
||||||
|
std::vector <std::string>::iterator line;
|
||||||
|
|
||||||
|
if (status == 0)
|
||||||
|
{
|
||||||
|
for (line = lines.begin (); line != lines.end (); ++line)
|
||||||
|
{
|
||||||
|
if (line->length () && (*line)[0] == '{')
|
||||||
|
{
|
||||||
|
Task newTask (*line);
|
||||||
|
context.tdb2.add (newTask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
context.footnote (*line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (line = lines.begin (); line != lines.end (); ++line)
|
||||||
|
context.error (*line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.timer_hooks.stop ();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Task hooks.
|
// Occurs when: A task is created, before it is committed.
|
||||||
bool Hooks::trigger (const std::string& event, Task& task)
|
// Data fed to stdin: task JSON
|
||||||
|
// Exit code: 0: Success
|
||||||
|
// !0: Failure
|
||||||
|
// Output handled: 0: modified JSON
|
||||||
|
// context.footnote ()
|
||||||
|
// !0: context.error ()
|
||||||
|
void Hooks::onAdd (Task& after)
|
||||||
{
|
{
|
||||||
return false;
|
context.timer_hooks.start ();
|
||||||
|
|
||||||
|
std::vector <std::string> matchingScripts = scripts ("on-add");
|
||||||
|
std::vector <std::string>::iterator i;
|
||||||
|
for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i)
|
||||||
|
{
|
||||||
|
std::string input = after.composeJSON () + "\n";
|
||||||
|
std::string output;
|
||||||
|
int status = execute (*i, input, output);
|
||||||
|
|
||||||
|
std::vector <std::string> lines;
|
||||||
|
split (lines, output, '\n');
|
||||||
|
std::vector <std::string>::iterator line;
|
||||||
|
|
||||||
|
if (status == 0)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
for (line = lines.begin (); line != lines.end (); ++line)
|
||||||
|
{
|
||||||
|
if (line->length () && (*line)[0] == '{')
|
||||||
|
{
|
||||||
|
Task newTask (*line);
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
after = newTask;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
context.tdb2.add (newTask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
context.footnote (*line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (line = lines.begin (); line != lines.end (); ++line)
|
||||||
|
context.error (*line);
|
||||||
|
|
||||||
|
throw 0; // This is how hooks silently terminate processing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.timer_hooks.stop ();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool Hooks::validProgramEvent (const std::string& event)
|
// Occurs when: A task is modified, before it is committed.
|
||||||
|
// Data fed to stdin: before JSON
|
||||||
|
// after JSON
|
||||||
|
// Exit code: 0: Success
|
||||||
|
// !0: Failure
|
||||||
|
// Output handled: 0: modified after JSON
|
||||||
|
// context.footnote ()
|
||||||
|
// !0: context.error ()
|
||||||
|
void Hooks::onModify (const Task& before, Task& after)
|
||||||
{
|
{
|
||||||
if (std::find (_validProgramEvents.begin (), _validProgramEvents.end (), event) != _validProgramEvents.end ())
|
context.timer_hooks.start ();
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
std::vector <std::string> matchingScripts = scripts ("on-modify");
|
||||||
|
std::vector <std::string>::iterator i;
|
||||||
|
for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i)
|
||||||
|
{
|
||||||
|
std::string afterJSON = after.composeJSON ();
|
||||||
|
std::string input = before.composeJSON ()
|
||||||
|
+ "\n"
|
||||||
|
+ afterJSON
|
||||||
|
+ "\n";
|
||||||
|
std::string output;
|
||||||
|
int status = execute (*i, input, output);
|
||||||
|
|
||||||
|
std::vector <std::string> lines;
|
||||||
|
split (lines, output, '\n');
|
||||||
|
std::vector <std::string>::iterator line;
|
||||||
|
|
||||||
|
if (status == 0)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
for (line = lines.begin (); line != lines.end (); ++line)
|
||||||
|
{
|
||||||
|
if (line->length () && (*line)[0] == '{')
|
||||||
|
{
|
||||||
|
Task newTask (*line);
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
after = newTask;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
context.tdb2.add (newTask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
context.footnote (*line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (line = lines.begin (); line != lines.end (); ++line)
|
||||||
|
context.error (*line);
|
||||||
|
|
||||||
|
throw 0; // This is how hooks silently terminate processing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.timer_hooks.stop ();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool Hooks::validTaskEvent (const std::string& event)
|
std::vector <std::string> Hooks::scripts (const std::string& event)
|
||||||
{
|
{
|
||||||
if (std::find (_validTaskEvents.begin (), _validTaskEvents.end (), event) != _validTaskEvents.end ())
|
std::vector <std::string> matching;
|
||||||
return true;
|
std::vector <std::string>::iterator i;
|
||||||
|
for (i = _scripts.begin (); i != _scripts.end (); ++i)
|
||||||
|
{
|
||||||
|
if (i->find ("/" + event) != std::string::npos)
|
||||||
|
{
|
||||||
|
File script (*i);
|
||||||
|
if (script.executable ())
|
||||||
|
matching.push_back (*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return matching;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int Hooks::execute (
|
||||||
|
const std::string& command,
|
||||||
|
const std::string& input,
|
||||||
|
std::string& output)
|
||||||
|
{
|
||||||
|
int status = -1;
|
||||||
|
FILE* fp = popen (command.c_str (), "r+");
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
// Write input to fp.
|
||||||
|
if (input != "" &&
|
||||||
|
input != "\n")
|
||||||
|
{
|
||||||
|
fputs (input.c_str (), fp);
|
||||||
|
fflush (fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read output from fp.
|
||||||
|
output = "";
|
||||||
|
char* line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
while (getline (&line, &len, fp) != -1)
|
||||||
|
{
|
||||||
|
output += line;
|
||||||
|
free (line);
|
||||||
|
line = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush (fp);
|
||||||
|
status = pclose (fp);
|
||||||
|
context.debug (format ("Hooks::execute {1} (status {2})", command, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
31
src/Hooks.h
31
src/Hooks.h
|
@ -30,22 +30,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
// Hook class representing a single hook, which is just a three-way map.
|
|
||||||
class Hook
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Hook ();
|
|
||||||
Hook (const std::string&, const std::string&, const std::string&);
|
|
||||||
Hook (const Hook&);
|
|
||||||
Hook& operator= (const Hook&);
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string _event;
|
|
||||||
std::string _file;
|
|
||||||
std::string _function;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hooks class for managing the loading and calling of hook functions.
|
|
||||||
class Hooks
|
class Hooks
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -56,18 +40,17 @@ public:
|
||||||
|
|
||||||
void initialize ();
|
void initialize ();
|
||||||
|
|
||||||
bool trigger (const std::string&); // Program
|
void onLaunch ();
|
||||||
bool trigger (const std::string&, Task&); // Task
|
void onExit ();
|
||||||
|
void onAdd (Task&);
|
||||||
|
void onModify (const Task&, Task&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool validProgramEvent (const std::string&);
|
std::vector <std::string> scripts (const std::string&);
|
||||||
bool validTaskEvent (const std::string&);
|
int execute (const std::string&, const std::string&, std::string&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector <Hook> _all; // All current hooks.
|
std::vector <std::string> _scripts;
|
||||||
|
|
||||||
std::vector <std::string> _validProgramEvents;
|
|
||||||
std::vector <std::string> _validTaskEvents;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
17
src/TDB2.cpp
17
src/TDB2.cpp
|
@ -134,10 +134,13 @@ bool TF2::get (const std::string& uuid, Task& task)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void TF2::add_task (const Task& task)
|
void TF2::add_task (const Task& task)
|
||||||
{
|
{
|
||||||
_tasks.push_back (task); // For subsequent queries
|
Task hookTask (task);
|
||||||
_added_tasks.push_back (task); // For commit/synch
|
context.hooks.onAdd (hookTask);
|
||||||
|
|
||||||
/* TODO handle 'add' and 'log'.
|
_tasks.push_back (hookTask); // For subsequent queries
|
||||||
|
_added_tasks.push_back (hookTask); // For commit/synch
|
||||||
|
|
||||||
|
/* TODO handle 'add' and 'log'?
|
||||||
int id = context.tdb2.next_id ();
|
int id = context.tdb2.next_id ();
|
||||||
_I2U[id] = task.get ("uuid");
|
_I2U[id] = task.get ("uuid");
|
||||||
_U2I[task.get ("uuid")] = id;
|
_U2I[task.get ("uuid")] = id;
|
||||||
|
@ -156,9 +159,13 @@ bool TF2::modify_task (const Task& task)
|
||||||
{
|
{
|
||||||
if (i->get ("uuid") == uuid)
|
if (i->get ("uuid") == uuid)
|
||||||
{
|
{
|
||||||
*i = task;
|
Task hookTask (task);
|
||||||
_modified_tasks.push_back (task);
|
context.hooks.onModify (*i, hookTask);
|
||||||
|
|
||||||
|
*i = hookTask;
|
||||||
|
_modified_tasks.push_back (hookTask);
|
||||||
_dirty = true;
|
_dirty = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue