]> granicus.if.org Git - neomutt/commitdiff
Adding basic Lua scripting basics to mutt
authorGuyzmo <guyzmo+github+pub@m0g.net>
Sat, 14 Jan 2017 12:31:58 +0000 (13:31 +0100)
committerRichard Russon <rich@flatcap.org>
Sat, 11 Mar 2017 19:10:59 +0000 (19:10 +0000)
* 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 <guyzmo+github+pub@m0g.net>
Makefile.am
configure.ac
curs_main.c
functions.h
globals.h
init.h
mutt-lua.c [deleted file]
mutt_lua.c [new file with mode: 0644]
mutt_lua.h [moved from mutt-lua.h with 83% similarity]

index ddb22655644964ed8a77da5559fc8e73c23347ad..282a5c3501b0c848c31eb7d33d5a6ff27b092290 100644 (file)
@@ -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 \
index 3385e1e9a096c88f281944763e1b8b733546a62e..b33ae11cf6768783b6db2687447c645f6ef0571c 100644 (file)
@@ -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)
 
index aba42ee36692657c9f2590e212a5a92ece166ba0..4a480f62dee23bb0f31c284e248861e6530badd3 100644 (file)
@@ -52,9 +52,6 @@
 #include "nntp.h"
 #endif
 
-#ifdef USE_LUA
-#include "mutt-lua.h"
-#endif
 
 #include <ctype.h>
 #include <stdlib.h>
@@ -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)
index f899987c5fbbac39abd4c166b5f3c5ceb8dc191d..b5a53a4cb39139a5bfe7140679c11db03ab2945c 100644 (file)
@@ -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,                   "%" },
index e87b525201b64b4b7977e09166cc6aef8df46a0b..f9f915027cd01269c193a5ded64f95cc2b2f6528 100644 (file)
--- 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 5508e0c277b392d1e15c550aa4b1491d981b0494..11eb339eb33077a88873a849ea6330097a4e19b9 100644 (file)
--- a/init.h
+++ b/init.h
 # 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 (file)
index 98aa5ef..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright (C) 2016 Richard Russon <rich@flatcap.org>
- *
- *     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 <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-
-#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 (file)
index 0000000..ec88406
--- /dev/null
@@ -0,0 +1,242 @@
+/* Copyright (C) 2016 Richard Russon <rich@flatcap.org>
+ *
+ *     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 <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <stdbool.h>
+
+#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;
+// }
+
similarity index 83%
rename from mutt-lua.h
rename to mutt_lua.h
index a7a3b077c220c95a1de393e8285be9514f31cb2f..4666105af296276bad0c421d7b2ed29a962fa98b 100644 (file)
@@ -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_ */