luafaudes.cpp
Go to the documentation of this file.
1 /** @file luafaudes.cpp Simple Lua stand-alone interpreter for lua/faudes
2 
3  This lua interpreter is almost a plain copy of the original lua.c
4  provided with the lua 5.1.3 distribution and under an MIT license;
5  see original lua.h or http://www.lua.org
6 
7  The Advanced-Readline patch has been applied, also with lua license,
8  copyright see below. SWIG based Lua bindings and minor adjustments
9  (wellcome string) have been added (and signed "luafaudes").
10  Thomas Moor, 2008, 2025.
11 
12 
13 @ingroup Tutorials
14 
15 
16 */
17 
18 
19 // luafaudes: skip doxygen
20 #ifndef FAUDES_DOXYGEN
21 
22 /*
23 ** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $
24 ** Lua stand-alone interpreter
25 ** See Copyright Notice in lua.h
26 */
27 
28 
29 // luafaudes: include c libs since we dont use lua.h
30 #include <csignal>
31 #include <cstdio>
32 #include <cstdlib>
33 #include <cstring>
34 
35 
36 // luafaudes: use my include file (incl. lpb_include.h)
37 #include "libfaudes.h"
38 
39 // luafaudes: import all
40 using namespace faudes;
41 
42 // luafaudes: dislike isatty on MSYS
43 #if defined(LUA_USE_ISATTY)
44 #include <unistd.h>
45 #define faudes_stdin_is_tty() isatty(0)
46 #else
47 #define faudes_stdin_is_tty() 1
48 #endif
49 
50 // below this line: all from lua provided interpreter lua.cpp, except tagged "luafaudes"
51 static lua_State *globalL = NULL;
52 static const char *progname = LUA_PROGNAME;
53 
54 
55 static void lstop (lua_State *L, lua_Debug *ar) {
56  (void)ar; /* unused arg. */
57  lua_sethook(L, NULL, 0, 0);
58  luaL_error(L, "interrupted!");
59 }
60 
61 
62 static void laction (int i) {
63  signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
64  terminate process (default action) */
65  lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
66 }
67 
68 
69 static void print_usage (void) {
70  fprintf(stderr,
71  "usage: %s [options] [script [args]].\n"
72  "Available options are:\n"
73  " -e stat execute string " LUA_QL("stat") "\n"
74  " -l name require library " LUA_QL("name") "\n"
75  " -i enter interactive mode after executing " LUA_QL("script") "\n"
76  " -v show version information\n"
77  " -d pass on libFAUDES messages to console\n" /* luafaudes: d-option */
78  " -x load libFAUDES extension\n" /* luafaudes: x-option */
79  " -- stop handling options\n"
80  " - execute stdin and stop handling options\n"
81  ,
82  progname);
83  fflush(stderr);
84 }
85 
86 
87 static void l_message (const char *pname, const char *msg) {
88  if (pname) fprintf(stderr, "%s: ", pname);
89  fprintf(stderr, "%s\n", msg);
90  fflush(stderr);
91 }
92 
93 
94 static int report (lua_State *L, int status) {
95  if (status && !lua_isnil(L, -1)) {
96  const char *msg = lua_tostring(L, -1);
97  if (msg == NULL) msg = "(error object is not a string)";
98  l_message(progname, msg);
99  lua_pop(L, 1);
100  }
101  return status;
102 }
103 
104 
105 static int traceback (lua_State *L) {
106  if (!lua_isstring(L, 1)) /* 'message' not a string? */
107  return 1; /* keep it intact */
108  lua_getfield(L, LUA_GLOBALSINDEX, "debug");
109  if (!lua_istable(L, -1)) {
110  lua_pop(L, 1);
111  return 1;
112  }
113  lua_getfield(L, -1, "traceback");
114  if (!lua_isfunction(L, -1)) {
115  lua_pop(L, 2);
116  return 1;
117  }
118  lua_pushvalue(L, 1); /* pass error message */
119  lua_pushinteger(L, 2); /* skip this function and traceback */
120  lua_call(L, 2, 1); /* call debug.traceback */
121  return 1;
122 }
123 
124 
125 static int docall (lua_State *L, int narg, int clear) {
126  int status;
127  int base = lua_gettop(L) - narg; /* function index */
128  lua_pushcfunction(L, traceback); /* push traceback function */
129  lua_insert(L, base); /* put it under chunk and args */
130  signal(SIGINT, laction);
131  status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
132  signal(SIGINT, SIG_DFL);
133  lua_remove(L, base); /* remove traceback function */
134  /* force a complete garbage collection in case of errors */
135  if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
136  return status;
137 }
138 
139 
140 // luafaudes: my print version
141 static void print_version (void) {
142  std::stringstream sstr;
143  sstr
144  << "Welcome to luafaudes console." << std::endl
145  << "Versions: " << VersionString() << " / " << LUA_VERSION << std::endl
146  << "Build: " << BuildString() << std::endl
147  << "Plug-Ins: " << PluginsString() << std::endl
148  << "Credits: This libFAUDES interpreter is based on the projects Lua and SWIG." << std::endl
149  << "Type 'faudes.Help()' for a list of faudes related types and functions." << std::endl
150  << "Enter Ctrl-C to exit the luafaudes interpreter" << std::endl;
151  l_message(NULL, sstr.str().c_str());
152 }
153 
154 
155 static int getargs (lua_State *L, char **argv, int n) {
156  int narg;
157  int i;
158  int argc = 0;
159  while (argv[argc]) argc++; /* count total number of arguments */
160  narg = argc - (n + 1); /* number of arguments to the script */
161  luaL_checkstack(L, narg + 3, "too many arguments to script");
162  for (i=n+1; i < argc; i++)
163  lua_pushstring(L, argv[i]);
164  lua_createtable(L, narg, n + 1);
165  for (i=0; i < argc; i++) {
166  lua_pushstring(L, argv[i]);
167  lua_rawseti(L, -2, i - n);
168  }
169  return narg;
170 }
171 
172 
173 static int dofile (lua_State *L, const char *name) {
174  int status = luaL_loadfile(L, name) || docall(L, 0, 1);
175  return report(L, status);
176 }
177 
178 
179 static int dostring (lua_State *L, const char *s, const char *name) {
180  int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
181  return report(L, status);
182 }
183 
184 
185 static int dolibrary (lua_State *L, const char *name) {
186  lua_getglobal(L, "require");
187  lua_pushstring(L, name);
188  return report(L, docall(L, 1, 1));
189 }
190 
191 
192 
193 /* ------------------------------------------------------------------------ */
194 
195 #ifdef LUA_USE_READLINE
196 /*
197 
198 ** luafaudes: here used to be advanced readline support patch by Mike Pall. However,
199 ** we required some variations for SWIG __index functions and we also wanted identifier-
200 ** completion in DESTool. Thus, we moved the original completer code to LuaState
201 ** (see lbp_function.cpp and lbp_completer.cpp) and provide a C interface via
202 ** luafaudes_complete() (see lbp_addons.cpp). The below code is reduced to the libreadline
203 ** interface. The original patch still ships with libFAUDES for reference and can be
204 ** inspected in lua.c in the Lua source tree. TMoor.
205 
206 ** Advanced readline support for the GNU readline and history libraries
207 ** or compatible replacements.
208 **
209 ** Copyright (C) 2004-2006 Mike Pall. Same license as Lua. See lua.h.
210 **
211 ** The (original) patch has been successfully tested with:
212 **
213 ** GNU readline 2.2.1 (1998-07-17)
214 ** GNU readline 4.0 (1999-02-18) [harmless compiler warning]
215 ** GNU readline 4.3 (2002-07-16)
216 ** GNU readline 5.0 (2004-07-27)
217 ** GNU readline 5.1 (2005-12-07)
218 ** NETBSD libedit 2.6.5 (2002-03-25)
219 ** NETBSD libedit 2.6.9 (2004-05-01)
220 */
221 
222 #include <ctype.h>
223 
224 static char *lua_rl_hist;
225 static int lua_rl_histsize;
226 
227 static lua_State *lua_rl_L; /* User data is not passed to rl callbacks. */
228 
229 /* luafaudes: pass on global state */
230 static char **faudes_complete_L(const char *text, int start, int end) {
231  return faudes_complete(lua_rl_L,text,start,end);
232 }
233 
234 /* Initialize readline library. */
235 static void lua_rl_init(lua_State *L)
236 {
237  char *s;
238 
239  lua_rl_L = L;
240 
241  /* This allows for $if lua ... $endif in ~/.inputrc. */
242  rl_readline_name = "lua";
243  /* Break words at every non-identifier character except '.' and ':'. */
244  rl_completer_word_break_characters = (char *)
245  "\t\r\n !\"#$%&'()*+,-/;<=>?@[\\]^`{|}~"; // luafaudes: pragmatic avoidance of compiler warning
246 
247  rl_completer_quote_characters = "\"'";
248  rl_completion_append_character = '\0';
249  rl_attempted_completion_function = faudes_complete_L; // luafaudes: use libFAUDES variant
250  rl_initialize();
251 
252  /* Start using history, optionally set history size and load history file. */
253  using_history();
254  if ((s = getenv("LUA_HISTSIZE")) &&
255  (lua_rl_histsize = atoi(s))) stifle_history(lua_rl_histsize);
256  if ((lua_rl_hist = getenv("LUA_HISTORY"))) read_history(lua_rl_hist);
257 }
258 
259 /* Finalize readline library. */
260 static void lua_rl_exit(lua_State *L)
261 {
262  /* Optionally save history file. */
263  if (lua_rl_hist) write_history(lua_rl_hist);
264 }
265 #else
266 #define lua_rl_init(L) ((void)L)
267 #define lua_rl_exit(L) ((void)L)
268 #endif
269 
270 /* ------------------------------------------------------------------------ */
271 
272 
273 static const char *get_prompt (lua_State *L, int firstline) {
274  const char *p;
275  lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
276  p = lua_tostring(L, -1);
277  if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
278  lua_pop(L, 1); /* remove global */
279  return p;
280 }
281 
282 
283 static int incomplete (lua_State *L, int status) {
284  if (status == LUA_ERRSYNTAX) {
285  size_t lmsg;
286  const char *msg = lua_tolstring(L, -1, &lmsg);
287  const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
288  if (strstr(msg, LUA_QL("<eof>")) == tp) {
289  lua_pop(L, 1);
290  return 1;
291  }
292  }
293  return 0; /* else... */
294 }
295 
296 
297 static int pushline (lua_State *L, int firstline) {
298  char buffer[LUA_MAXINPUT];
299  char *b = buffer;
300  size_t l;
301  const char *prmt = get_prompt(L, firstline);
302  if (lua_readline(L, b, prmt) == 0)
303  return 0; /* no input */
304  l = strlen(b);
305  if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
306  b[l-1] = '\0'; /* remove it */
307  if (firstline && b[0] == '=') /* first line starts with `=' ? */
308  lua_pushfstring(L, "return %s", b+1); /* change it to `return' */
309  else
310  lua_pushstring(L, b);
311  lua_freeline(L, b);
312  return 1;
313 }
314 
315 
316 static int loadline (lua_State *L) {
317  int status;
318  lua_settop(L, 0);
319  if (!pushline(L, 1))
320  return -1; /* no input */
321  for (;;) { /* repeat until gets a complete line */
322  status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
323  if (!incomplete(L, status)) break; /* cannot try to add lines? */
324  if (!pushline(L, 0)) /* no more input? */
325  return -1;
326  lua_pushliteral(L, "\n"); /* add a new line... */
327  lua_insert(L, -2); /* ...between the two lines */
328  lua_concat(L, 3); /* join them */
329  }
330  lua_saveline(L, 1);
331  lua_remove(L, 1); /* remove line */
332  return status;
333 }
334 
335 
336 static void dotty (lua_State *L) {
337  int status;
338  const char *oldprogname = progname;
339  progname = NULL;
340  lua_rl_init(L);
341  while ((status = loadline(L)) != -1) {
342  if (status == 0) status = docall(L, 0, 0);
343  report(L, status);
344  if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
345  lua_getglobal(L, "print");
346  lua_insert(L, 1);
347  if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
348  l_message(progname, lua_pushfstring(L,
349  "error calling " LUA_QL("print") " (%s)",
350  lua_tostring(L, -1)));
351  }
352  }
353  lua_settop(L, 0); /* clear stack */
354  fputs("\n", stdout);
355  fflush(stdout);
356  lua_rl_exit(L);
357  progname = oldprogname;
358 }
359 
360 static int handle_script (lua_State *L, char **argv, int n) {
361  int status;
362  const char *fname;
363  int narg = getargs(L, argv, n); /* collect arguments */
364  lua_setglobal(L, "arg");
365  fname = argv[n];
366  if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
367  fname = NULL; /* stdin */
368  status = luaL_loadfile(L, fname);
369  lua_insert(L, -(narg+1));
370  if (status == 0)
371  status = docall(L, narg, 0);
372  else
373  lua_pop(L, narg);
374  return report(L, status);
375 }
376 
377 
378 /* check that argument has no extra characters at the end */
379 #define notail(x) {if ((x)[2] != '\0') return -1;}
380 
381 
382 // luafaudes: have extra options -d and -x
383 static int collectargs (char **argv, int *pi, int *pv, int *pe, int *pd, int *px) {
384  int i;
385  for (i = 1; argv[i] != NULL; i++) {
386  if (argv[i][0] != '-') /* not an option? */
387  return i;
388  switch (argv[i][1]) { /* option */
389  case '-':
390  notail(argv[i]);
391  return (argv[i+1] != NULL ? i+1 : 0);
392  case '\0':
393  return i;
394  case 'i':
395  notail(argv[i]);
396  *pi = 1; /* go through */
397  case 'v':
398  notail(argv[i]);
399  *pv = 1;
400  break;
401  case 'e':
402  *pe = 1; /* go through */
403  case 'l':
404  if (argv[i][2] == '\0') {
405  i++;
406  if (argv[i] == NULL) return -1;
407  }
408  break;
409  case 'd': // luafaudes: d-option
410  notail(argv[i]);
411  *pd = 1;
412  break;
413  case 'x': // luafaudes: x-option
414  if (argv[i][2] == '\0') {
415  i++;
416  if (argv[i] == NULL) return -1;
417  }
418  *px = 1;
419  break;
420  default: return -1; /* invalid option */
421  }
422  }
423  return 0;
424 }
425 
426 
427 // luafaudes: have extra option -x
428 static int runargs (lua_State *L, char **argv, int n) {
429  int i;
430  for (i = 1; i < n; i++) {
431  if (argv[i] == NULL) continue;
432  lua_assert(argv[i][0] == '-');
433  switch (argv[i][1]) { /* option */
434  case 'e': {
435  const char *chunk = argv[i] + 2;
436  if (*chunk == '\0') chunk = argv[++i];
437  lua_assert(chunk != NULL);
438  if (dostring(L, chunk, "=(command line)") != 0)
439  return 1;
440  break;
441  }
442  case 'l': {
443  const char *filename = argv[i] + 2;
444  if (*filename == '\0') filename = argv[++i];
445  lua_assert(filename != NULL);
446  if (dolibrary(L, filename))
447  return 1; /* stop if file fails */
448  break;
449  }
450  case 'x': { // luafaudes option x
451  const char *filename = argv[i] + 2;
452  if (*filename == '\0') filename = argv[++i];
453  lua_assert(filename != NULL);
454  if (faudes_loadext(L, filename)) {
455  l_message("fatal error: failed to load extension", filename);
456  return 1; /* stop if file fails */
457  }
458  break;
459  }
460  default: break;
461  }
462  }
463  return 0;
464 }
465 
466 
467 static int handle_luainit (lua_State *L) {
468  const char *init = getenv(LUA_INIT);
469  if (init == NULL) return 0; /* status OK */
470  else if (init[0] == '@')
471  return dofile(L, init+1);
472  else
473  return dostring(L, init, "=" LUA_INIT);
474 }
475 
476 
477 struct Smain {
478  int argc;
479  char **argv;
480  int status;
481 };
482 
483 
484 static int pmain (lua_State *L) {
485  struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
486  char **argv = s->argv;
487  int script;
488  int has_i = 0, has_v = 0, has_e = 0, has_d=0, has_x=0; // luafaudes: -d and -x option
489  globalL = L;
490  if (argv[0] && argv[0][0]) progname = argv[0];
491  faudes_initialize(L); // luafaudes: all of the below plus my namespace
492  /*
493  // original lua
494  lua_gc(L, LUA_GCSTOP, 0);
495  luaL_openlibs(L);
496  lua_gc(L, LUA_GCRESTART, 0);
497  */
498  s->status = handle_luainit(L);
499  if (s->status != 0) return 0;
500  script = collectargs(argv, &has_i, &has_v, &has_e, &has_d, &has_x); // luafaudes: -d and -x option
501  if (script < 0) { /* invalid args? */
502  print_usage();
503  s->status = 1;
504  return 0;
505  }
506  if (has_v) print_version();
507  if (!has_d) Verbosity(0); /* luafaudes: mute */
508  if (!has_x) faudes_loaddefext(L, argv[0]); /* luafaudes: default extension */
509  s->status = runargs(L, argv, (script > 0) ? script : s->argc);
510  if (s->status != 0) return 0;
511  if (script)
512  s->status = handle_script(L, argv, script);
513  if (s->status != 0) return 0;
514  if (has_i) {
515  dotty(L);
516  return 0;
517  }
518  if (script == 0 && !has_e) {
519  if (faudes_stdin_is_tty()) {
520  if(!has_v) print_version();
521  dotty(L);
522  }
523  else dofile(L, NULL); /* executes stdin as a file */
524  }
525  return 0;
526 }
527 
528 
529 int main (int argc, char **argv) {
530  int status;
531  struct Smain s;
532  lua_State *L = lua_open(); /* create state */
533  if (L == NULL) {
534  l_message(argv[0], "cannot create state: not enough memory");
535  return EXIT_FAILURE;
536  }
537  s.argc = argc;
538  s.argv = argv;
539  status = lua_cpcall(L, &pmain, &s);
540  report(L, status);
541  lua_close(L);
542  return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
543 }
544 
545 
546 // luafaudes: end skip doxygen
547 #endif
static int report(lua_State *L, int status)
Definition: luafaudes.cpp:94
static void print_usage(void)
Definition: luafaudes.cpp:69
static int getargs(lua_State *L, char **argv, int n)
Definition: luafaudes.cpp:155
#define lua_rl_exit(L)
Definition: luafaudes.cpp:267
static void print_version(void)
Definition: luafaudes.cpp:141
int main(int argc, char **argv)
Definition: luafaudes.cpp:529
static int docall(lua_State *L, int narg, int clear)
Definition: luafaudes.cpp:125
#define lua_rl_init(L)
Definition: luafaudes.cpp:266
static int handle_luainit(lua_State *L)
Definition: luafaudes.cpp:467
static void lstop(lua_State *L, lua_Debug *ar)
Definition: luafaudes.cpp:55
static int pushline(lua_State *L, int firstline)
Definition: luafaudes.cpp:297
static int traceback(lua_State *L)
Definition: luafaudes.cpp:105
static int runargs(lua_State *L, char **argv, int n)
Definition: luafaudes.cpp:428
static int dofile(lua_State *L, const char *name)
Definition: luafaudes.cpp:173
static int pmain(lua_State *L)
Definition: luafaudes.cpp:484
static int collectargs(char **argv, int *pi, int *pv, int *pe, int *pd, int *px)
Definition: luafaudes.cpp:383
#define faudes_stdin_is_tty()
Definition: luafaudes.cpp:47
static void laction(int i)
Definition: luafaudes.cpp:62
static void l_message(const char *pname, const char *msg)
Definition: luafaudes.cpp:87
static int dolibrary(lua_State *L, const char *name)
Definition: luafaudes.cpp:185
#define notail(x)
Definition: luafaudes.cpp:379
static const char * progname
Definition: luafaudes.cpp:52
static int loadline(lua_State *L)
Definition: luafaudes.cpp:316
static int dostring(lua_State *L, const char *s, const char *name)
Definition: luafaudes.cpp:179
static int handle_script(lua_State *L, char **argv, int n)
Definition: luafaudes.cpp:360
static void dotty(lua_State *L)
Definition: luafaudes.cpp:336
static lua_State * globalL
Definition: luafaudes.cpp:51
static const char * get_prompt(lua_State *L, int firstline)
Definition: luafaudes.cpp:273
static int incomplete(lua_State *L, int status)
Definition: luafaudes.cpp:283
std::string VersionString()
Definition: cfl_utils.cpp:141
std::string PluginsString()
Definition: cfl_utils.cpp:146
int faudes_loadext(lua_State *pL, const char *filename)
Definition: lbp_addons.cpp:130
std::string BuildString()
Definition: cfl_utils.cpp:160
void faudes_initialize(lua_State *pL)
Definition: lbp_addons.cpp:125
char ** faudes_complete(lua_State *pL, const char *text, int start, int end)
Definition: lbp_addons.cpp:153
int faudes_loaddefext(lua_State *pL, const char *arg0)
Definition: lbp_addons.cpp:141
void Verbosity(int v)
Definition: cfl_utils.cpp:490
char ** argv
Definition: luafaudes.cpp:479
int argc
Definition: luafaudes.cpp:478
int status
Definition: luafaudes.cpp:480

libFAUDES 2.33h --- 2025.06.18 --- c++ api documentaion by doxygen