/* * lua-interface * Copyright (C) 2002-2008 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* $Id: lua-interface.cpp,v 1.11 2008-08-13 09:46:22 pixel Exp $ */ #define WIP #define VERSION "0.7" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lua-interface-hc.h" #ifndef _MSVC // this seems ignored under apple's gcc... dammit. #define WEAK __attribute__ ((weak)) #else #define WEAK #endif extern "C" { extern void luaconfigfiles_init(Lua * L) WEAK; extern void luaftgl_init(Lua * L) WEAK; extern void luagl_init(Lua * L) WEAK; extern void luahandle_init(Lua * L) WEAK; extern void luahttp_init(Lua * L) WEAK; extern void luasmtp_init(Lua * L) WEAK; extern void lualibs_init(Lua * L) WEAK; extern void luaocci_init(Lua * L) WEAK; extern void luaregex_init(Lua * L) WEAK; extern void luasql_init(Lua * L) WEAK; extern void luatask_init(Lua * L) WEAK; extern void luaxml_init(Lua * L) WEAK; extern void luaosmesa_init(Lua * L) WEAK; extern void luacd_init(Lua * L) WEAK; } #ifndef NO_DLL #define main dll_main #endif #ifdef _WIN32 #include #include #include #include #define EXT_PREFIX 0x1f8 #define KEV irec.Event.KeyEvent /* to make life easier */ #define KST irec.Event.KeyEvent.dwControlKeyState static int pending_key = 0; static int pending_count = 0; static int pending_prefix = 0; extern int _rl_last_c_pos; /* imported from display.c */ extern int _rl_last_v_pos; extern int rl_dispatching; /* imported from readline.c */ extern int rl_point; extern int rl_done; extern int rl_visible_prompt_length; extern "C" { extern int haveConsole; /* imported from rltty.c */ extern HANDLE hStdout, hStdin; } int my_getc (FILE * stream) { int key; if ( pending_count ) { --pending_count; if ( pending_prefix && (pending_count & 1) ) return pending_prefix; else return pending_key; } while ( 1 ) { DWORD dummy; if (WaitForSingleObject(hStdin, WAIT_FOR_INPUT) != WAIT_OBJECT_0) { if ( rl_done ) return( 0 ); else continue; } if ( haveConsole & FOR_INPUT ) { INPUT_RECORD irec; ReadConsoleInput(hStdin, &irec, 1, &dummy); switch(irec.EventType) { case KEY_EVENT: if ( KEV.bKeyDown && ((KEV.wVirtualKeyCode < VK_SHIFT) || (KEV.wVirtualKeyCode > VK_MENU)) ) { int mask = 0; key = KEV.uChar.AsciiChar & 0xff; // if ( KST & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) ) // mask=0x100; if ( key ) { /* Ascii direct */ pending_count = KEV.wRepeatCount - 1; pending_key = key; pending_prefix = 0; if ( mask ) key = tolower(key) | mask; } else /* Others prefixed */ { key = EXT_PREFIX; if ( mask ) key |= 4; if (KST & SHIFT_PRESSED) key |= 1; if (KST & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) key |= 2; mask |= EXT_PREFIX; pending_count = (KEV.wRepeatCount << 1) - 1; pending_key = KEV.wVirtualKeyCode; pending_prefix = key; } return key; } break; default: break; } } else { ReadFile(hStdin, &key, 1, &dummy, NULL); return key; } } } #endif bool interactive = false, hserver = false, tserver = false, mserver = false; class threaded_Lua : public Lua { public: threaded_Lua() { init_mutex(); } virtual ~threaded_Lua() { pthread_mutex_destroy(&mutex); } virtual void lock() { pthread_mutex_lock(&mutex); } virtual void unlock() { pthread_mutex_unlock(&mutex); } protected: virtual Lua * spawn_from_thread(lua_State * __L) { return new threaded_Lua(__L); } private: threaded_Lua(lua_State * __L) : Lua(__L) { init_mutex(); } void init_mutex() { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&mutex, &attr); pthread_mutexattr_destroy(&attr); } pthread_mutex_t mutex; }; class LuaStderrPrinter : public LuaPrinter { public: LuaStderrPrinter() { } virtual void puts(const char * msg) { fputs(msg, stderr); fputs("\n", stderr); } }; LuaStderrPrinter lp_stderr; String server_fname = "server.lua"; static int StartTaskLoop(void * foo) { TaskMan::MainLoop(); return 0; } class Luabaselua_interface : public LuaObject { public: static void pushstatics(Lua *) throw (GeneralException); }; typedef void baselua_interface; enum baselua_interface_t { BASELUA_INTERFACE_LOAD = 0, BASELUA_INTERFACE_PRELOAD, BASELUA_INTERFACE_LOADMODULE, BASELUA_INTERFACE_UNLINK, }; struct lua_functypes_t baselua_interface_functions[] = { { BASELUA_INTERFACE_LOAD, "load", 0, 1, { BLUA_STRING | BLUA_OBJECT } }, { BASELUA_INTERFACE_PRELOAD, "preload", 0, 1, { BLUA_STRING | BLUA_OBJECT } }, { BASELUA_INTERFACE_LOADMODULE, "loadmodule", 1, 1, { BLUA_STRING } }, { BASELUA_INTERFACE_UNLINK, "unlink", 1, 1, { BLUA_STRING } }, { -1, 0, 0, 0, 0 } }; class sLua_baselua_interface : public Base { public: DECLARE_FUNCTION(baselua_interface, BASELUA_INTERFACE_LOAD); DECLARE_FUNCTION(baselua_interface, BASELUA_INTERFACE_PRELOAD); DECLARE_FUNCTION(baselua_interface, BASELUA_INTERFACE_LOADMODULE); DECLARE_FUNCTION(baselua_interface, BASELUA_INTERFACE_UNLINK); private: static int baselua_interface_proceed_statics(Lua * L, int n, int caller); }; void Luabaselua_interface::pushstatics(Lua * L) throw (GeneralException ) { CHECK_FUNCTIONS(baselua_interface); PUSH_FUNCTION(baselua_interface, BASELUA_INTERFACE_LOAD); PUSH_FUNCTION(baselua_interface, BASELUA_INTERFACE_PRELOAD); PUSH_FUNCTION(baselua_interface, BASELUA_INTERFACE_LOADMODULE); PUSH_FUNCTION(baselua_interface, BASELUA_INTERFACE_UNLINK); } int sLua_baselua_interface::baselua_interface_proceed_statics(Lua * L, int n, int caller) { int r = 0; String filename; const char * t; switch (caller) { case BASELUA_INTERFACE_LOAD: if (!n) { L->load(&Input("lua-interface.lua")); } else { if (L->isstring(1)) { L->load(&Input(L->tostring(1))); } else { Handle * t = (Handle *) LuaObject::getme(L, 1); L->load(t); } } break; case BASELUA_INTERFACE_PRELOAD: r = 1; if (!n) { L->load(&Input("lua-interface.lua")); } else { if (L->isstring(1)) { L->load(&Input(L->tostring(1)), false); } else { Handle * t = (Handle *) LuaObject::getme(L, 1); L->load(t, false); } } break; case BASELUA_INTERFACE_LOADMODULE: filename = L->tostring(); if (0) { #if !(defined(__APPLE__) || defined(MODULES_BUILT_IN)) } else if ((filename == "luaconfigfiles") && (luaconfigfiles_init != NULL)) { luaconfigfiles_init(L); #ifndef _MSVC } else if ((filename == "luaftgl") && (luaftgl_init != NULL)) { luaftgl_init(L); } else if ((filename == "luagl") && (luagl_init != NULL)) { luagl_init(L); #endif } else if ((filename == "luahandle") && (luahandle_init != NULL)) { luahandle_init(L); } else if ((filename == "luahttp") && (luahttp_init != NULL)) { luahttp_init(L); } else if ((filename == "luasmtp") && (luasmtp_init != NULL)) { luasmtp_init(L); } else if ((filename == "lualibs") && (lualibs_init != NULL)) { lualibs_init(L); #ifndef _MSVC } else if ((filename == "luaocci") && (luaocci_init != NULL)) { luaocci_init(L); #endif } else if ((filename == "luaregex") && (luaregex_init != NULL)) { luaregex_init(L); } else if ((filename == "luasql") && (luasql_init != NULL)) { luasql_init(L); } else if ((filename == "luatask") && (luatask_init != NULL)) { luatask_init(L); } else if ((filename == "luaxml") && (luaxml_init != NULL)) { luaxml_init(L); #ifndef _MSVC } else if ((filename == "luaosmesa") && (luaosmesa_init != NULL)) { luaosmesa_init(L); #endif } else if ((filename == "luacd") && (luacd_init != NULL)) { luacd_init(L); #endif } else { LuaLoadPlugin(filename, L); } break; case BASELUA_INTERFACE_UNLINK: filename = L->tostring(1); t = filename.to_charp(); L->push((lua_Number) unlink(t)); r = 1; break; } return r; } class Lualua_interface : public LuaObject { public: static void pushstatics(Lua *) throw (GeneralException); }; typedef void lua_interface; enum lua_interface_functions_t { lua_interface_PRINTN, lua_interface_QUIT, lua_interface_EXIT, }; struct lua_functypes_t lua_interface_functions[] = { { lua_interface_PRINTN, "printn", 1, 1, { BLUA_ANY } }, { lua_interface_QUIT, "quit", 0, 0, 0 }, { lua_interface_EXIT, "exit", 0, 0, 0 }, { -1, 0, 0, 0, 0 } }; class sLua_lua_interface : public Base { public: DECLARE_FUNCTION(lua_interface, lua_interface_PRINTN); DECLARE_FUNCTION(lua_interface, lua_interface_QUIT); DECLARE_FUNCTION(lua_interface, lua_interface_EXIT); private: static int lua_interface_proceed_statics(Lua * L, int n, int caller); }; void Lualua_interface::pushstatics(Lua * L) throw (GeneralException ) { CHECK_FUNCTIONS(lua_interface); PUSH_FUNCTION(lua_interface, lua_interface_PRINTN); PUSH_FUNCTION(lua_interface, lua_interface_QUIT); PUSH_FUNCTION(lua_interface, lua_interface_EXIT); } int sLua_lua_interface::lua_interface_proceed_statics(Lua * L, int n, int caller) { int r = 0; String p; char * tc; String eol = ""; switch (caller) { case lua_interface_PRINTN: if (n) { p = L->tostring(1) + eol; } else { p = eol; } tc = p.strdup(); printm(M_BARE, "%s", tc); free(tc); break; case lua_interface_QUIT: case lua_interface_EXIT: interactive = false; break; } return r; } int lga = 0; enum { NO_OPTION = 0, }; int getopt_flag = NO_OPTION; struct option long_options[] = { {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"archive", 1, NULL, 'a'}, {"compile", 1, NULL, 'c'}, {"debug", 0, NULL, 'd'}, {"interactive", 0, NULL, 'i'}, {"line", 0, NULL, 'l'}, {"exec", 1, NULL, 'e'}, {"built-in", 0, NULL, 'b'}, {"probe", 0, NULL, 'p'}, {"log", 1, NULL, 'g'}, {"mail", 2, NULL, 'm'}, {"server", 2, NULL, 's'}, {"telnet", 2, NULL, 't'}, {"server_fname", 1, NULL, 'f'}, #ifdef HAVE_FORK {"daemonize", 1, NULL, 'z'}, #endif {0, 0, NULL, 0 } }; String LUACall_Names[] = {"identifier"}; String LUACall_Defaults[] = {""}; String LUACall_Invites[] = {"LUA function name:"}; String * LUACall_Lists[] = {0}; String * LUACall_Descs[] = {0}; class ReloadLUA : public Message { public: ReloadLUA(Lua * L, const String & title = "Reloading " + server_fname, const String & url = "reloadlua"); virtual ~ReloadLUA() { } virtual Task * Do(Variables *, Variables *, Handle *); private: Lua * L; }; ReloadLUA::ReloadLUA(Lua * __L, const String & _title, const String & _url) : L(__L), Message(_title, "Reloading " + server_fname + " in progress.", _url, 0) { } Task * ReloadLUA::Do(Variables * v, Variables * headers, Handle * h) { L->load(&Input(server_fname)); return Message::Do(v, headers, h); } class LUACall : public Message { public: LUACall(Lua * L, const String & title = "Calling a function.", const String & url = "luacall"); virtual ~LUACall() { } virtual Task * Do(Variables *, Variables *, Handle *); private: Lua * L; }; LUACall::LUACall(Lua * __L, const String & _title, const String & _url) : L(__L), Message(_title, "Function call in progress...", _url, 0) { } Task * LUACall::Do(Variables * v, Variables * headers, Handle * h) { static const Regex ValidIdentifier("[a-zA-Z0-9_.:]*"); if (ValidIdentifier.Match((*v)["identifier"])) { new LuaTask(L, (*v)["identifier"] + "()"); } return Message::Do(v, headers, h); } bool auto_exec = true; CODE_BEGINS /* That's the basic lua starter for non interactive mode */ Lua * start_basic_lua(void) { Lua * L = new threaded_Lua(); L->lock(); L->open_base(); L->open_math(); L->open_string(); L->open_table(); L->open_dir(); L->open_debug(); Luabaselua_interface::pushstatics(L); L->SetPrinter(&lp_stderr); L->unlock(); return L; } /* That's the extended stuff for interactive mode */ Lua * start_full_lua(void) { Lua * L = start_basic_lua(); Lualua_interface::pushstatics(L); autoload_exports(L); return L; } void showbanner() { printm(M_BARE, "Lua-Interface version " VERSION " (c) 2003-2008 Nicolas \"Pixel\" Noble\n" LUA_RELEASE " " LUA_COPYRIGHT "\n" #ifdef WIP "Special version Work In Progress, compiled the " __DATE__ " at " __TIME__ "\n" #endif "This is free software with ABSOLUTELY NO WARRANTY.\n" "\n"); } void showhelp(bool longhelp = false) { printm(M_BARE, "Usage:\n" "%s [options] [lua-script1] [lua-script2] ...\n" "\n" "Options:\n" " -v for verbose mode.\n" " -a to load an additionnal archive file.\n" " -c to dump the compiled byte code to file.\n" " -d to enable debug mode (ie, do not strip)\n" " -i to start interactive mode.\n" " -l to turn off the exec on end line.\n" " -e to execute this single command in LUA.\n" " -b to force the use of the built-in lua-interface.lua\n" " -g to log into a file.\n" " -m [mp] start the mail server on port mp (2500 by default).\n" " -s [hp] start the built-in http server on port hp (1500 by default).\n" " -t [tp] start the telnet server, on port tp (1550 by default).\n" " -f filename of server code to use (server.lua by default).\n" #ifdef HAVE_FORK " -z daemonize; incompatible with -i; use with -g.\n" #endif " -h for a help page.\n" , argv[0]); if (longhelp) printm(M_BARE, "\n" "Verbose mode can be somewhat a floody thing.\n" "Options -i/-e and -c are mutually exclusive.\n" "Options -i, -s and -e are NOT mutually exclusive. For example:\n" "\n" " $ %s -i -e \"main()\" somescript.lua\n" "\n" "This will first load the script 'somescript.lua', then execute main, and\n" "afterward, start the interactive mode.\n" "\n" "If a script contains inlined code, it will be run right after loading.\n" , argv[0]); } class lua_interface_printer_t : public printer_t { public: lua_interface_printer_t(Handle * h) : log(h) { } virtual bool printm(int level, const char * format, va_list ap) { char tmp[10241]; vsprintf(tmp, format, ap); (*log) << tmp; return false; } private: Handle * log; }; static void * interactive_prompt(void * __L) { char prompt[10], * line_read = 0; String line, endline; bool runit; Buffer command; int pos; Lua * L = (Lua *) __L; /* Interactive mode loop */ strcpy(prompt, "> "); #ifdef _WIN32 rl_getc_function = my_getc; #else sigset_t sigset; sigfillset(&sigset); pthread_sigmask(SIG_BLOCK, &sigset, NULL); #endif rl_bind_key('\t', rl_insert); while (interactive) { /* Basic usage of readline */ if (line_read) free(line_read); line_read = readline(prompt); if (!line_read) { printm(M_BARE, "\n"); break; } if (*line_read) add_history(line_read); line = line_read; line = line.trim(); endline = ""; /* Splitting the line between ;; */ while (line.strlen()) { runit = false; if ((pos = line.strstr(";;")) >= 0) { endline = line.extract(pos + 2); line = line.extract(0, pos); runit = true; } else { endline = ""; } if (line[line.strlen() - 1] == '\\') { line[line.strlen() - 1] = ' '; } else if (auto_exec) { runit = true; } command << line; if (runit) { try { L->load(&command); } catch (LuaException e) { /* If there was an error, ignore it, and free the stack */ while(L->gettop()) L->pop(); printm(M_ERROR, "%s\n", e.GetMsg()); } catch (GeneralException e) { /* A more severe exception... */ while(L->gettop()) L->pop(); printm(M_ERROR, "Aborted. LUA caused the following exception: %s\n", e.GetMsg()); } strcpy(prompt, "> "); } else { strcpy(prompt, "- "); command << "\n"; } line = endline.trim(); } } return NULL; } void autoload_exports(Lua * L) { #if not defined(__APPLE__) or defined(MODULES_BUILT_IN) if (lualibs_init) lualibs_init(L); if (luahandle_init) luahandle_init(L); if (luaconfigfiles_init) luaconfigfiles_init(L); if (luahttp_init) luahttp_init(L); if (luasmtp_init) luasmtp_init(L); if (luaregex_init) luaregex_init(L); if (luasql_init) luasql_init(L); if (luatask_init) luatask_init(L); if (luaxml_init) luaxml_init(L); #ifndef _MSVC if (luaftgl_init) luaftgl_init(L); if (luagl_init) luagl_init(L); if (luaosmesa_init) luaosmesa_init(L); if (luaocci_init) luaocci_init(L); #endif if (luacd_init) luacd_init(L); #endif } virtual int startup() throw (GeneralException) { char c; bool strip = true, todo = false, write = false, builtin = false, daemonize = false; char * file = 0, * output = 0, * compile = 0, * exec = 0; Lua * L = 0; String hport = "1500", tport = "1550", mport = "2500"; pthread_t interactive_thread; verbosity = M_WARNING; showbanner(); /* Let's start parsing options */ while ((c = getopt_long(argc, argv, "Hhva:c:dile:bg:m::s::t::f:z", long_options, NULL)) != EOF) { switch (c) { case 'h': case 'H': case '?': showhelp(true); throw Exit(0); case 'v': verbosity = M_INFO; break; case 'a': new Archive(optarg); break; case 'c': compile = strdup(optarg); break; case 'd': strip = false; break; case 'i': interactive = true; todo = true; break; case 'l': auto_exec = false; break; case 'e': exec = strdup(optarg); todo = true; break; case 'b': builtin = true; break; case 'g': printer = new lua_interface_printer_t(new Output(optarg)); break; case 's': hserver = true; todo = true; if (optarg) hport = optarg; break; case 'm': mserver = true; todo = true; if (optarg) mport = optarg; break; case 't': tserver = true; todo = true; if (optarg) tport = optarg; break; case 'f': server_fname = optarg; break; #ifdef HAVE_FORK case 'z': daemonize = true; break; #endif case 0: if (!getopt_flag) { showhelp(); throw Exit(-1); } switch(getopt_flag) { default: throw GeneralException("Unknow option on command-line."); } getopt_flag = NO_OPTION; break; default: showhelp(); throw Exit(-1); } } if (interactive && daemonize) { printm(M_ERROR, "Can not use daemonize and interactive mode.\n"); Exit(-1); } if (interactive) L = start_full_lua(); else L = start_basic_lua(); /* Loading lua-interface.lua (only when not compiling) */ if (!compile && !builtin) { try { L->load(&Input("lua-interface.lua")); } catch (GeneralException e) { printm(M_WARNING, "There was an error loading lua-interface.lua, using built-in: %s\n", e.GetMsg()); builtin = true; } } if (!compile && builtin) { Buffer built; built.write(lua_interface_lua, lua_interface_lua_size); try { L->load(&built); } catch (GeneralException e) { printm(M_WARNING, "There was an error loading built-in lua-interface.lua: %s\n", e.GetMsg()); } } /* Loading all the scripts */ while (optind < argc) { todo = true; L->load(&Input(argv[optind++]), !compile); } /* Doh... */ if (!todo) { showhelp(); throw Exit(0); } /* Compilation process will exit from itself right after */ if (compile) { L->dump(&Output(compile), strip); throw Exit(0); } /* One shot command */ if (exec) { Buffer command; command << exec; L->load(&command); } #ifdef HAVE_FORK if (daemonize) { if (fork()) { throw GeneralException("Going daemon..."); } } #endif if (interactive) { if (mserver || tserver || hserver) { pthread_create(&interactive_thread, NULL, interactive_prompt, L->thread(true)); L->pop(); } else { interactive_prompt(L); } } if (mserver || hserver || tserver) { TaskMan::Init(); if (hserver) L->load(&Input(server_fname)); } if (hserver) { HttpServ * httpserv = new HttpServ(new Message("Welcome", "Welcome.", "start"), hport.to_int(), "Lua Interface"); new Form("LUACall", "luacallform", "Enter the function name to call", LUACall_Names, LUACall_Invites, LUACall_Defaults, LUACall_Lists, LUACall_Descs, 1, new LUACall(L)); new ReloadLUA(L); } if (tserver) { new LuaCommandLine(L, tport.to_int()); } if (mserver) { new MailServer(mport.to_int()); } if (mserver || hserver || tserver) { StartTaskLoop(0); if (interactive) pthread_join(interactive_thread, NULL); } return 0; } CODE_ENDS #ifdef _WIN32 extern "C" { int ffs(int i) { register int bit = 0; if (i != 0) { if ((i & 0xffff) == 0) { bit += 16; i >>= 16; } if ((i & 0xff) == 0) { bit += 8; i >>= 8; } if ((i & 0xf) == 0) { bit += 4; i >>= 4; } while ((i & 1) == 0) { bit++; i >>= 1; } } return bit; } } #endif