unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH] Implement a simple read-eval-print loop.
@ 2010-11-20 14:20 servilio
  2010-11-20 21:15 ` Michal Sojka
  0 siblings, 1 reply; 8+ messages in thread
From: servilio @ 2010-11-20 14:20 UTC (permalink / raw)
  To: notmuch

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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH] Implement a simple read-eval-print loop.
  2010-11-20 14:20 [PATCH] Implement a simple read-eval-print loop servilio
@ 2010-11-20 21:15 ` Michal Sojka
  2010-11-20 23:38   ` servilio
  0 siblings, 1 reply; 8+ messages in thread
From: Michal Sojka @ 2010-11-20 21:15 UTC (permalink / raw)
  To: servilio, notmuch

On Sat, 20 Nov 2010, servilio wrote:
> 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.

Hi,

I haven't tested it yet, but it seems to be exactly the piece of code I
wanted to have to speed up notmuch-hello when notmuch is used remotely
over ssh. Spawning new ssh for every saved search to get the count of
matched messages has a way too big overhead. Is this the use case you
had in mind when implementing this?

Currently, I have only one comment to the patch. For me, "repl" is a bit
unintuitive. I was thinking about "shell" as the name of subcommand for
this.

-Michal

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Implement a simple read-eval-print loop.
  2010-11-20 21:15 ` Michal Sojka
@ 2010-11-20 23:38   ` servilio
  2010-11-21 17:35     ` Austin Clements
  0 siblings, 1 reply; 8+ messages in thread
From: servilio @ 2010-11-20 23:38 UTC (permalink / raw)
  To: Michal Sojka; +Cc: notmuch

Hi Michal,

On 20 November 2010 16:15, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> On Sat, 20 Nov 2010, servilio wrote:
>> 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.
>
> Hi,
>
> I haven't tested it yet, but it seems to be exactly the piece of code I
> wanted to have to speed up notmuch-hello when notmuch is used remotely
> over ssh. Spawning new ssh for every saved search to get the count of
> matched messages has a way too big overhead. Is this the use case you
> had in mind when implementing this?

Great to see that you find it useful. Yes, the remote usage was in my
mind, but it should enhance the local usage as well. We could also
create a quicker test run using the shell.

The disadvantage of using an interactive shell is that you can have
only one operation running in the UI, and I see no way of
interrumpting an operation unless you kill the process, but as
starting a new shell shouldn't be a biggie, this might not be as
limiting as might seem.

The ideal would be for libnotmuch to be able to connect to a
xapian-tcpsrv, but haven't had time to look at than, and an
interactive shell seemed to be less time demanding to implement (and
had the subjective promise of providing a lot of fun).

> Currently, I have only one comment to the patch. For me, "repl" is a bit
> unintuitive. I was thinking about "shell" as the name of subcommand for
> this.

I agree, maybe "shell" or "interactive" would be more intuitive, but
when I started "repl" was the only one I could think of.

Servilio

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Implement a simple read-eval-print loop.
  2010-11-20 23:38   ` servilio
@ 2010-11-21 17:35     ` Austin Clements
  2010-11-21 21:10       ` Michal Sojka
  0 siblings, 1 reply; 8+ messages in thread
From: Austin Clements @ 2010-11-21 17:35 UTC (permalink / raw)
  To: servilio; +Cc: notmuch

[-- Attachment #1: Type: text/plain, Size: 2456 bytes --]

Out of curiosity, why not simply use SSH control mastering?  You could even
make that part of the "standard" remote notmuch script, without requiring
the user to change anything in their ssh configuration.  This should be just
as fast as a remote notmuch shell, but retains the ability to run multiple
simultaneous operations and leverages all of the fancy session machinery
already built in to ssh.  It seems to me that ssh already does what you
want, and I'm curious what the advantage is to reinventing the wheel.

On Sat, Nov 20, 2010 at 6:38 PM, servilio <servilio@gmail.com> wrote:

> Hi Michal,
>
> On 20 November 2010 16:15, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> > On Sat, 20 Nov 2010, servilio wrote:
> >> 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.
> >
> > Hi,
> >
> > I haven't tested it yet, but it seems to be exactly the piece of code I
> > wanted to have to speed up notmuch-hello when notmuch is used remotely
> > over ssh. Spawning new ssh for every saved search to get the count of
> > matched messages has a way too big overhead. Is this the use case you
> > had in mind when implementing this?
>
> Great to see that you find it useful. Yes, the remote usage was in my
> mind, but it should enhance the local usage as well. We could also
> create a quicker test run using the shell.
>
> The disadvantage of using an interactive shell is that you can have
> only one operation running in the UI, and I see no way of
> interrumpting an operation unless you kill the process, but as
> starting a new shell shouldn't be a biggie, this might not be as
> limiting as might seem.
>
> The ideal would be for libnotmuch to be able to connect to a
> xapian-tcpsrv, but haven't had time to look at than, and an
> interactive shell seemed to be less time demanding to implement (and
> had the subjective promise of providing a lot of fun).
>
> > Currently, I have only one comment to the patch. For me, "repl" is a bit
> > unintuitive. I was thinking about "shell" as the name of subcommand for
> > this.
>
> I agree, maybe "shell" or "interactive" would be more intuitive, but
> when I started "repl" was the only one I could think of.
>
> Servilio
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
>

[-- Attachment #2: Type: text/html, Size: 3248 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Implement a simple read-eval-print loop.
  2010-11-21 17:35     ` Austin Clements
@ 2010-11-21 21:10       ` Michal Sojka
  2010-11-21 21:51         ` Michal Sojka
  2010-11-21 23:50         ` Austin Clements
  0 siblings, 2 replies; 8+ messages in thread
From: Michal Sojka @ 2010-11-21 21:10 UTC (permalink / raw)
  To: Austin Clements, servilio; +Cc: notmuch

On Sun, 21 Nov 2010, Austin Clements wrote:
> Out of curiosity, why not simply use SSH control mastering? 

I use control mastering, but it still takes about five seconds to display
notmuch hello screen.

> You could even make that part of the "standard" remote notmuch script,
> without requiring the user to change anything in their ssh
> configuration. 

This is a great idea. Now I use this script to invoke notmuch remotely
and I don't have to create master connection manually.

#!/bin/bash
socket="$HOME/.ssh/notmuch-connection"
if [[ ! -S $socket ]]; then
    # Create master connection in background (the connection is closed
    # after 10 minutes)
    ssh -f -M -S $socket example.org sleep 600
fi
printf -v args "%q " "$@"
ssh -S $socket example.org notmuch $args

> This should be just as fast as a remote notmuch shell, but retains the
> ability to run multiple simultaneous operations and leverages all of
> the fancy session machinery already built in to ssh. It seems to me
> that ssh already does what you want, and I'm curious what the
> advantage is to reinventing the wheel.

You are probably right that notmuch shell would not help here. The big
delay in displaying notmuch-hello is mostly caused by ping delays which
add for each execution of notmuch count. So better then notmuch shell
might be a possibility to specify several commands at once and get all
the results together. Probably, the simplest thing would be to enhance
count subcommand so that it accepts multiple queries, possibly from
stdin.

Then we could use something like the following to determine the counts
of matched messages for saved searches:

$ (echo $query1; echo $query2; ...) | notmuch count --stdin
<count1>
<count2>
...

-Michal

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Implement a simple read-eval-print loop.
  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
  1 sibling, 1 reply; 8+ messages in thread
From: Michal Sojka @ 2010-11-21 21:51 UTC (permalink / raw)
  To: Austin Clements, servilio; +Cc: notmuch

On Sun, 21 Nov 2010, Michal Sojka wrote:
> This is a great idea. Now I use this script to invoke notmuch remotely
> and I don't have to create master connection manually.
> 
> #!/bin/bash
> socket="$HOME/.ssh/notmuch-connection"
> if [[ ! -S $socket ]]; then
>     # Create master connection in background (the connection is closed
>     # after 10 minutes)
>     ssh -f -M -S $socket example.org sleep 600

The above line must be changed to 
     ssh -f -M -S $socket example.org sleep 600 >/dev/null 2>&1
Without the redirections emacs blocks until the command closes stdout,
which takes 10 minutes in this case.

> fi
> printf -v args "%q " "$@"
> ssh -S $socket example.org notmuch $args

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Implement a simple read-eval-print loop.
  2010-11-21 21:51         ` Michal Sojka
@ 2010-11-21 22:14           ` Michael Hudson
  0 siblings, 0 replies; 8+ messages in thread
From: Michael Hudson @ 2010-11-21 22:14 UTC (permalink / raw)
  To: Michal Sojka, Austin Clements, servilio; +Cc: notmuch

On Sun, 21 Nov 2010 22:51:29 +0100, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> On Sun, 21 Nov 2010, Michal Sojka wrote:
> > This is a great idea. Now I use this script to invoke notmuch remotely
> > and I don't have to create master connection manually.
> > 
> > #!/bin/bash
> > socket="$HOME/.ssh/notmuch-connection"
> > if [[ ! -S $socket ]]; then
> >     # Create master connection in background (the connection is closed
> >     # after 10 minutes)
> >     ssh -f -M -S $socket example.org sleep 600
> 
> The above line must be changed to 
>      ssh -f -M -S $socket example.org sleep 600 >/dev/null 2>&1
> Without the redirections emacs blocks until the command closes stdout,
> which takes 10 minutes in this case.

You can also use ssh -N, although that doesn't disconnect after 10
minutes. 'autossh' can also be handy in this sort of situation.

Cheers,
mwh

> > fi
> > printf -v args "%q " "$@"
> > ssh -S $socket example.org notmuch $args
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Implement a simple read-eval-print loop.
  2010-11-21 21:10       ` Michal Sojka
  2010-11-21 21:51         ` Michal Sojka
@ 2010-11-21 23:50         ` Austin Clements
  1 sibling, 0 replies; 8+ messages in thread
From: Austin Clements @ 2010-11-21 23:50 UTC (permalink / raw)
  To: Michal Sojka; +Cc: servilio, notmuch

[-- Attachment #1: Type: text/plain, Size: 652 bytes --]

What about
 notmuch count query1; notmuch count query2; ...
all as a single command issued by notmuch-hello?  That should give exactly
the same output, but eliminates the network round-trips without special
support from count.  It would be interesting to see how this compares with
your modified count and with the current round-trip-per-count approach.

On Sun, Nov 21, 2010 at 4:10 PM, Michal Sojka <sojkam1@fel.cvut.cz> wrote:

> Then we could use something like the following to determine the counts
> of matched messages for saved searches:
>
> $ (echo $query1; echo $query2; ...) | notmuch count --stdin
> <count1>
> <count2>
> ...
>
> -Michal
>

[-- Attachment #2: Type: text/html, Size: 993 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-11-21 23:50 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-20 14:20 [PATCH] Implement a simple read-eval-print loop servilio
2010-11-20 21:15 ` 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

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).