unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
From: servilio <servilio@gmail.com>
To: notmuch <notmuch@notmuchmail.org>
Subject: [PATCH] Implement a simple read-eval-print loop.
Date: Sat, 20 Nov 2010 09:20:14 -0500	[thread overview]
Message-ID: <AANLkTi=7eCt0=NqUiJFrGDcaZ17LOd3qNNqN1-ASwYzr@mail.gmail.com> (raw)

This implementation uses GNU readline for the prompt and command
history, with the default file completion enabled. GLib is used to
split the read line into an arguments list.
---
 Makefile.local |    2 +-
 configure      |   40 ++++++++++++++++++-
 notmuch.c      |  115 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 141 insertions(+), 16 deletions(-)

diff --git a/Makefile.local b/Makefile.local
index f9b5a9b..3aff873 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -31,7 +31,7 @@ GPG_FILE=$(SHA1_FILE).asc
 # Smash together user's values with our extra values
 FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CFLAGS) $(WARN_CFLAGS)
$(CONFIGURE_CFLAGS) $(extra_cflags)
 FINAL_CXXFLAGS = $(CXXFLAGS) $(WARN_CXXFLAGS) $(CONFIGURE_CXXFLAGS)
$(extra_cflags) $(extra_cxxflags)
-FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Llib -lnotmuch
$(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
+FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Llib -lnotmuch
$(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
$(READLINE_LDFLAGS)
 FINAL_NOTMUCH_LINKER = CC
 ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)
 FINAL_NOTMUCH_LDFLAGS += $(CONFIGURE_LDFLAGS)
diff --git a/configure b/configure
index c58dd0f..c9ea920 100755
--- a/configure
+++ b/configure
@@ -259,6 +259,32 @@ else
     errors=$((errors + 1))
 fi

+printf "Checking for readline... "
+
+echo "#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+int main(void)
+{
+    static char *line = (char *)NULL;
+    line = readline(\"\");
+    add_history(line);
+    return 0;
+}" > have_readline.c
+
+if ${CC} -lreadline -o have_readline have_readline.c > /dev/null 2>&1
+then
+    printf "Yes.\n"
+    have_readline=1
+    readline_ldflags="-lreadline"
+else
+    printf "No.\n"
+    have_readline=0
+    errors=$((errors + 1))
+fi
+rm -f have_readline have_readline.c
+
 printf "Checking for valgrind development files... "
 if pkg-config --exists valgrind; then
     printf "Yes.\n"
@@ -341,6 +367,10 @@ EOF
 	echo "	The talloc library (including development files such as headers)"
 	echo "	http://talloc.samba.org/"
     fi
+    if [ $have_readline -eq 0 ]; then
+	echo "	The readline library (including development files such as headers)"
+	echo "	http://tiswww.case.edu/php/chet/readline/rltop.html"
+    fi
     cat <<EOF

 With any luck, you're using a modern, package-based operating system
@@ -349,11 +379,11 @@ case a simple command will install everything
you need. For example:

 On Debian and similar systems:

-	sudo apt-get install libxapian-dev libgmime-2.4-dev libtalloc-dev
+	sudo apt-get install libxapian-dev libgmime-2.4-dev libtalloc-dev
libreadline-dev

 Or on Fedora and similar systems:

-	sudo yum install xapian-core-devel gmime-devel libtalloc-devel
+	sudo yum install xapian-core-devel gmime-devel libtalloc-devel readline-devel

 On other systems, similar commands can be used, but the details of the
 package names may be different.
@@ -560,6 +590,9 @@ GMIME_LDFLAGS = ${gmime_ldflags}
 TALLOC_CFLAGS = ${talloc_cflags}
 TALLOC_LDFLAGS = ${talloc_ldflags}

+# Flags needed to compile and link against readline
+READLINE_LDFLAGS = ${readline_ldflags}
+
 # Flags needed to have linker set rpath attribute
 RPATH_LDFLAGS = ${rpath_ldflags}

@@ -580,5 +613,6 @@ CONFIGURE_CXXFLAGS =
-DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS)    \\
 		     \$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\
 		     \$(VALGRIND_CFLAGS) \$(XAPIAN_CXXFLAGS)             \\
                      -DHAVE_STRCASESTR=\$(HAVE_STRCASESTR)
-CONFIGURE_LDFLAGS =  \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(XAPIAN_LDFLAGS)
+CONFIGURE_LDFLAGS =  \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \\
+                     \$(READLINE_LDFLAGS) \$(XAPIAN_LDFLAGS) \\
 EOF
diff --git a/notmuch.c b/notmuch.c
index 9ba0ec0..630e272 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -18,8 +18,17 @@
  *
  * Authors: Carl Worth <cworth@cworth.org>
  *	    Keith Packard <keithp@keithp.com>
+ *          Servilio Afre Puentes <servilio@gmail.com>
  */

+#include <stdio.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
 #include "notmuch-client.h"

 typedef int (*command_function_t) (void *ctx, int argc, char *argv[]);
@@ -35,6 +44,9 @@ typedef struct command {
 static int
 notmuch_help_command (void *ctx, int argc, char *argv[]);

+static int
+notmuch_repl_command (void *ctx, int argc, char *argv[]);
+
 static const char search_terms_help[] =
     "\tSeveral notmuch commands accept a comman syntax for search\n"
     "\tterms.\n"
@@ -361,6 +373,10 @@ command_t commands[] = {
       "\tby the \"--format=json\" option of \"notmuch show\". If the\n"
       "\tmessage specified by the search terms does not include a\n"
       "\tpart with the specified \"id\" there will be no output." },
+    { "repl", notmuch_repl_command,
+      NULL,
+      "Execute an interactive interpreter of notmuch commands.",
+      "\tAlso known as a read-eval-print loop.\n" },
     { "config", notmuch_config_command,
       "[get|set] <section>.<item> [value ...]",
       "Get or set settings in the notmuch configuration file.",
@@ -471,6 +487,90 @@ notmuch_help_command (unused (void *ctx), int
argc, char *argv[])
     return 1;
 }

+static int
+notmuch_command_dispatch (void *ctx, int argc, char *argv[])
+{
+    command_t *command;
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE (commands); i++) {
+	command = &commands[i];
+
+	if (strcmp (argv[0], command->name) == 0)
+	    return (command->function) (ctx, argc - 1, &argv[1]);
+    }
+
+    fprintf (stderr, "Error: Unknown command '%s' (see \"notmuch help\")\n",
+	     argv[0]);
+
+    return 1;
+}
+
+/*
+ * A notmuch REPL (Read-eval-print loop) with readline support.
+ */
+static int
+notmuch_repl_command (void *ctx, unused (int argc), unused (char *argv[]))
+{
+    const char *prompt = "notmuch> ";
+    static char *line = (char *)NULL;
+    int ret = 0;
+    gint read_argc = 0;
+    gchar **read_argv;
+    GError *parse_error;
+
+    /* Initialize readline. */
+    /* Allow conditional parsing of the ~/.inputrc file. */
+    rl_readline_name = "notmuch";
+
+    do
+    {
+        read_argv = NULL;
+        parse_error = NULL;
+        line = readline(prompt);
+	if (line && *line)
+	{
+	    g_shell_parse_argv((gchar *)line,
+			       &read_argc,
+			       &read_argv,
+			       &parse_error);
+
+	    if (parse_error == NULL)
+	    {
+	        add_history(line);
+	    }
+	    free (line);
+	    line = (char *)NULL;
+
+	    if (parse_error != NULL)
+	    {
+	        fprintf (stderr, "%s\n", parse_error->message);
+		g_error_free (parse_error);
+		continue;
+	    }
+
+	    if (STRNCMP_LITERAL (read_argv[0], "repl") == 0)
+	    {
+	        fprintf (stderr, "No nasty nestings, please!\n");
+	        continue;
+	    }
+
+	    if (STRNCMP_LITERAL (read_argv[0], "quit") == 0)
+	    {
+	        fprintf (stdout, "Bye!\n");
+	        break;
+	    }
+
+	    ret = notmuch_command_dispatch(ctx,
+					   read_argc,
+					   read_argv);
+	    g_strfreev(read_argv);
+	}
+    } while (1);
+
+    return 0;
+}
+
 /* Handle the case of "notmuch" being invoked with no command
  * argument. For now we just call notmuch_setup_command, but we plan
  * to be more clever about this in the future.
@@ -539,8 +639,7 @@ int
 main (int argc, char *argv[])
 {
     void *local;
-    command_t *command;
-    unsigned int i;
+    int res;

     local = talloc_new (NULL);

@@ -557,17 +656,9 @@ main (int argc, char *argv[])
 	return 0;
     }

-    for (i = 0; i < ARRAY_SIZE (commands); i++) {
-	command = &commands[i];
-
-	if (strcmp (argv[1], command->name) == 0)
-	    return (command->function) (local, argc - 2, &argv[2]);
-    }
-
-    fprintf (stderr, "Error: Unknown command '%s' (see \"notmuch help\")\n",
-	     argv[1]);
+    res = notmuch_command_dispatch (local, argc - 1, &argv[1]);

     talloc_free (local);

-    return 1;
+    return res;
 }
-- 
1.7.2.3

             reply	other threads:[~2010-11-20 14:20 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-11-20 14:20 servilio [this message]
2010-11-20 21:15 ` [PATCH] Implement a simple read-eval-print loop Michal Sojka
2010-11-20 23:38   ` servilio
2010-11-21 17:35     ` Austin Clements
2010-11-21 21:10       ` Michal Sojka
2010-11-21 21:51         ` Michal Sojka
2010-11-21 22:14           ` Michael Hudson
2010-11-21 23:50         ` Austin Clements

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://notmuchmail.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='AANLkTi=7eCt0=NqUiJFrGDcaZ17LOd3qNNqN1-ASwYzr@mail.gmail.com' \
    --to=servilio@gmail.com \
    --cc=notmuch@notmuchmail.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).