NeoMutt  2020-11-20
Teaching an old dog new tricks
DOXYGEN
mutt_lua.c
Go to the documentation of this file.
1 
30 #ifndef LUA_COMPAT_ALL
31 #define LUA_COMPAT_ALL
32 #endif
33 #ifndef LUA_COMPAT_5_1
34 #define LUA_COMPAT_5_1
35 #endif
36 
37 #include "config.h"
38 #include <lauxlib.h>
39 #include <limits.h>
40 #include <lua.h>
41 #include <lualib.h>
42 #include <stdbool.h>
43 #include <stdint.h>
44 #include <stdio.h>
45 #include "mutt/lib.h"
46 #include "config/lib.h"
47 #include "core/lib.h"
48 #include "mutt.h"
49 #include "mutt_lua.h"
50 #include "init.h"
51 #include "mutt_commands.h"
52 #include "muttlib.h"
53 #include "myvar.h"
54 
55 static const struct Command lua_commands[] = {
56  // clang-format off
57  { "lua", mutt_lua_parse, 0 },
58  { "lua-source", mutt_lua_source_file, 0 },
59  // clang-format on
60 };
61 
67 static int handle_panic(lua_State *l)
68 {
69  mutt_debug(LL_DEBUG1, "lua runtime panic: %s\n", lua_tostring(l, -1));
70  mutt_error("Lua runtime panic: %s", lua_tostring(l, -1));
71  lua_pop(l, 1);
72  return -1;
73 }
74 
80 static int handle_error(lua_State *l)
81 {
82  mutt_debug(LL_DEBUG1, "lua runtime error: %s\n", lua_tostring(l, -1));
83  mutt_error("Lua runtime error: %s", lua_tostring(l, -1));
84  lua_pop(l, 1);
85  return -1;
86 }
87 
94 static int lua_mutt_call(lua_State *l)
95 {
96  mutt_debug(LL_DEBUG2, " * lua_mutt_call()\n");
97  struct Buffer *err = mutt_buffer_pool_get();
98  struct Buffer *token = mutt_buffer_pool_get();
99  char buf[1024] = { 0 };
100  const struct Command *cmd = NULL;
101  int rc = 0;
102 
103  if (lua_gettop(l) == 0)
104  {
105  luaL_error(l, "Error command argument required.");
106  return -1;
107  }
108 
109  cmd = mutt_command_get(lua_tostring(l, 1));
110  if (!cmd)
111  {
112  luaL_error(l, "Error command %s not found.", lua_tostring(l, 1));
113  return -1;
114  }
115 
116  for (int i = 2; i <= lua_gettop(l); i++)
117  {
118  const char *s = lua_tostring(l, i);
119  mutt_strn_cat(buf, sizeof(buf), s, mutt_str_len(s));
120  mutt_strn_cat(buf, sizeof(buf), " ", 1);
121  }
122 
123  struct Buffer expn = mutt_buffer_make(0);
124  expn.data = buf;
125  expn.dptr = buf;
126  expn.dsize = mutt_str_len(buf);
127 
128  if (cmd->parse(token, &expn, cmd->data, err))
129  {
130  luaL_error(l, "NeoMutt error: %s", mutt_b2s(err));
131  rc = -1;
132  }
133  else
134  {
135  if (!lua_pushstring(l, mutt_b2s(err)))
136  handle_error(l);
137  else
138  rc++;
139  }
140 
141  mutt_buffer_pool_release(&token);
143  return rc;
144 }
145 
152 static int lua_mutt_set(lua_State *l)
153 {
154  const char *param = lua_tostring(l, -2);
155  mutt_debug(LL_DEBUG2, " * lua_mutt_set(%s)\n", param);
156 
157  if (mutt_str_startswith(param, "my_"))
158  {
159  const char *val = lua_tostring(l, -1);
160  myvar_set(param, val);
161  return 0;
162  }
163 
164  struct HashElem *he = cs_subset_lookup(NeoMutt->sub, param);
165  if (!he)
166  {
167  luaL_error(l, "NeoMutt parameter not found %s", param);
168  return -1;
169  }
170 
171  struct ConfigDef *cdef = he->data;
172 
173  int rc = 0;
174  struct Buffer err = mutt_buffer_make(256);
175 
176  switch (DTYPE(cdef->type))
177  {
178  case DT_ADDRESS:
179  case DT_ENUM:
180  case DT_MBTABLE:
181  case DT_PATH:
182  case DT_REGEX:
183  case DT_SLIST:
184  case DT_SORT:
185  case DT_STRING:
186  {
187  const char *value = lua_tostring(l, -1);
188  size_t val_size = lua_rawlen(l, -1);
189  struct Buffer value_buf = mutt_buffer_make(val_size);
190  mutt_buffer_strcpy_n(&value_buf, value, val_size);
191  if (DTYPE(he->type) == DT_PATH)
192  mutt_buffer_expand_path(&value_buf);
193 
194  int rv = cs_subset_he_string_set(NeoMutt->sub, he, value_buf.data, &err);
195  mutt_buffer_dealloc(&value_buf);
196  if (CSR_RESULT(rv) != CSR_SUCCESS)
197  rc = -1;
198  break;
199  }
200  case DT_NUMBER:
201  case DT_QUAD:
202  {
203  const intptr_t value = lua_tointeger(l, -1);
204  int rv = cs_subset_he_native_set(NeoMutt->sub, he, value, &err);
205  if (CSR_RESULT(rv) != CSR_SUCCESS)
206  rc = -1;
207  break;
208  }
209  case DT_BOOL:
210  {
211  const intptr_t value = lua_toboolean(l, -1);
212  int rv = cs_subset_he_native_set(NeoMutt->sub, he, value, &err);
213  if (CSR_RESULT(rv) != CSR_SUCCESS)
214  rc = -1;
215  break;
216  }
217  default:
218  luaL_error(l, "Unsupported NeoMutt parameter type %d for %s", DTYPE(cdef->type), param);
219  rc = -1;
220  break;
221  }
222 
223  mutt_buffer_dealloc(&err);
224  return rc;
225 }
226 
233 static int lua_mutt_get(lua_State *l)
234 {
235  const char *param = lua_tostring(l, -1);
236  mutt_debug(LL_DEBUG2, " * lua_mutt_get(%s)\n", param);
237 
238  if (mutt_str_startswith(param, "my_"))
239  {
240  const char *mv = myvar_get(param);
241  if (!mv)
242  {
243  luaL_error(l, "NeoMutt parameter not found %s", param);
244  return -1;
245  }
246 
247  lua_pushstring(l, mv);
248  return 1;
249  }
250 
251  struct HashElem *he = cs_subset_lookup(NeoMutt->sub, param);
252  if (!he)
253  {
254  mutt_debug(LL_DEBUG2, " * error\n");
255  luaL_error(l, "NeoMutt parameter not found %s", param);
256  return -1;
257  }
258 
259  struct ConfigDef *cdef = he->data;
260 
261  switch (DTYPE(cdef->type))
262  {
263  case DT_ADDRESS:
264  case DT_ENUM:
265  case DT_MBTABLE:
266  case DT_REGEX:
267  case DT_SLIST:
268  case DT_SORT:
269  case DT_STRING:
270  {
271  struct Buffer value = mutt_buffer_make(256);
272  int rc = cs_subset_he_string_get(NeoMutt->sub, he, &value);
273  if (CSR_RESULT(rc) != CSR_SUCCESS)
274  {
275  mutt_buffer_dealloc(&value);
276  return -1;
277  }
278 
279  struct Buffer escaped = mutt_buffer_make(256);
280  escape_string(&escaped, value.data);
281  lua_pushstring(l, escaped.data);
282  mutt_buffer_dealloc(&value);
283  mutt_buffer_dealloc(&escaped);
284  return 1;
285  }
286  case DT_QUAD:
287  lua_pushinteger(l, *(unsigned char *) cdef->var);
288  return 1;
289  case DT_NUMBER:
290  lua_pushinteger(l, (signed short) *((unsigned long *) cdef->var));
291  return 1;
292  case DT_BOOL:
293  lua_pushboolean(l, *((bool *) cdef->var));
294  return 1;
295  default:
296  luaL_error(l, "NeoMutt parameter type %d unknown for %s", cdef->type, param);
297  return -1;
298  }
299 }
300 
307 static int lua_mutt_enter(lua_State *l)
308 {
309  mutt_debug(LL_DEBUG2, " * lua_mutt_enter()\n");
310  struct Buffer *err = mutt_buffer_pool_get();
311  char *buf = mutt_str_dup(lua_tostring(l, -1));
312  int rc = 0;
313 
314  if (mutt_parse_rc_line(buf, err))
315  {
316  luaL_error(l, "NeoMutt error: %s", mutt_b2s(err));
317  rc = -1;
318  }
319  else
320  {
321  if (!lua_pushstring(l, mutt_b2s(err)))
322  handle_error(l);
323  else
324  rc++;
325  }
326 
327  FREE(&buf);
329 
330  return rc;
331 }
332 
338 static int lua_mutt_message(lua_State *l)
339 {
340  mutt_debug(LL_DEBUG2, " * lua_mutt_message()\n");
341  const char *msg = lua_tostring(l, -1);
342  if (msg)
343  mutt_message(msg);
344  return 0;
345 }
346 
352 static int lua_mutt_error(lua_State *l)
353 {
354  mutt_debug(LL_DEBUG2, " * lua_mutt_error()\n");
355  const char *msg = lua_tostring(l, -1);
356  if (msg)
357  mutt_error(msg);
358  return 0;
359 }
360 
366 static void lua_expose_command(void *p, const struct Command *cmd)
367 {
368  lua_State *l = (lua_State *) p;
369  char buf[1024];
370  snprintf(buf, sizeof(buf), "mutt.command.%s = function (...); mutt.call('%s', ...); end",
371  cmd->name, cmd->name);
372  (void) luaL_dostring(l, buf);
373 }
374 
375 lua_State *LuaState = NULL;
376 
377 static const luaL_Reg luaMuttDecl[] = {
378  { "set", lua_mutt_set }, { "get", lua_mutt_get },
379  { "call", lua_mutt_call }, { "enter", lua_mutt_enter },
380  { "print", lua_mutt_message }, { "message", lua_mutt_message },
381  { "error", lua_mutt_error }, { NULL, NULL },
382 };
383 
384 #define lua_add_lib_member(LUA, TABLE, KEY, VALUE, DATATYPE_HANDLER) \
385  lua_pushstring(LUA, KEY); \
386  DATATYPE_HANDLER(LUA, VALUE); \
387  lua_settable(LUA, TABLE);
388 
394 static int luaopen_mutt_decl(lua_State *l)
395 {
396  mutt_debug(LL_DEBUG2, " * luaopen_mutt()\n");
397  luaL_newlib(l, luaMuttDecl);
398  int lib_idx = lua_gettop(l);
399  /* table_idx, key value, value's type */
400  lua_add_lib_member(l, lib_idx, "VERSION", mutt_make_version(), lua_pushstring);
401  lua_add_lib_member(l, lib_idx, "QUAD_YES", MUTT_YES, lua_pushinteger);
402  lua_add_lib_member(l, lib_idx, "QUAD_NO", MUTT_NO, lua_pushinteger);
403  lua_add_lib_member(l, lib_idx, "QUAD_ASKYES", MUTT_ASKYES, lua_pushinteger);
404  lua_add_lib_member(l, lib_idx, "QUAD_ASKNO", MUTT_ASKNO, lua_pushinteger);
405  return 1;
406 }
407 
412 static void luaopen_mutt(lua_State *l)
413 {
414  luaL_requiref(l, "mutt", luaopen_mutt_decl, 1);
415  (void) luaL_dostring(l, "mutt.command = {}");
417 }
418 
424 static bool lua_init(lua_State **l)
425 {
426  if (!l)
427  return false;
428  if (*l)
429  return true;
430 
431  mutt_debug(LL_DEBUG2, " * lua_init()\n");
432  *l = luaL_newstate();
433 
434  if (!*l)
435  {
436  mutt_error(_("Error: Couldn't load the lua interpreter"));
437  return false;
438  }
439 
440  lua_atpanic(*l, handle_panic);
441 
442  /* load various Lua libraries */
443  luaL_openlibs(*l);
444  luaopen_mutt(*l);
445 
446  return true;
447 }
448 
452 void mutt_lua_init(void)
453 {
454  COMMANDS_REGISTER(lua_commands);
455 }
456 
460 enum CommandResult mutt_lua_parse(struct Buffer *buf, struct Buffer *s,
461  intptr_t data, struct Buffer *err)
462 {
463  lua_init(&LuaState);
464  mutt_debug(LL_DEBUG2, " * mutt_lua_parse(%s)\n", buf->data);
465 
466  if (luaL_dostring(LuaState, s->dptr))
467  {
468  mutt_debug(LL_DEBUG2, " * %s -> failure\n", s->dptr);
469  mutt_buffer_printf(err, "%s: %s", s->dptr, lua_tostring(LuaState, -1));
470  /* pop error message from the stack */
471  lua_pop(LuaState, 1);
472  return MUTT_CMD_ERROR;
473  }
474  mutt_debug(LL_DEBUG2, " * %s -> success\n", s->dptr);
475  mutt_buffer_reset(s); // Clear the rest of the line
476  return MUTT_CMD_SUCCESS;
477 }
478 
482 enum CommandResult mutt_lua_source_file(struct Buffer *buf, struct Buffer *s,
483  intptr_t data, struct Buffer *err)
484 {
485  mutt_debug(LL_DEBUG2, " * mutt_lua_source()\n");
486 
487  lua_init(&LuaState);
488 
489  char path[PATH_MAX];
490 
491  if (mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS) != 0)
492  {
493  mutt_buffer_printf(err, _("source: error at %s"), s->dptr);
494  return MUTT_CMD_ERROR;
495  }
496  if (MoreArgs(s))
497  {
498  mutt_buffer_printf(err, _("%s: too many arguments"), "source");
499  return MUTT_CMD_WARNING;
500  }
501  mutt_str_copy(path, buf->data, sizeof(path));
502  mutt_expand_path(path, sizeof(path));
503 
504  if (luaL_dofile(LuaState, path))
505  {
506  mutt_error(_("Couldn't source lua source: %s"), lua_tostring(LuaState, -1));
507  lua_pop(LuaState, 1);
508  return MUTT_CMD_ERROR;
509  }
510  return MUTT_CMD_SUCCESS;
511 }
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
#define lua_add_lib_member(LUA, TABLE, KEY, VALUE, DATATYPE_HANDLER)
Definition: mutt_lua.c:384
Ask the user, defaulting to &#39;No&#39;.
Definition: quad.h:41
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
intptr_t data
Data or flags to pass to the command.
Definition: mutt_commands.h:59
CommandResult
Error codes for command_t parse functions.
Definition: mutt_commands.h:34
Config/command parsing.
Error: Can&#39;t help the user.
Definition: mutt_commands.h:36
void mutt_lua_init(void)
Setup feature commands.
Definition: mutt_lua.c:452
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:414
#define mutt_message(...)
Definition: logging.h:83
#define DT_REGEX
regular expressions
Definition: types.h:38
static int handle_panic(lua_State *l)
Handle a panic in the Lua interpreter.
Definition: mutt_lua.c:67
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
String manipulation buffer.
Definition: buffer.h:33
enum CommandResult mutt_parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: init.c:1039
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define DT_SORT
sorting methods
Definition: types.h:40
#define _(a)
Definition: message.h:28
A user-callable command.
Definition: mutt_commands.h:45
const char * name
Name of the command.
Definition: mutt_commands.h:47
#define DT_SLIST
a list of strings
Definition: types.h:39
#define DT_MBTABLE
multibyte char table
Definition: types.h:34
static int lua_mutt_set(lua_State *l)
Set a NeoMutt variable.
Definition: mutt_lua.c:152
struct HashElem * cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
Find an inherited config item.
Definition: subset.c:168
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
Config item definition.
Definition: set.h:61
#define DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:37
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define DTYPE(x)
Mask for the Data Type.
Definition: types.h:44
lua_State * LuaState
Definition: mutt_lua.c:375
#define COMMANDS_REGISTER(cmds)
Definition: mutt_commands.h:77
Integrated Lua scripting.
Convenience wrapper for the config headers.
enum CommandResult(* parse)(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Function to parse a command.
Definition: mutt_commands.h:57
static int lua_mutt_enter(lua_State *l)
Execute NeoMutt config from Lua.
Definition: mutt_lua.c:307
#define CSR_RESULT(x)
Definition: set.h:52
int cs_subset_he_string_set(const struct ConfigSubset *sub, struct HashElem *he, const char *value, struct Buffer *err)
Set a config item by string.
Definition: subset.c:373
Some miscellaneous functions.
size_t dsize
Length of data.
Definition: buffer.h:37
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:128
#define MoreArgs(buf)
Definition: buffer.h:43
static int handle_error(lua_State *l)
Handle an error in the Lua interpreter.
Definition: mutt_lua.c:80
static bool lua_init(lua_State **l)
Initialise a Lua State.
Definition: mutt_lua.c:424
Many unsorted constants and some structs.
Log at debug level 2.
Definition: logging.h:41
int cs_subset_he_string_get(const struct ConfigSubset *sub, struct HashElem *he, struct Buffer *result)
Get a config item as a string.
Definition: subset.c:341
Convenience wrapper for the core headers.
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
void mutt_commands_apply(void *data, void(*application)(void *, const struct Command *))
Run a callback function on every Command.
#define DT_ADDRESS
e-mail address
Definition: types.h:29
#define mutt_b2s(buf)
Definition: buffer.h:41
#define DT_STRING
a string
Definition: types.h:41
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
#define DT_PATH
a path to a file/directory
Definition: types.h:36
#define PATH_MAX
Definition: mutt.h:44
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:394
size_t mutt_buffer_strcpy_n(struct Buffer *buf, const char *s, size_t len)
Copy a string into a Buffer.
Definition: buffer.c:327
char * dptr
Current read/write position.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:35
static int lua_mutt_message(lua_State *l)
Display a message in Neomutt.
Definition: mutt_lua.c:338
Definitions of NeoMutt commands.
enum CommandResult mutt_lua_parse(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the &#39;lua&#39; command - Implements Command::parse()
Definition: mutt_lua.c:460
Ask the user, defaulting to &#39;Yes&#39;.
Definition: quad.h:42
static int lua_mutt_call(lua_State *l)
Call a NeoMutt command by name.
Definition: mutt_lua.c:94
void myvar_set(const char *var, const char *val)
Set the value of a "my_" variable.
Definition: myvar.c:91
static int luaopen_mutt_decl(lua_State *l)
Declare some NeoMutt types to the Lua interpreter.
Definition: mutt_lua.c:394
struct Command * mutt_command_get(const char *s)
Get a Command by its name.
const char * myvar_get(const char *var)
Get the value of a "my_" variable.
Definition: myvar.c:73
void * data
User-supplied data.
Definition: hash.h:47
void * var
Pointer to the global variable.
Definition: set.h:65
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
unsigned int type
Variable type, e.g. DT_STRING.
Definition: set.h:64
static int lua_mutt_error(lua_State *l)
Display an error in Neomutt.
Definition: mutt_lua.c:352
Success: Command worked.
Definition: mutt_commands.h:38
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:323
Log at debug level 1.
Definition: logging.h:40
Warning: Help given to the user.
Definition: mutt_commands.h:37
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:716
#define mutt_error(...)
Definition: logging.h:84
size_t escape_string(struct Buffer *buf, const char *src)
Write a string to a buffer, escaping special characters.
Definition: dump.c:46
static const luaL_Reg luaMuttDecl[]
Definition: mutt_lua.c:377
#define DT_ENUM
an enumeration
Definition: types.h:31
static void lua_expose_command(void *p, const struct Command *cmd)
Expose a NeoMutt command to the Lua interpreter.
Definition: mutt_lua.c:366
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition: hash.h:45
#define FREE(x)
Definition: memory.h:40
static int lua_mutt_get(lua_State *l)
Get a NeoMutt variable.
Definition: mutt_lua.c:233
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
The item stored in a Hash Table.
Definition: hash.h:43
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
static void luaopen_mutt(lua_State *l)
Expose a &#39;Mutt&#39; object to the Lua interpreter.
Definition: mutt_lua.c:412
Convenience wrapper for the library headers.
Handling of personal config (&#39;my&#39; variables)
enum CommandResult mutt_lua_source_file(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the &#39;lua-source&#39; command - Implements Command::parse()
Definition: mutt_lua.c:482
#define DT_NUMBER
a number
Definition: types.h:35
#define DT_BOOL
boolean option
Definition: types.h:30
int cs_subset_he_native_set(const struct ConfigSubset *sub, struct HashElem *he, intptr_t value, struct Buffer *err)
Natively set the value of a HashElem config item.
Definition: subset.c:270
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:70
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1460
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40