[PATCH] Implement a simple read-eval-print loop.
servilio
servilio at gmail.com
Sat Nov 20 06:20:14 PST 2010
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 at cworth.org>
* Keith Packard <keithp at keithp.com>
+ * Servilio Afre Puentes <servilio at 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
More information about the notmuch
mailing list