#
# add_file "tests/t_lua_includedir.at"
#
# add_file "tests/t_rcfile_dir.at"
#
# patch "ChangeLog"
# from [7b060225f75201d67f68458d3e8349f051d28c8d]
# to [b62f15f717fc6e20423c234e28e14a9ab4911ce8]
#
# patch "lua.cc"
# from [c5a01c71aef633d6ad5ad78650e4e0601295b349]
# to [e4dd123e0c07b59361555b521f3b2cc17aa445b5]
#
# patch "monotone.texi"
# from [28008d0dc7e6abd7e2e40cab143add9c4faecbca]
# to [6403bd5e94050c2dad166cd94378e4a067415874]
#
# patch "tests/t_lua_includedir.at"
# from []
# to [5b4ce1e6a2781a7a841039a1e02a96cc0221f3ff]
#
# patch "tests/t_rcfile_dir.at"
# from []
# to [06128ab21f137c3a69e4fce2db25b0ea3fa424de]
#
# patch "testsuite.at"
# from [2635480ab5d48f5eae96909a5e59ef20a7b0c36d]
# to [2e17e62213b9878d08d7c792f244b6e193498618]
#
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,12 @@
+2005-06-17 Riccardo Ghetta
+ Adds include()/includedir() to lua hooks and extend --rcfile
+ * lua.cc: handle --rcfile with directories, implement
+ include() and includedir()
+ * testsuite.at, t_lua_includedir.at, t_rcfile_dir.at:
+ test new functionality
+ * monotone.texi: document all functions available to hook
+ writers, including the new include() and includedir()
+
2005-06-16 Nathaniel Smith
* diff_patch.cc (merge_extents): Typo caught by anonymous reader.
--- lua.cc
+++ lua.cc
@@ -30,6 +30,7 @@
#include "sanity.hh"
#include "vocab.hh"
#include "platform.hh"
+#include "transforms.hh"
// defined in {std,test}_hooks.lua, converted
#include "test_hooks.h"
@@ -45,164 +46,6 @@
}
*/
-extern "C"
-{
- static int
- monotone_mkstemp_for_lua(lua_State *L)
- {
- int fd = -1;
- FILE **pf = NULL;
- char const *filename = lua_tostring (L, -1);
- std::string dup(filename);
-
- fd = monotone_mkstemp(dup);
-
- if (fd == -1)
- return 0;
-
- // this magic constructs a lua object which the lua io library
- // will enjoy working with
- pf = static_cast(lua_newuserdata(L, sizeof(FILE *)));
- *pf = fdopen(fd, "r+");
- lua_pushstring(L, "FILE*");
- lua_rawget(L, LUA_REGISTRYINDEX);
- lua_setmetatable(L, -2);
-
- lua_pushstring(L, dup.c_str());
-
- if (*pf == NULL)
- {
- lua_pushnil(L);
- lua_pushfstring(L, "%s", strerror(errno));
- lua_pushnumber(L, errno);
- return 3;
- }
- else
- return 2;
- }
-
- static int
- monotone_existsonpath_for_lua(lua_State *L)
- {
- const char *exe = lua_tostring(L, -1);
- lua_pushnumber(L, existsonpath(exe));
- return 1;
- }
-
- static int
- monotone_is_executable_for_lua(lua_State *L)
- {
- const char *path = lua_tostring(L, -1);
- lua_pushboolean(L, is_executable(path));
- return 1;
- }
-
- static int
- monotone_make_executable_for_lua(lua_State *L)
- {
- const char *path = lua_tostring(L, -1);
- lua_pushnumber(L, make_executable(path));
- return 1;
- }
-
- static int
- monotone_spawn_for_lua(lua_State *L)
- {
- int n = lua_gettop(L);
- const char *path = lua_tostring(L, -n);
- char **argv = (char**)malloc((n+1)*sizeof(char*));
- int i;
- pid_t ret;
- if (argv==NULL)
- return 0;
- argv[0] = (char*)path;
- for (i=1; i1)
- sig = (int)lua_tonumber(L, -1);
- else
- sig = SIGTERM;
- lua_pushnumber(L, process_kill(pid, sig));
- return 1;
- }
-
- static int
- monotone_sleep_for_lua(lua_State *L)
- {
- int seconds = (int)lua_tonumber(L, -1);
- lua_pushnumber(L, process_sleep(seconds));
- return 1;
- }
-
- static int
- monotone_guess_binary_for_lua(lua_State *L)
- {
- const char *path = lua_tostring(L, -1);
- N(path, F("guess_binary called with an invalid parameter"));
- lua_pushboolean(L, guess_binary(std::string(path, lua_strlen(L, -1))));
- return 1;
- }
-}
-
-
-lua_hooks::lua_hooks()
-{
- st = lua_open ();
- I(st);
-
- // no atpanic support in 4.x
- // lua_atpanic (st, &panic_thrower);
-
- luaopen_base(st);
- luaopen_io(st);
- luaopen_string(st);
- luaopen_math(st);
- luaopen_table(st);
- luaopen_debug(st);
-
- // add monotone-specific functions
- lua_register(st, "mkstemp", monotone_mkstemp_for_lua);
- lua_register(st, "existsonpath", monotone_existsonpath_for_lua);
- lua_register(st, "is_executable", monotone_is_executable_for_lua);
- lua_register(st, "make_executable", monotone_make_executable_for_lua);
- lua_register(st, "spawn", monotone_spawn_for_lua);
- lua_register(st, "wait", monotone_wait_for_lua);
- lua_register(st, "kill", monotone_kill_for_lua);
- lua_register(st, "sleep", monotone_sleep_for_lua);
- lua_register(st, "guess_binary", monotone_guess_binary_for_lua);
-}
-
-lua_hooks::~lua_hooks()
-{
- if (st)
- lua_close (st);
-}
-
-
// This Lua object represents a single imperative transaction with the lua
// interpreter. if it fails at any point, all further commands in the
// transaction are ignored. it cleans the lua stack up when it is
@@ -535,6 +378,217 @@
std::set Lua::missing_functions;
+
+
+
+extern "C"
+{
+ static int
+ monotone_mkstemp_for_lua(lua_State *L)
+ {
+ int fd = -1;
+ FILE **pf = NULL;
+ char const *filename = lua_tostring (L, -1);
+ std::string dup(filename);
+
+ fd = monotone_mkstemp(dup);
+
+ if (fd == -1)
+ return 0;
+
+ // this magic constructs a lua object which the lua io library
+ // will enjoy working with
+ pf = static_cast(lua_newuserdata(L, sizeof(FILE *)));
+ *pf = fdopen(fd, "r+");
+ lua_pushstring(L, "FILE*");
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+
+ lua_pushstring(L, dup.c_str());
+
+ if (*pf == NULL)
+ {
+ lua_pushnil(L);
+ lua_pushfstring(L, "%s", strerror(errno));
+ lua_pushnumber(L, errno);
+ return 3;
+ }
+ else
+ return 2;
+ }
+
+ static int
+ monotone_existsonpath_for_lua(lua_State *L)
+ {
+ const char *exe = lua_tostring(L, -1);
+ lua_pushnumber(L, existsonpath(exe));
+ return 1;
+ }
+
+ static int
+ monotone_is_executable_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ lua_pushboolean(L, is_executable(path));
+ return 1;
+ }
+
+ static int
+ monotone_make_executable_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ lua_pushnumber(L, make_executable(path));
+ return 1;
+ }
+
+ static int
+ monotone_spawn_for_lua(lua_State *L)
+ {
+ int n = lua_gettop(L);
+ const char *path = lua_tostring(L, -n);
+ char **argv = (char**)malloc((n+1)*sizeof(char*));
+ int i;
+ pid_t ret;
+ if (argv==NULL)
+ return 0;
+ argv[0] = (char*)path;
+ for (i=1; i1)
+ sig = (int)lua_tonumber(L, -1);
+ else
+ sig = SIGTERM;
+ lua_pushnumber(L, process_kill(pid, sig));
+ return 1;
+ }
+
+ static int
+ monotone_sleep_for_lua(lua_State *L)
+ {
+ int seconds = (int)lua_tonumber(L, -1);
+ lua_pushnumber(L, process_sleep(seconds));
+ return 1;
+ }
+
+ static int
+ monotone_guess_binary_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ N(path, F("guess_binary called with an invalid parameter"));
+ lua_pushboolean(L, guess_binary(std::string(path, lua_strlen(L, -1))));
+ return 1;
+ }
+
+ static int
+ monotone_include_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ N(path, F("Include called with an invalid parameter"));
+
+ bool res =Lua(L)
+ .loadfile(std::string(path, lua_strlen(L, -1)))
+ .call(0,1)
+ .ok();
+
+ lua_pushboolean(L, res);
+ return 1;
+ }
+
+ static int
+ monotone_includedir_for_lua(lua_State *L)
+ {
+ const char *pathstr = lua_tostring(L, -1);
+ N(pathstr, F("IncludeDir called with an invalid parameter"));
+
+ fs::path locpath(pathstr);
+ N(fs::exists(locpath), F("Directory '%s' does not exists") % pathstr);
+ N(fs::is_directory(locpath), F("'%s' is not a directory") % pathstr);
+
+ // directory, iterate over it, skipping subdirs, taking every filename,
+ // sorting them and loading in sorted order
+ fs::directory_iterator it(locpath);
+ std::vector arr;
+ while (it != fs::directory_iterator())
+ {
+ if (!fs::is_directory(*it))
+ arr.push_back(*it);
+ ++it;
+ }
+ std::sort(arr.begin(), arr.end());
+ for (std::vector::iterator i= arr.begin(); i != arr.end(); ++i)
+ {
+ bool res =Lua(L)
+ .loadfile(i->string())
+ .call(0,1)
+ .ok();
+ N(res, F("lua error while loading rcfile '%s'") % i->string());
+ }
+
+ lua_pushboolean(L, true);
+ return 1;
+ }
+}
+
+
+lua_hooks::lua_hooks()
+{
+ st = lua_open ();
+ I(st);
+
+ // no atpanic support in 4.x
+ // lua_atpanic (st, &panic_thrower);
+
+ luaopen_base(st);
+ luaopen_io(st);
+ luaopen_string(st);
+ luaopen_math(st);
+ luaopen_table(st);
+ luaopen_debug(st);
+
+ // add monotone-specific functions
+ lua_register(st, "mkstemp", monotone_mkstemp_for_lua);
+ lua_register(st, "existsonpath", monotone_existsonpath_for_lua);
+ lua_register(st, "is_executable", monotone_is_executable_for_lua);
+ lua_register(st, "make_executable", monotone_make_executable_for_lua);
+ lua_register(st, "spawn", monotone_spawn_for_lua);
+ lua_register(st, "wait", monotone_wait_for_lua);
+ lua_register(st, "kill", monotone_kill_for_lua);
+ lua_register(st, "sleep", monotone_sleep_for_lua);
+ lua_register(st, "guess_binary", monotone_guess_binary_for_lua);
+ lua_register(st, "include", monotone_include_for_lua);
+ lua_register(st, "includedir", monotone_includedir_for_lua);
+}
+
+lua_hooks::~lua_hooks()
+{
+ if (st)
+ lua_close (st);
+}
+
static bool
run_string(lua_State * st, string const &str, string const & identity)
{
@@ -591,6 +645,29 @@
lua_hooks::load_rcfile(utf8 const & rc)
{
I(st);
+ if (rc() != "-")
+ {
+ fs::path locpath(localized(rc));
+ if (fs::exists(locpath) && fs::is_directory(locpath))
+ {
+ // directory, iterate over it, skipping subdirs, taking every filename,
+ // sorting them and loading in sorted order
+ fs::directory_iterator it(locpath);
+ std::vector arr;
+ while (it != fs::directory_iterator())
+ {
+ if (!fs::is_directory(*it))
+ arr.push_back(*it);
+ ++it;
+ }
+ std::sort(arr.begin(), arr.end());
+ for (std::vector::iterator i= arr.begin(); i != arr.end(); ++i)
+ {
+ load_rcfile(*i, true);
+ }
+ return; // directory read, skip the rest ...
+ }
+ }
data dat;
L(F("opening rcfile '%s' ...\n") % rc);
read_data_for_command_line(rc, dat);
--- monotone.texi
+++ monotone.texi
@@ -5281,9 +5281,21 @@
specified on the command-line will shadow hooks from the the automatic
files.
-The remainder of this section documents the existing hook functions
-and their default definitions.
+Monotone also exports to hooks a number of helper functions exposing
+functionality not available with standard lua.
address@hidden
+* Hooks:: All hooks called by monotone.
+* Additional Lua Functions:: Extra functionality availabe to hook writers.
address@hidden menu
+
address@hidden
address@hidden Hooks
address@hidden Hooks
+
+This section documents the existing hook functions and their default
+definitions.
+
@ftable @code
@item note_commit (@var{new_id}, @var{certs})
@@ -6168,6 +6180,118 @@
@end ftable
address@hidden
address@hidden Additional Lua Functions
address@hidden Additional Lua Functions
+
+This section documents the additional lua functions made available to
+hook writers.
+
address@hidden @code
+
address@hidden existonpath(@var{possible_command})
+
+This function receives a string containing the name of an external
+program and returns 0 if it exists on path and is executable, -1
+otherwise.
+As an example, @code{existonpath("xxdiff")} returns 0 if the
+program xxdiff is available.
+On windows, this function automatically appends ``.exe'' to the
+program name. In the previous example, @code{existonpath} would search
+for ``xxdiff.exe''.
+
address@hidden guess_binary(@var{filespec})
+
+Returns true if the file appears to be binary, i.e. contains one or
+more of the following characters:
address@hidden
address@hidden
+0x00 thru 0x06
+0x0E thru 0x1a
+0x1c thru 0x1f
address@hidden group
address@hidden smallexample
+
address@hidden include(@var{scriptfile})
+
+This function tries to load and execute the script contained into
+scriptfile. It returns true for success and false if there is an
+error.
+
address@hidden includedir(@var{scriptpath})
+
+This function loads and executes in alphabetical order all the scripts
+contained into the directory scriptpath.
+If one of the scripts has an error, the functions doesn't process the
+remaining scripts and immediately returns false.
+
address@hidden is_executable(@var{filespec})
+
+This function returns true if the file is executable, false
+otherwise. On windows this function returns always false.
+
address@hidden kill(@var{pid} [, @var{signal}])
+
+This function calls the kill() C library function on posix systems and
+TerminateProcess on Win32 (in that case @var{pid} is the process
+handle). If the optional @var{signal} parameter is missing, SIGTERM
+will be used.
+Returns 0 on succes, -1 on error.
+
address@hidden make_executable(@var{filespec})
+
+This function marks the named file as executable. On windows has no
+effect.
+
address@hidden mkstemp(@var{template})
+
+Like its C library counterpart, mkstemp creates a unique name and
+returns a file descriptor for the newly created file.
+The value of template should be a pointer to a character buffer loaded
+with a null-terminated string that consists of contiguous, legal file
+ad path name characters followed by six Xs.
+The function mkstemp replaces the Xs by an alpha-numeric sequence
+that is chosen to ensure that no file in the chosen directory has that
+name.
+Furthermore, subsequent calls to mkstemp within the same process
+each yield different file names.
+Unlike other implementations, monotone mkstemp allows the template
+string to contain a complete path, not only a filename, allowing users
+to create temporary files outside the current directory.
+
address@hidden notice:address@hidden
+To create a temporary file, you must use the @code{temp_file()}
+function, unless you need to run monotone with the @option{--nostd}
+option. @code{temp_file()} builds on @code{mkstemp()} and creates a
+file in the standard TMP/TEMP directories.
+
address@hidden sleep(@var{seconds})
+
+Makes the calling process sleep for the specified number of seconds.
+
address@hidden spawn(@var{executable} [, @var{args ...}])
+
+Starts the named executable with the given arguments. Returns the
+process pid on Posix systems, the process handle on Win32 or -1 if
+there was an error.
+Calls fork/execvp on Posix, CreateProcess on Win32.
+
address@hidden notice:address@hidden
+To spawn a process and wait for its completion, use the @code{execute()}
+function, unless you need to run monotone with the @option{--nostd}
+option. @code{execute()} builds on @code{spawn()} and @code{wait()}
+in a standardized way.
+
address@hidden wait(@var{pid})
+
+Wait until the process with given pid (process handle on Win32) exits.
+Returns two values: a result value and the exit code of the waited-for
+process.
+The exit code is meaningful only if the result value is 0.
+
address@hidden ftable
+
+
@node Special Topics
@chapter Special Topics
--- tests/t_lua_includedir.at
+++ tests/t_lua_includedir.at
@@ -0,0 +1,44 @@
+AT_SETUP([include() and includedir() lua functions])
+MONOTONE_SETUP
+
+AT_CHECK(mkdir gongolo)
+
+AT_DATA(include.lua, [include("../gongolo/aaa.rc")
+])
+
+AT_DATA(includedir.lua, [includedir("../gongolo")
+])
+
+# write two files and check that they will be invoked in alphabetic order
+AT_DATA(gongolo/aaa.rc, [function paraponzi()
+ io.write("BOOGA BOOGA")
+end
+paraponzi()
+])
+AT_DATA(gongolo/bbb.zz, [function labellagigogin()
+ io.write("CICCA CICCA")
+end
+labellagigogin()
+])
+
+# setup a wrk dir
+AT_CHECK(MONOTONE setup alt_wrk, [], [ignore], [ignore])
+
+# include directly a single file
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../include.lua status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGA" stdout)
+
+# include dirs
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../includedir.lua status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGACICCA CICCA" stdout)
+
+# write a third file: should be read beetween the two previous ones
+AT_DATA(gongolo/aba.rc, [function notwowithoutthree()
+ io.write("hu hu")
+end
+notwowithoutthree()
+])
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../includedir.lua status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGAhu huCICCA CICCA" stdout)
+
+AT_CLEANUP
--- tests/t_rcfile_dir.at
+++ tests/t_rcfile_dir.at
@@ -0,0 +1,32 @@
+AT_SETUP([--rcfile=directory])
+MONOTONE_SETUP
+
+AT_CHECK(mkdir gongolo)
+
+# write two files and check that they will be invoked in alphabetic order
+AT_DATA(gongolo/aaa.rc, [function paraponzi()
+ io.write("BOOGA BOOGA")
+end
+paraponzi()
+])
+AT_DATA(gongolo/bbb.rc, [function labellagigogin()
+ io.write("CICCA CICCA")
+end
+labellagigogin()
+])
+
+# note: rcfile is placed outside workdir
+AT_CHECK(MONOTONE setup alt_wrk, [], [ignore], [ignore])
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../gongolo status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGACICCA CICCA" stdout)
+
+# write a third file: should be read beetween the two previous ones
+AT_DATA(gongolo/aba.rc, [function notwowithoutthree()
+ io.write("hu hu")
+end
+notwowithoutthree()
+])
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../gongolo status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGAhu huCICCA CICCA" stdout)
+
+AT_CLEANUP
--- testsuite.at
+++ testsuite.at
@@ -659,4 +659,5 @@
m4_include(tests/t_merge_manual.at)
m4_include(tests/t_revert_restrict.at)
m4_include(tests/t_status.at)
+m4_include(tests/t_rcfile_dir.at)
+m4_include(tests/t_lua_includedir.at)
-