# # 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) -