summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2015-06-28 17:51:43 +0200
committerMarcin Bukat <marcin.bukat@gmail.com>2015-06-28 17:55:17 +0200
commite70ea5d21ff1aca5b8c534da8f3a1ccadd330a2e (patch)
tree611750d6bd70f20585801cfd7e8c441180798bee
parent465eb727a30fd1f4470ff6c172f7b41856775167 (diff)
downloadrockbox-e70ea5d21ff1aca5b8c534da8f3a1ccadd330a2e.tar.gz
rockbox-e70ea5d21ff1aca5b8c534da8f3a1ccadd330a2e.tar.bz2
rockbox-e70ea5d21ff1aca5b8c534da8f3a1ccadd330a2e.zip
hwstub: Add completion and some pretty printing to the shell
This uses slightly hacked luaprompt to provide all the goodis. See https://github.com/dpapavas/luaprompt for original. Change-Id: Iedddb79abae5809299322bc215722dd928c35cca
-rw-r--r--utils/hwstub/tools/Makefile2
-rw-r--r--utils/hwstub/tools/hwstub_shell.cpp20
-rw-r--r--utils/hwstub/tools/lua/hwlib.lua6
-rw-r--r--utils/hwstub/tools/prompt.c1659
-rw-r--r--utils/hwstub/tools/prompt.h55
5 files changed, 1724 insertions, 18 deletions
diff --git a/utils/hwstub/tools/Makefile b/utils/hwstub/tools/Makefile
index 6db0c709b1..f718300623 100644
--- a/utils/hwstub/tools/Makefile
+++ b/utils/hwstub/tools/Makefile
@@ -26,7 +26,7 @@ $(REGTOOLS_LIB_DIR)/libsocdesc.a:
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
-hwstub_shell: hwstub_shell.o $(LIBS)
+hwstub_shell: hwstub_shell.o prompt.o $(LIBS)
$(LD) -o $@ $^ $(LDFLAGS)
hwstub_load: hwstub_load.o $(LIBS)
diff --git a/utils/hwstub/tools/hwstub_shell.cpp b/utils/hwstub/tools/hwstub_shell.cpp
index 8b7a8b9e80..f59ca8b82a 100644
--- a/utils/hwstub/tools/hwstub_shell.cpp
+++ b/utils/hwstub/tools/hwstub_shell.cpp
@@ -29,6 +29,9 @@
#include <lua.hpp>
#include <unistd.h>
#include "soc_desc.hpp"
+extern "C" {
+#include "prompt.h"
+}
#if LUA_VERSION_NUM < 502
#warning You need at least lua 5.2
@@ -941,21 +944,8 @@ int main(int argc, char **argv)
printf("error: %s\n", lua_tostring(g_lua, -1));
}
- // use readline to provide some history and completion
- rl_bind_key('\t', rl_complete);
- while(!g_exit)
- {
- char *input = readline("> ");
- if(!input)
- break;
- add_history(input);
- // evaluate string
- if(luaL_dostring(g_lua, input))
- printf("error: %s\n", lua_tostring(g_lua, -1));
- // pop everything to start from a clean stack
- lua_pop(g_lua, lua_gettop(g_lua));
- free(input);
- }
+ // start interactive shell
+ luap_enter(g_lua, &g_exit);
Lerr:
// display log if handled
diff --git a/utils/hwstub/tools/lua/hwlib.lua b/utils/hwstub/tools/lua/hwlib.lua
index 5bbd1e2668..02ab9718d4 100644
--- a/utils/hwstub/tools/lua/hwlib.lua
+++ b/utils/hwstub/tools/lua/hwlib.lua
@@ -22,6 +22,8 @@ function HWLIB.load_blob(filename, address)
io.close(f)
end
-function HWLIB.printf(s,...)
- return io.write(s:format(...))
+function HWLIB.printf(...)
+ local function wrapper(...) io.write(string.format(...)) end
+ local status, result = pcall(wrapper, ...)
+ if not status then error(result, 2) end
end
diff --git a/utils/hwstub/tools/prompt.c b/utils/hwstub/tools/prompt.c
new file mode 100644
index 0000000000..4674158df6
--- /dev/null
+++ b/utils/hwstub/tools/prompt.c
@@ -0,0 +1,1659 @@
+/* Copyright (C) 2012-2015 Papavasileiou Dimitris
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "prompt.h"
+
+#ifdef HAVE_IOCTL
+#include <sys/ioctl.h>
+#endif
+
+#include <glob.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+
+#if LUA_VERSION_NUM == 501
+#define lua_pushglobaltable(L) lua_pushvalue (L, LUA_GLOBALSINDEX)
+#define LUA_OK 0
+#define lua_rawlen lua_objlen
+#endif
+
+#ifdef HAVE_LIBREADLINE
+#include <readline/readline.h>
+#else
+
+/* This is a simple readline-like function in case readline is not
+ * available. */
+
+#define MAXINPUT 1024
+
+static char *readline(char *prompt)
+{
+ char *line = NULL;
+ int k;
+
+ line = malloc (MAXINPUT);
+
+ fputs(prompt, stdout);
+ fflush(stdout);
+
+ if (!fgets(line, MAXINPUT, stdin)) {
+ return NULL;
+ }
+
+ k = strlen (line);
+
+ if (line[k - 1] == '\n') {
+ line[k - 1] = '\0';
+ }
+
+ return line;
+}
+
+#endif /* HAVE_LIBREADLINE */
+
+#ifdef HAVE_READLINE_HISTORY
+#include <readline/history.h>
+#endif /* HAVE_READLINE_HISTORY */
+
+#if LUA_VERSION_NUM == 501
+#define EOF_MARKER "'<eof>'"
+#else
+#define EOF_MARKER "<eof>"
+#endif
+
+#define print_output(...) fprintf (stdout, __VA_ARGS__), fflush(stdout)
+#define print_error(...) fprintf (stderr, __VA_ARGS__), fflush(stderr)
+#define absolute(L, i) (i < 0 ? lua_gettop (L) + i + 1 : i)
+
+#define COLOR(i) (colorize ? colors[i] : "")
+
+static lua_State *M;
+static int initialized = 0;
+static char *logfile, *chunkname, *prompts[2][2], *buffer = NULL;
+
+#ifdef SAVE_RESULTS
+static int results = LUA_REFNIL, results_n = 0;
+#endif
+
+static int colorize = 1;
+static const char *colors[] = {"\033[0m",
+ "\033[0;31m",
+ "\033[1;31m",
+ "\033[0;32m",
+ "\033[1;32m",
+ "\033[0;33m",
+ "\033[1;33m",
+ "\033[1m",
+ "\033[22m"};
+
+#ifdef HAVE_LIBREADLINE
+
+static void display_matches (char **matches, int num_matches, int max_length)
+{
+ print_output ("%s", COLOR(7));
+ rl_display_match_list (matches, num_matches, max_length);
+ print_output ("%s", COLOR(0));
+ rl_on_new_line ();
+}
+
+#ifdef COMPLETE_KEYWORDS
+static char *keyword_completions (const char *text, int state)
+{
+ static const char **c, *keywords[] = {
+#if LUA_VERSION_NUM == 502
+ "goto",
+#endif
+ "and", "break", "do", "else", "elseif", "end", "false", "for",
+ "function", "if", "in", "local", "nil", "not", "or",
+ "repeat", "return", "then", "true", "until", "while", NULL
+ };
+
+ int s, t;
+
+ if (state == 0) {
+ c = keywords - 1;
+ }
+
+ /* Loop through the list of keywords and return the ones that
+ * match. */
+
+ for (c += 1 ; *c ; c += 1) {
+ s = strlen (*c);
+ t = strlen(text);
+
+ if (s >= t && !strncmp (*c, text, t)) {
+ return strdup (*c);
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+#ifdef COMPLETE_TABLE_KEYS
+
+static int look_up_metatable;
+
+static char *table_key_completions (const char *text, int state)
+{
+ static const char *c, *token;
+ static char oper;
+ static int h;
+
+ if (state == 0) {
+ h = lua_gettop(M);
+
+ /* Scan to the beginning of the to-be-completed token. */
+
+ for (c = text + strlen (text) - 1;
+ c >= text && *c != '.' && *c != ':' && *c != '[';
+ c -= 1);
+
+ if (c > text) {
+ oper = *c;
+ token = c + 1;
+
+ /* Get the iterable value, the keys of which we wish to
+ * complete. */
+
+ lua_pushliteral (M, "return ");
+ lua_pushlstring (M, text, token - text - 1);
+ lua_concat (M, 2);
+
+ if (luaL_loadstring (M, lua_tostring (M, -1)) ||
+ lua_pcall (M, 0, 1, 0) ||
+ (lua_type (M, -1) != LUA_TUSERDATA &&
+ lua_type (M, -1) != LUA_TTABLE)) {
+
+ lua_settop(M, h);
+ return NULL;
+ }
+ } else {
+ oper = 0;
+ token = text;
+
+ lua_pushglobaltable(M);
+ }
+
+ if (look_up_metatable) {
+ /* Replace the to-be-iterated value with it's metatable
+ * and set up a call to next. */
+
+ if (!luaL_getmetafield(M, -1, "__index") ||
+ (lua_type (M, -1) != LUA_TUSERDATA &&
+ lua_type (M, -1) != LUA_TTABLE)) {
+ lua_settop(M, h);
+ return NULL;
+ }
+
+ lua_getglobal(M, "next");
+ lua_replace(M, -3);
+ lua_pushnil(M);
+ } else {
+ /* Call the standard pairs function. */
+
+ lua_getglobal (M, "pairs");
+ lua_insert (M, -2);
+
+ if(lua_type (M, -2) != LUA_TFUNCTION ||
+ lua_pcall (M, 1, 3, 0)) {
+
+ lua_settop(M, h);
+ return NULL;
+ }
+ }
+ }
+
+ /* Iterate the table/userdata and generate matches. */
+
+ while (lua_pushvalue(M, -3), lua_insert (M, -3),
+ lua_pushvalue(M, -2), lua_insert (M, -4),
+ lua_pcall (M, 2, 2, 0) == 0) {
+ char *candidate;
+ size_t l, m;
+ int suppress, type, keytype;
+
+ if (lua_isnil(M, -2)) {
+ lua_settop(M, h);
+ return NULL;
+ }
+
+ /* Make some notes about the value we're completing. We'll
+ * make use of them later on. */
+
+ type = lua_type (M, -1);
+ suppress = (type == LUA_TTABLE || type == LUA_TUSERDATA ||
+ type == LUA_TFUNCTION);
+
+ keytype = LUA_TNIL;
+ if (type == LUA_TTABLE) {
+ lua_pushnil(M);
+ if (lua_next(M, -2)) {
+ keytype = lua_type (M, -2);
+ lua_pop (M, 2);
+ } else {
+ /* There are no keys in the table so we won't want to
+ * index it. Add a space. */
+
+ suppress = 0;
+ }
+ }
+
+ /* Pop the value, keep the key. */
+
+ lua_pop (M, 1);
+
+ /* We're mainly interested in strings at this point but if
+ * we're completing for the table[key] syntax we consider
+ * numeric keys too. */
+
+ if (lua_type (M, -1) == LUA_TSTRING ||
+ (oper == '[' && lua_type (M, -1) == LUA_TNUMBER)) {
+ if (oper == '[') {
+ if (lua_type (M, -1) == LUA_TNUMBER) {
+ lua_Number n;
+ int i;
+
+ n = lua_tonumber (M, -1);
+ i = lua_tointeger (M, -1);
+
+ /* If this isn't an integer key, we may as well
+ * forget about it. */
+
+ if ((lua_Number)i == n) {
+ l = asprintf (&candidate, "%d]", i);
+ } else {
+ continue;
+ }
+ } else {
+ char q;
+
+ q = token[0];
+ if (q != '"' && q != '\'') {
+ q = '"';
+ }
+
+ l = asprintf (&candidate, "%c%s%c]",
+ q, lua_tostring (M, -1), q);
+ }
+ } else {
+ candidate = strdup((char *)lua_tolstring (M, -1, &l));
+ }
+
+ m = strlen(token);
+
+ if (l >= m && !strncmp (token, candidate, m) &&
+ (oper != ':' || type == LUA_TFUNCTION)
+#ifdef HIDDEN_KEY_PREFIX
+ && strncmp(candidate, HIDDEN_KEY_PREFIX,
+ sizeof(HIDDEN_KEY_PREFIX) - 1)
+#endif
+ ) {
+ char *match;
+
+ /* If the candidate has been fully typed (or
+ * previously completed) consider adding certain
+ * helpful suffixes. */
+#ifndef ALWAYS_APPEND_SUFFIXES
+ if (l == m) {
+#endif
+ if (type == LUA_TFUNCTION) {
+ rl_completion_append_character = '('; suppress = 0;
+ } else if (type == LUA_TTABLE) {
+ if (keytype == LUA_TSTRING) {
+ rl_completion_append_character = '.'; suppress = 0;
+ } else if (keytype != LUA_TNIL) {
+ rl_completion_append_character = '['; suppress = 0;
+ }
+ }
+#ifndef ALWAYS_APPEND_SUFFIXES
+ };
+#endif
+
+ if (token > text) {
+ /* Were not completing a global variable. Put the
+ * completed string together out of the table and
+ * the key. */
+
+ match = (char *)malloc ((token - text) + l + 1);
+ strncpy (match, text, token - text);
+ strcpy (match + (token - text), candidate);
+
+ free(candidate);
+ } else {
+ /* Return the whole candidate as is, to be freed
+ * by Readline. */
+
+ match = candidate;
+ }
+
+ /* Suppress the newline when completing a table
+ * or other potentially complex value. */
+
+ if (suppress) {
+ rl_completion_suppress_append = 1;
+ }
+
+ return match;
+ } else {
+ free(candidate);
+ }
+ }
+ }
+
+ lua_settop(M, h);
+ return NULL;
+}
+#endif
+
+#ifdef COMPLETE_MODULES
+static char *module_completions (const char *text, int state)
+{
+ char *match = NULL;
+ static int h;
+
+ if (state == 0) {
+ glob_t vector;
+ const char *b, *d, *q, *s, *t, *strings[3];
+ int i, n = 0, ondot, hasdot, quoted;
+
+ hasdot = strchr(text, '.') != NULL;
+ ondot = text[0] != '\0' && text[strlen(text) - 1] == '.';
+ quoted = text[0] == '\'' || text[0] == '"';
+
+#ifdef NO_MODULE_LOAD
+ if(!quoted) {
+ return NULL;
+ }
+#endif
+
+ lua_newtable(M);
+ h = lua_gettop(M);
+
+ /* Try to load the input as a module. */
+
+ lua_getglobal(M, "require");
+
+ if (!lua_isfunction (M, -1)) {
+ lua_settop(M, h - 1);
+ return NULL;
+ }
+
+ lua_pushliteral(M, "package");
+
+ if(lua_pcall(M, 1, 1, 0) != LUA_OK) {
+ lua_settop(M, h - 1);
+ return NULL;
+ }
+
+ if (!ondot && !quoted && text[0] != '\0') {
+ lua_getfield(M, -1, "loaded");
+ lua_pushstring(M, text);
+ lua_gettable(M, -2);
+
+ /* If it's not an already loaded module, check whether the
+ * input is an available module by searching for it and/or
+ * trying to load it. */
+
+ if (!lua_toboolean(M, -1)) {
+ int load = 1;
+
+ lua_pop(M, 2);
+
+#ifdef CONFIRM_MODULE_LOAD
+ /* Look for the module as require would and ask the
+ * user whether it should be loaded or not. */
+
+#if LUA_VERSION_NUM == 501
+ lua_getfield(M, -1, "loaders");
+#else
+ lua_getfield(M, -1, "searchers");
+#endif
+ lua_pushnil(M);
+
+ while((load = lua_next(M, -2))) {
+ lua_pushstring(M, text);
+ lua_call(M, 1, 1);
+
+ if (lua_isfunction(M, -1)) {
+ char c;
+
+ print_output ("\nLoad module '%s' (y or n)", text);
+
+ while ((c = tolower(rl_read_key())) != 'y' && c != 'n');
+
+ if (c == 'y') {
+ lua_pop(M, 3);
+ break;
+ } else {
+ print_output ("\n");
+ rl_on_new_line ();
+
+ /* If it was found but not loaded, return
+ * the module name as a match to avoid
+ * asking the user againg if the tab key
+ * is pressed repeatedly. */
+
+ lua_settop(M, h);
+ return strdup(text);
+ }
+ }
+
+ lua_pop(M, 1);
+ }
+#endif
+
+ /* Load the model if needed. */
+
+ if (load) {
+ lua_pushfstring (M, "%s=require(\"%s\")", text, text);
+
+ if (luaL_loadstring (M, lua_tostring (M, -1)) == LUA_OK &&
+ lua_pcall (M, 0, 0, 0) == LUA_OK) {
+#ifdef CONFIRM_MODULE_LOAD
+ print_output (" ...loaded\n");
+#else
+ print_output ("\nLoaded module '%s'.\n", text);
+#endif
+
+ rl_on_new_line ();
+
+ lua_settop(M, h - 1);
+ return NULL;
+ }
+ }
+ } else {
+ lua_settop(M, h - 1);
+ return NULL;
+ }
+
+ /* Clean up but leave the package.table on the stack. */
+
+ lua_settop(M, h + 1);
+ }
+
+ /* Look for matches in package.preload. */
+
+ lua_getfield(M, -1, "preload");
+
+ lua_pushnil(M);
+ while(lua_next(M, -2)) {
+ lua_pop(M, 1);
+
+ if (lua_type(M, -1) == LUA_TSTRING &&
+ !strncmp(text + quoted, lua_tostring(M, -1),
+ strlen(text + quoted))) {
+
+ lua_pushstring(M, text);
+ lua_rawseti (M, h, (n += 1));
+ }
+ }
+
+ lua_pop(M, 1);
+
+ /* Get the configuration (directory, path separators, module
+ * name wildcard). */
+
+ lua_getfield(M, -1, "config");
+ for (s = (char *)lua_tostring(M, -1), i = 0;
+ i < 3;
+ s = t + 1, i += 1) {
+
+ t = strchr(s, '\n');
+ lua_pushlstring(M, s, t - s);
+ strings[i] = lua_tostring(M, -1);
+ }
+
+ lua_remove(M, -4);
+
+ /* Get the path and cpath */
+
+ lua_getfield(M, -4, "path");
+ lua_pushstring(M, strings[1]);
+ lua_getfield(M, -6, "cpath");
+ lua_pushstring(M, strings[1]);
+ lua_concat(M, 4);
+
+ /* Synthesize the pattern. */
+
+ if (hasdot) {
+ luaL_gsub(M, text + quoted, ".", strings[0]);
+ } else {
+ lua_pushstring(M, text + quoted);
+ }
+
+ lua_pushliteral(M, "*");
+ lua_concat(M, 2);
+
+ for (b = d = lua_tostring(M, -2) ; d ; b = d + 1) {
+ size_t i;
+
+ d = strstr(b, strings[1]);
+ q = strstr(b, strings[2]);
+
+ if (!q || q > d) {
+ continue;
+ }
+
+ lua_pushlstring(M, b, d - b);
+ luaL_gsub(M, lua_tostring(M, -1), strings[2],
+ lua_tostring(M, -2));
+
+ glob(lua_tostring(M, -1), 0, NULL, &vector);
+
+ lua_pop(M, 2);
+
+ for (i = 0 ; i < vector.gl_pathc ; i += 1) {
+ char *p = vector.gl_pathv[i];
+
+ if (quoted) {
+ lua_pushlstring(M, text, 1);
+ }
+
+ lua_pushlstring(M, p + (q - b), strlen(p) - (d - b) + 1);
+
+ if (hasdot) {
+ luaL_gsub(M, lua_tostring(M, -1), strings[0], ".");
+ lua_replace(M, -2);
+ }
+
+ {
+ const char *s;
+ size_t l;
+
+ s = lua_tolstring(M, -1, &l);
+
+ /* Suppress submodules named init. */
+
+ if (l < sizeof("init") - 1 ||
+ strcmp(s + l - sizeof("init") + 1, "init")) {
+
+ if (quoted) {
+ lua_pushlstring(M, text, 1);
+
+ lua_concat(M, 3);
+ }
+
+ lua_rawseti(M, h, (n += 1));
+ } else {
+ lua_pop(M, 1 + quoted);
+ }
+ }
+ }
+
+ globfree(&vector);
+ }
+
+ lua_pop(M, 6);
+ }
+
+ /* Return the next match from the table of matches. */
+
+ lua_pushnil(M);
+ if (lua_next(M, -2)) {
+ match = strdup(lua_tostring(M, -1));
+
+ rl_completion_suppress_append = !(match[0] == '"' || match[0] == '\'');
+
+ /* Pop the match. */
+
+ lua_pushnil(M);
+ lua_rawseti(M, -4, lua_tointeger(M, -3));
+
+ /* Pop key/value. */
+
+ lua_pop(M, 2);
+ } else {
+ /* Pop the empty table. */
+
+ lua_pop(M, 1);
+ }
+
+ return match;
+}
+#endif
+
+static char *generator (const char *text, int state)
+{
+ static int which;
+ char *match = NULL;
+
+ if (state == 0) {
+ which = 0;
+ }
+
+ /* Try to complete a keyword. */
+
+ if (which == 0) {
+#ifdef COMPLETE_KEYWORDS
+ if ((match = keyword_completions (text, state))) {
+ return match;
+ }
+#endif
+ which += 1;
+ state = 0;
+ }
+
+ /* Try to complete a module name. */
+
+ if (which == 1) {
+#ifdef COMPLETE_MODULES
+ if ((match = module_completions (text, state))) {
+ return match;
+ }
+#endif
+ which += 1;
+ state = 0;
+ }
+
+ /* Try to complete a table access. */
+
+ if (which == 2) {
+#ifdef COMPLETE_TABLE_KEYS
+ look_up_metatable = 0;
+ if ((match = table_key_completions (text, state))) {
+ return match;
+ }
+#endif
+ which += 1;
+ state = 0;
+ }
+
+ /* Try to complete a metatable access. */
+
+ if (which == 3) {
+#ifdef COMPLETE_METATABLE_KEYS
+ look_up_metatable = 1;
+ if ((match = table_key_completions (text, state))) {
+ return match;
+ }
+#endif
+ which += 1;
+ state = 0;
+ }
+
+#ifdef COMPLETE_FILE_NAMES
+ /* Try to complete a file name. */
+
+ if (which == 4) {
+ if (text[0] == '\'' || text[0] == '"') {
+ match = rl_filename_completion_function (text + 1, state);
+
+ if (match) {
+ struct stat s;
+ int n;
+
+ n = strlen (match);
+ stat(match, &s);
+
+ /* If a match was produced, add the quote
+ * characters. */
+
+ match = (char *)realloc (match, n + 3);
+ memmove (match + 1, match, n);
+ match[0] = text[0];
+
+ /* If the file's a directory, add a trailing backslash
+ * and suppress the space, otherwise add the closing
+ * quote. */
+
+ if (S_ISDIR(s.st_mode)) {
+ match[n + 1] = '/';
+
+ rl_completion_suppress_append = 1;
+ } else {
+ match[n + 1] = text[0];
+ }
+
+ match[n + 2] = '\0';
+ }
+ }
+ }
+#endif
+
+ return match;
+}
+#endif
+
+static void finish ()
+{
+#ifdef HAVE_READLINE_HISTORY
+ /* Save the command history on exit. */
+
+ if (logfile) {
+ write_history (logfile);
+ }
+#endif
+}
+
+static int traceback(lua_State *L)
+{
+ lua_Debug ar;
+ int i;
+
+ if (lua_isnoneornil (L, 1) ||
+ (!lua_isstring (L, 1) &&
+ !luaL_callmeta(L, 1, "__tostring"))) {
+ lua_pushliteral(L, "(no error message)");
+ }
+
+ if (lua_gettop (L) > 1) {
+ lua_replace (L, 1);
+ lua_settop (L, 1);
+ }
+
+ /* Print the Lua stack. */
+
+ lua_pushstring(L, "\n\nStack trace:\n");
+
+ for (i = 0 ; lua_getstack (L, i, &ar) ; i += 1) {
+#if LUA_VERSION_NUM == 501
+ lua_getinfo(M, "Snl", &ar);
+#else
+ lua_getinfo(M, "Snlt", &ar);
+
+ if (ar.istailcall) {
+ lua_pushfstring(L, "\t... tail calls\n");
+ }
+#endif
+
+ if (!strcmp (ar.what, "C")) {
+ lua_pushfstring(L, "\t#%d %s[C]:%s in function ",
+ i, COLOR(7), COLOR(8));
+
+ if (ar.name) {
+ lua_pushfstring(L, "'%s%s%s'\n",
+ COLOR(7), ar.name, COLOR(8));
+ } else {
+ lua_pushfstring(L, "%s?%s\n", COLOR(7), COLOR(8));
+ }
+ } else if (!strcmp (ar.what, "main")) {
+ lua_pushfstring(L, "\t#%d %s%s:%d:%s in the main chunk\n",
+ i, COLOR(7), ar.short_src, ar.currentline,
+ COLOR(8));
+ } else if (!strcmp (ar.what, "Lua")) {
+ lua_pushfstring(L, "\t#%d %s%s:%d:%s in function ",
+ i, COLOR(7), ar.short_src, ar.currentline,
+ COLOR(8));
+
+ if (ar.name) {
+ lua_pushfstring(L, "'%s%s%s'\n",
+ COLOR(7), ar.name, COLOR(8));
+ } else {
+ lua_pushfstring(L, "%s?%s\n", COLOR(7), COLOR(8));
+ }
+ }
+ }
+
+ if (i == 0) {
+ lua_pushstring (L, "No activation records.\n");
+ }
+
+ lua_concat (L, lua_gettop(L));
+
+ return 1;
+}
+
+static int execute ()
+{
+ int i, h_0, h, status;
+
+#ifdef SAVE_RESULTS
+ /* Get the results table, and stash it behind the to-be-executed
+ * chunk. */
+
+ lua_rawgeti(M, LUA_REGISTRYINDEX, results);
+ lua_insert(M, -2);
+#endif
+
+ h_0 = lua_gettop(M);
+ status = luap_call (M, 0);
+ h = lua_gettop (M) - h_0 + 1;
+
+ for (i = h ; i > 0 ; i -= 1) {
+ const char *result;
+
+ result = luap_describe (M, -i);
+
+#ifdef SAVE_RESULTS
+ lua_pushvalue (M, -i);
+ lua_rawseti(M, h_0 - 1, (results_n += 1));
+
+ print_output ("%s%s[%d]%s = %s%s\n",
+ COLOR(4), RESULTS_TABLE_NAME, results_n,
+ COLOR(3), result, COLOR(0));
+#else
+ if (h == 1) {
+ print_output ("%s%s%s\n", COLOR(3), result, COLOR(0));
+ } else {
+ print_output ("%s%d%s: %s%s\n", COLOR(4), h - i + 1,
+ COLOR(3), result, COLOR(0));
+ }
+#endif
+ }
+
+ /* Clean up. We need to remove the results table as well if we
+ * track results. */
+
+#ifdef SAVE_RESULTS
+ lua_settop (M, h_0 - 2);
+#else
+ lua_settop (M, h_0 - 1);
+#endif
+
+ return status;
+}
+
+/* This is the pretty-printing related stuff. */
+
+static char *dump;
+static int length, offset, indent, column, linewidth, ancestors;
+
+#define dump_literal(s) (check_fit(sizeof(s) - 1), \
+ strcpy (dump + offset, s), \
+ offset += sizeof(s) - 1, \
+ column += width(s))
+
+#define dump_character(c) (check_fit(1), \
+ dump[offset] = c, \
+ offset += 1, \
+ column += 1)
+
+static int width (const char *s)
+{
+ const char *c;
+ int n, discard = 0;
+
+ /* Calculate the printed width of the chunk s ignoring escape
+ * sequences. */
+
+ for (c = s, n = 0 ; *c ; c += 1) {
+ if (!discard && *c == '\033') {
+ discard = 1;
+ }
+
+ if (!discard) {
+ n+= 1;
+ }
+
+ if (discard && *c == 'm') {
+ discard = 0;
+ }
+ }
+
+ return n;
+}
+
+static void check_fit (int size)
+{
+ /* Check if a chunk fits in the buffer and expand as necessary. */
+
+ if (offset + size + 1 > length) {
+ length = offset + size + 1;
+ dump = (char *)realloc (dump, length * sizeof (char));
+ }
+}
+
+static int is_identifier (const char *s, int n)
+{
+ int i;
+
+ /* Check whether a string can be used as a key without quotes and
+ * braces. */
+
+ for (i = 0 ; i < n ; i += 1) {
+ if (!isalpha(s[i]) &&
+ (i == 0 || !isalnum(s[i])) &&
+ s[i] != '_') {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void break_line ()
+{
+ int i;
+
+ check_fit (indent + 1);
+
+ /* Add a line break. */
+
+ dump[offset] = '\n';
+
+ /* And indent to the current level. */
+
+ for (i = 1 ; i <= indent ; i += 1) {
+ dump[offset + i] = ' ';
+ }
+
+ offset += indent + 1;
+ column = indent;
+}
+
+static void dump_string (const char *s, int n)
+{
+ int l;
+
+ /* Break the line if the current chunk doesn't fit but it would
+ * fit if we started on a fresh line at the current indent. */
+
+ l = width(s);
+
+ if (column + l > linewidth && indent + l <= linewidth) {
+ break_line();
+ }
+
+ check_fit (n);
+
+ /* Copy the string to the buffer. */
+
+ memcpy (dump + offset, s, n);
+ dump[offset + n] = '\0';
+
+ offset += n;
+ column += l;
+}
+
+static void describe (lua_State *L, int index)
+{
+ char *s;
+ size_t n;
+ int type;
+
+ index = absolute (L, index);
+ type = lua_type (L, index);
+
+ if (luaL_getmetafield (L, index, "__tostring")) {
+ lua_pushvalue (L, index);
+ lua_pcall (L, 1, 1, 0);
+ s = (char *)lua_tolstring (L, -1, &n);
+ lua_pop (L, 1);
+
+ dump_string (s, n);
+ } else if (type == LUA_TNUMBER) {
+ /* Copy the value to avoid mutating it. */
+
+ lua_pushvalue (L, index);
+ s = (char *)lua_tolstring (L, -1, &n);
+ lua_pop (L, 1);
+
+ dump_string (s, n);
+ } else if (type == LUA_TSTRING) {
+ int i, started, score, level, uselevel = 0;
+
+ s = (char *)lua_tolstring (L, index, &n);
+
+ /* Scan the string to decide how to print it. */
+
+ for (i = 0, score = n, started = 0 ; i < (int)n ; i += 1) {
+ if (s[i] == '\n' || s[i] == '\t' ||
+ s[i] == '\v' || s[i] == '\r') {
+ /* These characters show up better in a long sting so
+ * bias towards that. */
+
+ score += linewidth / 2;
+ } else if (s[i] == '\a' || s[i] == '\b' ||
+ s[i] == '\f' || !isprint(s[i])) {
+ /* These however go better with an escaped short
+ * string (unless you like the bell or weird
+ * characters). */
+
+ score -= linewidth / 4;
+ }
+
+ /* Check what long string delimeter level to use so that
+ * the string won't be closed prematurely. */
+
+ if (!started) {
+ if (s[i] == ']') {
+ started = 1;
+ level = 0;
+ }
+ } else {
+ if (s[i] == '=') {
+ level += 1;
+ } else if (s[i] == ']') {
+ if (level >= uselevel) {
+ uselevel = level + 1;
+ }
+ } else {
+ started = 0;
+ }
+ }
+ }
+
+ if (score > linewidth) {
+ /* Dump the string as a long string. */
+
+ dump_character ('[');
+ for (i = 0 ; i < uselevel ; i += 1) {
+ dump_character ('=');
+ }
+ dump_literal ("[\n");
+
+ dump_string (s, n);
+
+ dump_character (']');
+ for (i = 0 ; i < uselevel ; i += 1) {
+ dump_character ('=');
+ }
+ dump_literal ("]");
+ } else {
+ /* Escape the string as needed and print it as a normal
+ * string. */
+
+ dump_literal ("\"");
+
+ for (i = 0 ; i < (int)n ; i += 1) {
+ if (s[i] == '"' || s[i] == '\\') {
+ dump_literal ("\\");
+ dump_character (s[i]);
+ } else if (s[i] == '\a') {
+ dump_literal ("\\a");
+ } else if (s[i] == '\b') {
+ dump_literal ("\\b");
+ } else if (s[i] == '\f') {
+ dump_literal ("\\f");
+ } else if (s[i] == '\n') {
+ dump_literal ("\\n");
+ } else if (s[i] == '\r') {
+ dump_literal ("\\r");
+ } else if (s[i] == '\t') {
+ dump_literal ("\\t");
+ } else if (s[i] == '\v') {
+ dump_literal ("\\v");
+ } else if (isprint(s[i])) {
+ dump_character (s[i]);
+ } else {
+ char t[5];
+ size_t n;
+
+ n = sprintf (t, "\\%03u", ((unsigned char *)s)[i]);
+ dump_string (t, n);
+ }
+ }
+
+ dump_literal ("\"");
+ }
+ } else if (type == LUA_TNIL) {
+ n = asprintf (&s, "%snil%s", COLOR(7), COLOR(8));
+ dump_string (s, n);
+ free(s);
+ } else if (type == LUA_TBOOLEAN) {
+ n = asprintf (&s, "%s%s%s",
+ COLOR(7),
+ lua_toboolean (L, index) ? "true" : "false",
+ COLOR(8));
+ dump_string (s, n);
+ free(s);
+ } else if (type == LUA_TFUNCTION) {
+ n = asprintf (&s, "<%sfunction:%s %p>",
+ COLOR(7), COLOR(8), lua_topointer (L, index));
+ dump_string (s, n);
+ free(s);
+ } else if (type == LUA_TUSERDATA) {
+ n = asprintf (&s, "<%suserdata:%s %p>",
+ COLOR(7), COLOR(8), lua_topointer (L, index));
+
+ dump_string (s, n);
+ free(s);
+ } else if (type == LUA_TTHREAD) {
+ n = asprintf (&s, "<%sthread:%s %p>",
+ COLOR(7), COLOR(8), lua_topointer (L, index));
+ dump_string (s, n);
+ free(s);
+ } else if (type == LUA_TTABLE) {
+ int i, l, n, oldindent, multiline, nobreak;
+
+ /* Check if table is too deeply nested. */
+
+ if (indent > 8 * linewidth / 10) {
+ char *s;
+ size_t n;
+
+ n = asprintf (&s, "{ %s...%s }", COLOR(7), COLOR(8));
+ dump_string (s, n);
+ free(s);
+
+ return;
+ }
+
+ /* Check if the table introduces a cycle by checking whether
+ * it is a back-edge (that is equal to an ancestor table. */
+
+ lua_rawgeti (L, LUA_REGISTRYINDEX, ancestors);
+ n = lua_rawlen(L, -1);
+
+ for (i = 0 ; i < n ; i += 1) {
+ lua_rawgeti (L, -1, n - i);
+#if LUA_VERSION_NUM == 501
+ if(lua_equal (L, -1, -3)) {
+#else
+ if(lua_compare (L, -1, -3, LUA_OPEQ)) {
+#endif
+ char *s;
+ size_t n;
+
+ n = asprintf (&s, "{ %s[%d]...%s }",
+ COLOR(7), -(i + 1), COLOR(8));
+ dump_string (s, n);
+ free(s);
+ lua_pop (L, 2);
+
+ return;
+ }
+
+ lua_pop (L, 1);
+ }
+
+ /* Add the table to the ancestor list and pop the ancestor
+ * list table. */
+
+ lua_pushvalue (L, index);
+ lua_rawseti (L, -2, n + 1);
+ lua_pop (L, 1);
+
+ /* Open the table and update the indentation level to the
+ * current column. */
+
+ dump_literal ("{ ");
+ oldindent = indent;
+ indent = column;
+ multiline = 0;
+ nobreak = 0;
+
+ l = lua_rawlen (L, index);
+
+ /* Traverse the array part first. */
+
+ for (i = 0 ; i < l ; i += 1) {
+ lua_pushinteger (L, i + 1);
+ lua_gettable (L, index);
+
+ /* Start a fresh line when dumping tables to make sure
+ * there's plenty of room. */
+
+ if (lua_istable (L, -1)) {
+ if (!nobreak) {
+ break_line();
+ }
+
+ multiline = 1;
+ }
+
+ nobreak = 0;
+
+ /* Dump the value and separating comma. */
+
+ describe (L, -1);
+ dump_literal (", ");
+
+ if (lua_istable (L, -1) && i != l - 1) {
+ break_line();
+ nobreak = 1;
+ }
+
+ lua_pop (L, 1);
+ }
+
+ /* Now for the hash part. */
+
+ lua_pushnil (L);
+ while (lua_next (L, index) != 0) {
+ if (lua_type (L, -2) != LUA_TNUMBER ||
+ lua_tonumber (L, -2) != lua_tointeger (L, -2) ||
+ lua_tointeger (L, -2) < 1 ||
+ lua_tointeger (L, -2) > l) {
+
+ /* Keep each key-value pair on a separate line. */
+
+ break_line ();
+ multiline = 1;
+
+ /* Dump the key and value. */
+
+ if (lua_type (L, -2) == LUA_TSTRING) {
+ char *s;
+ size_t n;
+
+ s = (char *)lua_tolstring (L, -2, &n);
+
+ if(is_identifier (s, n)) {
+ dump_string (COLOR(7), strlen(COLOR(7)));
+ dump_string (s, n);
+ dump_string (COLOR(8), strlen(COLOR(8)));
+ } else {
+ dump_literal ("[");
+ describe (L, -2);
+ dump_literal ("]");
+ }
+ } else {
+ dump_literal ("[");
+ describe (L, -2);
+ dump_literal ("]");
+ }
+
+ dump_literal (" = ");
+ describe (L, -1);
+ dump_literal (",");
+ }
+
+ lua_pop (L, 1);
+ }
+
+ /* Remove the table from the ancestor list. */
+
+ lua_rawgeti (L, LUA_REGISTRYINDEX, ancestors);
+ lua_pushnil (L);
+ lua_rawseti (L, -2, n + 1);
+ lua_pop (L, 1);
+
+ /* Pop the indentation level. */
+
+ indent = oldindent;
+
+ if (multiline) {
+ break_line();
+ dump_literal ("}");
+ } else {
+ dump_literal (" }");
+ }
+ }
+}
+
+char *luap_describe (lua_State *L, int index)
+{
+ int oldcolorize;
+
+#ifdef HAVE_IOCTL
+ struct winsize w;
+
+ /* Initialize the state. */
+
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) {
+ linewidth = 80;
+ } else {
+ linewidth = w.ws_col;
+ }
+#else
+ linewidth = 80;
+#endif
+
+ index = absolute (L, index);
+ offset = 0;
+ indent = 0;
+ column = 0;
+
+ /* Suppress colorization, to avoid escape sequences in the
+ * returned strings. */
+
+ oldcolorize = colorize;
+ colorize = 0;
+
+ /* Create a table to hold the ancestors for checking for cycles
+ * when printing table hierarchies. */
+
+ lua_newtable (L);
+ ancestors = luaL_ref (L, LUA_REGISTRYINDEX);
+
+ describe (L, index);
+
+ luaL_unref (L, LUA_REGISTRYINDEX, ancestors);
+ colorize = oldcolorize;
+
+ return dump;
+}
+
+/* These are custom commands. */
+
+#ifdef HAVE_LIBREADLINE
+static int describe_stack (int count, int key)
+{
+ int i, h;
+
+ print_output ("%s", COLOR(7));
+
+ h = lua_gettop (M);
+
+ if (count < 0) {
+ i = h + count + 1;
+
+ if (i > 0 && i <= h) {
+ print_output ("\nValue at stack index %d(%d):\n%s%s",
+ i, -h + i - 1, COLOR(3), luap_describe (M, i));
+ } else {
+ print_error ("Invalid stack index.\n");
+ }
+ } else {
+ if (h > 0) {
+ print_output ("\nThe stack contains %d values.\n", h);
+ for (i = 1 ; i <= h ; i += 1) {
+ print_output ("\n%d(%d):\t%s", i, -h + i - 1, lua_typename(M, lua_type(M, i)));
+ }
+ } else {
+ print_output ("\nThe stack is empty.");
+ }
+ }
+
+ print_output ("%s\n", COLOR(0));
+
+ rl_on_new_line ();
+
+ return 0;
+}
+#endif
+
+int luap_call (lua_State *L, int n) {
+ int h, status;
+
+ /* We can wind up here before reaching luap_enter, so this is
+ * needed. */
+
+ M = L;
+
+ /* Push the error handler onto the stack. */
+
+ h = lua_gettop(L) - n;
+ lua_pushcfunction (L, traceback);
+ lua_insert (L, h);
+
+ /* Try to execute the supplied chunk and keep note of any return
+ * values. */
+
+ status = lua_pcall(L, n, LUA_MULTRET, h);
+
+ /* Print any errors. */
+
+ if (status != LUA_OK) {
+ print_error ("%s%s%s\n", COLOR(1), lua_tostring (L, -1), COLOR(0));
+ lua_pop (L, 1);
+ }
+
+ /* Remove the error handler. */
+
+ lua_remove (L, h);
+
+ return status;
+}
+
+void luap_setprompts(lua_State *L, const char *single, const char *multi)
+{
+ /* Plain, uncolored prompts. */
+
+ prompts[0][0] = (char *)realloc (prompts[0][0], strlen (single) + 1);
+ prompts[0][1] = (char *)realloc (prompts[0][1], strlen (multi) + 1);
+ strcpy(prompts[0][0], single);
+ strcpy(prompts[0][1], multi);
+
+ /* Colored prompts. */
+
+ prompts[1][0] = (char *)realloc (prompts[1][0], strlen (single) + 16);
+ prompts[1][1] = (char *)realloc (prompts[1][1], strlen (multi) + 16);
+#ifdef HAVE_LIBREADLINE
+ sprintf (prompts[1][0], "\001%s\002%s\001%s\002",
+ COLOR(6), single, COLOR(0));
+ sprintf (prompts[1][1], "\001%s\002%s\001%s\002",
+ COLOR(6), multi, COLOR(0));
+#else
+ sprintf (prompts[1][0], "%s%s%s", COLOR(6), single, COLOR(0));
+ sprintf (prompts[1][1], "%s%s%s", COLOR(6), multi, COLOR(0));
+#endif
+}
+
+void luap_sethistory(lua_State *L, const char *file)
+{
+ if (file) {
+ logfile = realloc (logfile, strlen(file) + 1);
+ strcpy (logfile, file);
+ } else if (logfile) {
+ free(logfile);
+ logfile = NULL;
+ }
+}
+
+void luap_setcolor(lua_State *L, int enable)
+{
+ /* Don't allow color if we're not writing to a terminal. */
+
+ if (!isatty (STDOUT_FILENO) || !isatty (STDERR_FILENO)) {
+ colorize = 0;
+ } else {
+ colorize = enable;
+ }
+}
+
+void luap_setname(lua_State *L, const char *name)
+{
+ chunkname = (char *)realloc (chunkname, strlen(name) + 2);
+ chunkname[0] = '=';
+ strcpy (chunkname + 1, name);
+}
+
+void luap_getprompts(lua_State *L, const char **single, const char **multi)
+{
+ *single = prompts[0][0];
+ *multi = prompts[0][1];
+}
+
+void luap_gethistory(lua_State *L, const char **file)
+{
+ *file = logfile;
+}
+
+void luap_getcolor(lua_State *L, int *enabled)
+{
+ *enabled = colorize;
+}
+
+void luap_getname(lua_State *L, const char **name)
+{
+ *name = chunkname + 1;
+}
+
+void luap_enter(lua_State *L, bool *terminate)
+{
+ int incomplete = 0, s = 0, t = 0, l;
+ char *line, *prepended;
+#ifdef SAVE_RESULTS
+ int cleanup = 0;
+#endif
+
+ /* Save the state since it needs to be passed to some readline
+ * callbacks. */
+
+ M = L;
+
+ if (!initialized) {
+#ifdef HAVE_LIBREADLINE
+ rl_basic_word_break_characters = " \t\n`@$><=;|&{(";
+ rl_completion_entry_function = generator;
+ rl_completion_display_matches_hook = display_matches;
+
+ rl_add_defun ("lua-describe-stack", describe_stack, META('s'));
+#endif
+
+#ifdef HAVE_READLINE_HISTORY
+ /* Load the command history if there is one. */
+
+ if (logfile) {
+ read_history (logfile);
+ }
+#endif
+ if (!chunkname) {
+ luap_setname (L, "lua");
+ }
+
+ if (!prompts[0][0]) {
+ luap_setprompts (L, "> ", ">> ");
+ }
+
+ atexit (finish);
+
+ initialized = 1;
+ }
+
+#ifdef SAVE_RESULTS
+ if (results == LUA_REFNIL) {
+ lua_newtable(L);
+
+#ifdef WEAK_RESULTS
+ lua_newtable(L);
+ lua_pushliteral(L, "v");
+ lua_setfield(L, -2, "__mode");
+ lua_setmetatable(L, -2);
+#endif
+
+ results = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ lua_getglobal(L, RESULTS_TABLE_NAME);
+ if (lua_isnil(L, -1)) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, results);
+ lua_setglobal(L, RESULTS_TABLE_NAME);
+
+ cleanup = 1;
+ }
+ lua_pop(L, 1);
+#endif
+
+ while (!(*terminate) &&
+ (line = readline (incomplete ?
+ prompts[colorize][1] : prompts[colorize][0]))) {
+ int status;
+
+ if (*line == '\0') {
+ free(line);
+ continue;
+ }
+
+ /* Add/copy the line to the buffer. */
+
+ if (incomplete) {
+ s += strlen (line) + 1;
+
+ if (s > t) {
+ buffer = (char *)realloc (buffer, s + 1);
+ t = s;
+ }
+
+ strcat (buffer, "\n");
+ strcat (buffer, line);
+ } else {
+ s = strlen (line);
+
+ if (s > t) {
+ buffer = (char *)realloc (buffer, s + 1);
+ t = s;
+ }
+
+ strcpy (buffer, line);
+ }
+
+ /* Try to execute the line with a return prepended first. If
+ * this works we can show returned values. */
+
+ l = asprintf (&prepended, "return %s", buffer);
+
+ if (luaL_loadbuffer(L, prepended, l, chunkname) == LUA_OK) {
+ execute();
+
+ incomplete = 0;
+ } else {
+ lua_pop (L, 1);
+
+ /* Try to execute the line as-is. */
+
+ status = luaL_loadbuffer(L, buffer, s, chunkname);
+
+ incomplete = 0;
+
+ if (status == LUA_ERRSYNTAX) {
+ const char *message;
+ const int k = sizeof(EOF_MARKER) / sizeof(char) - 1;
+ size_t n;
+
+ message = lua_tolstring (L, -1, &n);
+
+ /* If the error message mentions an unexpected eof
+ * then consider this a multi-line statement and wait
+ * for more input. If not then just print the error
+ * message.*/
+
+ if ((int)n > k &&
+ !strncmp (message + n - k, EOF_MARKER, k)) {
+ incomplete = 1;
+ } else {
+ print_error ("%s%s%s\n", COLOR(1), lua_tostring (L, -1),
+ COLOR(0));
+ }
+
+ lua_pop (L, 1);
+ } else if (status == LUA_ERRMEM) {
+ print_error ("%s%s%s\n", COLOR(1), lua_tostring (L, -1),
+ COLOR(0));
+ lua_pop (L, 1);
+ } else {
+ /* Try to execute the loaded chunk. */
+
+ execute ();
+ incomplete = 0;
+ }
+ }
+
+#ifdef HAVE_READLINE_HISTORY
+ /* Add the line to the history if non-empty. */
+
+ if (!incomplete) {
+ add_history (buffer);
+ }
+#endif
+
+ free (prepended);
+ free (line);
+ }
+
+#ifdef SAVE_RESULTS
+ if (cleanup) {
+ lua_pushnil(L);
+ lua_setglobal(L, RESULTS_TABLE_NAME);
+ }
+#endif
+
+ print_output ("\n");
+}
diff --git a/utils/hwstub/tools/prompt.h b/utils/hwstub/tools/prompt.h
new file mode 100644
index 0000000000..0ad044b934
--- /dev/null
+++ b/utils/hwstub/tools/prompt.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2012-2015 Papavasileiou Dimitris
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _PROMPT_H_
+#define _PROMPT_H_
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#define HAVE_LIBREADLINE
+#define HAVE_READLINE_HISTORY
+#define HAVE_IOCTL
+#define COMPLETE_KEYWORDS
+#define COMPLETE_MODULES
+#define COMPLETE_TABLE_KEYS
+#define COMPLETE_METATABLE_KEYS
+#define COMPLETE_FILE_NAMES
+
+#define LUAP_VERSION "0.6"
+
+void luap_setprompts(lua_State *L, const char *single, const char *multi);
+void luap_sethistory(lua_State *L, const char *file);
+void luap_setname(lua_State *L, const char *name);
+void luap_setcolor(lua_State *L, int enable);
+
+void luap_getprompts(lua_State *L, const char **single, const char **multi);
+void luap_gethistory(lua_State *L, const char **file);
+void luap_getcolor(lua_State *L, int *enabled);
+void luap_getname(lua_State *L, const char **name);
+
+void luap_enter(lua_State *L, bool *terminate);
+char *luap_describe (lua_State *L, int index);
+int luap_call (lua_State *L, int n);
+
+#endif