diff options
Diffstat (limited to 'utils/hwstub/tools/prompt.c')
-rw-r--r-- | utils/hwstub/tools/prompt.c | 1659 |
1 files changed, 1659 insertions, 0 deletions
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 @@ | |||
1 | /* Copyright (C) 2012-2015 Papavasileiou Dimitris | ||
2 | * | ||
3 | * Permission is hereby granted, free of charge, to any person | ||
4 | * obtaining a copy of this software and associated documentation | ||
5 | * files (the "Software"), to deal in the Software without | ||
6 | * restriction, including without limitation the rights to use, copy, | ||
7 | * modify, merge, publish, distribute, sublicense, and/or sell copies | ||
8 | * of the Software, and to permit persons to whom the Software is | ||
9 | * furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be | ||
12 | * included in all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
17 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
18 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
19 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
21 | * SOFTWARE. | ||
22 | */ | ||
23 | |||
24 | #ifdef HAVE_CONFIG_H | ||
25 | #include <config.h> | ||
26 | #endif | ||
27 | |||
28 | #define _GNU_SOURCE | ||
29 | #include <stdio.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #include <ctype.h> | ||
33 | #include <stdbool.h> | ||
34 | #include <sys/stat.h> | ||
35 | #include <unistd.h> | ||
36 | #include "prompt.h" | ||
37 | |||
38 | #ifdef HAVE_IOCTL | ||
39 | #include <sys/ioctl.h> | ||
40 | #endif | ||
41 | |||
42 | #include <glob.h> | ||
43 | |||
44 | #include <lualib.h> | ||
45 | #include <lauxlib.h> | ||
46 | |||
47 | |||
48 | #if LUA_VERSION_NUM == 501 | ||
49 | #define lua_pushglobaltable(L) lua_pushvalue (L, LUA_GLOBALSINDEX) | ||
50 | #define LUA_OK 0 | ||
51 | #define lua_rawlen lua_objlen | ||
52 | #endif | ||
53 | |||
54 | #ifdef HAVE_LIBREADLINE | ||
55 | #include <readline/readline.h> | ||
56 | #else | ||
57 | |||
58 | /* This is a simple readline-like function in case readline is not | ||
59 | * available. */ | ||
60 | |||
61 | #define MAXINPUT 1024 | ||
62 | |||
63 | static char *readline(char *prompt) | ||
64 | { | ||
65 | char *line = NULL; | ||
66 | int k; | ||
67 | |||
68 | line = malloc (MAXINPUT); | ||
69 | |||
70 | fputs(prompt, stdout); | ||
71 | fflush(stdout); | ||
72 | |||
73 | if (!fgets(line, MAXINPUT, stdin)) { | ||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | k = strlen (line); | ||
78 | |||
79 | if (line[k - 1] == '\n') { | ||
80 | line[k - 1] = '\0'; | ||
81 | } | ||
82 | |||
83 | return line; | ||
84 | } | ||
85 | |||
86 | #endif /* HAVE_LIBREADLINE */ | ||
87 | |||
88 | #ifdef HAVE_READLINE_HISTORY | ||
89 | #include <readline/history.h> | ||
90 | #endif /* HAVE_READLINE_HISTORY */ | ||
91 | |||
92 | #if LUA_VERSION_NUM == 501 | ||
93 | #define EOF_MARKER "'<eof>'" | ||
94 | #else | ||
95 | #define EOF_MARKER "<eof>" | ||
96 | #endif | ||
97 | |||
98 | #define print_output(...) fprintf (stdout, __VA_ARGS__), fflush(stdout) | ||
99 | #define print_error(...) fprintf (stderr, __VA_ARGS__), fflush(stderr) | ||
100 | #define absolute(L, i) (i < 0 ? lua_gettop (L) + i + 1 : i) | ||
101 | |||
102 | #define COLOR(i) (colorize ? colors[i] : "") | ||
103 | |||
104 | static lua_State *M; | ||
105 | static int initialized = 0; | ||
106 | static char *logfile, *chunkname, *prompts[2][2], *buffer = NULL; | ||
107 | |||
108 | #ifdef SAVE_RESULTS | ||
109 | static int results = LUA_REFNIL, results_n = 0; | ||
110 | #endif | ||
111 | |||
112 | static int colorize = 1; | ||
113 | static const char *colors[] = {"\033[0m", | ||
114 | "\033[0;31m", | ||
115 | "\033[1;31m", | ||
116 | "\033[0;32m", | ||
117 | "\033[1;32m", | ||
118 | "\033[0;33m", | ||
119 | "\033[1;33m", | ||
120 | "\033[1m", | ||
121 | "\033[22m"}; | ||
122 | |||
123 | #ifdef HAVE_LIBREADLINE | ||
124 | |||
125 | static void display_matches (char **matches, int num_matches, int max_length) | ||
126 | { | ||
127 | print_output ("%s", COLOR(7)); | ||
128 | rl_display_match_list (matches, num_matches, max_length); | ||
129 | print_output ("%s", COLOR(0)); | ||
130 | rl_on_new_line (); | ||
131 | } | ||
132 | |||
133 | #ifdef COMPLETE_KEYWORDS | ||
134 | static char *keyword_completions (const char *text, int state) | ||
135 | { | ||
136 | static const char **c, *keywords[] = { | ||
137 | #if LUA_VERSION_NUM == 502 | ||
138 | "goto", | ||
139 | #endif | ||
140 | "and", "break", "do", "else", "elseif", "end", "false", "for", | ||
141 | "function", "if", "in", "local", "nil", "not", "or", | ||
142 | "repeat", "return", "then", "true", "until", "while", NULL | ||
143 | }; | ||
144 | |||
145 | int s, t; | ||
146 | |||
147 | if (state == 0) { | ||
148 | c = keywords - 1; | ||
149 | } | ||
150 | |||
151 | /* Loop through the list of keywords and return the ones that | ||
152 | * match. */ | ||
153 | |||
154 | for (c += 1 ; *c ; c += 1) { | ||
155 | s = strlen (*c); | ||
156 | t = strlen(text); | ||
157 | |||
158 | if (s >= t && !strncmp (*c, text, t)) { | ||
159 | return strdup (*c); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | return NULL; | ||
164 | } | ||
165 | #endif | ||
166 | |||
167 | #ifdef COMPLETE_TABLE_KEYS | ||
168 | |||
169 | static int look_up_metatable; | ||
170 | |||
171 | static char *table_key_completions (const char *text, int state) | ||
172 | { | ||
173 | static const char *c, *token; | ||
174 | static char oper; | ||
175 | static int h; | ||
176 | |||
177 | if (state == 0) { | ||
178 | h = lua_gettop(M); | ||
179 | |||
180 | /* Scan to the beginning of the to-be-completed token. */ | ||
181 | |||
182 | for (c = text + strlen (text) - 1; | ||
183 | c >= text && *c != '.' && *c != ':' && *c != '['; | ||
184 | c -= 1); | ||
185 | |||
186 | if (c > text) { | ||
187 | oper = *c; | ||
188 | token = c + 1; | ||
189 | |||
190 | /* Get the iterable value, the keys of which we wish to | ||
191 | * complete. */ | ||
192 | |||
193 | lua_pushliteral (M, "return "); | ||
194 | lua_pushlstring (M, text, token - text - 1); | ||
195 | lua_concat (M, 2); | ||
196 | |||
197 | if (luaL_loadstring (M, lua_tostring (M, -1)) || | ||
198 | lua_pcall (M, 0, 1, 0) || | ||
199 | (lua_type (M, -1) != LUA_TUSERDATA && | ||
200 | lua_type (M, -1) != LUA_TTABLE)) { | ||
201 | |||
202 | lua_settop(M, h); | ||
203 | return NULL; | ||
204 | } | ||
205 | } else { | ||
206 | oper = 0; | ||
207 | token = text; | ||
208 | |||
209 | lua_pushglobaltable(M); | ||
210 | } | ||
211 | |||
212 | if (look_up_metatable) { | ||
213 | /* Replace the to-be-iterated value with it's metatable | ||
214 | * and set up a call to next. */ | ||
215 | |||
216 | if (!luaL_getmetafield(M, -1, "__index") || | ||
217 | (lua_type (M, -1) != LUA_TUSERDATA && | ||
218 | lua_type (M, -1) != LUA_TTABLE)) { | ||
219 | lua_settop(M, h); | ||
220 | return NULL; | ||
221 | } | ||
222 | |||
223 | lua_getglobal(M, "next"); | ||
224 | lua_replace(M, -3); | ||
225 | lua_pushnil(M); | ||
226 | } else { | ||
227 | /* Call the standard pairs function. */ | ||
228 | |||
229 | lua_getglobal (M, "pairs"); | ||
230 | lua_insert (M, -2); | ||
231 | |||
232 | if(lua_type (M, -2) != LUA_TFUNCTION || | ||
233 | lua_pcall (M, 1, 3, 0)) { | ||
234 | |||
235 | lua_settop(M, h); | ||
236 | return NULL; | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | |||
241 | /* Iterate the table/userdata and generate matches. */ | ||
242 | |||
243 | while (lua_pushvalue(M, -3), lua_insert (M, -3), | ||
244 | lua_pushvalue(M, -2), lua_insert (M, -4), | ||
245 | lua_pcall (M, 2, 2, 0) == 0) { | ||
246 | char *candidate; | ||
247 | size_t l, m; | ||
248 | int suppress, type, keytype; | ||
249 | |||
250 | if (lua_isnil(M, -2)) { | ||
251 | lua_settop(M, h); | ||
252 | return NULL; | ||
253 | } | ||
254 | |||
255 | /* Make some notes about the value we're completing. We'll | ||
256 | * make use of them later on. */ | ||
257 | |||
258 | type = lua_type (M, -1); | ||
259 | suppress = (type == LUA_TTABLE || type == LUA_TUSERDATA || | ||
260 | type == LUA_TFUNCTION); | ||
261 | |||
262 | keytype = LUA_TNIL; | ||
263 | if (type == LUA_TTABLE) { | ||
264 | lua_pushnil(M); | ||
265 | if (lua_next(M, -2)) { | ||
266 | keytype = lua_type (M, -2); | ||
267 | lua_pop (M, 2); | ||
268 | } else { | ||
269 | /* There are no keys in the table so we won't want to | ||
270 | * index it. Add a space. */ | ||
271 | |||
272 | suppress = 0; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | /* Pop the value, keep the key. */ | ||
277 | |||
278 | lua_pop (M, 1); | ||
279 | |||
280 | /* We're mainly interested in strings at this point but if | ||
281 | * we're completing for the table[key] syntax we consider | ||
282 | * numeric keys too. */ | ||
283 | |||
284 | if (lua_type (M, -1) == LUA_TSTRING || | ||
285 | (oper == '[' && lua_type (M, -1) == LUA_TNUMBER)) { | ||
286 | if (oper == '[') { | ||
287 | if (lua_type (M, -1) == LUA_TNUMBER) { | ||
288 | lua_Number n; | ||
289 | int i; | ||
290 | |||
291 | n = lua_tonumber (M, -1); | ||
292 | i = lua_tointeger (M, -1); | ||
293 | |||
294 | /* If this isn't an integer key, we may as well | ||
295 | * forget about it. */ | ||
296 | |||
297 | if ((lua_Number)i == n) { | ||
298 | l = asprintf (&candidate, "%d]", i); | ||
299 | } else { | ||
300 | continue; | ||
301 | } | ||
302 | } else { | ||
303 | char q; | ||
304 | |||
305 | q = token[0]; | ||
306 | if (q != '"' && q != '\'') { | ||
307 | q = '"'; | ||
308 | } | ||
309 | |||
310 | l = asprintf (&candidate, "%c%s%c]", | ||
311 | q, lua_tostring (M, -1), q); | ||
312 | } | ||
313 | } else { | ||
314 | candidate = strdup((char *)lua_tolstring (M, -1, &l)); | ||
315 | } | ||
316 | |||
317 | m = strlen(token); | ||
318 | |||
319 | if (l >= m && !strncmp (token, candidate, m) && | ||
320 | (oper != ':' || type == LUA_TFUNCTION) | ||
321 | #ifdef HIDDEN_KEY_PREFIX | ||
322 | && strncmp(candidate, HIDDEN_KEY_PREFIX, | ||
323 | sizeof(HIDDEN_KEY_PREFIX) - 1) | ||
324 | #endif | ||
325 | ) { | ||
326 | char *match; | ||
327 | |||
328 | /* If the candidate has been fully typed (or | ||
329 | * previously completed) consider adding certain | ||
330 | * helpful suffixes. */ | ||
331 | #ifndef ALWAYS_APPEND_SUFFIXES | ||
332 | if (l == m) { | ||
333 | #endif | ||
334 | if (type == LUA_TFUNCTION) { | ||
335 | rl_completion_append_character = '('; suppress = 0; | ||
336 | } else if (type == LUA_TTABLE) { | ||
337 | if (keytype == LUA_TSTRING) { | ||
338 | rl_completion_append_character = '.'; suppress = 0; | ||
339 | } else if (keytype != LUA_TNIL) { | ||
340 | rl_completion_append_character = '['; suppress = 0; | ||
341 | } | ||
342 | } | ||
343 | #ifndef ALWAYS_APPEND_SUFFIXES | ||
344 | }; | ||
345 | #endif | ||
346 | |||
347 | if (token > text) { | ||
348 | /* Were not completing a global variable. Put the | ||
349 | * completed string together out of the table and | ||
350 | * the key. */ | ||
351 | |||
352 | match = (char *)malloc ((token - text) + l + 1); | ||
353 | strncpy (match, text, token - text); | ||
354 | strcpy (match + (token - text), candidate); | ||
355 | |||
356 | free(candidate); | ||
357 | } else { | ||
358 | /* Return the whole candidate as is, to be freed | ||
359 | * by Readline. */ | ||
360 | |||
361 | match = candidate; | ||
362 | } | ||
363 | |||
364 | /* Suppress the newline when completing a table | ||
365 | * or other potentially complex value. */ | ||
366 | |||
367 | if (suppress) { | ||
368 | rl_completion_suppress_append = 1; | ||
369 | } | ||
370 | |||
371 | return match; | ||
372 | } else { | ||
373 | free(candidate); | ||
374 | } | ||
375 | } | ||
376 | } | ||
377 | |||
378 | lua_settop(M, h); | ||
379 | return NULL; | ||
380 | } | ||
381 | #endif | ||
382 | |||
383 | #ifdef COMPLETE_MODULES | ||
384 | static char *module_completions (const char *text, int state) | ||
385 | { | ||
386 | char *match = NULL; | ||
387 | static int h; | ||
388 | |||
389 | if (state == 0) { | ||
390 | glob_t vector; | ||
391 | const char *b, *d, *q, *s, *t, *strings[3]; | ||
392 | int i, n = 0, ondot, hasdot, quoted; | ||
393 | |||
394 | hasdot = strchr(text, '.') != NULL; | ||
395 | ondot = text[0] != '\0' && text[strlen(text) - 1] == '.'; | ||
396 | quoted = text[0] == '\'' || text[0] == '"'; | ||
397 | |||
398 | #ifdef NO_MODULE_LOAD | ||
399 | if(!quoted) { | ||
400 | return NULL; | ||
401 | } | ||
402 | #endif | ||
403 | |||
404 | lua_newtable(M); | ||
405 | h = lua_gettop(M); | ||
406 | |||
407 | /* Try to load the input as a module. */ | ||
408 | |||
409 | lua_getglobal(M, "require"); | ||
410 | |||
411 | if (!lua_isfunction (M, -1)) { | ||
412 | lua_settop(M, h - 1); | ||
413 | return NULL; | ||
414 | } | ||
415 | |||
416 | lua_pushliteral(M, "package"); | ||
417 | |||
418 | if(lua_pcall(M, 1, 1, 0) != LUA_OK) { | ||
419 | lua_settop(M, h - 1); | ||
420 | return NULL; | ||
421 | } | ||
422 | |||
423 | if (!ondot && !quoted && text[0] != '\0') { | ||
424 | lua_getfield(M, -1, "loaded"); | ||
425 | lua_pushstring(M, text); | ||
426 | lua_gettable(M, -2); | ||
427 | |||
428 | /* If it's not an already loaded module, check whether the | ||
429 | * input is an available module by searching for it and/or | ||
430 | * trying to load it. */ | ||
431 | |||
432 | if (!lua_toboolean(M, -1)) { | ||
433 | int load = 1; | ||
434 | |||
435 | lua_pop(M, 2); | ||
436 | |||
437 | #ifdef CONFIRM_MODULE_LOAD | ||
438 | /* Look for the module as require would and ask the | ||
439 | * user whether it should be loaded or not. */ | ||
440 | |||
441 | #if LUA_VERSION_NUM == 501 | ||
442 | lua_getfield(M, -1, "loaders"); | ||
443 | #else | ||
444 | lua_getfield(M, -1, "searchers"); | ||
445 | #endif | ||
446 | lua_pushnil(M); | ||
447 | |||
448 | while((load = lua_next(M, -2))) { | ||
449 | lua_pushstring(M, text); | ||
450 | lua_call(M, 1, 1); | ||
451 | |||
452 | if (lua_isfunction(M, -1)) { | ||
453 | char c; | ||
454 | |||
455 | print_output ("\nLoad module '%s' (y or n)", text); | ||
456 | |||
457 | while ((c = tolower(rl_read_key())) != 'y' && c != 'n'); | ||
458 | |||
459 | if (c == 'y') { | ||
460 | lua_pop(M, 3); | ||
461 | break; | ||
462 | } else { | ||
463 | print_output ("\n"); | ||
464 | rl_on_new_line (); | ||
465 | |||
466 | /* If it was found but not loaded, return | ||
467 | * the module name as a match to avoid | ||
468 | * asking the user againg if the tab key | ||
469 | * is pressed repeatedly. */ | ||
470 | |||
471 | lua_settop(M, h); | ||
472 | return strdup(text); | ||
473 | } | ||
474 | } | ||
475 | |||
476 | lua_pop(M, 1); | ||
477 | } | ||
478 | #endif | ||
479 | |||
480 | /* Load the model if needed. */ | ||
481 | |||
482 | if (load) { | ||
483 | lua_pushfstring (M, "%s=require(\"%s\")", text, text); | ||
484 | |||
485 | if (luaL_loadstring (M, lua_tostring (M, -1)) == LUA_OK && | ||
486 | lua_pcall (M, 0, 0, 0) == LUA_OK) { | ||
487 | #ifdef CONFIRM_MODULE_LOAD | ||
488 | print_output (" ...loaded\n"); | ||
489 | #else | ||
490 | print_output ("\nLoaded module '%s'.\n", text); | ||
491 | #endif | ||
492 | |||
493 | rl_on_new_line (); | ||
494 | |||
495 | lua_settop(M, h - 1); | ||
496 | return NULL; | ||
497 | } | ||
498 | } | ||
499 | } else { | ||
500 | lua_settop(M, h - 1); | ||
501 | return NULL; | ||
502 | } | ||
503 | |||
504 | /* Clean up but leave the package.table on the stack. */ | ||
505 | |||
506 | lua_settop(M, h + 1); | ||
507 | } | ||
508 | |||
509 | /* Look for matches in package.preload. */ | ||
510 | |||
511 | lua_getfield(M, -1, "preload"); | ||
512 | |||
513 | lua_pushnil(M); | ||
514 | while(lua_next(M, -2)) { | ||
515 | lua_pop(M, 1); | ||
516 | |||
517 | if (lua_type(M, -1) == LUA_TSTRING && | ||
518 | !strncmp(text + quoted, lua_tostring(M, -1), | ||
519 | strlen(text + quoted))) { | ||
520 | |||
521 | lua_pushstring(M, text); | ||
522 | lua_rawseti (M, h, (n += 1)); | ||
523 | } | ||
524 | } | ||
525 | |||
526 | lua_pop(M, 1); | ||
527 | |||
528 | /* Get the configuration (directory, path separators, module | ||
529 | * name wildcard). */ | ||
530 | |||
531 | lua_getfield(M, -1, "config"); | ||
532 | for (s = (char *)lua_tostring(M, -1), i = 0; | ||
533 | i < 3; | ||
534 | s = t + 1, i += 1) { | ||
535 | |||
536 | t = strchr(s, '\n'); | ||
537 | lua_pushlstring(M, s, t - s); | ||
538 | strings[i] = lua_tostring(M, -1); | ||
539 | } | ||
540 | |||
541 | lua_remove(M, -4); | ||
542 | |||
543 | /* Get the path and cpath */ | ||
544 | |||
545 | lua_getfield(M, -4, "path"); | ||
546 | lua_pushstring(M, strings[1]); | ||
547 | lua_getfield(M, -6, "cpath"); | ||
548 | lua_pushstring(M, strings[1]); | ||
549 | lua_concat(M, 4); | ||
550 | |||
551 | /* Synthesize the pattern. */ | ||
552 | |||
553 | if (hasdot) { | ||
554 | luaL_gsub(M, text + quoted, ".", strings[0]); | ||
555 | } else { | ||
556 | lua_pushstring(M, text + quoted); | ||
557 | } | ||
558 | |||
559 | lua_pushliteral(M, "*"); | ||
560 | lua_concat(M, 2); | ||
561 | |||
562 | for (b = d = lua_tostring(M, -2) ; d ; b = d + 1) { | ||
563 | size_t i; | ||
564 | |||
565 | d = strstr(b, strings[1]); | ||
566 | q = strstr(b, strings[2]); | ||
567 | |||
568 | if (!q || q > d) { | ||
569 | continue; | ||
570 | } | ||
571 | |||
572 | lua_pushlstring(M, b, d - b); | ||
573 | luaL_gsub(M, lua_tostring(M, -1), strings[2], | ||
574 | lua_tostring(M, -2)); | ||
575 | |||
576 | glob(lua_tostring(M, -1), 0, NULL, &vector); | ||
577 | |||
578 | lua_pop(M, 2); | ||
579 | |||
580 | for (i = 0 ; i < vector.gl_pathc ; i += 1) { | ||
581 | char *p = vector.gl_pathv[i]; | ||
582 | |||
583 | if (quoted) { | ||
584 | lua_pushlstring(M, text, 1); | ||
585 | } | ||
586 | |||
587 | lua_pushlstring(M, p + (q - b), strlen(p) - (d - b) + 1); | ||
588 | |||
589 | if (hasdot) { | ||
590 | luaL_gsub(M, lua_tostring(M, -1), strings[0], "."); | ||
591 | lua_replace(M, -2); | ||
592 | } | ||
593 | |||
594 | { | ||
595 | const char *s; | ||
596 | size_t l; | ||
597 | |||
598 | s = lua_tolstring(M, -1, &l); | ||
599 | |||
600 | /* Suppress submodules named init. */ | ||
601 | |||
602 | if (l < sizeof("init") - 1 || | ||
603 | strcmp(s + l - sizeof("init") + 1, "init")) { | ||
604 | |||
605 | if (quoted) { | ||
606 | lua_pushlstring(M, text, 1); | ||
607 | |||
608 | lua_concat(M, 3); | ||
609 | } | ||
610 | |||
611 | lua_rawseti(M, h, (n += 1)); | ||
612 | } else { | ||
613 | lua_pop(M, 1 + quoted); | ||
614 | } | ||
615 | } | ||
616 | } | ||
617 | |||
618 | globfree(&vector); | ||
619 | } | ||
620 | |||
621 | lua_pop(M, 6); | ||
622 | } | ||
623 | |||
624 | /* Return the next match from the table of matches. */ | ||
625 | |||
626 | lua_pushnil(M); | ||
627 | if (lua_next(M, -2)) { | ||
628 | match = strdup(lua_tostring(M, -1)); | ||
629 | |||
630 | rl_completion_suppress_append = !(match[0] == '"' || match[0] == '\''); | ||
631 | |||
632 | /* Pop the match. */ | ||
633 | |||
634 | lua_pushnil(M); | ||
635 | lua_rawseti(M, -4, lua_tointeger(M, -3)); | ||
636 | |||
637 | /* Pop key/value. */ | ||
638 | |||
639 | lua_pop(M, 2); | ||
640 | } else { | ||
641 | /* Pop the empty table. */ | ||
642 | |||
643 | lua_pop(M, 1); | ||
644 | } | ||
645 | |||
646 | return match; | ||
647 | } | ||
648 | #endif | ||
649 | |||
650 | static char *generator (const char *text, int state) | ||
651 | { | ||
652 | static int which; | ||
653 | char *match = NULL; | ||
654 | |||
655 | if (state == 0) { | ||
656 | which = 0; | ||
657 | } | ||
658 | |||
659 | /* Try to complete a keyword. */ | ||
660 | |||
661 | if (which == 0) { | ||
662 | #ifdef COMPLETE_KEYWORDS | ||
663 | if ((match = keyword_completions (text, state))) { | ||
664 | return match; | ||
665 | } | ||
666 | #endif | ||
667 | which += 1; | ||
668 | state = 0; | ||
669 | } | ||
670 | |||
671 | /* Try to complete a module name. */ | ||
672 | |||
673 | if (which == 1) { | ||
674 | #ifdef COMPLETE_MODULES | ||
675 | if ((match = module_completions (text, state))) { | ||
676 | return match; | ||
677 | } | ||
678 | #endif | ||
679 | which += 1; | ||
680 | state = 0; | ||
681 | } | ||
682 | |||
683 | /* Try to complete a table access. */ | ||
684 | |||
685 | if (which == 2) { | ||
686 | #ifdef COMPLETE_TABLE_KEYS | ||
687 | look_up_metatable = 0; | ||
688 | if ((match = table_key_completions (text, state))) { | ||
689 | return match; | ||
690 | } | ||
691 | #endif | ||
692 | which += 1; | ||
693 | state = 0; | ||
694 | } | ||
695 | |||
696 | /* Try to complete a metatable access. */ | ||
697 | |||
698 | if (which == 3) { | ||
699 | #ifdef COMPLETE_METATABLE_KEYS | ||
700 | look_up_metatable = 1; | ||
701 | if ((match = table_key_completions (text, state))) { | ||
702 | return match; | ||
703 | } | ||
704 | #endif | ||
705 | which += 1; | ||
706 | state = 0; | ||
707 | } | ||
708 | |||
709 | #ifdef COMPLETE_FILE_NAMES | ||
710 | /* Try to complete a file name. */ | ||
711 | |||
712 | if (which == 4) { | ||
713 | if (text[0] == '\'' || text[0] == '"') { | ||
714 | match = rl_filename_completion_function (text + 1, state); | ||
715 | |||
716 | if (match) { | ||
717 | struct stat s; | ||
718 | int n; | ||
719 | |||
720 | n = strlen (match); | ||
721 | stat(match, &s); | ||
722 | |||
723 | /* If a match was produced, add the quote | ||
724 | * characters. */ | ||
725 | |||
726 | match = (char *)realloc (match, n + 3); | ||
727 | memmove (match + 1, match, n); | ||
728 | match[0] = text[0]; | ||
729 | |||
730 | /* If the file's a directory, add a trailing backslash | ||
731 | * and suppress the space, otherwise add the closing | ||
732 | * quote. */ | ||
733 | |||
734 | if (S_ISDIR(s.st_mode)) { | ||
735 | match[n + 1] = '/'; | ||
736 | |||
737 | rl_completion_suppress_append = 1; | ||
738 | } else { | ||
739 | match[n + 1] = text[0]; | ||
740 | } | ||
741 | |||
742 | match[n + 2] = '\0'; | ||
743 | } | ||
744 | } | ||
745 | } | ||
746 | #endif | ||
747 | |||
748 | return match; | ||
749 | } | ||
750 | #endif | ||
751 | |||
752 | static void finish () | ||
753 | { | ||
754 | #ifdef HAVE_READLINE_HISTORY | ||
755 | /* Save the command history on exit. */ | ||
756 | |||
757 | if (logfile) { | ||
758 | write_history (logfile); | ||
759 | } | ||
760 | #endif | ||
761 | } | ||
762 | |||
763 | static int traceback(lua_State *L) | ||
764 | { | ||
765 | lua_Debug ar; | ||
766 | int i; | ||
767 | |||
768 | if (lua_isnoneornil (L, 1) || | ||
769 | (!lua_isstring (L, 1) && | ||
770 | !luaL_callmeta(L, 1, "__tostring"))) { | ||
771 | lua_pushliteral(L, "(no error message)"); | ||
772 | } | ||
773 | |||
774 | if (lua_gettop (L) > 1) { | ||
775 | lua_replace (L, 1); | ||
776 | lua_settop (L, 1); | ||
777 | } | ||
778 | |||
779 | /* Print the Lua stack. */ | ||
780 | |||
781 | lua_pushstring(L, "\n\nStack trace:\n"); | ||
782 | |||
783 | for (i = 0 ; lua_getstack (L, i, &ar) ; i += 1) { | ||
784 | #if LUA_VERSION_NUM == 501 | ||
785 | lua_getinfo(M, "Snl", &ar); | ||
786 | #else | ||
787 | lua_getinfo(M, "Snlt", &ar); | ||
788 | |||
789 | if (ar.istailcall) { | ||
790 | lua_pushfstring(L, "\t... tail calls\n"); | ||
791 | } | ||
792 | #endif | ||
793 | |||
794 | if (!strcmp (ar.what, "C")) { | ||
795 | lua_pushfstring(L, "\t#%d %s[C]:%s in function ", | ||
796 | i, COLOR(7), COLOR(8)); | ||
797 | |||
798 | if (ar.name) { | ||
799 | lua_pushfstring(L, "'%s%s%s'\n", | ||
800 | COLOR(7), ar.name, COLOR(8)); | ||
801 | } else { | ||
802 | lua_pushfstring(L, "%s?%s\n", COLOR(7), COLOR(8)); | ||
803 | } | ||
804 | } else if (!strcmp (ar.what, "main")) { | ||
805 | lua_pushfstring(L, "\t#%d %s%s:%d:%s in the main chunk\n", | ||
806 | i, COLOR(7), ar.short_src, ar.currentline, | ||
807 | COLOR(8)); | ||
808 | } else if (!strcmp (ar.what, "Lua")) { | ||
809 | lua_pushfstring(L, "\t#%d %s%s:%d:%s in function ", | ||
810 | i, COLOR(7), ar.short_src, ar.currentline, | ||
811 | COLOR(8)); | ||
812 | |||
813 | if (ar.name) { | ||
814 | lua_pushfstring(L, "'%s%s%s'\n", | ||
815 | COLOR(7), ar.name, COLOR(8)); | ||
816 | } else { | ||
817 | lua_pushfstring(L, "%s?%s\n", COLOR(7), COLOR(8)); | ||
818 | } | ||
819 | } | ||
820 | } | ||
821 | |||
822 | if (i == 0) { | ||
823 | lua_pushstring (L, "No activation records.\n"); | ||
824 | } | ||
825 | |||
826 | lua_concat (L, lua_gettop(L)); | ||
827 | |||
828 | return 1; | ||
829 | } | ||
830 | |||
831 | static int execute () | ||
832 | { | ||
833 | int i, h_0, h, status; | ||
834 | |||
835 | #ifdef SAVE_RESULTS | ||
836 | /* Get the results table, and stash it behind the to-be-executed | ||
837 | * chunk. */ | ||
838 | |||
839 | lua_rawgeti(M, LUA_REGISTRYINDEX, results); | ||
840 | lua_insert(M, -2); | ||
841 | #endif | ||
842 | |||
843 | h_0 = lua_gettop(M); | ||
844 | status = luap_call (M, 0); | ||
845 | h = lua_gettop (M) - h_0 + 1; | ||
846 | |||
847 | for (i = h ; i > 0 ; i -= 1) { | ||
848 | const char *result; | ||
849 | |||
850 | result = luap_describe (M, -i); | ||
851 | |||
852 | #ifdef SAVE_RESULTS | ||
853 | lua_pushvalue (M, -i); | ||
854 | lua_rawseti(M, h_0 - 1, (results_n += 1)); | ||
855 | |||
856 | print_output ("%s%s[%d]%s = %s%s\n", | ||
857 | COLOR(4), RESULTS_TABLE_NAME, results_n, | ||
858 | COLOR(3), result, COLOR(0)); | ||
859 | #else | ||
860 | if (h == 1) { | ||
861 | print_output ("%s%s%s\n", COLOR(3), result, COLOR(0)); | ||
862 | } else { | ||
863 | print_output ("%s%d%s: %s%s\n", COLOR(4), h - i + 1, | ||
864 | COLOR(3), result, COLOR(0)); | ||
865 | } | ||
866 | #endif | ||
867 | } | ||
868 | |||
869 | /* Clean up. We need to remove the results table as well if we | ||
870 | * track results. */ | ||
871 | |||
872 | #ifdef SAVE_RESULTS | ||
873 | lua_settop (M, h_0 - 2); | ||
874 | #else | ||
875 | lua_settop (M, h_0 - 1); | ||
876 | #endif | ||
877 | |||
878 | return status; | ||
879 | } | ||
880 | |||
881 | /* This is the pretty-printing related stuff. */ | ||
882 | |||
883 | static char *dump; | ||
884 | static int length, offset, indent, column, linewidth, ancestors; | ||
885 | |||
886 | #define dump_literal(s) (check_fit(sizeof(s) - 1), \ | ||
887 | strcpy (dump + offset, s), \ | ||
888 | offset += sizeof(s) - 1, \ | ||
889 | column += width(s)) | ||
890 | |||
891 | #define dump_character(c) (check_fit(1), \ | ||
892 | dump[offset] = c, \ | ||
893 | offset += 1, \ | ||
894 | column += 1) | ||
895 | |||
896 | static int width (const char *s) | ||
897 | { | ||
898 | const char *c; | ||
899 | int n, discard = 0; | ||
900 | |||
901 | /* Calculate the printed width of the chunk s ignoring escape | ||
902 | * sequences. */ | ||
903 | |||
904 | for (c = s, n = 0 ; *c ; c += 1) { | ||
905 | if (!discard && *c == '\033') { | ||
906 | discard = 1; | ||
907 | } | ||
908 | |||
909 | if (!discard) { | ||
910 | n+= 1; | ||
911 | } | ||
912 | |||
913 | if (discard && *c == 'm') { | ||
914 | discard = 0; | ||
915 | } | ||
916 | } | ||
917 | |||
918 | return n; | ||
919 | } | ||
920 | |||
921 | static void check_fit (int size) | ||
922 | { | ||
923 | /* Check if a chunk fits in the buffer and expand as necessary. */ | ||
924 | |||
925 | if (offset + size + 1 > length) { | ||
926 | length = offset + size + 1; | ||
927 | dump = (char *)realloc (dump, length * sizeof (char)); | ||
928 | } | ||
929 | } | ||
930 | |||
931 | static int is_identifier (const char *s, int n) | ||
932 | { | ||
933 | int i; | ||
934 | |||
935 | /* Check whether a string can be used as a key without quotes and | ||
936 | * braces. */ | ||
937 | |||
938 | for (i = 0 ; i < n ; i += 1) { | ||
939 | if (!isalpha(s[i]) && | ||
940 | (i == 0 || !isalnum(s[i])) && | ||
941 | s[i] != '_') { | ||
942 | return 0; | ||
943 | } | ||
944 | } | ||
945 | |||
946 | return 1; | ||
947 | } | ||
948 | |||
949 | static void break_line () | ||
950 | { | ||
951 | int i; | ||
952 | |||
953 | check_fit (indent + 1); | ||
954 | |||
955 | /* Add a line break. */ | ||
956 | |||
957 | dump[offset] = '\n'; | ||
958 | |||
959 | /* And indent to the current level. */ | ||
960 | |||
961 | for (i = 1 ; i <= indent ; i += 1) { | ||
962 | dump[offset + i] = ' '; | ||
963 | } | ||
964 | |||
965 | offset += indent + 1; | ||
966 | column = indent; | ||
967 | } | ||
968 | |||
969 | static void dump_string (const char *s, int n) | ||
970 | { | ||
971 | int l; | ||
972 | |||
973 | /* Break the line if the current chunk doesn't fit but it would | ||
974 | * fit if we started on a fresh line at the current indent. */ | ||
975 | |||
976 | l = width(s); | ||
977 | |||
978 | if (column + l > linewidth && indent + l <= linewidth) { | ||
979 | break_line(); | ||
980 | } | ||
981 | |||
982 | check_fit (n); | ||
983 | |||
984 | /* Copy the string to the buffer. */ | ||
985 | |||
986 | memcpy (dump + offset, s, n); | ||
987 | dump[offset + n] = '\0'; | ||
988 | |||
989 | offset += n; | ||
990 | column += l; | ||
991 | } | ||
992 | |||
993 | static void describe (lua_State *L, int index) | ||
994 | { | ||
995 | char *s; | ||
996 | size_t n; | ||
997 | int type; | ||
998 | |||
999 | index = absolute (L, index); | ||
1000 | type = lua_type (L, index); | ||
1001 | |||
1002 | if (luaL_getmetafield (L, index, "__tostring")) { | ||
1003 | lua_pushvalue (L, index); | ||
1004 | lua_pcall (L, 1, 1, 0); | ||
1005 | s = (char *)lua_tolstring (L, -1, &n); | ||
1006 | lua_pop (L, 1); | ||
1007 | |||
1008 | dump_string (s, n); | ||
1009 | } else if (type == LUA_TNUMBER) { | ||
1010 | /* Copy the value to avoid mutating it. */ | ||
1011 | |||
1012 | lua_pushvalue (L, index); | ||
1013 | s = (char *)lua_tolstring (L, -1, &n); | ||
1014 | lua_pop (L, 1); | ||
1015 | |||
1016 | dump_string (s, n); | ||
1017 | } else if (type == LUA_TSTRING) { | ||
1018 | int i, started, score, level, uselevel = 0; | ||
1019 | |||
1020 | s = (char *)lua_tolstring (L, index, &n); | ||
1021 | |||
1022 | /* Scan the string to decide how to print it. */ | ||
1023 | |||
1024 | for (i = 0, score = n, started = 0 ; i < (int)n ; i += 1) { | ||
1025 | if (s[i] == '\n' || s[i] == '\t' || | ||
1026 | s[i] == '\v' || s[i] == '\r') { | ||
1027 | /* These characters show up better in a long sting so | ||
1028 | * bias towards that. */ | ||
1029 | |||
1030 | score += linewidth / 2; | ||
1031 | } else if (s[i] == '\a' || s[i] == '\b' || | ||
1032 | s[i] == '\f' || !isprint(s[i])) { | ||
1033 | /* These however go better with an escaped short | ||
1034 | * string (unless you like the bell or weird | ||
1035 | * characters). */ | ||
1036 | |||
1037 | score -= linewidth / 4; | ||
1038 | } | ||
1039 | |||
1040 | /* Check what long string delimeter level to use so that | ||
1041 | * the string won't be closed prematurely. */ | ||
1042 | |||
1043 | if (!started) { | ||
1044 | if (s[i] == ']') { | ||
1045 | started = 1; | ||
1046 | level = 0; | ||
1047 | } | ||
1048 | } else { | ||
1049 | if (s[i] == '=') { | ||
1050 | level += 1; | ||
1051 | } else if (s[i] == ']') { | ||
1052 | if (level >= uselevel) { | ||
1053 | uselevel = level + 1; | ||
1054 | } | ||
1055 | } else { | ||
1056 | started = 0; | ||
1057 | } | ||
1058 | } | ||
1059 | } | ||
1060 | |||
1061 | if (score > linewidth) { | ||
1062 | /* Dump the string as a long string. */ | ||
1063 | |||
1064 | dump_character ('['); | ||
1065 | for (i = 0 ; i < uselevel ; i += 1) { | ||
1066 | dump_character ('='); | ||
1067 | } | ||
1068 | dump_literal ("[\n"); | ||
1069 | |||
1070 | dump_string (s, n); | ||
1071 | |||
1072 | dump_character (']'); | ||
1073 | for (i = 0 ; i < uselevel ; i += 1) { | ||
1074 | dump_character ('='); | ||
1075 | } | ||
1076 | dump_literal ("]"); | ||
1077 | } else { | ||
1078 | /* Escape the string as needed and print it as a normal | ||
1079 | * string. */ | ||
1080 | |||
1081 | dump_literal ("\""); | ||
1082 | |||
1083 | for (i = 0 ; i < (int)n ; i += 1) { | ||
1084 | if (s[i] == '"' || s[i] == '\\') { | ||
1085 | dump_literal ("\\"); | ||
1086 | dump_character (s[i]); | ||
1087 | } else if (s[i] == '\a') { | ||
1088 | dump_literal ("\\a"); | ||
1089 | } else if (s[i] == '\b') { | ||
1090 | dump_literal ("\\b"); | ||
1091 | } else if (s[i] == '\f') { | ||
1092 | dump_literal ("\\f"); | ||
1093 | } else if (s[i] == '\n') { | ||
1094 | dump_literal ("\\n"); | ||
1095 | } else if (s[i] == '\r') { | ||
1096 | dump_literal ("\\r"); | ||
1097 | } else if (s[i] == '\t') { | ||
1098 | dump_literal ("\\t"); | ||
1099 | } else if (s[i] == '\v') { | ||
1100 | dump_literal ("\\v"); | ||
1101 | } else if (isprint(s[i])) { | ||
1102 | dump_character (s[i]); | ||
1103 | } else { | ||
1104 | char t[5]; | ||
1105 | size_t n; | ||
1106 | |||
1107 | n = sprintf (t, "\\%03u", ((unsigned char *)s)[i]); | ||
1108 | dump_string (t, n); | ||
1109 | } | ||
1110 | } | ||
1111 | |||
1112 | dump_literal ("\""); | ||
1113 | } | ||
1114 | } else if (type == LUA_TNIL) { | ||
1115 | n = asprintf (&s, "%snil%s", COLOR(7), COLOR(8)); | ||
1116 | dump_string (s, n); | ||
1117 | free(s); | ||
1118 | } else if (type == LUA_TBOOLEAN) { | ||
1119 | n = asprintf (&s, "%s%s%s", | ||
1120 | COLOR(7), | ||
1121 | lua_toboolean (L, index) ? "true" : "false", | ||
1122 | COLOR(8)); | ||
1123 | dump_string (s, n); | ||
1124 | free(s); | ||
1125 | } else if (type == LUA_TFUNCTION) { | ||
1126 | n = asprintf (&s, "<%sfunction:%s %p>", | ||
1127 | COLOR(7), COLOR(8), lua_topointer (L, index)); | ||
1128 | dump_string (s, n); | ||
1129 | free(s); | ||
1130 | } else if (type == LUA_TUSERDATA) { | ||
1131 | n = asprintf (&s, "<%suserdata:%s %p>", | ||
1132 | COLOR(7), COLOR(8), lua_topointer (L, index)); | ||
1133 | |||
1134 | dump_string (s, n); | ||
1135 | free(s); | ||
1136 | } else if (type == LUA_TTHREAD) { | ||
1137 | n = asprintf (&s, "<%sthread:%s %p>", | ||
1138 | COLOR(7), COLOR(8), lua_topointer (L, index)); | ||
1139 | dump_string (s, n); | ||
1140 | free(s); | ||
1141 | } else if (type == LUA_TTABLE) { | ||
1142 | int i, l, n, oldindent, multiline, nobreak; | ||
1143 | |||
1144 | /* Check if table is too deeply nested. */ | ||
1145 | |||
1146 | if (indent > 8 * linewidth / 10) { | ||
1147 | char *s; | ||
1148 | size_t n; | ||
1149 | |||
1150 | n = asprintf (&s, "{ %s...%s }", COLOR(7), COLOR(8)); | ||
1151 | dump_string (s, n); | ||
1152 | free(s); | ||
1153 | |||
1154 | return; | ||
1155 | } | ||
1156 | |||
1157 | /* Check if the table introduces a cycle by checking whether | ||
1158 | * it is a back-edge (that is equal to an ancestor table. */ | ||
1159 | |||
1160 | lua_rawgeti (L, LUA_REGISTRYINDEX, ancestors); | ||
1161 | n = lua_rawlen(L, -1); | ||
1162 | |||
1163 | for (i = 0 ; i < n ; i += 1) { | ||
1164 | lua_rawgeti (L, -1, n - i); | ||
1165 | #if LUA_VERSION_NUM == 501 | ||
1166 | if(lua_equal (L, -1, -3)) { | ||
1167 | #else | ||
1168 | if(lua_compare (L, -1, -3, LUA_OPEQ)) { | ||
1169 | #endif | ||
1170 | char *s; | ||
1171 | size_t n; | ||
1172 | |||
1173 | n = asprintf (&s, "{ %s[%d]...%s }", | ||
1174 | COLOR(7), -(i + 1), COLOR(8)); | ||
1175 | dump_string (s, n); | ||
1176 | free(s); | ||
1177 | lua_pop (L, 2); | ||
1178 | |||
1179 | return; | ||
1180 | } | ||
1181 | |||
1182 | lua_pop (L, 1); | ||
1183 | } | ||
1184 | |||
1185 | /* Add the table to the ancestor list and pop the ancestor | ||
1186 | * list table. */ | ||
1187 | |||
1188 | lua_pushvalue (L, index); | ||
1189 | lua_rawseti (L, -2, n + 1); | ||
1190 | lua_pop (L, 1); | ||
1191 | |||
1192 | /* Open the table and update the indentation level to the | ||
1193 | * current column. */ | ||
1194 | |||
1195 | dump_literal ("{ "); | ||
1196 | oldindent = indent; | ||
1197 | indent = column; | ||
1198 | multiline = 0; | ||
1199 | nobreak = 0; | ||
1200 | |||
1201 | l = lua_rawlen (L, index); | ||
1202 | |||
1203 | /* Traverse the array part first. */ | ||
1204 | |||
1205 | for (i = 0 ; i < l ; i += 1) { | ||
1206 | lua_pushinteger (L, i + 1); | ||
1207 | lua_gettable (L, index); | ||
1208 | |||
1209 | /* Start a fresh line when dumping tables to make sure | ||
1210 | * there's plenty of room. */ | ||
1211 | |||
1212 | if (lua_istable (L, -1)) { | ||
1213 | if (!nobreak) { | ||
1214 | break_line(); | ||
1215 | } | ||
1216 | |||
1217 | multiline = 1; | ||
1218 | } | ||
1219 | |||
1220 | nobreak = 0; | ||
1221 | |||
1222 | /* Dump the value and separating comma. */ | ||
1223 | |||
1224 | describe (L, -1); | ||
1225 | dump_literal (", "); | ||
1226 | |||
1227 | if (lua_istable (L, -1) && i != l - 1) { | ||
1228 | break_line(); | ||
1229 | nobreak = 1; | ||
1230 | } | ||
1231 | |||
1232 | lua_pop (L, 1); | ||
1233 | } | ||
1234 | |||
1235 | /* Now for the hash part. */ | ||
1236 | |||
1237 | lua_pushnil (L); | ||
1238 | while (lua_next (L, index) != 0) { | ||
1239 | if (lua_type (L, -2) != LUA_TNUMBER || | ||
1240 | lua_tonumber (L, -2) != lua_tointeger (L, -2) || | ||
1241 | lua_tointeger (L, -2) < 1 || | ||
1242 | lua_tointeger (L, -2) > l) { | ||
1243 | |||
1244 | /* Keep each key-value pair on a separate line. */ | ||
1245 | |||
1246 | break_line (); | ||
1247 | multiline = 1; | ||
1248 | |||
1249 | /* Dump the key and value. */ | ||
1250 | |||
1251 | if (lua_type (L, -2) == LUA_TSTRING) { | ||
1252 | char *s; | ||
1253 | size_t n; | ||
1254 | |||
1255 | s = (char *)lua_tolstring (L, -2, &n); | ||
1256 | |||
1257 | if(is_identifier (s, n)) { | ||
1258 | dump_string (COLOR(7), strlen(COLOR(7))); | ||
1259 | dump_string (s, n); | ||
1260 | dump_string (COLOR(8), strlen(COLOR(8))); | ||
1261 | } else { | ||
1262 | dump_literal ("["); | ||
1263 | describe (L, -2); | ||
1264 | dump_literal ("]"); | ||
1265 | } | ||
1266 | } else { | ||
1267 | dump_literal ("["); | ||
1268 | describe (L, -2); | ||
1269 | dump_literal ("]"); | ||
1270 | } | ||
1271 | |||
1272 | dump_literal (" = "); | ||
1273 | describe (L, -1); | ||
1274 | dump_literal (","); | ||
1275 | } | ||
1276 | |||
1277 | lua_pop (L, 1); | ||
1278 | } | ||
1279 | |||
1280 | /* Remove the table from the ancestor list. */ | ||
1281 | |||
1282 | lua_rawgeti (L, LUA_REGISTRYINDEX, ancestors); | ||
1283 | lua_pushnil (L); | ||
1284 | lua_rawseti (L, -2, n + 1); | ||
1285 | lua_pop (L, 1); | ||
1286 | |||
1287 | /* Pop the indentation level. */ | ||
1288 | |||
1289 | indent = oldindent; | ||
1290 | |||
1291 | if (multiline) { | ||
1292 | break_line(); | ||
1293 | dump_literal ("}"); | ||
1294 | } else { | ||
1295 | dump_literal (" }"); | ||
1296 | } | ||
1297 | } | ||
1298 | } | ||
1299 | |||
1300 | char *luap_describe (lua_State *L, int index) | ||
1301 | { | ||
1302 | int oldcolorize; | ||
1303 | |||
1304 | #ifdef HAVE_IOCTL | ||
1305 | struct winsize w; | ||
1306 | |||
1307 | /* Initialize the state. */ | ||
1308 | |||
1309 | if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) { | ||
1310 | linewidth = 80; | ||
1311 | } else { | ||
1312 | linewidth = w.ws_col; | ||
1313 | } | ||
1314 | #else | ||
1315 | linewidth = 80; | ||
1316 | #endif | ||
1317 | |||
1318 | index = absolute (L, index); | ||
1319 | offset = 0; | ||
1320 | indent = 0; | ||
1321 | column = 0; | ||
1322 | |||
1323 | /* Suppress colorization, to avoid escape sequences in the | ||
1324 | * returned strings. */ | ||
1325 | |||
1326 | oldcolorize = colorize; | ||
1327 | colorize = 0; | ||
1328 | |||
1329 | /* Create a table to hold the ancestors for checking for cycles | ||
1330 | * when printing table hierarchies. */ | ||
1331 | |||
1332 | lua_newtable (L); | ||
1333 | ancestors = luaL_ref (L, LUA_REGISTRYINDEX); | ||
1334 | |||
1335 | describe (L, index); | ||
1336 | |||
1337 | luaL_unref (L, LUA_REGISTRYINDEX, ancestors); | ||
1338 | colorize = oldcolorize; | ||
1339 | |||
1340 | return dump; | ||
1341 | } | ||
1342 | |||
1343 | /* These are custom commands. */ | ||
1344 | |||
1345 | #ifdef HAVE_LIBREADLINE | ||
1346 | static int describe_stack (int count, int key) | ||
1347 | { | ||
1348 | int i, h; | ||
1349 | |||
1350 | print_output ("%s", COLOR(7)); | ||
1351 | |||
1352 | h = lua_gettop (M); | ||
1353 | |||
1354 | if (count < 0) { | ||
1355 | i = h + count + 1; | ||
1356 | |||
1357 | if (i > 0 && i <= h) { | ||
1358 | print_output ("\nValue at stack index %d(%d):\n%s%s", | ||
1359 | i, -h + i - 1, COLOR(3), luap_describe (M, i)); | ||
1360 | } else { | ||
1361 | print_error ("Invalid stack index.\n"); | ||
1362 | } | ||
1363 | } else { | ||
1364 | if (h > 0) { | ||
1365 | print_output ("\nThe stack contains %d values.\n", h); | ||
1366 | for (i = 1 ; i <= h ; i += 1) { | ||
1367 | print_output ("\n%d(%d):\t%s", i, -h + i - 1, lua_typename(M, lua_type(M, i))); | ||
1368 | } | ||
1369 | } else { | ||
1370 | print_output ("\nThe stack is empty."); | ||
1371 | } | ||
1372 | } | ||
1373 | |||
1374 | print_output ("%s\n", COLOR(0)); | ||
1375 | |||
1376 | rl_on_new_line (); | ||
1377 | |||
1378 | return 0; | ||
1379 | } | ||
1380 | #endif | ||
1381 | |||
1382 | int luap_call (lua_State *L, int n) { | ||
1383 | int h, status; | ||
1384 | |||
1385 | /* We can wind up here before reaching luap_enter, so this is | ||
1386 | * needed. */ | ||
1387 | |||
1388 | M = L; | ||
1389 | |||
1390 | /* Push the error handler onto the stack. */ | ||
1391 | |||
1392 | h = lua_gettop(L) - n; | ||
1393 | lua_pushcfunction (L, traceback); | ||
1394 | lua_insert (L, h); | ||
1395 | |||
1396 | /* Try to execute the supplied chunk and keep note of any return | ||
1397 | * values. */ | ||
1398 | |||
1399 | status = lua_pcall(L, n, LUA_MULTRET, h); | ||
1400 | |||
1401 | /* Print any errors. */ | ||
1402 | |||
1403 | if (status != LUA_OK) { | ||
1404 | print_error ("%s%s%s\n", COLOR(1), lua_tostring (L, -1), COLOR(0)); | ||
1405 | lua_pop (L, 1); | ||
1406 | } | ||
1407 | |||
1408 | /* Remove the error handler. */ | ||
1409 | |||
1410 | lua_remove (L, h); | ||
1411 | |||
1412 | return status; | ||
1413 | } | ||
1414 | |||
1415 | void luap_setprompts(lua_State *L, const char *single, const char *multi) | ||
1416 | { | ||
1417 | /* Plain, uncolored prompts. */ | ||
1418 | |||
1419 | prompts[0][0] = (char *)realloc (prompts[0][0], strlen (single) + 1); | ||
1420 | prompts[0][1] = (char *)realloc (prompts[0][1], strlen (multi) + 1); | ||
1421 | strcpy(prompts[0][0], single); | ||
1422 | strcpy(prompts[0][1], multi); | ||
1423 | |||
1424 | /* Colored prompts. */ | ||
1425 | |||
1426 | prompts[1][0] = (char *)realloc (prompts[1][0], strlen (single) + 16); | ||
1427 | prompts[1][1] = (char *)realloc (prompts[1][1], strlen (multi) + 16); | ||
1428 | #ifdef HAVE_LIBREADLINE | ||
1429 | sprintf (prompts[1][0], "\001%s\002%s\001%s\002", | ||
1430 | COLOR(6), single, COLOR(0)); | ||
1431 | sprintf (prompts[1][1], "\001%s\002%s\001%s\002", | ||
1432 | COLOR(6), multi, COLOR(0)); | ||
1433 | #else | ||
1434 | sprintf (prompts[1][0], "%s%s%s", COLOR(6), single, COLOR(0)); | ||
1435 | sprintf (prompts[1][1], "%s%s%s", COLOR(6), multi, COLOR(0)); | ||
1436 | #endif | ||
1437 | } | ||
1438 | |||
1439 | void luap_sethistory(lua_State *L, const char *file) | ||
1440 | { | ||
1441 | if (file) { | ||
1442 | logfile = realloc (logfile, strlen(file) + 1); | ||
1443 | strcpy (logfile, file); | ||
1444 | } else if (logfile) { | ||
1445 | free(logfile); | ||
1446 | logfile = NULL; | ||
1447 | } | ||
1448 | } | ||
1449 | |||
1450 | void luap_setcolor(lua_State *L, int enable) | ||
1451 | { | ||
1452 | /* Don't allow color if we're not writing to a terminal. */ | ||
1453 | |||
1454 | if (!isatty (STDOUT_FILENO) || !isatty (STDERR_FILENO)) { | ||
1455 | colorize = 0; | ||
1456 | } else { | ||
1457 | colorize = enable; | ||
1458 | } | ||
1459 | } | ||
1460 | |||
1461 | void luap_setname(lua_State *L, const char *name) | ||
1462 | { | ||
1463 | chunkname = (char *)realloc (chunkname, strlen(name) + 2); | ||
1464 | chunkname[0] = '='; | ||
1465 | strcpy (chunkname + 1, name); | ||
1466 | } | ||
1467 | |||
1468 | void luap_getprompts(lua_State *L, const char **single, const char **multi) | ||
1469 | { | ||
1470 | *single = prompts[0][0]; | ||
1471 | *multi = prompts[0][1]; | ||
1472 | } | ||
1473 | |||
1474 | void luap_gethistory(lua_State *L, const char **file) | ||
1475 | { | ||
1476 | *file = logfile; | ||
1477 | } | ||
1478 | |||
1479 | void luap_getcolor(lua_State *L, int *enabled) | ||
1480 | { | ||
1481 | *enabled = colorize; | ||
1482 | } | ||
1483 | |||
1484 | void luap_getname(lua_State *L, const char **name) | ||
1485 | { | ||
1486 | *name = chunkname + 1; | ||
1487 | } | ||
1488 | |||
1489 | void luap_enter(lua_State *L, bool *terminate) | ||
1490 | { | ||
1491 | int incomplete = 0, s = 0, t = 0, l; | ||
1492 | char *line, *prepended; | ||
1493 | #ifdef SAVE_RESULTS | ||
1494 | int cleanup = 0; | ||
1495 | #endif | ||
1496 | |||
1497 | /* Save the state since it needs to be passed to some readline | ||
1498 | * callbacks. */ | ||
1499 | |||
1500 | M = L; | ||
1501 | |||
1502 | if (!initialized) { | ||
1503 | #ifdef HAVE_LIBREADLINE | ||
1504 | rl_basic_word_break_characters = " \t\n`@$><=;|&{("; | ||
1505 | rl_completion_entry_function = generator; | ||
1506 | rl_completion_display_matches_hook = display_matches; | ||
1507 | |||
1508 | rl_add_defun ("lua-describe-stack", describe_stack, META('s')); | ||
1509 | #endif | ||
1510 | |||
1511 | #ifdef HAVE_READLINE_HISTORY | ||
1512 | /* Load the command history if there is one. */ | ||
1513 | |||
1514 | if (logfile) { | ||
1515 | read_history (logfile); | ||
1516 | } | ||
1517 | #endif | ||
1518 | if (!chunkname) { | ||
1519 | luap_setname (L, "lua"); | ||
1520 | } | ||
1521 | |||
1522 | if (!prompts[0][0]) { | ||
1523 | luap_setprompts (L, "> ", ">> "); | ||
1524 | } | ||
1525 | |||
1526 | atexit (finish); | ||
1527 | |||
1528 | initialized = 1; | ||
1529 | } | ||
1530 | |||
1531 | #ifdef SAVE_RESULTS | ||
1532 | if (results == LUA_REFNIL) { | ||
1533 | lua_newtable(L); | ||
1534 | |||
1535 | #ifdef WEAK_RESULTS | ||
1536 | lua_newtable(L); | ||
1537 | lua_pushliteral(L, "v"); | ||
1538 | lua_setfield(L, -2, "__mode"); | ||
1539 | lua_setmetatable(L, -2); | ||
1540 | #endif | ||
1541 | |||
1542 | results = luaL_ref(L, LUA_REGISTRYINDEX); | ||
1543 | } | ||
1544 | |||
1545 | lua_getglobal(L, RESULTS_TABLE_NAME); | ||
1546 | if (lua_isnil(L, -1)) { | ||
1547 | lua_rawgeti(L, LUA_REGISTRYINDEX, results); | ||
1548 | lua_setglobal(L, RESULTS_TABLE_NAME); | ||
1549 | |||
1550 | cleanup = 1; | ||
1551 | } | ||
1552 | lua_pop(L, 1); | ||
1553 | #endif | ||
1554 | |||
1555 | while (!(*terminate) && | ||
1556 | (line = readline (incomplete ? | ||
1557 | prompts[colorize][1] : prompts[colorize][0]))) { | ||
1558 | int status; | ||
1559 | |||
1560 | if (*line == '\0') { | ||
1561 | free(line); | ||
1562 | continue; | ||
1563 | } | ||
1564 | |||
1565 | /* Add/copy the line to the buffer. */ | ||
1566 | |||
1567 | if (incomplete) { | ||
1568 | s += strlen (line) + 1; | ||
1569 | |||
1570 | if (s > t) { | ||
1571 | buffer = (char *)realloc (buffer, s + 1); | ||
1572 | t = s; | ||
1573 | } | ||
1574 | |||
1575 | strcat (buffer, "\n"); | ||
1576 | strcat (buffer, line); | ||
1577 | } else { | ||
1578 | s = strlen (line); | ||
1579 | |||
1580 | if (s > t) { | ||
1581 | buffer = (char *)realloc (buffer, s + 1); | ||
1582 | t = s; | ||
1583 | } | ||
1584 | |||
1585 | strcpy (buffer, line); | ||
1586 | } | ||
1587 | |||
1588 | /* Try to execute the line with a return prepended first. If | ||
1589 | * this works we can show returned values. */ | ||
1590 | |||
1591 | l = asprintf (&prepended, "return %s", buffer); | ||
1592 | |||
1593 | if (luaL_loadbuffer(L, prepended, l, chunkname) == LUA_OK) { | ||
1594 | execute(); | ||
1595 | |||
1596 | incomplete = 0; | ||
1597 | } else { | ||
1598 | lua_pop (L, 1); | ||
1599 | |||
1600 | /* Try to execute the line as-is. */ | ||
1601 | |||
1602 | status = luaL_loadbuffer(L, buffer, s, chunkname); | ||
1603 | |||
1604 | incomplete = 0; | ||
1605 | |||
1606 | if (status == LUA_ERRSYNTAX) { | ||
1607 | const char *message; | ||
1608 | const int k = sizeof(EOF_MARKER) / sizeof(char) - 1; | ||
1609 | size_t n; | ||
1610 | |||
1611 | message = lua_tolstring (L, -1, &n); | ||
1612 | |||
1613 | /* If the error message mentions an unexpected eof | ||
1614 | * then consider this a multi-line statement and wait | ||
1615 | * for more input. If not then just print the error | ||
1616 | * message.*/ | ||
1617 | |||
1618 | if ((int)n > k && | ||
1619 | !strncmp (message + n - k, EOF_MARKER, k)) { | ||
1620 | incomplete = 1; | ||
1621 | } else { | ||
1622 | print_error ("%s%s%s\n", COLOR(1), lua_tostring (L, -1), | ||
1623 | COLOR(0)); | ||
1624 | } | ||
1625 | |||
1626 | lua_pop (L, 1); | ||
1627 | } else if (status == LUA_ERRMEM) { | ||
1628 | print_error ("%s%s%s\n", COLOR(1), lua_tostring (L, -1), | ||
1629 | COLOR(0)); | ||
1630 | lua_pop (L, 1); | ||
1631 | } else { | ||
1632 | /* Try to execute the loaded chunk. */ | ||
1633 | |||
1634 | execute (); | ||
1635 | incomplete = 0; | ||
1636 | } | ||
1637 | } | ||
1638 | |||
1639 | #ifdef HAVE_READLINE_HISTORY | ||
1640 | /* Add the line to the history if non-empty. */ | ||
1641 | |||
1642 | if (!incomplete) { | ||
1643 | add_history (buffer); | ||
1644 | } | ||
1645 | #endif | ||
1646 | |||
1647 | free (prepended); | ||
1648 | free (line); | ||
1649 | } | ||
1650 | |||
1651 | #ifdef SAVE_RESULTS | ||
1652 | if (cleanup) { | ||
1653 | lua_pushnil(L); | ||
1654 | lua_setglobal(L, RESULTS_TABLE_NAME); | ||
1655 | } | ||
1656 | #endif | ||
1657 | |||
1658 | print_output ("\n"); | ||
1659 | } | ||