mirror of
				https://github.com/Rockbox/rockbox.git
				synced 2025-10-29 00:36:22 -04:00 
			
		
		
		
	This uses slightly hacked luaprompt to provide all the goodis. See https://github.com/dpapavas/luaprompt for original. Change-Id: Iedddb79abae5809299322bc215722dd928c35cca
		
			
				
	
	
		
			1659 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1659 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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");
 | |
| }
 |