diff options
Diffstat (limited to 'Sluift/main.cpp')
-rw-r--r-- | Sluift/main.cpp | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/Sluift/main.cpp b/Sluift/main.cpp new file mode 100644 index 0000000..505cc5c --- /dev/null +++ b/Sluift/main.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2013-2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <string> +#include <vector> +#include <lua.hpp> +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/Platform.h> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include <boost/program_options.hpp> +#include <boost/version.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <boost/assign/list_of.hpp> +#include <Sluift/globals.h> +#include <Sluift/Console.h> +#include <Sluift/StandardTerminal.h> +#include <Sluift/sluift.h> +#include <Sluift/Lua/LuaUtils.h> +#ifdef HAVE_EDITLINE +#include <Sluift/EditlineTerminal.h> +#endif +#include <Sluift/Version.h> + +using namespace Swift; + +#ifdef SWIFTEN_PLATFORM_WINDOWS +#define EXIT_KEY "Z" +#else +#define EXIT_KEY "D" +#endif + +static const std::string SLUIFT_WELCOME_STRING( + "== Sluift XMPP Console (" SLUIFT_VERSION_STRING ")\nPress Ctrl-" EXIT_KEY " to exit. Type help() for help."); + +static const luaL_Reg defaultLibraries[] = { + {"", luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_DBLIBNAME, luaopen_debug}, + {"sluift", luaopen_sluift}, + {NULL, NULL} +}; + +static void handleInterruptSignal(int) { + Sluift::globals.interruptRequested = 1; +} + +static void checkResult(lua_State* L, int result) { + if (result && !lua_isnil(L, -1)) { + const char* errorMessage = lua_tostring(L, -1); + throw std::runtime_error(errorMessage ? errorMessage : "Unknown error"); + } +} + +static void initialize(lua_State* L) { + lua_gc(L, LUA_GCSTOP, 0); + for (const luaL_Reg* lib = defaultLibraries; lib->func; lib++) { +#if LUA_VERSION_NUM >= 502 + luaL_requiref(L, lib->name, lib->func, 1); + lua_pop(L, 1); +#else + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); +#endif + } + lua_gc(L, LUA_GCRESTART, 0); +} + +static void runScript(lua_State* L, const std::string& script, const std::vector<std::string>& scriptArguments) { + // Create arguments table + lua_createtable(L, boost::numeric_cast<int>(scriptArguments.size()), 0); + for (size_t i = 0; i < scriptArguments.size(); ++i) { + lua_pushstring(L, scriptArguments[i].c_str()); + lua_rawseti(L, -2, boost::numeric_cast<int>(i+1)); + } + lua_setglobal(L, "arg"); + + // Load file + checkResult(L, luaL_loadfile(L, script.c_str())); + foreach (const std::string& scriptArgument, scriptArguments) { + lua_pushstring(L, scriptArgument.c_str()); + } + checkResult(L, Console::call(L, boost::numeric_cast<int>(scriptArguments.size()), false)); +} + +int main(int argc, char* argv[]) { + // Parse program options + boost::program_options::options_description visibleOptions("Options"); + visibleOptions.add_options() + ("help,h", "Display this help message") + ("version,v", "Display version information") + ("interactive,i", "Enter interactive mode after executing script") + ; + boost::program_options::options_description hiddenOptions("Hidden Options"); + hiddenOptions.add_options() + ("script", boost::program_options::value< std::string >(), "Script to be executed") + ("script-arguments", boost::program_options::value< std::vector<std::string> >(), "Script arguments") + ; + boost::program_options::options_description options("All Options"); + options.add(visibleOptions).add(hiddenOptions); + + boost::program_options::positional_options_description positional_options; + positional_options.add("script", 1).add("script-arguments", -1); + + boost::program_options::variables_map arguments; + try { + boost::program_options::store( + boost::program_options::command_line_parser(argc, argv) + .options(options) + .positional(positional_options).run(), arguments); + } + catch (const boost::program_options::unknown_option& option) { +#if BOOST_VERSION >= 104200 + std::cout << "Ignoring unknown option " << option.get_option_name() << " but continuing." << std::endl; +#else + std::cout << "Error: " << option.what() << " (continuing)" << std::endl; +#endif + } + catch (const std::exception& e) { + std::cout << "Error: " << e.what() << std::endl; + return -1; + } + boost::program_options::notify(arguments); + + // Help & version + if (arguments.count("help")) { + std::cout << visibleOptions << "\n"; + return 0; + } + else if (arguments.count("version")) { + std::cout << SLUIFT_VERSION_STRING; + return 0; + } + + lua_State* L = luaL_newstate(); + initialize(L); + try { + // Run script + if (arguments.count("script")) { + std::vector<std::string> scriptArguments; + if (arguments.count("script-arguments")) { + scriptArguments = arguments["script-arguments"].as< std::vector<std::string> >(); + } + runScript(L, arguments["script"].as<std::string>(), scriptArguments); + } + + // Run console + if (arguments.count("interactive") || arguments.count("script") == 0) { + // Set up signal handler + signal(SIGINT, handleInterruptSignal); + + // Import some useful functions into the global namespace + lua_getglobal(L, "sluift"); + std::vector<std::string> globalImports = boost::assign::list_of + ("help")("with"); + foreach (const std::string& globalImport, globalImports) { + lua_getfield(L, -1, globalImport.c_str()); + lua_setglobal(L, globalImport.c_str()); + } + lua_pop(L, 1); + + std::cout << SLUIFT_WELCOME_STRING << std::endl; +#ifdef HAVE_EDITLINE + EditlineTerminal& terminal = EditlineTerminal::getInstance(); +#else + StandardTerminal terminal; +#endif + Console console(L, &terminal); + console.run(); + } + } + catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + lua_close(L); + return -1; + } + lua_close(L); + return 0; +} |