# # # rename "lua.cc" # to "lua_hooks.cc" # # rename "lua.hh" # to "lua_hooks.hh" # # add_file "lua.cc" # content [8412d7a38d765381e449c100f085a2dbdca3ae63] # # add_file "lua.hh" # content [54e0054144207e223df17764f6da0e6134cb6a02] # # patch "ChangeLog" # from [a64826396081e97e2428a6c982d7e5b709a3c17d] # to [76e0a67eba2c4c2926795e0d54961b50f2950ae9] # # patch "Makefile.am" # from [ad1680df7c76acbdaef7ad40c4e32e01d89daf7f] # to [0afb3360e47c62b19a8f6c73f240f304ad29001e] # # patch "app_state.hh" # from [6274d29f9d4e43054180da2ba49c564b15fd2a26] # to [4d4ab0e35a58144708b8a67252fec3a37f0ea71a] # # patch "file_io.cc" # from [547bf6f195554fd36499a1cfc95c1f571c2304b3] # to [df2709c7a36633e5266ced0a9b7b7d6ee3f41903] # # patch "keys.cc" # from [b61b2cda83c67d33c1a2fb3ff0f7098ecfeb1570] # to [cfb8170e6ffd325bc6833b70739b6435e603454e] # # patch "lua_hooks.cc" # from [6abb5987206ee45b2389366b815cc391e9ecce7b] # to [2c8a4626208b7b0128a96da8ea532f7d3fce0e85] # # patch "lua_hooks.hh" # from [664f93c8b443254f5a75fc40ee0bd12d50a38240] # to [2b7f5a8aebde4f44fd25b0c65e0434d2ceec6813] # # patch "transforms.hh" # from [b7c20f2b92989c820febdb61bb3b55cb6887964a] # to [8dabc3f8fe642f4c1d3dddab53fae3a4628617a1] # ============================================================ --- lua.cc 8412d7a38d765381e449c100f085a2dbdca3ae63 +++ lua.cc 8412d7a38d765381e449c100f085a2dbdca3ae63 @@ -0,0 +1,799 @@ + + +#include "config.h" + +extern "C" { +#include +#include +#include +} +#include +#include +//#include +//#include +//#include + +#include "lua.hh" + +#include "sanity.hh" +#include "platform.hh" +#include "mkstemp.hh" +#include "globish.hh" +#include "basic_io.hh" +#include "file_io.hh" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using std::string; +using std::vector; +using std::pair; + +// adapted from "programming in lua", section 24.2.3 +// http://www.lua.org/pil/24.2.3.html +// output is from bottom (least accessible) to top (most accessible, where +// push and pop happen). +static std::string +dump_stack(lua_State * st) +{ + std::string out; + int i; + int top = lua_gettop(st); + for (i = 1; i <= top; i++) { /* repeat for each level */ + int t = lua_type(st, i); + switch (t) { + case LUA_TSTRING: /* strings */ + out += (boost::format("`%s'") % std::string(lua_tostring(st, i), lua_strlen(st, i))).str(); + break; + + case LUA_TBOOLEAN: /* booleans */ + out += (lua_toboolean(st, i) ? "true" : "false"); + break; + + case LUA_TNUMBER: /* numbers */ + out += (boost::format("%g") % lua_tonumber(st, i)).str(); + break; + + default: /* other values */ + out += (boost::format("%s") % lua_typename(st, t)).str(); + break; + + } + out += " "; /* put a separator */ + } + return out; +} + + +Lua::Lua(lua_State * s) : + st(s), failed(false) +{} + +Lua::~Lua() +{ + lua_settop(st, 0); +} + +void +Lua::fail(std::string const & reason) +{ + L(FL("lua failure: %s; stack = %s\n") % reason % dump_stack(st)); + failed = true; +} + +bool +Lua::ok() +{ + if (failed) + L(FL("Lua::ok(): failed")); + return !failed; +} + +void +Lua::report_error() +{ + I(lua_isstring(st, -1)); + string err = string(lua_tostring(st, -1), lua_strlen(st, -1)); + W(i18n_format("%s\n") % err); + L(FL("lua stack: %s") % dump_stack(st)); + lua_pop(st, 1); + failed = true; +} + +// getters + +Lua & +Lua::get(int idx) +{ + if (failed) return *this; + if (!lua_istable (st, idx)) + { + fail("istable() in get"); + return *this; + } + if (lua_gettop (st) < 1) + { + fail("stack top > 0 in get"); + return *this; + } + lua_gettable(st, idx); + return *this; +} + +Lua & +Lua::get_fn(int idx) +{ + if (failed) return *this; + get(idx); + if (!lua_isfunction (st, -1)) + fail("isfunction() in get_fn"); + return *this; +} + +Lua & +Lua::get_tab(int idx) +{ + if (failed) return *this; + get(idx); + if (!lua_istable (st, -1)) + fail("istable() in get_tab"); + return *this; +} + +Lua & +Lua::get_str(int idx) +{ + if (failed) return *this; + get(idx); + if (!lua_isstring (st, -1)) + fail("isstring() in get_str"); + return *this; +} + +Lua & +Lua::get_num(int idx) +{ + if (failed) return *this; + get(idx); + if (!lua_isnumber (st, -1)) + fail("isnumber() in get_num"); + return *this; +} + +Lua & +Lua::get_bool(int idx) +{ + if (failed) return *this; + get(idx); + if (!lua_isboolean (st, -1)) + fail("isboolean() in get_bool"); + return *this; +} + +// extractors + +Lua & +Lua::extract_str_nolog(string & str) +{ + if (failed) return *this; + if (!lua_isstring (st, -1)) + { + fail("isstring() in extract_str"); + return *this; + } + str = string(lua_tostring(st, -1), lua_strlen(st, -1)); + return *this; +} + +Lua & +Lua::extract_str(string & str) +{ + extract_str_nolog(str); + L(FL("lua: extracted string = %s") % str); + return *this; +} + +Lua & +Lua::extract_classified_str(string & str) +{ + extract_str_nolog(str); + L(FL("lua: extracted string [CLASSIFIED]")); + return *this; +} + +Lua & +Lua::extract_int(int & i) +{ + if (failed) return *this; + if (!lua_isnumber (st, -1)) + { + fail("isnumber() in extract_int"); + return *this; + } + i = static_cast(lua_tonumber(st, -1)); + L(FL("lua: extracted int = %i") % i); + return *this; +} + +Lua & +Lua::extract_double(double & i) +{ + if (failed) return *this; + if (!lua_isnumber (st, -1)) + { + fail("isnumber() in extract_double"); + return *this; + } + i = lua_tonumber(st, -1); + L(FL("lua: extracted double = %i") % i); + return *this; +} + + +Lua & +Lua::extract_bool(bool & i) +{ + if (failed) return *this; + if (!lua_isboolean (st, -1)) + { + fail("isboolean() in extract_bool"); + return *this; + } + i = (lua_toboolean(st, -1) == 1); + L(FL("lua: extracted bool = %i") % i); + return *this; +} + + +// table iteration + +Lua & +Lua::begin() +{ + if (failed) return *this; + if (!lua_istable(st, -1)) + { + fail("istable() in begin"); + return *this; + } + I(lua_checkstack (st, 1)); + lua_pushnil(st); + return *this; +} + +bool +Lua::next() +{ + if (failed) return false; + if (!lua_istable(st, -2)) + { + fail("istable() in next"); + return false; + } + I(lua_checkstack (st, 1)); + if (lua_next(st, -2) != 0) + { + return true; + } + pop(); + return false; +} + +// pushers + +Lua & +Lua::push_str(string const & str) +{ + if (failed) return *this; + I(lua_checkstack (st, 1)); + lua_pushlstring(st, str.c_str(), str.size()); + return *this; +} + +Lua & +Lua::push_int(int num) +{ + if (failed) return *this; + I(lua_checkstack (st, 1)); + lua_pushnumber(st, num); + return *this; +} + +Lua & +Lua::push_int(double num) +{ + if (failed) return *this; + I(lua_checkstack (st, 1)); + lua_pushnumber(st, num); + return *this; +} + +Lua & +Lua::push_bool(bool b) +{ + if (failed) return *this; + I(lua_checkstack (st, 1)); + lua_pushboolean(st, b); + return *this; +} + +Lua & +Lua::push_nil() +{ + if (failed) return *this; + I(lua_checkstack (st, 1)); + lua_pushnil(st); + return *this; +} + +Lua & +Lua::push_table() +{ + if (failed) return *this; + I(lua_checkstack (st, 1)); + lua_newtable(st); + return *this; +} + +Lua & +Lua::set_table(int idx) +{ + if (failed) return *this; + I(lua_checkstack (st, 1)); + lua_settable(st, idx); + return *this; +} + +Lua & +Lua::call(int in, int out) +{ + if (failed) return *this; + I(lua_checkstack (st, out)); + if (lua_pcall(st, in, out, 0) != 0) + { + report_error(); + } + return *this; +} + +Lua & +Lua::pop(int count) +{ + if (failed) return *this; + if (lua_gettop (st) < count) + { + fail("stack top is not >= count in pop"); + return *this; + } + lua_pop(st, count); + return *this; +} + +Lua & +Lua::func(string const & fname) +{ + L(FL("loading lua hook %s") % fname); + if (!failed) + { + if (missing_functions.find(fname) != missing_functions.end()) + failed = true; + else + { + push_str(fname); + get_fn(); + if (failed) + missing_functions.insert(fname); + } + } + return *this; +} + +Lua & +Lua::loadstring(string const & str, string const & identity) +{ + if (!failed) + { + if (luaL_loadbuffer(st, str.c_str(), str.size(), identity.c_str())) + { + report_error(); + } + } + return *this; +} + +Lua & +Lua::loadfile(string const & filename) +{ + if (!failed) + { + if (luaL_loadfile(st, filename.c_str())) + { + report_error(); + } + } + return *this; +} + +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 = luaL_checkstring (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 = luaL_checkstring(L, -1); + lua_pushnumber(L, existsonpath(exe)); + return 1; + } + + static int + monotone_is_executable_for_lua(lua_State *L) + { + const char *path = luaL_checkstring(L, -1); + lua_pushboolean(L, is_executable(path)); + return 1; + } + + static int + monotone_make_executable_for_lua(lua_State *L) + { + const char *path = luaL_checkstring(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 = luaL_checkstring(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; i(luaL_checknumber(L, -1)); + int res; + int ret; + ret = process_wait(pid, &res); + lua_pushnumber(L, res); + lua_pushnumber(L, ret); + return 2; + } + + static int + monotone_kill_for_lua(lua_State *L) + { + int n = lua_gettop(L); + pid_t pid = static_cast(luaL_checknumber(L, -2)); + int sig; + if (n>1) + sig = static_cast(luaL_checknumber(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 = static_cast(luaL_checknumber(L, -1)); + lua_pushnumber(L, process_sleep(seconds)); + return 1; + } + + static int + monotone_guess_binary_file_contents_for_lua(lua_State *L) + { + const char *path = luaL_checkstring(L, -1); + N(path, F("%s called with an invalid parameter") % "guess_binary"); + + std::ifstream file(path, std::ios_base::binary); + if (!file) + { + lua_pushnil(L); + return 1; + } + const int bufsize = 8192; + char tmpbuf[bufsize]; + string buf; + while (file.read(tmpbuf, sizeof tmpbuf)) + { + I(file.gcount() <= static_cast(sizeof tmpbuf)); + buf.assign(tmpbuf, file.gcount()); + if (guess_binary(buf)) + { + lua_pushboolean(L, true); + return 1; + } + } + lua_pushboolean(L, false); + return 1; + } + + static int + monotone_include_for_lua(lua_State *L) + { + const char *path = luaL_checkstring(L, -1); + N(path, F("%s called with an invalid parameter") % "Include"); + + 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 = luaL_checkstring(L, -1); + N(pathstr, F("%s called with an invalid parameter") % "IncludeDir"); + + fs::path locpath(pathstr, fs::native); + N(fs::exists(locpath), F("Directory '%s' does not exist") % 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; + } + + static int + monotone_regex_search_for_lua(lua_State *L) + { + const char *re = luaL_checkstring(L, -2); + const char *str = luaL_checkstring(L, -1); + boost::cmatch what; + + bool result = false; + try { + result = boost::regex_search(str, what, boost::regex(re)); + } catch (boost::bad_pattern e) { + lua_pushstring(L, e.what()); + lua_error(L); + return 0; + } + lua_pushboolean(L, result); + return 1; + } + + static int + monotone_globish_match_for_lua(lua_State *L) + { + const char *re = luaL_checkstring(L, -2); + const char *str = luaL_checkstring(L, -1); + + bool result = false; + try { + string r(re); + string n; + string s(str); + result = globish_matcher(r, n)(s); + } catch (informative_failure & e) { + lua_pushstring(L, e.what.c_str()); + lua_error(L); + return 0; + } catch (boost::bad_pattern & e) { + lua_pushstring(L, e.what()); + lua_error(L); + return 0; + } catch (...) { + lua_pushstring(L, "Unknown error."); + lua_error(L); + return 0; + } + lua_pushboolean(L, result); + return 1; + } + + static int + monotone_gettext_for_lua(lua_State *L) + { + const char *msgid = luaL_checkstring(L, -1); + lua_pushstring(L, gettext(msgid)); + return 1; + } + + static int + monotone_parse_basic_io_for_lua(lua_State *L) + { + vector > > res; + const string str(luaL_checkstring(L, -1), lua_strlen(L, -1)); + basic_io::input_source in(str, "monotone_parse_basic_io_for_lua"); + basic_io::tokenizer tok(in); + try + { + string got; + basic_io::token_type tt; + do + { + tt = tok.get_token(got); + switch (tt) + { + case basic_io::TOK_SYMBOL: + res.push_back(make_pair(got, vector())); + break; + case basic_io::TOK_STRING: + case basic_io::TOK_HEX: + E(!res.empty(), F("bad input to parse_basic_io")); + res.back().second.push_back(got); + break; + default: + break; + } + } + while (tt != basic_io::TOK_NONE); + } + catch (informative_failure & e) + {// there was a syntax error in our string + lua_pushnil(L); + return 0; + } + lua_newtable(L); + int n = 1; + for (vector > >::const_iterator i = res.begin(); + i != res.end(); ++i) + { + lua_pushnumber(L, n++); + lua_newtable(L); + lua_pushstring(L, "name"); + lua_pushstring(L, i->first.c_str()); + lua_settable(L, -3); + lua_pushstring(L, "values"); + lua_newtable(L); + int m = 1; + for (vector::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) + { + lua_pushnumber(L, m++); + lua_pushstring(L, j->c_str()); + lua_settable(L, -3); + } + lua_settable(L, -3); + lua_settable(L, -3); + } + return 1; + } +} + +bool +run_string(lua_State * st, string const &str, string const & identity) +{ + I(st); + return + Lua(st) + .loadstring(str, identity) + .call(0,1) + .ok(); +} + +bool +run_file(lua_State * st, string const &filename) +{ + I(st); + return + Lua(st) + .loadfile(filename) + .call(0,1) + .ok(); +} + +void +add_functions(lua_State * 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_file_contents", monotone_guess_binary_file_contents_for_lua); + lua_register(st, "include", monotone_include_for_lua); + lua_register(st, "includedir", monotone_includedir_for_lua); + lua_register(st, "gettext", monotone_gettext_for_lua); + lua_register(st, "parse_basic_io", monotone_parse_basic_io_for_lua); + + // add regex functions: + lua_newtable(st); + lua_pushstring(st, "regex"); + lua_pushvalue(st, -2); + lua_settable(st, LUA_GLOBALSINDEX); + + lua_pushstring(st, "search"); + lua_pushcfunction(st, monotone_regex_search_for_lua); + lua_settable(st, -3); + + // add globish functions: + lua_newtable(st); + lua_pushstring(st, "globish"); + lua_pushvalue(st, -2); + lua_settable(st, LUA_GLOBALSINDEX); + + lua_pushstring(st, "match"); + lua_pushcfunction(st, monotone_globish_match_for_lua); + lua_settable(st, -3); + + lua_pop(st, 1); +} ============================================================ --- lua.hh 54e0054144207e223df17764f6da0e6134cb6a02 +++ lua.hh 54e0054144207e223df17764f6da0e6134cb6a02 @@ -0,0 +1,69 @@ +#ifndef __LUA_HH__ +#define __LUA_HH__ + +#include +#include + +struct lua_State; + +// 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 +// destructed, so no need to pop values when you're done. + +struct +Lua +{ + lua_State * st; + bool failed; + static std::set missing_functions; + + Lua(lua_State * s); + ~Lua(); + + void fail(std::string const & reason); + bool ok(); + void report_error(); + + // getters + Lua & get(int idx = LUA_GLOBALSINDEX); + Lua & get_fn(int idx = LUA_GLOBALSINDEX); + Lua & get_tab(int idx = LUA_GLOBALSINDEX); + Lua & get_str(int idx = LUA_GLOBALSINDEX); + Lua & get_num(int idx = LUA_GLOBALSINDEX); + Lua & get_bool(int idx = LUA_GLOBALSINDEX); + + // extractors + Lua & extract_str_nolog(std::string & str); + Lua & extract_str(std::string & str); + Lua & extract_classified_str(std::string & str); + Lua & extract_int(int & i); + Lua & extract_double(double & i); + Lua & extract_bool(bool & i); + + // table iteration + Lua & begin(); + bool next(); + + // pushers + Lua & push_str(std::string const & str); + Lua & push_int(int num); + Lua & push_int(double num); + Lua & push_bool(bool b); + Lua & push_nil(); + Lua & push_table(); + Lua & set_table(int idx = -3); + Lua & call(int in, int out); + Lua & pop(int count = 1); + Lua & func(std::string const & fname); + Lua & loadstring(std::string const & str, std::string const & identity); + Lua & loadfile(std::string const & filename); +}; + +bool run_string(lua_State * st, + std::string const &str, + std::string const & identity); +bool run_file(lua_State * st, std::string const &filename); +void add_functions(lua_State * st); + +#endif ============================================================ --- ChangeLog a64826396081e97e2428a6c982d7e5b709a3c17d +++ ChangeLog 76e0a67eba2c4c2926795e0d54961b50f2950ae9 @@ -1,3 +1,15 @@ +2006-05-21 Timothy Brownawell + + Split lua.cc into two files: lua_hooks.cc gets the lua_hooks class + and the one callback that needs an app_state, lua.cc gets everything + else. + * lua.{cc,hh}: Rename to lua_hooks.{cc,hh}. Move almost everything + except the lua_hooks class to... + * lua.{cc,hh}: New files. + * Makefile.am: Adjust accordingly. + * app_state.hh file_io.cc keys.cc transforms.hh: Adjust includes to + account for rename. + 2006-05-21 Richard Levitte * po/sv.po: Correct a translation that was clearly cut and pasted @@ -40,11 +52,11 @@ * po/sv.po: Retranslate two fuzzies. -2006-05-16 Timothy Brownawell +2006-05-19 Timothy Brownawell * tests/t_quiet.at: --quiet now allows warnings to still be printed -2006-05-16 Timothy Brownawell +2006-05-19 Timothy Brownawell * sanity.cc: second sanity::set_quiet should be sanity::set_reallyquiet ============================================================ --- Makefile.am ad1680df7c76acbdaef7ad40c4e32e01d89daf7f +++ Makefile.am 0afb3360e47c62b19a8f6c73f240f304ad29001e @@ -10,7 +10,7 @@ app_state.cc app_state.hh \ commands.cc commands.hh $(CMD_SOURCES) \ diff_patch.cc diff_patch.hh \ - lua.cc lua.hh \ + lua.cc lua.hh lua_hooks.cc lua_hooks.hh \ transforms.cc transforms.hh \ update.cc update.hh \ work.cc work.hh \ ============================================================ --- app_state.hh 6274d29f9d4e43054180da2ba49c564b15fd2a26 +++ app_state.hh 4d4ab0e35a58144708b8a67252fec3a37f0ea71a @@ -17,7 +17,7 @@ #include #include "database.hh" -#include "lua.hh" +#include "lua_hooks.hh" #include "work.hh" #include "vocab.hh" #include "paths.hh" ============================================================ --- file_io.cc 547bf6f195554fd36499a1cfc95c1f571c2304b3 +++ file_io.cc df2709c7a36633e5266ced0a9b7b7d6ee3f41903 @@ -14,7 +14,7 @@ #include "botan/botan.h" #include "file_io.hh" -#include "lua.hh" +#include "lua_hooks.hh" #include "sanity.hh" #include "transforms.hh" #include "platform.hh" ============================================================ --- keys.cc b61b2cda83c67d33c1a2fb3ff0f7098ecfeb1570 +++ keys.cc cfb8170e6ffd325bc6833b70739b6435e603454e @@ -13,7 +13,7 @@ #include "constants.hh" #include "keys.hh" -#include "lua.hh" +#include "lua_hooks.hh" #include "netio.hh" #include "platform.hh" #include "safe_map.hh" ============================================================ --- lua.cc 6abb5987206ee45b2389366b815cc391e9ecce7b +++ lua_hooks.cc 2c8a4626208b7b0128a96da8ea532f7d3fce0e85 @@ -13,31 +13,23 @@ #include } -#include -#include -#include -#include -#include #include #include #include -#include #include #include #include +#include "lua.hh" + #include "app_state.hh" #include "file_io.hh" -#include "lua.hh" -#include "mkstemp.hh" +#include "lua_hooks.hh" #include "sanity.hh" #include "vocab.hh" -#include "platform.hh" #include "transforms.hh" #include "paths.hh" -#include "globish.hh" -#include "basic_io.hh" // defined in {std,test}_hooks.lua, converted #include "test_hooks.h" @@ -46,6 +38,11 @@ using namespace std; using boost::lexical_cast; +static int panic_thrower(lua_State * st) +{ + throw oops("lua panic"); +} + // this lets the lua callbacks (monotone_*_for_lua) have access to the // app_state the're associated with. // it was added so that the confdir (normally ~/.monotone) can be specified on @@ -54,632 +51,9 @@ // please *don't* use it for complex things that can throw errors static std::map map_of_lua_to_app; -static int panic_thrower(lua_State * st) -{ - throw oops("lua panic"); -} - -// adapted from "programming in lua", section 24.2.3 -// http://www.lua.org/pil/24.2.3.html -// output is from bottom (least accessible) to top (most accessible, where -// push and pop happen). -static std::string -dump_stack(lua_State * st) -{ - std::string out; - int i; - int top = lua_gettop(st); - for (i = 1; i <= top; i++) { /* repeat for each level */ - int t = lua_type(st, i); - switch (t) { - case LUA_TSTRING: /* strings */ - out += (boost::format("`%s'") % std::string(lua_tostring(st, i), lua_strlen(st, i))).str(); - break; - - case LUA_TBOOLEAN: /* booleans */ - out += (lua_toboolean(st, i) ? "true" : "false"); - break; - - case LUA_TNUMBER: /* numbers */ - out += (boost::format("%g") % lua_tonumber(st, i)).str(); - break; - - default: /* other values */ - out += (boost::format("%s") % lua_typename(st, t)).str(); - break; - - } - out += " "; /* put a separator */ - } - return out; -} - -// 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 -// destructed, so no need to pop values when you're done. - -struct -Lua -{ - lua_State * st; - bool failed; - static std::set missing_functions; - - Lua(lua_State * s) : - st(s), failed(false) - {} - - ~Lua() - { - lua_settop(st, 0); - } - - void fail(std::string const & reason) - { - L(FL("lua failure: %s; stack = %s\n") % reason % dump_stack(st)); - failed = true; - } - - bool ok() - { - if (failed) - L(FL("Lua::ok(): failed")); - return !failed; - } - - void report_error() - { - I(lua_isstring(st, -1)); - string err = string(lua_tostring(st, -1), lua_strlen(st, -1)); - W(i18n_format("%s\n") % err); - L(FL("lua stack: %s") % dump_stack(st)); - lua_pop(st, 1); - failed = true; - } - - // getters - - Lua & get(int idx = LUA_GLOBALSINDEX) - { - if (failed) return *this; - if (!lua_istable (st, idx)) - { - fail("istable() in get"); - return *this; - } - if (lua_gettop (st) < 1) - { - fail("stack top > 0 in get"); - return *this; - } - lua_gettable(st, idx); - return *this; - } - - Lua & get_fn(int idx = LUA_GLOBALSINDEX) - { - if (failed) return *this; - get(idx); - if (!lua_isfunction (st, -1)) - fail("isfunction() in get_fn"); - return *this; - } - - Lua & get_tab(int idx = LUA_GLOBALSINDEX) - { - if (failed) return *this; - get(idx); - if (!lua_istable (st, -1)) - fail("istable() in get_tab"); - return *this; - } - - Lua & get_str(int idx = LUA_GLOBALSINDEX) - { - if (failed) return *this; - get(idx); - if (!lua_isstring (st, -1)) - fail("isstring() in get_str"); - return *this; - } - - Lua & get_num(int idx = LUA_GLOBALSINDEX) - { - if (failed) return *this; - get(idx); - if (!lua_isnumber (st, -1)) - fail("isnumber() in get_num"); - return *this; - } - - Lua & get_bool(int idx = LUA_GLOBALSINDEX) - { - if (failed) return *this; - get(idx); - if (!lua_isboolean (st, -1)) - fail("isboolean() in get_bool"); - return *this; - } - - // extractors - - Lua & extract_str_nolog(string & str) - { - if (failed) return *this; - if (!lua_isstring (st, -1)) - { - fail("isstring() in extract_str"); - return *this; - } - str = string(lua_tostring(st, -1), lua_strlen(st, -1)); - return *this; - } - - Lua & extract_str(string & str) - { - extract_str_nolog(str); - L(FL("lua: extracted string = %s") % str); - return *this; - } - - Lua & extract_classified_str(string & str) - { - extract_str_nolog(str); - L(FL("lua: extracted string [CLASSIFIED]")); - return *this; - } - - Lua & extract_int(int & i) - { - if (failed) return *this; - if (!lua_isnumber (st, -1)) - { - fail("isnumber() in extract_int"); - return *this; - } - i = static_cast(lua_tonumber(st, -1)); - L(FL("lua: extracted int = %i") % i); - return *this; - } - - Lua & extract_double(double & i) - { - if (failed) return *this; - if (!lua_isnumber (st, -1)) - { - fail("isnumber() in extract_double"); - return *this; - } - i = lua_tonumber(st, -1); - L(FL("lua: extracted double = %i") % i); - return *this; - } - - - Lua & extract_bool(bool & i) - { - if (failed) return *this; - if (!lua_isboolean (st, -1)) - { - fail("isboolean() in extract_bool"); - return *this; - } - i = (lua_toboolean(st, -1) == 1); - L(FL("lua: extracted bool = %i") % i); - return *this; - } - - - // table iteration - - Lua & begin() - { - if (failed) return *this; - if (!lua_istable(st, -1)) - { - fail("istable() in begin"); - return *this; - } - I(lua_checkstack (st, 1)); - lua_pushnil(st); - return *this; - } - - bool next() - { - if (failed) return false; - if (!lua_istable(st, -2)) - { - fail("istable() in next"); - return false; - } - I(lua_checkstack (st, 1)); - if (lua_next(st, -2) != 0) - { - return true; - } - pop(); - return false; - } - - // pushers - - Lua & push_str(string const & str) - { - if (failed) return *this; - I(lua_checkstack (st, 1)); - lua_pushlstring(st, str.c_str(), str.size()); - return *this; - } - - Lua & push_int(int num) - { - if (failed) return *this; - I(lua_checkstack (st, 1)); - lua_pushnumber(st, num); - return *this; - } - - Lua & push_int(double num) - { - if (failed) return *this; - I(lua_checkstack (st, 1)); - lua_pushnumber(st, num); - return *this; - } - - Lua & push_bool(bool b) - { - if (failed) return *this; - I(lua_checkstack (st, 1)); - lua_pushboolean(st, b); - return *this; - } - - Lua & push_nil() - { - if (failed) return *this; - I(lua_checkstack (st, 1)); - lua_pushnil(st); - return *this; - } - - Lua & push_table() - { - if (failed) return *this; - I(lua_checkstack (st, 1)); - lua_newtable(st); - return *this; - } - - Lua & set_table(int idx = -3) - { - if (failed) return *this; - I(lua_checkstack (st, 1)); - lua_settable(st, idx); - return *this; - } - - Lua & call(int in, int out) - { - if (failed) return *this; - I(lua_checkstack (st, out)); - if (lua_pcall(st, in, out, 0) != 0) - { - report_error(); - } - return *this; - } - - Lua & pop(int count = 1) - { - if (failed) return *this; - if (lua_gettop (st) < count) - { - fail("stack top is not >= count in pop"); - return *this; - } - lua_pop(st, count); - return *this; - } - - Lua & func(string const & fname) - { - L(FL("loading lua hook %s") % fname); - if (!failed) - { - if (missing_functions.find(fname) != missing_functions.end()) - failed = true; - else - { - push_str(fname); - get_fn(); - if (failed) - missing_functions.insert(fname); - } - } - return *this; - } - - Lua & loadstring(string const & str, string const & identity) - { - if (!failed) - { - if (luaL_loadbuffer(st, str.c_str(), str.size(), identity.c_str())) - { - report_error(); - } - } - return *this; - } - - Lua & loadfile(string const & filename) - { - if (!failed) - { - if (luaL_loadfile(st, filename.c_str())) - { - report_error(); - } - } - return *this; - } -}; - -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 = luaL_checkstring (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 = luaL_checkstring(L, -1); - lua_pushnumber(L, existsonpath(exe)); - return 1; - } - - static int - monotone_is_executable_for_lua(lua_State *L) - { - const char *path = luaL_checkstring(L, -1); - lua_pushboolean(L, is_executable(path)); - return 1; - } - - static int - monotone_make_executable_for_lua(lua_State *L) - { - const char *path = luaL_checkstring(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 = luaL_checkstring(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; i(luaL_checknumber(L, -1)); - int res; - int ret; - ret = process_wait(pid, &res); - lua_pushnumber(L, res); - lua_pushnumber(L, ret); - return 2; - } - - static int - monotone_kill_for_lua(lua_State *L) - { - int n = lua_gettop(L); - pid_t pid = static_cast(luaL_checknumber(L, -2)); - int sig; - if (n>1) - sig = static_cast(luaL_checknumber(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 = static_cast(luaL_checknumber(L, -1)); - lua_pushnumber(L, process_sleep(seconds)); - return 1; - } - - static int - monotone_guess_binary_file_contents_for_lua(lua_State *L) - { - const char *path = luaL_checkstring(L, -1); - N(path, F("%s called with an invalid parameter") % "guess_binary"); - - std::ifstream file(path, ios_base::binary); - if (!file) - { - lua_pushnil(L); - return 1; - } - const int bufsize = 8192; - char tmpbuf[bufsize]; - string buf; - while (file.read(tmpbuf, sizeof tmpbuf)) - { - I(file.gcount() <= static_cast(sizeof tmpbuf)); - buf.assign(tmpbuf, file.gcount()); - if (guess_binary(buf)) - { - lua_pushboolean(L, true); - return 1; - } - } - lua_pushboolean(L, false); - return 1; - } - - static int - monotone_include_for_lua(lua_State *L) - { - const char *path = luaL_checkstring(L, -1); - N(path, F("%s called with an invalid parameter") % "Include"); - - 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 = luaL_checkstring(L, -1); - N(pathstr, F("%s called with an invalid parameter") % "IncludeDir"); - - fs::path locpath(pathstr, fs::native); - N(fs::exists(locpath), F("Directory '%s' does not exist") % 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; - } - - static int - monotone_regex_search_for_lua(lua_State *L) - { - const char *re = luaL_checkstring(L, -2); - const char *str = luaL_checkstring(L, -1); - boost::cmatch what; - - bool result = false; - try { - result = boost::regex_search(str, what, boost::regex(re)); - } catch (boost::bad_pattern e) { - lua_pushstring(L, e.what()); - lua_error(L); - return 0; - } - lua_pushboolean(L, result); - return 1; - } - - static int - monotone_globish_match_for_lua(lua_State *L) - { - const char *re = luaL_checkstring(L, -2); - const char *str = luaL_checkstring(L, -1); - - bool result = false; - try { - string r(re); - string n; - string s(str); - result = globish_matcher(r, n)(s); - } catch (informative_failure & e) { - lua_pushstring(L, e.what.c_str()); - lua_error(L); - return 0; - } catch (boost::bad_pattern & e) { - lua_pushstring(L, e.what()); - lua_error(L); - return 0; - } catch (...) { - lua_pushstring(L, "Unknown error."); - lua_error(L); - return 0; - } - lua_pushboolean(L, result); - return 1; - } - - static int - monotone_gettext_for_lua(lua_State *L) - { - const char *msgid = luaL_checkstring(L, -1); - lua_pushstring(L, gettext(msgid)); - return 1; - } - - static int monotone_get_confdir_for_lua(lua_State *L) { map::iterator i = map_of_lua_to_app.find(L); @@ -693,91 +67,8 @@ lua_pushnil(L); return 1; } - - static int - monotone_parse_basic_io_for_lua(lua_State *L) - { - vector > > res; - const string str(luaL_checkstring(L, -1), lua_strlen(L, -1)); - basic_io::input_source in(str, "monotone_parse_basic_io_for_lua"); - basic_io::tokenizer tok(in); - try - { - string got; - basic_io::token_type tt; - do - { - tt = tok.get_token(got); - switch (tt) - { - case basic_io::TOK_SYMBOL: - res.push_back(make_pair(got, vector())); - break; - case basic_io::TOK_STRING: - case basic_io::TOK_HEX: - E(!res.empty(), F("bad input to parse_basic_io")); - res.back().second.push_back(got); - break; - default: - break; - } - } - while (tt != basic_io::TOK_NONE); - } - catch (informative_failure & e) - {// there was a syntax error in our string - lua_pushnil(L); - return 0; - } - lua_newtable(L); - int n = 1; - for (vector > >::const_iterator i = res.begin(); - i != res.end(); ++i) - { - lua_pushnumber(L, n++); - lua_newtable(L); - lua_pushstring(L, "name"); - lua_pushstring(L, i->first.c_str()); - lua_settable(L, -3); - lua_pushstring(L, "values"); - lua_newtable(L); - int m = 1; - for (vector::const_iterator j = i->second.begin(); - j != i->second.end(); ++j) - { - lua_pushnumber(L, m++); - lua_pushstring(L, j->c_str()); - lua_settable(L, -3); - } - lua_settable(L, -3); - lua_settable(L, -3); - } - return 1; - } } -static bool -run_string(lua_State * st, string const &str, string const & identity) -{ - I(st); - return - Lua(st) - .loadstring(str, identity) - .call(0,1) - .ok(); -} - -static bool -run_file(lua_State * st, string const &filename) -{ - I(st); - return - Lua(st) - .loadfile(filename) - .call(0,1) - .ok(); -} - lua_hooks::lua_hooks() { st = lua_open (); @@ -792,44 +83,9 @@ 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_file_contents", monotone_guess_binary_file_contents_for_lua); - lua_register(st, "include", monotone_include_for_lua); - lua_register(st, "includedir", monotone_includedir_for_lua); - lua_register(st, "gettext", monotone_gettext_for_lua); lua_register(st, "get_confdir", monotone_get_confdir_for_lua); - lua_register(st, "parse_basic_io", monotone_parse_basic_io_for_lua); + add_functions(st); - // add regex functions: - lua_newtable(st); - lua_pushstring(st, "regex"); - lua_pushvalue(st, -2); - lua_settable(st, LUA_GLOBALSINDEX); - - lua_pushstring(st, "search"); - lua_pushcfunction(st, monotone_regex_search_for_lua); - lua_settable(st, -3); - - // add globish functions: - lua_newtable(st); - lua_pushstring(st, "globish"); - lua_pushvalue(st, -2); - lua_settable(st, LUA_GLOBALSINDEX); - - lua_pushstring(st, "match"); - lua_pushcfunction(st, monotone_globish_match_for_lua); - lua_settable(st, -3); - - lua_pop(st, 1); - // Disable any functions we don't want. This is easiest // to do just by running a lua string. if (!run_string(st, ============================================================ --- lua.hh 664f93c8b443254f5a75fc40ee0bd12d50a38240 +++ lua_hooks.hh 2b7f5a8aebde4f44fd25b0c65e0434d2ceec6813 @@ -1,5 +1,5 @@ -#ifndef __LUA__ -#define __LUA__ +#ifndef __LUA_HOOKS_HH__ +#define __LUA_HOOKS_HH__ // copyright (C) 2002, 2003 graydon hoare // all rights reserved. @@ -131,4 +131,4 @@ bool hook_note_netsync_end(std::string nonce); }; +#endif // __LUA_HOOKS_HH__ -#endif // __LUA_HH__ ============================================================ --- transforms.hh b7c20f2b92989c820febdb61bb3b55cb6887964a +++ transforms.hh 8dabc3f8fe642f4c1d3dddab53fae3a4628617a1 @@ -7,7 +7,7 @@ // see the file COPYING for details #include "revision.hh" -#include "lua.hh" +#include "lua_hooks.hh" #include "vocab.hh" #include "roster.hh"