From 516e870dc8f3a45f99cc3d6d3f28ad9cfb7c78e3 Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tarantool.org>
Date: Thu, 17 Aug 2017 14:53:05 +0300
Subject: [PATCH] lua: add -i CLI option to force interactive mode

`tarantool -i SCRIPT` forces Tarantool to enter into interactive
mode after executing SCRIPT or stdin.

A part of #1265
---
 src/lua/init.c          | 43 ++++++++++++++++++++++++++++++-----------
 src/lua/init.h          |  7 +++++--
 src/main.cc             | 15 +++++++++++---
 test/box-py/args.result |  6 ++++--
 4 files changed, 53 insertions(+), 18 deletions(-)

diff --git a/src/lua/init.c b/src/lua/init.c
index 8945f5d6b6..fefcf8076d 100644
--- a/src/lua/init.c
+++ b/src/lua/init.c
@@ -465,6 +465,24 @@ tarantool_lua_slab_cache()
 	return &cord()->slabc;
 }
 
+/**
+ * Push argument and call a function on the top of Lua stack
+ */
+static void
+lua_main(lua_State *L, int argc, char **argv)
+{
+	assert(lua_isfunction(L, -1));
+	lua_checkstack(L, argc - 1);
+	for (int i = 1; i < argc; i++)
+		lua_pushstring(L, argv[i]);
+	if (luaT_call(L, lua_gettop(L) - 1, 0) != 0) {
+		struct error *e = diag_last_error(&fiber()->diag);
+		panic("%s", e->errmsg);
+	}
+	/* clear the stack from return values. */
+	lua_settop(L, 0);
+}
+
 /**
  * Execute start-up script.
  */
@@ -473,6 +491,7 @@ run_script_f(va_list ap)
 {
 	struct lua_State *L = va_arg(ap, struct lua_State *);
 	const char *path = va_arg(ap, const char *);
+	bool interactive = va_arg(ap, int);
 	int argc = va_arg(ap, int);
 	char **argv = va_arg(ap, char **);
 
@@ -487,11 +506,21 @@ run_script_f(va_list ap)
 		/* Execute script. */
 		if (luaL_loadfile(L, path) != 0)
 			panic("%s", lua_tostring(L, -1));
+		lua_main(L, argc, argv);
 	} else if (!isatty(STDIN_FILENO)) {
 		/* Execute stdin */
 		if (luaL_loadfile(L, NULL) != 0)
 			panic("%s", lua_tostring(L, -1));
+		lua_main(L, argc, argv);
 	} else {
+		interactive = true;
+	}
+
+	/*
+	 * Start interactive mode when it was explicitly requested
+	 * by "-i" option or stdin is TTY or there are no script.
+	 */
+	if (interactive) {
 		say_crit("version %s\ntype 'help' for interactive help",
 			 tarantool_version());
 		/* get console.start from package.loaded */
@@ -501,17 +530,9 @@ run_script_f(va_list ap)
 		lua_remove(L, -2); /* remove package.loaded.console */
 		lua_remove(L, -2); /* remove package.loaded */
 		start_loop = false;
-	}
-	lua_checkstack(L, argc - 1);
-	for (int i = 1; i < argc; i++)
-		lua_pushstring(L, argv[i]);
-	if (luaT_call(L, lua_gettop(L) - 1, 0) != 0) {
-		struct error *e = diag_last_error(&fiber()->diag);
-		panic("%s", e->errmsg);
+		lua_main(L, argc, argv);
 	}
 
-	/* clear the stack from return values. */
-	lua_settop(L, 0);
 	/*
 	 * Lua script finished. Stop the auxiliary event loop and
 	 * return control back to tarantool_lua_run_script.
@@ -521,7 +542,7 @@ run_script_f(va_list ap)
 }
 
 void
-tarantool_lua_run_script(char *path, int argc, char **argv)
+tarantool_lua_run_script(char *path, bool interactive, int argc, char **argv)
 {
 	const char *title = path ? basename(path) : "interactive";
 	/*
@@ -534,7 +555,7 @@ tarantool_lua_run_script(char *path, int argc, char **argv)
 	script_fiber = fiber_new(title, run_script_f);
 	if (script_fiber == NULL)
 		panic("%s", diag_last_error(diag_get())->errmsg);
-	fiber_start(script_fiber, tarantool_L, path, argc, argv);
+	fiber_start(script_fiber, tarantool_L, path, interactive, argc, argv);
 
 	/*
 	 * Run an auxiliary event loop to re-schedule run_script fiber.
diff --git a/src/lua/init.h b/src/lua/init.h
index 8330081dd5..cbe7c6e4e9 100644
--- a/src/lua/init.h
+++ b/src/lua/init.h
@@ -70,10 +70,13 @@ tarantool_lua_tostring(struct lua_State *L, int index);
 /**
  * Load and execute start-up file
  *
- * @param L is a Lua State.
+ * @param interactive force interactive mode
+ * @param argc argc the number of command line arguments
+ * @param argv argv command line arguments
  */
 void
-tarantool_lua_run_script(char *path, int argc, char **argv);
+tarantool_lua_run_script(char *path, bool force_interactive, int argc,
+			 char **argv);
 
 extern char *history;
 
diff --git a/src/main.cc b/src/main.cc
index 66561a921e..9253de825a 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -526,12 +526,13 @@ print_help(const char *program)
 {
 	puts("Tarantool - a Lua application server");
 	puts("");
-	printf("Usage: %s script.lua [OPTIONS]\n", program);
+	printf("Usage: %s script.lua [OPTIONS] [SCRIPT [ARGS]]\n", program);
 	puts("");
 	puts("All command line options are passed to the interpreted script.");
 	puts("When no script name is provided, the server responds to:");
 	puts("  -h, --help\t\t\tdisplay this help and exit");
 	puts("  -V, --version\t\t\tprint program version and exit");
+	puts("  -i\t\t\t\tenter interactive mode after executing 'SCRIPT'");
 	puts("");
 	puts("Please visit project home page at http://tarantool.org");
 	puts("to see online documentation, submit bugs or contribute a patch.");
@@ -548,12 +549,15 @@ main(int argc, char **argv)
 		fprintf(stderr, "Failed to set locale to C.UTF-8\n");
 	fpconv_check();
 
+	/* Enter interactive mode after executing 'script' */
+	bool interactive = false;
+
 	static struct option longopts[] = {
 		{"help", no_argument, 0, 'h'},
 		{"version", no_argument, 0, 'V'},
 		{NULL, 0, 0, 0},
 	};
-	static const char *opts = "+hV";
+	static const char *opts = "+hVi";
 
 	int ch;
 	while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
@@ -564,6 +568,10 @@ main(int argc, char **argv)
 		case 'h':
 			print_help(basename(argv[0]));
 			return 0;
+		case 'i':
+			/* Force interactive mode */
+			interactive = true;
+			break;
 		default:
 			/* "invalid option" is printed by getopt */
 			return EX_USAGE;
@@ -647,7 +655,8 @@ main(int argc, char **argv)
 		 * is why script must run only after the server was fully
 		 * initialized.
 		 */
-		tarantool_lua_run_script(script, main_argc, main_argv);
+		tarantool_lua_run_script(script, interactive, main_argc,
+					 main_argv);
 		/*
 		 * Start event loop after executing Lua script if signal_cb()
 		 * wasn't triggered and there is some new events. Initial value
diff --git a/test/box-py/args.result b/test/box-py/args.result
index 0ee25fcce7..90845f52de 100644
--- a/test/box-py/args.result
+++ b/test/box-py/args.result
@@ -1,12 +1,13 @@
 tarantool --help
 Tarantool - a Lua application server
 
-Usage: tarantool script.lua [OPTIONS]
+Usage: tarantool script.lua [OPTIONS] [SCRIPT [ARGS]]
 
 All command line options are passed to the interpreted script.
 When no script name is provided, the server responds to:
   -h, --help			display this help and exit
   -V, --version			print program version and exit
+  -i				enter interactive mode after executing 'SCRIPT'
 
 Please visit project home page at http://tarantool.org
 to see online documentation, submit bugs or contribute a patch.
@@ -14,12 +15,13 @@ to see online documentation, submit bugs or contribute a patch.
 tarantool -h
 Tarantool - a Lua application server
 
-Usage: tarantool script.lua [OPTIONS]
+Usage: tarantool script.lua [OPTIONS] [SCRIPT [ARGS]]
 
 All command line options are passed to the interpreted script.
 When no script name is provided, the server responds to:
   -h, --help			display this help and exit
   -V, --version			print program version and exit
+  -i				enter interactive mode after executing 'SCRIPT'
 
 Please visit project home page at http://tarantool.org
 to see online documentation, submit bugs or contribute a patch.
-- 
GitLab