From 22fff07bdc6cbbaea683ab8e0e2b5f0b9b2ddd90 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov <kostja@tarantool.org> Date: Tue, 8 Apr 2014 17:38:47 +0400 Subject: [PATCH] [gh-226] First approximation of interactive mode. tarantool_box without arguments runs in interactive mode. @todo: - log_level by default should be 'critical' - wal_mode by default should be 'none' - pid_file by default should be 'none' - readline should be called via coeio, to not block event loop while it's running --- CMakeLists.txt | 5 +++++ src/CMakeLists.txt | 3 +++ src/ffisyms.cc | 6 +++++- src/lua/init.cc | 47 +++++++++++++++++++++++------------------ src/lua/init.h | 6 ++++++ src/lua/interactive.lua | 18 ++++++++++++++++ src/tarantool.cc | 10 +++++---- 7 files changed, 70 insertions(+), 25 deletions(-) create mode 100644 src/lua/interactive.lua diff --git a/CMakeLists.txt b/CMakeLists.txt index 777083e1fe..f9af8888b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,11 @@ include(cmake/os.cmake) include(cmake/compiler.cmake) include(cmake/simd.cmake) include(cmake/profile.cmake) +include(cmake/FindReadline.cmake) + +if (NOT READLINE_FOUND) + message(FATAL_ERROR "readline library not found.") +endif() check_symbol_exists(MAP_ANON sys/mman.h HAVE_MAP_ANON) check_symbol_exists(MAP_ANONYMOUS sys/mman.h HAVE_MAP_ANONYMOUS) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2a8220a154..4da5d26b80 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ include_directories(${LIBEV_INCLUDE_DIR}) include_directories(${LIBEIO_INCLUDE_DIR}) include_directories(${LIBCORO_INCLUDE_DIR}) include_directories(${LIBGOPT_INCLUDE_DIR}) +include_directories(${READLINE_INCLUDE_DIR}) # Require pthread globally if compiling with GCC if (CMAKE_COMPILER_IS_GNUCC) @@ -23,6 +24,7 @@ set(lua_sources) lua_source(lua_sources lua/uuid.lua) lua_source(lua_sources lua/session.lua) lua_source(lua_sources lua/msgpackffi.lua) +lua_source(lua_sources lua/interactive.lua) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/third_party/luafun) lua_source(lua_sources ../third_party/luafun/fun.lua) set(bin_sources) @@ -113,6 +115,7 @@ list(APPEND common_libraries ${LIBGOPT_LIBRARIES} ${LIBCJSON_LIBRARIES} ${LIBYAML_LIBRARIES} + ${READLINE_LIBRARIES} ${LUAJIT_LIB} misc ) diff --git a/src/ffisyms.cc b/src/ffisyms.cc index 6b3dc5d202..b82e23bd4a 100644 --- a/src/ffisyms.cc +++ b/src/ffisyms.cc @@ -4,6 +4,8 @@ #include <box/tuple.h> #include <box/lua/index.h> #include <box/lua/call.h> +#include <readline/readline.h> +#include <lua/init.h> /* * A special hack to cc/ld to keep symbols in an optimized binary. @@ -25,5 +27,7 @@ void *ffi_symbols[] = { (void *) port_ffi_create, (void *) port_ffi_destroy, (void *) boxffi_select, - (void *) password_prepare + (void *) password_prepare, + (void *) readline, + (void *) tarantool_lua_interactive }; diff --git a/src/lua/init.cc b/src/lua/init.cc index f0307a6fa9..853061e659 100644 --- a/src/lua/init.cc +++ b/src/lua/init.cc @@ -67,8 +67,10 @@ extern "C" { struct lua_State *tarantool_L; /* contents of src/lua/ files */ -extern char uuid_lua[], session_lua[], msgpackffi_lua[], fun_lua[]; -static const char *lua_sources[] = { uuid_lua, session_lua, NULL }; +extern char uuid_lua[], session_lua[], msgpackffi_lua[], fun_lua[], + interactive_lua[]; +static const char *lua_sources[] = { uuid_lua, session_lua, interactive_lua, + NULL }; static const char *lua_modules[] = { "msgpackffi", msgpackffi_lua, "fun", fun_lua, NULL }; /* @@ -354,16 +356,6 @@ tarantool_lua_dostring(struct lua_State *L, const char *str) return 0; } -static int -tarantool_lua_dofile(struct lua_State *L, const char *filename) -{ - lua_getglobal(L, "dofile"); - lua_pushstring(L, filename); - lbox_pcall(L); - bool result = lua_toboolean(L, 1); - return result ? 0 : 1; -} - extern "C" { int yamlL_encode(lua_State*); }; @@ -432,6 +424,17 @@ tarantool_lua(struct lua_State *L, } } +extern "C" void +tarantool_lua_interactive(char *line) +{ + struct tbuf *out = tbuf_new(&fiber()->gc); + struct lua_State *L = lua_newthread(tarantool_L); + tarantool_lua(L, out, line); + lua_pop(tarantool_L, 1); + printf("%.*s", out->size, out->data); + fiber_gc(); +} + /** * Check if the given literal is a number/boolean or string * literal. A string literal needs quotes. @@ -569,12 +572,17 @@ run_script(va_list ap) if (access(path, F_OK) == 0) { say_info("loading %s", path); /* Execute the init file. */ - if (tarantool_lua_dofile(L, path)) - panic("%s", lua_tostring(L, -1)); - - /* clear the stack from return values. */ - lua_settop(L, 0); + lua_getglobal(L, "dofile"); + lua_pushstring(L, path); + } else { + lua_getglobal(L, "interactive"); } + lbox_pcall(L); + if (! lua_toboolean(L, 1)) + panic("%s", lua_tostring(L, -1)); + + /* clear the stack from return values. */ + lua_settop(L, 0); /* * The file doesn't exist. It's OK, tarantool may * have no init file. @@ -590,8 +598,7 @@ run_script(va_list ap) void tarantool_lua_run_script(char *path) { - if (path == NULL) - return; + const char *title = path ? basename(path) : "interactive"; /* * init script can call box.fiber.yield (including implicitly via * box.insert, box.update, etc...), but box.fiber.yield() today, @@ -599,7 +606,7 @@ tarantool_lua_run_script(char *path) * To work this problem around we must run init script in * a separate fiber. */ - struct fiber *loader = fiber_new(basename(path), run_script); + struct fiber *loader = fiber_new(title, run_script); fiber_call(loader, tarantool_L, path); /* diff --git a/src/lua/init.h b/src/lua/init.h index 00944ac013..34d2fb3b70 100644 --- a/src/lua/init.h +++ b/src/lua/init.h @@ -87,4 +87,10 @@ void tarantool_lua(struct lua_State *L, struct tbuf *out, const char *str); +/** + * Eval line and print output. + */ +extern "C" void +tarantool_lua_interactive(char *line); + #endif /* INCLUDES_TARANTOOL_LUA_H */ diff --git a/src/lua/interactive.lua b/src/lua/interactive.lua new file mode 100644 index 0000000000..5cf219356b --- /dev/null +++ b/src/lua/interactive.lua @@ -0,0 +1,18 @@ +local ffi = require('ffi') +ffi.cdef([[ + char *readline(const char *prompt); + void tarantool_lua_interactive(char *); + void free(void *ptr); +]]) + +function interactive() + while true do + line = ffi.C.readline("tarantool> ") + if line then + ffi.C.tarantool_lua_interactive(line) + ffi.C.free(line) + else + break; + end + end +end diff --git a/src/tarantool.cc b/src/tarantool.cc index 255f8770ed..4e85176ecd 100644 --- a/src/tarantool.cc +++ b/src/tarantool.cc @@ -570,7 +570,7 @@ main(int argc, char **argv) __libc_stack_end = (void*) &argv; #endif - if (argc <= 1 || access(argv[1], R_OK) != 0) { + if (argc > 1 && access(argv[1], R_OK) != 0) { void *opt = gopt_sort(&argc, (const char **)argv, opt_def); if (gopt(opt, 'V')) { printf("Tarantool %s\n", tarantool_version()); @@ -612,9 +612,11 @@ main(int argc, char **argv) * - in case one uses #!/usr/bin/env tarantool * such options (in script line) don't work */ - argv++; - argc--; - script = abspath(argv[0]); + if (argc > 1) { + argv++; + argc--; + script = abspath(argv[0]); + } random_init(); say_init(argv[0]); -- GitLab