NeoMutt  2019-12-07
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/mutt.h"
46 #include "config/lib.h"
47 #include "mutt.h"
48 #include "mutt_lua.h"
49 #include "globals.h"
50 #include "mutt_commands.h"
51 #include "muttlib.h"
52 #include "myvar.h"
53 
59 static int handle_panic(lua_State *l)
60 {
61  mutt_debug(LL_DEBUG1, "lua runtime panic: %s\n", lua_tostring(l, -1));
62  mutt_error("Lua runtime panic: %s", lua_tostring(l, -1));
63  lua_pop(l, 1);
64  return -1;
65 }
66 
72 static int handle_error(lua_State *l)
73 {
74  mutt_debug(LL_DEBUG1, "lua runtime error: %s\n", lua_tostring(l, -1));
75  mutt_error("Lua runtime error: %s", lua_tostring(l, -1));
76  lua_pop(l, 1);
77  return -1;
78 }
79 
86 static int lua_mutt_call(lua_State *l)
87 {
88  mutt_debug(LL_DEBUG2, " * lua_mutt_call()\n");
89  struct Buffer *err = mutt_buffer_pool_get();
90  struct Buffer *token = mutt_buffer_pool_get();
91  char buf[1024] = { 0 };
92  const struct Command *cmd = NULL;
93  int rc = 0;
94 
95  if (lua_gettop(l) == 0)
96  {
97  luaL_error(l, "Error command argument required.");
98  return -1;
99  }
100 
101  cmd = mutt_command_get(lua_tostring(l, 1));
102  if (!cmd)
103  {
104  luaL_error(l, "Error command %s not found.", lua_tostring(l, 1));
105  return -1;
106  }
107 
108  for (int i = 2; i <= lua_gettop(l); i++)
109  {
110  const char *s = lua_tostring(l, i);
111  mutt_str_strncat(buf, sizeof(buf), s, mutt_str_strlen(s));
112  mutt_str_strncat(buf, sizeof(buf), " ", 1);
113  }
114 
115  struct Buffer expn = mutt_buffer_make(0);
116  expn.data = buf;
117  expn.dptr = buf;
118  expn.dsize = mutt_str_strlen(buf);
119 
120  if (cmd->func(token, &expn, cmd->data, err))
121  {
122  luaL_error(l, "NeoMutt error: %s", mutt_b2s(err));
123  rc = -1;
124  }
125  else
126  {
127  if (!lua_pushstring(l, mutt_b2s(err)))
128  handle_error(l);
129  else
130  rc++;
131  }
132 
133  mutt_buffer_pool_release(&token);
135  return rc;
136 }
137 
144 static int lua_mutt_set(lua_State *l)
145 {
146  const char *param = lua_tostring(l, -2);
147  mutt_debug(LL_DEBUG2, " * lua_mutt_set(%s)\n", param);
148 
149  if (mutt_str_startswith(param, "my_", CASE_MATCH))
150  {
151  const char *val = lua_tostring(l, -1);
152  myvar_set(param, val);
153  return 0;
154  }
155 
156  struct HashElem *he = cs_get_elem(Config, param);
157  if (!he)
158  {
159  luaL_error(l, "NeoMutt parameter not found %s", param);
160  return -1;
161  }
162 
163  struct ConfigDef *cdef = he->data;
164 
165  int rc = 0;
166  struct Buffer err = mutt_buffer_make(256);
167 
168  switch (DTYPE(cdef->type))
169  {
170  case DT_ADDRESS:
171  case DT_ENUM:
172  case DT_MBTABLE:
173  case DT_REGEX:
174  case DT_SLIST:
175  case DT_SORT:
176  case DT_STRING:
177  {
178  const char *value = lua_tostring(l, -1);
179  size_t val_size = lua_strlen(l, -1);
180  struct Buffer value_buf = mutt_buffer_make(val_size);
181  mutt_buffer_strcpy_n(&value_buf, value, val_size);
182  if (IS_PATH(he))
183  mutt_buffer_expand_path(&value_buf);
184 
185  int rv = cs_he_string_set(Config, he, value_buf.data, &err);
186  mutt_buffer_dealloc(&value_buf);
187  if (CSR_RESULT(rv) != CSR_SUCCESS)
188  rc = -1;
189  break;
190  }
191  case DT_NUMBER:
192  case DT_QUAD:
193  {
194  const intptr_t value = lua_tointeger(l, -1);
195  int rv = cs_he_native_set(Config, he, value, &err);
196  if (CSR_RESULT(rv) != CSR_SUCCESS)
197  rc = -1;
198  break;
199  }
200  case DT_BOOL:
201  {
202  const intptr_t value = lua_toboolean(l, -1);
203  int rv = cs_he_native_set(Config, he, value, &err);
204  if (CSR_RESULT(rv) != CSR_SUCCESS)
205  rc = -1;
206  break;
207  }
208  default:
209  luaL_error(l, "Unsupported NeoMutt parameter type %d for %s", DTYPE(cdef->type), param);
210  rc = -1;
211  break;
212  }
213 
214  mutt_buffer_dealloc(&err);
215  return rc;
216 }
217 
224 static int lua_mutt_get(lua_State *l)
225 {
226  const char *param = lua_tostring(l, -1);
227  mutt_debug(LL_DEBUG2, " * lua_mutt_get(%s)\n", param);
228 
229  if (mutt_str_startswith(param, "my_", CASE_MATCH))
230  {
231  const char *mv = myvar_get(param);
232  if (!mv)
233  {
234  luaL_error(l, "NeoMutt parameter not found %s", param);
235  return -1;
236  }
237 
238  lua_pushstring(l, mv);
239  return 1;
240  }
241 
242  struct HashElem *he = cs_get_elem(Config, param);
243  if (!he)
244  {
245  mutt_debug(LL_DEBUG2, " * error\n");
246  luaL_error(l, "NeoMutt parameter not found %s", param);
247  return -1;
248  }
249 
250  struct ConfigDef *cdef = he->data;
251 
252  switch (DTYPE(cdef->type))
253  {
254  case DT_ADDRESS:
255  case DT_ENUM:
256  case DT_MBTABLE:
257  case DT_REGEX:
258  case DT_SLIST:
259  case DT_SORT:
260  case DT_STRING:
261  {
262  struct Buffer value = mutt_buffer_make(256);
263  int rc = cs_he_string_get(Config, he, &value);
264  if (CSR_RESULT(rc) != CSR_SUCCESS)
265  {
266  mutt_buffer_dealloc(&value);
267  return -1;
268  }
269 
270  struct Buffer escaped = mutt_buffer_make(256);
271  escape_string(&escaped, value.data);
272  lua_pushstring(l, escaped.data);
273  mutt_buffer_dealloc(&value);
274  mutt_buffer_dealloc(&escaped);
275  return 1;
276  }
277  case DT_QUAD:
278  lua_pushinteger(l, *(unsigned char *) cdef->var);
279  return 1;
280  case DT_NUMBER:
281  lua_pushinteger(l, (signed short) *((unsigned long *) cdef->var));
282  return 1;
283  case DT_BOOL:
284  lua_pushboolean(l, *((bool *) cdef->var));
285  return 1;
286  default:
287  luaL_error(l, "NeoMutt parameter type %d unknown for %s", cdef->type, param);
288  return -1;
289  }
290 }
291 
298 static int lua_mutt_enter(lua_State *l)
299 {
300  mutt_debug(LL_DEBUG2, " * lua_mutt_enter()\n");
301  struct Buffer *err = mutt_buffer_pool_get();
302  struct Buffer *token = mutt_buffer_pool_get();
303  char *buf = mutt_str_strdup(lua_tostring(l, -1));
304  int rc = 0;
305 
306  if (mutt_parse_rc_line(buf, token, err))
307  {
308  luaL_error(l, "NeoMutt error: %s", mutt_b2s(err));
309  rc = -1;
310  }
311  else
312  {
313  if (!lua_pushstring(l, mutt_b2s(err)))
314  handle_error(l);
315  else
316  rc++;
317  }
318 
319  FREE(&buf);
320  mutt_buffer_pool_release(&token);
322 
323  return rc;
324 }
325 
331 static int lua_mutt_message(lua_State *l)
332 {
333  mutt_debug(LL_DEBUG2, " * lua_mutt_message()\n");
334  const char *msg = lua_tostring(l, -1);
335  if (msg)
336  mutt_message(msg);
337  return 0;
338 }
339 
345 static int lua_mutt_error(lua_State *l)
346 {
347  mutt_debug(LL_DEBUG2, " * lua_mutt_error()\n");
348  const char *msg = lua_tostring(l, -1);
349  if (msg)
350  mutt_error(msg);
351  return 0;
352 }
353 
359 static void lua_expose_command(void *p, const struct Command *cmd)
360 {
361  lua_State *l = (lua_State *) p;
362  char buf[1024];
363  snprintf(buf, sizeof(buf), "mutt.command.%s = function (...); mutt.call('%s', ...); end",
364  cmd->name, cmd->name);
365  (void) luaL_dostring(l, buf);
366 }
367 
368 lua_State *LuaState = NULL;
369 
370 static const luaL_Reg luaMuttDecl[] = {
371  { "set", lua_mutt_set }, { "get", lua_mutt_get },
372  { "call", lua_mutt_call }, { "enter", lua_mutt_enter },
373  { "print", lua_mutt_message }, { "message", lua_mutt_message },
374  { "error", lua_mutt_error }, { NULL, NULL },
375 };
376 
377 #define lua_add_lib_member(LUA, TABLE, KEY, VALUE, DATATYPE_HANDLER) \
378  lua_pushstring(LUA, KEY); \
379  DATATYPE_HANDLER(LUA, VALUE); \
380  lua_settable(LUA, TABLE);
381 
387 static int luaopen_mutt_decl(lua_State *l)
388 {
389  mutt_debug(LL_DEBUG2, " * luaopen_mutt()\n");
390  luaL_newlib(l, luaMuttDecl);
391  int lib_idx = lua_gettop(l);
392  /* table_idx, key value, value's type */
393  lua_add_lib_member(l, lib_idx, "VERSION", mutt_make_version(), lua_pushstring);
394  lua_add_lib_member(l, lib_idx, "QUAD_YES", MUTT_YES, lua_pushinteger);
395  lua_add_lib_member(l, lib_idx, "QUAD_NO", MUTT_NO, lua_pushinteger);
396  lua_add_lib_member(l, lib_idx, "QUAD_ASKYES", MUTT_ASKYES, lua_pushinteger);
397  lua_add_lib_member(l, lib_idx, "QUAD_ASKNO", MUTT_ASKNO, lua_pushinteger);
398  return 1;
399 }
400 
405 static void luaopen_mutt(lua_State *l)
406 {
407  luaL_requiref(l, "mutt", luaopen_mutt_decl, 1);
408  (void) luaL_dostring(l, "mutt.command = {}");
410 }
411 
417 static bool lua_init(lua_State **l)
418 {
419  if (!l)
420  return false;
421  if (*l)
422  return true;
423 
424  mutt_debug(LL_DEBUG2, " * lua_init()\n");
425  *l = luaL_newstate();
426 
427  if (!*l)
428  {
429  mutt_error(_("Error: Couldn't load the lua interpreter"));
430  return false;
431  }
432 
433  lua_atpanic(*l, handle_panic);
434 
435  /* load various Lua libraries */
436  luaL_openlibs(*l);
437  luaopen_mutt(*l);
438 
439  return true;
440 }
441 
445 enum CommandResult mutt_lua_parse(struct Buffer *buf, struct Buffer *s,
446  unsigned long data, struct Buffer *err)
447 {
448  lua_init(&LuaState);
449  mutt_debug(LL_DEBUG2, " * mutt_lua_parse(%s)\n", buf->data);
450 
451  if (luaL_dostring(LuaState, s->dptr))
452  {
453  mutt_debug(LL_DEBUG2, " * %s -> failure\n", s->dptr);
454  mutt_buffer_printf(err, "%s: %s", s->dptr, lua_tostring(LuaState, -1));
455  /* pop error message from the stack */
456  lua_pop(LuaState, 1);
457  return MUTT_CMD_ERROR;
458  }
459  mutt_debug(LL_DEBUG2, " * %s -> success\n", s->dptr);
460  mutt_buffer_reset(s); // Clear the rest of the line
461  return MUTT_CMD_SUCCESS;
462 }
463 
467 enum CommandResult mutt_lua_source_file(struct Buffer *buf, struct Buffer *s,
468  unsigned long data, struct Buffer *err)
469 {
470  mutt_debug(LL_DEBUG2, " * mutt_lua_source()\n");
471 
472  lua_init(&LuaState);
473 
474  char path[PATH_MAX];
475 
476  if (mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS) != 0)
477  {
478  mutt_buffer_printf(err, _("source: error at %s"), s->dptr);
479  return MUTT_CMD_ERROR;
480  }
481  if (MoreArgs(s))
482  {
483  mutt_buffer_printf(err, _("%s: too many arguments"), "source");
484  return MUTT_CMD_WARNING;
485  }
486  mutt_str_strfcpy(path, buf->data, sizeof(path));
487  mutt_expand_path(path, sizeof(path));
488 
489  if (luaL_dofile(LuaState, path))
490  {
491  mutt_error(_("Couldn't source lua source: %s"), lua_tostring(LuaState, -1));
492  lua_pop(LuaState, 1);
493  return MUTT_CMD_ERROR;
494  }
495  return MUTT_CMD_SUCCESS;
496 }
#define lua_add_lib_member(LUA, TABLE, KEY, VALUE, DATATYPE_HANDLER)
Definition: mutt_lua.c:377
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:58
#define IS_PATH(x)
Definition: types.h:55
CommandResult
Error codes for command_t parse functions.
Definition: mutt_commands.h:33
#define CSR_RESULT(x)
Definition: set.h:62
Error: Can&#39;t help the user.
Definition: mutt_commands.h:35
#define mutt_message(...)
Definition: logging.h:83
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
#define DT_REGEX
regular expressions
Definition: types.h:37
static int handle_panic(lua_State *l)
Handle a panic in the Lua interpreter.
Definition: mutt_lua.c:59
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
command_t func
Function to parse the command.
Definition: mutt_commands.h:57
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
#define DT_SORT
sorting methods
Definition: types.h:39
#define _(a)
Definition: message.h:28
WHERE struct ConfigSet * Config
Wrapper around the user&#39;s config settings.
Definition: globals.h:40
A user-callable command.
Definition: mutt_commands.h:54
const char * name
Name of the command.
Definition: mutt_commands.h:56
#define DT_SLIST
a list of strings
Definition: types.h:38
Match case when comparing strings.
Definition: string2.h:67
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
#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:144
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:153
#define DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:36
char * mutt_str_strncat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:424
const struct Command * mutt_command_get(const char *s)
Get a Command by its name.
Definition: init.c:2643
#define DTYPE(x)
Mask for the Data Type.
Definition: types.h:43
lua_State * LuaState
Definition: mutt_lua.c:368
Integrated Lua scripting.
Convenience wrapper for the config headers.
static int lua_mutt_enter(lua_State *l)
Execute NeoMutt config from Lua.
Definition: mutt_lua.c:298
Hundreds of global variables to back the user variables.
Ask the user, defaulting to &#39;Yes&#39;.
Definition: quad.h:41
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:134
#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:72
static bool lua_init(lua_State **l)
Initialise a Lua State.
Definition: mutt_lua.c:417
Log at debug level 2.
Definition: logging.h:41
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
int cs_he_native_set(const struct ConfigSet *cs, struct HashElem *he, intptr_t value, struct Buffer *err)
Natively set the value of a HashElem config item.
Definition: set.c:721
struct HashElem * cs_get_elem(const struct ConfigSet *cs, const char *name)
Get the HashElem representing a config item.
Definition: set.c:216
enum CommandResult mutt_lua_parse(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;lua&#39; command - Implements command_t.
Definition: mutt_lua.c:445
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:46
#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:40
enum CommandResult mutt_lua_source_file(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err)
Parse the &#39;lua-source&#39; command - Implements command_t.
Definition: mutt_lua.c:467
#define PATH_MAX
Definition: mutt.h:50
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:2674
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
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
static int lua_mutt_message(lua_State *l)
Display a message in Neomutt.
Definition: mutt_lua.c:331
Mapping from user command name to function.
static int lua_mutt_call(lua_State *l)
Call a NeoMutt command by name.
Definition: mutt_lua.c:86
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:387
void mutt_commands_apply(void *data, void(*application)(void *, const struct Command *))
const char * myvar_get(const char *var)
Get the value of a "my_" variable.
Definition: myvar.c:73
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
int cs_he_string_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result)
Get a config item as a string.
Definition: set.c:650
void * data
Definition: hash.h:46
void * var
Pointer to the global variable.
Definition: set.h:157
unsigned int type
Variable type, e.g. DT_STRING.
Definition: set.h:156
static int lua_mutt_error(lua_State *l)
Display an error in Neomutt.
Definition: mutt_lua.c:345
Success: Command worked.
Definition: mutt_commands.h:37
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:343
Log at debug level 1.
Definition: logging.h:40
Warning: Help given to the user.
Definition: mutt_commands.h:36
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
Ask the user, defaulting to &#39;No&#39;.
Definition: quad.h:40
#define mutt_error(...)
Definition: logging.h:84
static const luaL_Reg luaMuttDecl[]
Definition: mutt_lua.c:370
#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:359
#define FREE(x)
Definition: memory.h:40
static int lua_mutt_get(lua_State *l)
Get a NeoMutt variable.
Definition: mutt_lua.c:224
size_t escape_string(struct Buffer *buf, const char *src)
Write a string to a buffer, escaping special characters.
Definition: dump.c:46
The item stored in a Hash Table.
Definition: hash.h:42
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
static void luaopen_mutt(lua_State *l)
Expose a &#39;Mutt&#39; object to the Lua interpreter.
Definition: mutt_lua.c:405
Handling of personal config (&#39;my&#39; variables)
Convenience wrapper for the library headers.
#define DT_NUMBER
a number
Definition: types.h:35
#define DT_BOOL
boolean option
Definition: types.h:30
enum CommandResult mutt_parse_rc_line(char *line, struct Buffer *token, struct Buffer *err)
Parse a line of user config.
Definition: init.c:3251
int cs_he_string_set(const struct ConfigSet *cs, struct HashElem *he, const char *value, struct Buffer *err)
Set a config item by string.
Definition: set.c:576
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:76
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1560