NeoMutt  2025-01-09-146-g8c4f7e
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mutt_lua.c
Go to the documentation of this file.
1
34#ifndef LUA_COMPAT_ALL
35#define LUA_COMPAT_ALL
36#endif
37#ifndef LUA_COMPAT_5_1
38#define LUA_COMPAT_5_1
39#endif
40
41#include "config.h"
42#include <lauxlib.h>
43#include <lua.h>
44#include <lualib.h>
45#include <stdbool.h>
46#include <stdint.h>
47#include <stdio.h>
48#include "mutt/lib.h"
49#include "config/lib.h"
50#include "core/lib.h"
51#include "mutt_lua.h"
52#include "parse/lib.h"
53#include "muttlib.h"
54#include "version.h"
55
57static lua_State *LuaState = NULL;
58
62static const struct Command LuaCommands[] = {
63 // clang-format off
64 { "lua", mutt_lua_parse, 0 },
65 { "lua-source", mutt_lua_source_file, 0 },
66 { NULL, NULL, 0 },
67 // clang-format on
68};
69
75static int handle_panic(lua_State *l)
76{
77 mutt_debug(LL_DEBUG1, "lua runtime panic: %s\n", lua_tostring(l, -1));
78 mutt_error("Lua runtime panic: %s", lua_tostring(l, -1));
79 lua_pop(l, 1);
80 return -1;
81}
82
88static int handle_error(lua_State *l)
89{
90 mutt_debug(LL_DEBUG1, "lua runtime error: %s\n", lua_tostring(l, -1));
91 mutt_error("Lua runtime error: %s", lua_tostring(l, -1));
92 lua_pop(l, 1);
93 return -1;
94}
95
102static int lua_mutt_call(lua_State *l)
103{
104 mutt_debug(LL_DEBUG2, " * lua_mutt_call()\n");
105 struct Buffer *err = buf_pool_get();
106 struct Buffer *token = buf_pool_get();
107 struct Buffer *buf = buf_pool_get();
108 const struct Command *cmd = NULL;
109 int rc = 0;
110
111 if (lua_gettop(l) == 0)
112 {
113 luaL_error(l, "Error command argument required.");
114 return -1;
115 }
116
117 cmd = commands_get(&NeoMutt->commands, lua_tostring(l, 1));
118 if (!cmd)
119 {
120 luaL_error(l, "Error command %s not found.", lua_tostring(l, 1));
121 return -1;
122 }
123
124 for (int i = 2; i <= lua_gettop(l); i++)
125 {
126 buf_addstr(buf, lua_tostring(l, i));
127 buf_addch(buf, ' ');
128 }
129 buf_seek(buf, 0);
130
131 if (cmd->parse(token, buf, cmd->data, err))
132 {
133 luaL_error(l, "NeoMutt error: %s", buf_string(err));
134 rc = -1;
135 }
136 else
137 {
138 if (!lua_pushstring(l, buf_string(err)))
139 handle_error(l);
140 else
141 rc++;
142 }
143
144 buf_pool_release(&buf);
145 buf_pool_release(&token);
146 buf_pool_release(&err);
147 return rc;
148}
149
156static int lua_mutt_set(lua_State *l)
157{
158 const char *param = lua_tostring(l, -2);
159 mutt_debug(LL_DEBUG2, " * lua_mutt_set(%s)\n", param);
160
161 struct Buffer *err = buf_pool_get();
162 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, param);
163 if (!he)
164 {
165 // In case it is a my_var, we have to create it
166 if (mutt_str_startswith(param, "my_"))
167 {
168 struct ConfigDef my_cdef = { 0 };
169 my_cdef.name = param;
170 my_cdef.type = DT_MYVAR;
171 he = cs_create_variable(NeoMutt->sub->cs, &my_cdef, err);
172 if (!he)
173 return -1;
174 }
175 else
176 {
177 luaL_error(l, "NeoMutt parameter not found %s", param);
178 return -1;
179 }
180 }
181
182 struct ConfigDef *cdef = he->data;
183
184 int rc = 0;
185
186 switch (CONFIG_TYPE(cdef->type))
187 {
188 case DT_ADDRESS:
189 case DT_ENUM:
190 case DT_MBTABLE:
191 case DT_MYVAR:
192 case DT_PATH:
193 case DT_REGEX:
194 case DT_SLIST:
195 case DT_SORT:
196 case DT_STRING:
197 {
198 const char *value = lua_tostring(l, -1);
199 size_t val_size = lua_rawlen(l, -1);
200 struct Buffer *value_buf = buf_pool_get();
201 buf_strcpy_n(value_buf, value, val_size);
202 if (CONFIG_TYPE(he->type) == DT_PATH)
203 buf_expand_path(value_buf);
204
205 int rv = cs_subset_he_string_set(NeoMutt->sub, he, buf_string(value_buf), err);
206 buf_pool_release(&value_buf);
207 if (CSR_RESULT(rv) != CSR_SUCCESS)
208 rc = -1;
209 break;
210 }
211 case DT_NUMBER:
212 case DT_QUAD:
213 {
214 const intptr_t value = lua_tointeger(l, -1);
215 int rv = cs_subset_he_native_set(NeoMutt->sub, he, value, err);
216 if (CSR_RESULT(rv) != CSR_SUCCESS)
217 rc = -1;
218 break;
219 }
220 case DT_BOOL:
221 {
222 const intptr_t value = lua_toboolean(l, -1);
223 int rv = cs_subset_he_native_set(NeoMutt->sub, he, value, err);
224 if (CSR_RESULT(rv) != CSR_SUCCESS)
225 rc = -1;
226 break;
227 }
228 default:
229 luaL_error(l, "Unsupported NeoMutt parameter type %d for %s",
230 CONFIG_TYPE(cdef->type), param);
231 rc = -1;
232 break;
233 }
234
235 buf_pool_release(&err);
236 return rc;
237}
238
245static int lua_mutt_get(lua_State *l)
246{
247 const char *param = lua_tostring(l, -1);
248 mutt_debug(LL_DEBUG2, " * lua_mutt_get(%s)\n", param);
249
250 struct HashElem *he = cs_subset_lookup(NeoMutt->sub, param);
251 if (!he)
252 {
253 mutt_debug(LL_DEBUG2, " * error\n");
254 luaL_error(l, "NeoMutt parameter not found %s", param);
255 return -1;
256 }
257
258 struct ConfigDef *cdef = he->data;
259
260 switch (CONFIG_TYPE(cdef->type))
261 {
262 case DT_ADDRESS:
263 case DT_ENUM:
264 case DT_MBTABLE:
265 case DT_MYVAR:
266 case DT_REGEX:
267 case DT_SLIST:
268 case DT_SORT:
269 case DT_STRING:
270 {
271 struct Buffer *value = buf_pool_get();
272 int rc = cs_subset_he_string_get(NeoMutt->sub, he, value);
273 if (CSR_RESULT(rc) != CSR_SUCCESS)
274 {
275 buf_pool_release(&value);
276 return -1;
277 }
278
279 struct Buffer *escaped = buf_pool_get();
280 escape_string(escaped, buf_string(value));
281 lua_pushstring(l, buf_string(escaped));
282 buf_pool_release(&value);
283 buf_pool_release(&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) 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
307static int lua_mutt_enter(lua_State *l)
308{
309 mutt_debug(LL_DEBUG2, " * lua_mutt_enter()\n");
310 struct Buffer *err = buf_pool_get();
311 char *buf = mutt_str_dup(lua_tostring(l, -1));
312 int rc = 0;
313
314 if (parse_rc_line(buf, err))
315 {
316 luaL_error(l, "NeoMutt error: %s", buf_string(err));
317 rc = -1;
318 }
319 else
320 {
321 if (!lua_pushstring(l, buf_string(err)))
322 handle_error(l);
323 else
324 rc++;
325 }
326
327 FREE(&buf);
328 buf_pool_release(&err);
329
330 return rc;
331}
332
338static 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("%s", msg);
344 return 0;
345}
346
352static 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("%s", msg);
358 return 0;
359}
360
366static void lua_expose_command(lua_State *l, const struct Command *cmd)
367{
368 char buf[1024] = { 0 };
369 snprintf(buf, sizeof(buf), "mutt.command.%s = function (...); mutt.call('%s', ...); end",
370 cmd->name, cmd->name);
371 (void) luaL_dostring(l, buf);
372}
373
383static const luaL_Reg LuaMuttCommands[] = {
384 // clang-format off
385 { "set", lua_mutt_set },
386 { "get", lua_mutt_get },
387 { "call", lua_mutt_call },
388 { "enter", lua_mutt_enter },
389 { "print", lua_mutt_message },
390 { "message", lua_mutt_message },
391 { "error", lua_mutt_error },
392 { NULL, NULL },
393 // clang-format on
394};
395
401static int luaopen_mutt_decl(lua_State *l)
402{
403 mutt_debug(LL_DEBUG2, " * luaopen_mutt()\n");
404 luaL_newlib(l, LuaMuttCommands);
405 int lib_idx = lua_gettop(l);
406
407 // clang-format off
408 lua_pushstring(l, "VERSION"); lua_pushstring(l, mutt_make_version()); lua_settable(l, lib_idx);;
409 lua_pushstring(l, "QUAD_YES"); lua_pushinteger(l, MUTT_YES); lua_settable(l, lib_idx);;
410 lua_pushstring(l, "QUAD_NO"); lua_pushinteger(l, MUTT_NO); lua_settable(l, lib_idx);;
411 lua_pushstring(l, "QUAD_ASKYES"); lua_pushinteger(l, MUTT_ASKYES); lua_settable(l, lib_idx);;
412 lua_pushstring(l, "QUAD_ASKNO"); lua_pushinteger(l, MUTT_ASKNO); lua_settable(l, lib_idx);;
413 // clang-format on
414
415 return 1;
416}
417
422static void luaopen_mutt(lua_State *l)
423{
424 luaL_requiref(l, "mutt", luaopen_mutt_decl, 1);
425 (void) luaL_dostring(l, "mutt.command = {}");
426
427 const struct Command **cp = NULL;
429 {
430 const struct Command *cmd = *cp;
431
432 lua_expose_command(l, cmd);
433 }
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 buf_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 buf_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 if (parse_extract_token(buf, s, TOKEN_NO_FLAGS) != 0)
507 {
508 buf_printf(err, _("source: error at %s"), s->dptr);
509 return MUTT_CMD_ERROR;
510 }
511 if (MoreArgs(s))
512 {
513 buf_printf(err, _("%s: too many arguments"), "source");
514 return MUTT_CMD_WARNING;
515 }
516
517 struct Buffer *path = buf_pool_get();
518 buf_copy(path, buf);
519 buf_expand_path(path);
520
521 if (luaL_dofile(LuaState, buf_string(path)))
522 {
523 mutt_error(_("Couldn't source lua source: %s"), lua_tostring(LuaState, -1));
524 lua_pop(LuaState, 1);
525 buf_pool_release(&path);
526 return MUTT_CMD_ERROR;
527 }
528
529 buf_pool_release(&path);
530 return MUTT_CMD_SUCCESS;
531}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:214
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
void buf_seek(struct Buffer *buf, size_t offset)
Set current read/write position to offset from beginning.
Definition: buffer.c:622
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
size_t buf_strcpy_n(struct Buffer *buf, const char *s, size_t len)
Copy a string into a Buffer.
Definition: buffer.c:416
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:601
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
CommandResult
Error codes for command_t parse functions.
Definition: command.h:35
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition: command.h:38
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition: command.h:36
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition: command.h:37
size_t escape_string(struct Buffer *buf, const char *src)
Write a string to a buffer, escaping special characters.
Definition: dump.c:48
Convenience wrapper for the config headers.
struct HashElem * cs_create_variable(const struct ConfigSet *cs, struct ConfigDef *cdef, struct Buffer *err)
Create and register one config item.
Definition: set.c:326
#define CSR_RESULT(x)
Definition: set.h:50
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:33
bool commands_register(struct CommandArray *ca, const struct Command *cmds)
Add commands to Commands array.
Definition: command.c:51
const struct Command * commands_get(struct CommandArray *ca, const char *name)
Get a Command by its name.
Definition: command.c:82
Convenience wrapper for the core headers.
int parse_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: extract.c:49
#define MoreArgs(buf)
Definition: extract.h:32
#define TOKEN_NO_FLAGS
No flags are set.
Definition: extract.h: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: logging2.h:93
#define mutt_message(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:90
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:45
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:44
#define FREE(x)
Definition: memory.h:55
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:254
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:231
static int lua_mutt_enter(lua_State *l)
Execute NeoMutt config from Lua.
Definition: mutt_lua.c:307
static int lua_mutt_message(lua_State *l)
Display a message in Neomutt.
Definition: mutt_lua.c:338
static void luaopen_mutt(lua_State *l)
Expose a 'Mutt' object to the Lua interpreter.
Definition: mutt_lua.c:422
void mutt_lua_init(void)
Setup feature commands.
Definition: mutt_lua.c:469
static void lua_expose_command(lua_State *l, const struct Command *cmd)
Expose a NeoMutt command to the Lua interpreter.
Definition: mutt_lua.c:366
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:75
static int lua_mutt_call(lua_State *l)
Call a NeoMutt command by name.
Definition: mutt_lua.c:102
static int handle_error(lua_State *l)
Handle an error in the Lua interpreter.
Definition: mutt_lua.c:88
static int lua_mutt_get(lua_State *l)
Get a NeoMutt variable.
Definition: mutt_lua.c:245
static lua_State * LuaState
Global Lua State.
Definition: mutt_lua.c:57
static const luaL_Reg LuaMuttCommands[]
List of Lua commands to register.
Definition: mutt_lua.c:383
static int lua_mutt_error(lua_State *l)
Display an error in Neomutt.
Definition: mutt_lua.c:352
static const struct Command LuaCommands[]
List of NeoMutt commands to register.
Definition: mutt_lua.c:62
static int luaopen_mutt_decl(lua_State *l)
Declare some NeoMutt types to the Lua interpreter.
Definition: mutt_lua.c:401
static int lua_mutt_set(lua_State *l)
Set a NeoMutt variable.
Definition: mutt_lua.c:156
Integrated Lua scripting.
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:315
Some miscellaneous functions.
Text parsing functions.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
@ 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
enum CommandResult parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: rc.c:109
String manipulation buffer.
Definition: buffer.h:36
char * dptr
Current read/write position.
Definition: buffer.h:38
char * data
Pointer to data.
Definition: buffer.h:37
enum CommandResult(* parse)(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Definition: command.h:64
intptr_t data
Data or flags to pass to the command.
Definition: command.h:66
const char * name
Name of the command.
Definition: command.h:51
Definition: set.h:62
const char * name
User-visible name.
Definition: set.h:63
intptr_t var
Storage for the variable.
Definition: set.h:82
uint32_t type
Variable type, e.g. DT_STRING.
Definition: set.h:64
struct ConfigSet * cs
Parent ConfigSet.
Definition: subset.h:50
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:43
struct CommandArray commands
NeoMutt commands.
Definition: neomutt.h:51
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
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:334
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:277
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:366
struct HashElem * cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
Find an inherited config item.
Definition: subset.c:189
#define CONFIG_TYPE(t)
Definition: types.h:49
@ DT_NUMBER
a number
Definition: types.h:38
@ DT_SLIST
a list of strings
Definition: types.h:42
@ DT_BOOL
boolean option
Definition: types.h:32
@ DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:40
@ DT_STRING
a string
Definition: types.h:44
@ DT_SORT
sorting methods
Definition: types.h:43
@ DT_MYVAR
a user-defined variable (my_foo)
Definition: types.h:37
@ DT_MBTABLE
multibyte char table
Definition: types.h:36
@ DT_ADDRESS
e-mail address
Definition: types.h:31
@ DT_ENUM
an enumeration
Definition: types.h:33
@ DT_REGEX
regular expressions
Definition: types.h:41
@ DT_PATH
a path to a file/directory
Definition: types.h:39
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: version.c:295
Display version and copyright about NeoMutt.