NeoMutt  2022-04-29-249-gaae397
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
56lua_State *LuaState = NULL;
57
61static const struct Command LuaCommands[] = {
62 // clang-format off
63 { "lua", mutt_lua_parse, 0 },
64 { "lua-source", mutt_lua_source_file, 0 },
65 // clang-format on
66};
67
73static int handle_panic(lua_State *l)
74{
75 mutt_debug(LL_DEBUG1, "lua runtime panic: %s\n", lua_tostring(l, -1));
76 mutt_error("Lua runtime panic: %s", lua_tostring(l, -1));
77 lua_pop(l, 1);
78 return -1;
79}
80
86static int handle_error(lua_State *l)
87{
88 mutt_debug(LL_DEBUG1, "lua runtime error: %s\n", lua_tostring(l, -1));
89 mutt_error("Lua runtime error: %s", lua_tostring(l, -1));
90 lua_pop(l, 1);
91 return -1;
92}
93
100static int lua_mutt_call(lua_State *l)
101{
102 mutt_debug(LL_DEBUG2, " * lua_mutt_call()\n");
103 struct Buffer *err = mutt_buffer_pool_get();
104 struct Buffer *token = mutt_buffer_pool_get();
105 char buf[1024] = { 0 };
106 const struct Command *cmd = NULL;
107 int rc = 0;
108
109 if (lua_gettop(l) == 0)
110 {
111 luaL_error(l, "Error command argument required.");
112 return -1;
113 }
114
115 cmd = mutt_command_get(lua_tostring(l, 1));
116 if (!cmd)
117 {
118 luaL_error(l, "Error command %s not found.", lua_tostring(l, 1));
119 return -1;
120 }
121
122 for (int i = 2; i <= lua_gettop(l); i++)
123 {
124 const char *s = lua_tostring(l, i);
125 mutt_strn_cat(buf, sizeof(buf), s, mutt_str_len(s));
126 mutt_strn_cat(buf, sizeof(buf), " ", 1);
127 }
128
129 struct Buffer expn = mutt_buffer_make(0);
130 expn.data = buf;
131 expn.dptr = buf;
132 expn.dsize = mutt_str_len(buf);
133
134 if (cmd->parse(token, &expn, cmd->data, err))
135 {
136 luaL_error(l, "NeoMutt error: %s", mutt_buffer_string(err));
137 rc = -1;
138 }
139 else
140 {
141 if (!lua_pushstring(l, mutt_buffer_string(err)))
142 handle_error(l);
143 else
144 rc++;
145 }
146
149 return rc;
150}
151
158static int lua_mutt_set(lua_State *l)
159{
160 const char *param = lua_tostring(l, -2);
161 mutt_debug(LL_DEBUG2, " * lua_mutt_set(%s)\n", param);
162
163 if (mutt_str_startswith(param, "my_"))
164 {
165 const char *val = lua_tostring(l, -1);
166 myvar_set(param, val);
167 return 0;
168 }
169
170 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, param);
171 if (!he)
172 {
173 luaL_error(l, "NeoMutt parameter not found %s", param);
174 return -1;
175 }
176
177 struct ConfigDef *cdef = he->data;
178
179 int rc = 0;
180 struct Buffer err = mutt_buffer_make(256);
181
182 switch (DTYPE(cdef->type))
183 {
184 case DT_ADDRESS:
185 case DT_ENUM:
186 case DT_MBTABLE:
187 case DT_PATH:
188 case DT_REGEX:
189 case DT_SLIST:
190 case DT_SORT:
191 case DT_STRING:
192 {
193 const char *value = lua_tostring(l, -1);
194 size_t val_size = lua_rawlen(l, -1);
195 struct Buffer value_buf = mutt_buffer_make(val_size);
196 mutt_buffer_strcpy_n(&value_buf, value, val_size);
197 if (DTYPE(he->type) == DT_PATH)
198 mutt_buffer_expand_path(&value_buf);
199
200 int rv = cs_subset_he_string_set(NeoMutt->sub, he, value_buf.data, &err);
201 mutt_buffer_dealloc(&value_buf);
202 if (CSR_RESULT(rv) != CSR_SUCCESS)
203 rc = -1;
204 break;
205 }
206 case DT_NUMBER:
207 case DT_QUAD:
208 {
209 const intptr_t value = lua_tointeger(l, -1);
210 int rv = cs_subset_he_native_set(NeoMutt->sub, he, value, &err);
211 if (CSR_RESULT(rv) != CSR_SUCCESS)
212 rc = -1;
213 break;
214 }
215 case DT_BOOL:
216 {
217 const intptr_t value = lua_toboolean(l, -1);
218 int rv = cs_subset_he_native_set(NeoMutt->sub, he, value, &err);
219 if (CSR_RESULT(rv) != CSR_SUCCESS)
220 rc = -1;
221 break;
222 }
223 default:
224 luaL_error(l, "Unsupported NeoMutt parameter type %d for %s", DTYPE(cdef->type), param);
225 rc = -1;
226 break;
227 }
228
230 return rc;
231}
232
239static int lua_mutt_get(lua_State *l)
240{
241 const char *param = lua_tostring(l, -1);
242 mutt_debug(LL_DEBUG2, " * lua_mutt_get(%s)\n", param);
243
244 if (mutt_str_startswith(param, "my_"))
245 {
246 const char *mv = myvar_get(param);
247 if (!mv)
248 {
249 luaL_error(l, "NeoMutt parameter not found %s", param);
250 return -1;
251 }
252
253 lua_pushstring(l, mv);
254 return 1;
255 }
256
257 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, param);
258 if (!he)
259 {
260 mutt_debug(LL_DEBUG2, " * error\n");
261 luaL_error(l, "NeoMutt parameter not found %s", param);
262 return -1;
263 }
264
265 struct ConfigDef *cdef = he->data;
266
267 switch (DTYPE(cdef->type))
268 {
269 case DT_ADDRESS:
270 case DT_ENUM:
271 case DT_MBTABLE:
272 case DT_REGEX:
273 case DT_SLIST:
274 case DT_SORT:
275 case DT_STRING:
276 {
277 struct Buffer value = mutt_buffer_make(256);
278 int rc = cs_subset_he_string_get(NeoMutt->sub, he, &value);
279 if (CSR_RESULT(rc) != CSR_SUCCESS)
280 {
281 mutt_buffer_dealloc(&value);
282 return -1;
283 }
284
285 struct Buffer escaped = mutt_buffer_make(256);
286 escape_string(&escaped, value.data);
287 lua_pushstring(l, escaped.data);
288 mutt_buffer_dealloc(&value);
289 mutt_buffer_dealloc(&escaped);
290 return 1;
291 }
292 case DT_QUAD:
293 lua_pushinteger(l, (unsigned char) cdef->var);
294 return 1;
295 case DT_NUMBER:
296 lua_pushinteger(l, (signed short) cdef->var);
297 return 1;
298 case DT_BOOL:
299 lua_pushboolean(l, (bool) cdef->var);
300 return 1;
301 default:
302 luaL_error(l, "NeoMutt parameter type %d unknown for %s", cdef->type, param);
303 return -1;
304 }
305}
306
313static int lua_mutt_enter(lua_State *l)
314{
315 mutt_debug(LL_DEBUG2, " * lua_mutt_enter()\n");
316 struct Buffer *err = mutt_buffer_pool_get();
317 char *buf = mutt_str_dup(lua_tostring(l, -1));
318 int rc = 0;
319
320 if (mutt_parse_rc_line(buf, err))
321 {
322 luaL_error(l, "NeoMutt error: %s", mutt_buffer_string(err));
323 rc = -1;
324 }
325 else
326 {
327 if (!lua_pushstring(l, mutt_buffer_string(err)))
328 handle_error(l);
329 else
330 rc++;
331 }
332
333 FREE(&buf);
335
336 return rc;
337}
338
344static int lua_mutt_message(lua_State *l)
345{
346 mutt_debug(LL_DEBUG2, " * lua_mutt_message()\n");
347 const char *msg = lua_tostring(l, -1);
348 if (msg)
349 mutt_message(msg);
350 return 0;
351}
352
358static int lua_mutt_error(lua_State *l)
359{
360 mutt_debug(LL_DEBUG2, " * lua_mutt_error()\n");
361 const char *msg = lua_tostring(l, -1);
362 if (msg)
363 mutt_error(msg);
364 return 0;
365}
366
372static void lua_expose_command(void *p, const struct Command *cmd)
373{
374 lua_State *l = (lua_State *) p;
375 char buf[1024] = { 0 };
376 snprintf(buf, sizeof(buf), "mutt.command.%s = function (...); mutt.call('%s', ...); end",
377 cmd->name, cmd->name);
378 (void) luaL_dostring(l, buf);
379}
380
390static const luaL_Reg LuaMuttCommands[] = {
391 // clang-format off
392 { "set", lua_mutt_set },
393 { "get", lua_mutt_get },
394 { "call", lua_mutt_call },
395 { "enter", lua_mutt_enter },
396 { "print", lua_mutt_message },
397 { "message", lua_mutt_message },
398 { "error", lua_mutt_error },
399 { NULL, NULL },
400 // clang-format on
401};
402
408static int luaopen_mutt_decl(lua_State *l)
409{
410 mutt_debug(LL_DEBUG2, " * luaopen_mutt()\n");
411 luaL_newlib(l, LuaMuttCommands);
412 int lib_idx = lua_gettop(l);
413
414 // clang-format off
415 lua_pushstring(l, "VERSION"); lua_pushstring(l, mutt_make_version()); lua_settable(l, lib_idx);;
416 lua_pushstring(l, "QUAD_YES"); lua_pushinteger(l, MUTT_YES); lua_settable(l, lib_idx);;
417 lua_pushstring(l, "QUAD_NO"); lua_pushinteger(l, MUTT_NO); lua_settable(l, lib_idx);;
418 lua_pushstring(l, "QUAD_ASKYES"); lua_pushinteger(l, MUTT_ASKYES); lua_settable(l, lib_idx);;
419 lua_pushstring(l, "QUAD_ASKNO"); lua_pushinteger(l, MUTT_ASKNO); lua_settable(l, lib_idx);;
420 // clang-format on
421
422 return 1;
423}
424
429static void luaopen_mutt(lua_State *l)
430{
431 luaL_requiref(l, "mutt", luaopen_mutt_decl, 1);
432 (void) luaL_dostring(l, "mutt.command = {}");
434}
435
441static bool lua_init(lua_State **l)
442{
443 if (!l)
444 return false;
445 if (*l)
446 return true;
447
448 mutt_debug(LL_DEBUG2, " * lua_init()\n");
449 *l = luaL_newstate();
450
451 if (!*l)
452 {
453 mutt_error(_("Error: Couldn't load the lua interpreter"));
454 return false;
455 }
456
457 lua_atpanic(*l, handle_panic);
458
459 /* load various Lua libraries */
460 luaL_openlibs(*l);
461 luaopen_mutt(*l);
462
463 return true;
464}
465
470{
472}
473
477enum CommandResult mutt_lua_parse(struct Buffer *buf, struct Buffer *s,
478 intptr_t data, struct Buffer *err)
479{
481 mutt_debug(LL_DEBUG2, " * mutt_lua_parse(%s)\n", buf->data);
482
483 if (luaL_dostring(LuaState, s->dptr))
484 {
485 mutt_debug(LL_DEBUG2, " * %s -> failure\n", s->dptr);
486 mutt_buffer_printf(err, "%s: %s", s->dptr, lua_tostring(LuaState, -1));
487 /* pop error message from the stack */
488 lua_pop(LuaState, 1);
489 return MUTT_CMD_ERROR;
490 }
491 mutt_debug(LL_DEBUG2, " * %s -> success\n", s->dptr);
492 mutt_buffer_reset(s); // Clear the rest of the line
493 return MUTT_CMD_SUCCESS;
494}
495
500 intptr_t data, struct Buffer *err)
501{
502 mutt_debug(LL_DEBUG2, " * mutt_lua_source()\n");
503
505
506 char path[PATH_MAX] = { 0 };
507
508 if (mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS) != 0)
509 {
510 mutt_buffer_printf(err, _("source: error at %s"), s->dptr);
511 return MUTT_CMD_ERROR;
512 }
513 if (MoreArgs(s))
514 {
515 mutt_buffer_printf(err, _("%s: too many arguments"), "source");
516 return MUTT_CMD_WARNING;
517 }
518 mutt_str_copy(path, buf->data, sizeof(path));
519 mutt_expand_path(path, sizeof(path));
520
521 if (luaL_dofile(LuaState, path))
522 {
523 mutt_error(_("Couldn't source lua source: %s"), lua_tostring(LuaState, -1));
524 lua_pop(LuaState, 1);
525 return MUTT_CMD_ERROR;
526 }
527 return MUTT_CMD_SUCCESS;
528}
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:67
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:309
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
size_t mutt_buffer_strcpy_n(struct Buffer *buf, const char *s, size_t len)
Copy a string into a Buffer.
Definition: buffer.c:342
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
#define MoreArgs(buf)
Definition: buffer.h:40
CommandResult
Error codes for command_t parse functions.
Definition: command.h:34
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition: command.h:37
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition: command.h:35
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition: command.h:36
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
size_t escape_string(struct Buffer *buf, const char *src)
Write a string to a buffer, escaping special characters.
Definition: dump.c:46
enum CommandResult mutt_lua_source_file(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'lua-source' command - Implements Command::parse() -.
Definition: mutt_lua.c:499
enum CommandResult mutt_lua_parse(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'lua' command - Implements Command::parse() -.
Definition: mutt_lua.c:477
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:273
enum CommandResult mutt_parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: init.c:894
Config/command parsing.
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:294
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
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:652
Many unsorted constants and some structs.
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:67
#define PATH_MAX
Definition: mutt.h:40
void mutt_commands_apply(void *data, void(*application)(void *, const struct Command *))
Run a callback function on every Command.
struct Command * mutt_command_get(const char *s)
Get a Command by its name.
Definitions of NeoMutt commands.
#define COMMANDS_REGISTER(cmds)
Definition: mutt_commands.h:47
static int lua_mutt_enter(lua_State *l)
Execute NeoMutt config from Lua.
Definition: mutt_lua.c:313
static int lua_mutt_message(lua_State *l)
Display a message in Neomutt.
Definition: mutt_lua.c:344
static void luaopen_mutt(lua_State *l)
Expose a 'Mutt' object to the Lua interpreter.
Definition: mutt_lua.c:429
void mutt_lua_init(void)
Setup feature commands.
Definition: mutt_lua.c:469
static bool lua_init(lua_State **l)
Initialise a Lua State.
Definition: mutt_lua.c:441
static int handle_panic(lua_State *l)
Handle a panic in the Lua interpreter.
Definition: mutt_lua.c:73
static int lua_mutt_call(lua_State *l)
Call a NeoMutt command by name.
Definition: mutt_lua.c:100
static int handle_error(lua_State *l)
Handle an error in the Lua interpreter.
Definition: mutt_lua.c:86
static int lua_mutt_get(lua_State *l)
Get a NeoMutt variable.
Definition: mutt_lua.c:239
lua_State * LuaState
Global Lua State.
Definition: mutt_lua.c:56
static const luaL_Reg LuaMuttCommands[]
List of Lua commands to register.
Definition: mutt_lua.c:390
static void lua_expose_command(void *p, const struct Command *cmd)
Expose a NeoMutt command to the Lua interpreter.
Definition: mutt_lua.c:372
static int lua_mutt_error(lua_State *l)
Display an error in Neomutt.
Definition: mutt_lua.c:358
static const struct Command LuaCommands[]
List of NeoMutt commands to register.
Definition: mutt_lua.c:61
static int luaopen_mutt_decl(lua_State *l)
Declare some NeoMutt types to the Lua interpreter.
Definition: mutt_lua.c:408
static int lua_mutt_set(lua_State *l)
Set a NeoMutt variable.
Definition: mutt_lua.c:158
Integrated Lua scripting.
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:123
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1470
Some miscellaneous functions.
void myvar_set(const char *var, const char *val)
Set the value of a "my_" variable.
Definition: myvar.c:109
const char * myvar_get(const char *var)
Get the value of a "my_" variable.
Definition: myvar.c:92
Handling of personal config ('my' variables)
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
@ MUTT_ASKNO
Ask the user, defaulting to 'No'.
Definition: quad.h:40
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
@ MUTT_ASKYES
Ask the user, defaulting to 'Yes'.
Definition: quad.h:41
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
#define CSR_RESULT(x)
Definition: set.h:52
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
String manipulation buffer.
Definition: buffer.h:34
char * dptr
Current read/write position.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
enum CommandResult(* parse)(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Definition: command.h:63
intptr_t data
Data or flags to pass to the command.
Definition: command.h:65
const char * name
Name of the command.
Definition: command.h:50
Definition: set.h:64
intptr_t var
Storage for the variable.
Definition: set.h:85
uint32_t type
Variable type, e.g. DT_STRING.
Definition: set.h:66
The item stored in a Hash Table.
Definition: hash.h:44
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition: hash.h:45
void * data
User-supplied data.
Definition: hash.h:47
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
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:354
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:283
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:386
struct HashElem * cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
Find an inherited config item.
Definition: subset.c:179
#define DT_SORT
sorting methods
Definition: types.h:40
#define DTYPE(x)
Mask for the Data Type.
Definition: types.h:44
#define DT_SLIST
a list of strings
Definition: types.h:39
#define DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:37
#define DT_BOOL
boolean option
Definition: types.h:30
#define DT_PATH
a path to a file/directory
Definition: types.h:36
#define DT_STRING
a string
Definition: types.h:41
#define DT_ENUM
an enumeration
Definition: types.h:31
#define DT_ADDRESS
e-mail address
Definition: types.h:29
#define DT_REGEX
regular expressions
Definition: types.h:38
#define DT_MBTABLE
multibyte char table
Definition: types.h:34
#define DT_NUMBER
a number
Definition: types.h:35