From 7faacc4fa72e0554fc587247b25056191404c673 Mon Sep 17 00:00:00 2001 From: Guyzmo Date: Sat, 14 Jan 2017 13:31:58 +0100 Subject: [PATCH] Adding basic Lua scripting basics to mutt * adding two commands: - lua: to parse a line of lua code - lua-source: to load and parse a lua file * binding two mutt functions in lua: - mutt_message and - mutt_error cf #153 Signed-off-by: Guyzmo --- Makefile.am | 4 +- configure.ac | 8 +- curs_main.c | 8 -- functions.h | 3 - globals.h | 3 - init.h | 15 +-- mutt-lua.c | 128 --------------------- mutt_lua.c | 242 +++++++++++++++++++++++++++++++++++++++ mutt-lua.h => mutt_lua.h | 5 +- 9 files changed, 260 insertions(+), 156 deletions(-) delete mode 100644 mutt-lua.c create mode 100644 mutt_lua.c rename mutt-lua.h => mutt_lua.h (83%) diff --git a/Makefile.am b/Makefile.am index ddb226556..282a5c350 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,7 +69,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c compress.c crypt-gpgme.c crypt-mod-pgp-c mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \ mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \ pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \ - mutt-lua.c nntp.c newsrc.c \ + mutt_lua.c nntp.c newsrc.c \ sidebar.c smime.c smtp.c utf8.c wcwidth.c \ bcache.h browser.h hcache.h mbyte.h mutt_idna.h remailer.h url.h @@ -81,7 +81,7 @@ EXTRA_DIST = COPYRIGHT LICENSE.md OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO UPDATING mutt_regex.h mutt_sasl.h mutt_sasl_plain.h mutt_socket.h mutt_ssl.h \ mutt_tunnel.h mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \ rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types \ - mutt-lua.h nntp.h ChangeLog.nntp \ + mutt_lua.h nntp.h ChangeLog.nntp \ _regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \ mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \ README.SSL README.md README.neomutt README.notmuch smime.h group.h \ diff --git a/configure.ac b/configure.ac index 3385e1e9a..b33ae11cf 100644 --- a/configure.ac +++ b/configure.ac @@ -302,10 +302,10 @@ AX_LUA_HEADERS(:,enable_lua=no) AX_LUA_LIBS(:,enable_lua=no) AS_IF([test x$enable_lua = "xyes"], [ - AC_DEFINE(USE_LUA, 1, [Define if you want support for LUA.]) - MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS mutt-lua.o" - CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" - LIBS="$LIBS $LUA_LIB" + AC_DEFINE(USE_LUA, 1, [Define if you want support for LUA.]) + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS mutt_lua.o" + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + LIBS="$LIBS $LUA_LIB" ]) AM_CONDITIONAL(BUILD_LUA, test x$enable_lua = xyes) diff --git a/curs_main.c b/curs_main.c index aba42ee36..4a480f62d 100644 --- a/curs_main.c +++ b/curs_main.c @@ -52,9 +52,6 @@ #include "nntp.h" #endif -#ifdef USE_LUA -#include "mutt-lua.h" -#endif #include #include @@ -3213,11 +3210,6 @@ int mutt_index_menu (void) case OP_SIDEBAR_TOGGLE_VIRTUAL: mutt_sb_toggle_virtual(); break; -#endif -#ifdef USE_LUA - case OP_LUA_RUN: - lua_test(); - break; #endif default: if (menu->menu == MENU_MAIN) diff --git a/functions.h b/functions.h index f899987c5..b5a53a4cb 100644 --- a/functions.h +++ b/functions.h @@ -129,9 +129,6 @@ const struct binding_t OpMain[] = { /* map: index */ { "limit-current-thread", OP_LIMIT_CURRENT_THREAD, NULL }, { "link-threads", OP_MAIN_LINK_THREADS, "&" }, { "list-reply", OP_LIST_REPLY, "L" }, -#ifdef USE_LUA - { "lua-run", OP_LUA_RUN, NULL }, -#endif { "mail", OP_MAIL, "m" }, { "toggle-new", OP_TOGGLE_NEW, "N" }, { "toggle-write", OP_TOGGLE_WRITE, "%" }, diff --git a/globals.h b/globals.h index e87b52520..f9f915027 100644 --- a/globals.h +++ b/globals.h @@ -70,9 +70,6 @@ WHERE char *ImapUser INITVAL (NULL); #endif WHERE char *Inbox; WHERE char *Ispell; -#ifdef USE_LUA -WHERE char *LuaScript; -#endif WHERE char *MailcapPath; WHERE char *Maildir; #if defined(USE_IMAP) || defined(USE_POP) || defined(USE_NNTP) diff --git a/init.h b/init.h index 5508e0c27..11eb339eb 100644 --- a/init.h +++ b/init.h @@ -25,6 +25,10 @@ # include "mx.h" #endif +#ifdef USE_LUA +#include "mutt_lua.h" +#endif + #include "buffy.h" #ifndef _MAKEDOC @@ -1604,13 +1608,6 @@ struct option_t MuttVars[] = { ** ``$$keywords_standard'' are \fCfalse\fP, mutt will save keywords ** to legacy headers to ensure that it does not lose your labels. */ -#ifdef USE_LUA - { "lua_script", DT_STR, R_NONE, UL &LuaScript, 0 }, - /* - ** .pp - ** External Lua script to run. - */ -#endif { "mail_check", DT_NUM, R_NONE, UL &BuffyTimeout, 5 }, /* ** .pp @@ -4487,6 +4484,10 @@ const struct command_t Commands[] = { #endif { "ignore", parse_ignore, 0 }, { "lists", parse_lists, 0 }, +#ifdef USE_LUA + { "lua", mutt_lua_parse, 0 }, + { "lua-source", mutt_lua_source_file, 0 }, +#endif { "macro", mutt_parse_macro, 0 }, { "mailboxes", mutt_parse_mailboxes, MUTT_MAILBOXES }, { "unmailboxes", mutt_parse_mailboxes, MUTT_UNMAILBOXES }, diff --git a/mutt-lua.c b/mutt-lua.c deleted file mode 100644 index 98aa5efd8..000000000 --- a/mutt-lua.c +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright (C) 2016 Richard Russon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "mutt.h" -#include "globals.h" - -/* The function we'll call from the lua script */ -int -average (lua_State *l) -{ - /* get number of arguments */ - int n = lua_gettop (l); - int sum = 0; - int i; - - /* loop through each argument */ - for (i = 1; i <= n; i++) - { -#if LUA_VERSION_NUM >= 503 - if (!lua_isinteger (l, i)) - { - lua_pushstring (l, "Incorrect argument to 'average'"); - lua_error (l); - } -#endif - - /* total the arguments */ - sum += lua_tointeger (l, i); - } - - /* push the average */ - lua_pushinteger (l, sum / n); - - /* return the number of results */ - return 1; -} - -int -get_lua_integer (lua_State *l, const char *name) -{ - if (!name) - return -1; - - /* lookup the name and put it on the stack */ - lua_getglobal (l, name); - if (!lua_isnumber (l, -1)) - return -1; - - int ret = lua_tonumber (l, -1); - lua_pop (l, 1); - - return ret; -} - -int -lua_test (void) -{ - lua_State *l; - - if (!LuaScript) - return 0; - - /* initialise Lua */ - l = luaL_newstate(); - if (!l) - return 0; - - /* load Lua base libraries */ - luaL_openlibs (l); - - /* register our function */ - lua_register (l, "average", average); - - /* set some default values */ - lua_pushinteger (l, 15); lua_setglobal (l, "apple"); - lua_pushinteger (l, 27); lua_setglobal (l, "banana"); - lua_pushinteger (l, 39); lua_setglobal (l, "cherry"); - - if (luaL_dofile (l, LuaScript) == 0) - { - /* retrieve the values */ - mutt_message ("apple = %d", get_lua_integer (l, "apple")); mutt_sleep(1); - mutt_message ("banana = %d", get_lua_integer (l, "banana")); mutt_sleep(1); - mutt_message ("cherry = %d", get_lua_integer (l, "cherry")); mutt_sleep(1); - - /* one value on the stack */ - if (lua_gettop (l) == 1) - { - mutt_message ("lua returned: %lld", lua_tointeger (l, 1)); - } - else - { - mutt_message ("lua returned"); - } - } - else - { - mutt_error ("error running lua script"); - } - - /* cleanup Lua */ - lua_close (l); - - return 1; -} - diff --git a/mutt_lua.c b/mutt_lua.c new file mode 100644 index 000000000..ec8840621 --- /dev/null +++ b/mutt_lua.c @@ -0,0 +1,242 @@ +/* Copyright (C) 2016 Richard Russon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include + +#include "mutt.h" +#include "globals.h" + +lua_State *Lua = NULL; + +static int _lua_mutt_message(lua_State *l) { + mutt_debug(2, "—→ _lua_mutt_message()\n"); + const char *msg = lua_tostring(l, -1); + if (msg != NULL) + mutt_message(msg); + return 0; +} + +static int _lua_mutt_error(lua_State *l) { + mutt_debug(2, "—→ _lua_mutt_error()\n"); + const char *msg = lua_tostring(l, -1); + if (msg != NULL) + mutt_error(msg); + return 0; +} + +static int _lua_mutt_error_handler(lua_State *l) +{ + mutt_error("Oops..."); + return 0; +} + + +static const luaL_Reg luaMuttDecl[] = +{ + { "message", _lua_mutt_message }, + { "error", _lua_mutt_error }, + { NULL, NULL } +}; + + +static int luaopen_mutt_decl(lua_State *l) +{ + mutt_debug(2, "—→ luaopen_mutt()\n"); + luaL_newlib(l, luaMuttDecl); + return 1; +} +#define luaopen_mutt(L) do { luaL_requiref(L, "mutt", luaopen_mutt_decl, 1); } while (0); + +static bool _lua_init(void) +{ + if (Lua == NULL) { + mutt_debug(2, "—→ lua_init()\n"); + Lua = luaL_newstate(); + + if (Lua == NULL) { + mutt_error("Error: Couldn't load the lua interpreter."); + return false; + } + + lua_atpanic (Lua, _lua_mutt_error_handler); + + /* load various Lua libraries */ + luaL_openlibs(Lua); + luaopen_mutt(Lua); + } + + return true; +} + +// static void _lua_close(void) +// { +// printf("—→ lua_close()"); +// lua_close(Lua); +// } + +int mutt_lua_parse (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) +{ + int error; + _lua_init(); + mutt_debug(2, "—→ mutt_lua_parse(%s)\n", tmp->data); + // printf("—→ mutt_lua_parse(%s)\n", s->dptr); + + if ((error = luaL_dostring(Lua, s->dptr))) { + mutt_debug(2, "—→ mutt_lua_parse(%s) → failure\n", s->dptr); + // printf("—→ mutt_lua_parse(%s) → failure\n", s->dptr); + mutt_error("Lua error: %s\n", lua_tostring(Lua, -1)); + lua_pop(Lua, 1); +/* pop error message from the stack */ + return -1; + } + mutt_debug(2, "—→ mutt_lua_parse(%s) → success\n", s->dptr); + // printf("—→ mutt_lua_parse(%s) → success\n", tmp->data); + return 1; +} + +int mutt_lua_source_file (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) +{ + mutt_debug(2, "—→ mutt_lua_source()\n"); + + _lua_init(); + + char path[_POSIX_PATH_MAX]; + + if (mutt_extract_token (tmp, s, 0) != 0) + { + snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr); + return (-1); + } + if (MoreArgs (s)) + { + strfcpy (err->data, _("source: too many arguments"), err->dsize); + return (-1); + } + strfcpy (path, tmp->data, sizeof (path)); + mutt_expand_path (path, sizeof (path)); + + if (luaL_dofile(Lua, path)) { + mutt_error("Couldn't source lua source: %s", lua_tostring(Lua, -1)); + lua_pop(Lua, 1); + return -1; + } + return 1; +} + +/* The function we'll call from the lua script */ +int average (lua_State *l) +{ + /* get number of arguments */ + int n = lua_gettop (l); + int sum = 0; + int i; + + /* loop through each argument */ + for (i = 1; i <= n; i++) + { +#if LUA_VERSION_NUM >= 503 + if (!lua_isinteger (l, i)) + { + lua_pushstring (l, "Incorrect argument to 'average'"); + lua_error (l); + } +#endif + + /* total the arguments */ + sum += lua_tointeger (l, i); + } + + /* push the average */ + lua_pushinteger (l, sum / n); + + /* return the number of results */ + return 1; +} + +int get_lua_integer (lua_State *l, const char *name) +{ + if (!name) + return -1; + + /* lookup the name and put it on the stack */ + lua_getglobal (l, name); + if (!lua_isnumber (l, -1)) + return -1; + + int ret = lua_tonumber (l, -1); + lua_pop (l, 1); + + return ret; +} + +// int lua_test (void) +// { +// if (!LuaScript) +// return 0; + +// /* initialise Lua */ +// l = luaL_newstate(); +// if (!l) +// return 0; + +// /* load Lua base libraries */ +// luaL_openlibs (l); + +// /* register our function */ +// lua_register (l, "average", average); + +// /* set some default values */ +// lua_pushinteger (l, 15); lua_setglobal (l, "apple"); +// lua_pushinteger (l, 27); lua_setglobal (l, "banana"); +// lua_pushinteger (l, 39); lua_setglobal (l, "cherry"); + +// if (luaL_dofile (l, LuaScript) == 0) +// { +// /* retrieve the values */ +// mutt_message ("apple = %d", get_lua_integer (l, "apple")); mutt_sleep(1); +// mutt_message ("banana = %d", get_lua_integer (l, "banana")); mutt_sleep(1); +// mutt_message ("cherry = %d", get_lua_integer (l, "cherry")); mutt_sleep(1); + +// /* one value on the stack */ +// if (lua_gettop (l) == 1) +// { +// mutt_message ("lua returned: %lld", lua_tointeger (l, 1)); +// } +// else +// { +// mutt_message ("lua returned"); +// } +// } +// else +// { +// mutt_error ("error running lua script"); +// } + +// /* cleanup Lua */ +// lua_close (l); + +// return 1; +// } + diff --git a/mutt-lua.h b/mutt_lua.h similarity index 83% rename from mutt-lua.h rename to mutt_lua.h index a7a3b077c..4666105af 100644 --- a/mutt-lua.h +++ b/mutt_lua.h @@ -18,6 +18,9 @@ #ifndef _MUTT_LUA_H_ #define _MUTT_LUA_H_ -int lua_test (void); +#include "mutt.h" + +int mutt_lua_parse (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err); +int mutt_lua_source_file (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err); #endif /* _MUTT_LUA_H_ */ -- 2.40.0