#include #include #include #include #include #include #include #include #include "util.hpp" #include "table.hpp" #ifndef WIN32 #include #endif #include "luastack.hpp" #ifdef WIN32 #define stat _stat #endif //////////////////////////////////////////////////////////// // // The source database is a lua table that maps filenames // to file info. A file info is a lua table containing: // // name: filename as a string // mtime: file modification time as human-readable string // code: the entire contents of the source file as a string // error: a syntax error message, or nil // hash: 128-bit hash of the code // closure: a lua closure, the compiled file // sequence: the position of the file in control.lst // // Operations on a source DB: // // source.peek() // - peek at the source database, for inspection purposes. // source.read() // - return a new source, reusing info from old. // //////////////////////////////////////////////////////////// static std::string get_mtime(const std::string &fn) { struct stat result; if(stat(fn.c_str(), &result)==0) { std::stringstream ss; ss << result.st_mtime; return ss.str(); } return ""; } static std::string get_code(const std::string &fn) { std::ifstream fs(fn); std::stringstream buffer; buffer << fs.rdbuf(); return buffer.str(); } int lpx_source_updatefile(lua_State *L) { LuaArg source, fn; LuaRet info; LuaVar mtime, null; LuaStack LS(L, source, fn, info, mtime, null); // Get the existing info table from the source DB. if (LS.istable(source)) { LS.rawget(info, source, fn); if (!LS.istable(info)) { LS.newtable(info); } } else { LS.newtable(info); } // If the file modification is wrong, update // these fields: code, mtime, closure, error // Otherwise, update nothing. std::string cfn = LS.tostring(fn); LS.getfield(mtime, info, "mtime"); std::string old_mtime; if (LS.isstring(mtime)) { old_mtime = LS.tostring(mtime); } std::cerr << "Probing " << cfn << std::endl; std::string new_mtime = get_mtime("syslua/" + cfn); LS.setnil(null); if ((old_mtime == "") || (old_mtime != new_mtime)) { std::cerr << "Rereading " << cfn << std::endl; std::string ccode = get_code("syslua/" + cfn); LS.setfield(info, "name", fn); LS.setfield(info, "mtime", new_mtime); LS.setfield(info, "code", ccode); if ((new_mtime == "")||(ccode == "")) { LS.setfield(info, "error", "cannot read source file"); LS.setfield(info, "closure", null); } else if (luaL_loadbuffer(L, ccode.c_str(), ccode.size(), cfn.c_str()) == 0) { lua_setfield(L, info.index(), "closure"); LS.setfield(info, "error", null); } else { lua_setfield(L, info.index(), "error"); LS.setfield(info, "closure", null); } } return LS.result(); } int lpx_source_updateall(lua_State *L) { LuaArg source; LuaRet newdb; LuaVar info, fn, seq; LuaStack LS(L, source, newdb, info, fn, seq); // Read the list of filenames. std::string ctrl = "syslua/control.lst"; util::stringvec filenames = util::trim_and_uncomment(util::read_lines(ctrl)); if (filenames.empty()) { luaL_error(L, "cannot read source database control.lst"); } // Process the files one by one. LS.newtable(newdb); for (int i = 0; i < filenames.size(); i++) { LS.setstring(fn, filenames[i]); // Call source_updatefile to get the updated info for one file. lua_pushvalue(L, source.index()); lua_pushvalue(L, fn.index()); lpx_source_updatefile(L); lua_replace(L, info.index()); // Insert the sequence number and put finalized info into the new database. LS.setnumber(seq, i + 1); LS.setfield(info, "sequence", seq); LS.rawset(newdb, fn, info); } return LS.result(); } // Get a class from the class database. // // CLASSNAME // if classname is already present, and is a table, return it. // if classname is already present, and not a table, error. // if classname is not present, create and initialize it. // int lpx_source_class(lua_State *L) { LuaArg classname; LuaRet classtab; LuaVar classdb, action; LuaStack LS(L, classname, classtab, classdb, action); LS.checktype(classname, LUA_TSTRING); // Get a pointer to the classdb. LS.getfield(classdb, LUA_REGISTRYINDEX, "classdb"); if (!LS.istable(classdb)) { LS.newtable(classdb); LS.setfield(LUA_REGISTRYINDEX, "classdb", classdb); } // Get the classtab from the classdb, sanity check it. LS.rawget(classtab, classdb, classname); if (LS.istable(classtab)) { return LS.result(); } else if (!LS.isnil(classtab)) { luaL_error(L, "%s is not a class", LS.tostring(classname).c_str()); } // Create a new classtab and store it in the classdb. LS.newtable(classtab); LS.rawset(classdb, classname, classtab); LS.setfield(classtab, "__index", classtab); LS.setfield(classtab, "__class", classname); LS.newtable(action); LS.setfield(classtab, "action", action); return LS.result(); } // Reset a class database: // // Clear out all classes. Instead of replacing the class tables, // it simply deletes all keys. That way, if somebody has a pointer // to a class, the pointer is not invalidated. // int lpx_source_resetclasses(lua_State *L) { LuaVar classdb, classname, classtab, action, key; LuaStack LS(L, classname, classtab, classdb, action, key); // Get a pointer to the classdb. LS.getfield(classdb, LUA_REGISTRYINDEX, "classdb"); if (!LS.istable(classdb)) { LS.newtable(classdb); LS.setfield(LUA_REGISTRYINDEX, "classdb", classdb); } // Iterate over the classdb, clearing it. LS.setnil(classname); while (LS.next(classdb, classname, classtab) != 0) { if (LS.istable(classtab)) { LS.setstring(key, "action"); LS.rawget(action, classtab, key); if (LS.istable(action)) { lua_pushvalue(L, action.index()); lpx_table_clear(L); } else { LS.newtable(action); } lua_pushvalue(L, classtab.index()); lpx_table_clear(L); LS.setfield(classtab, "__index", classtab); LS.setfield(classtab, "__class", classname); LS.setfield(classtab, "action", action); } } return LS.result(); } void luaopen_lpx_source(lua_State *L) { LuaStack::reg(L, "source", "updatefile", LuaArgCheck<2, lpx_source_updatefile>); LuaStack::reg(L, "source", "updateall", LuaArgCheck<1, lpx_source_updateall>); LuaStack::reg(L, "source", "class", LuaArgCheck<1, lpx_source_class>); LuaStack::reg(L, "source", "resetclasses", LuaArgCheck<0, lpx_source_resetclasses>); LuaStack::reg(L, 0 , "class", LuaArgCheck<1, lpx_source_class>); }