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 <cwo...@cworth.org> * Keith Packard <kei...@keithp.com> + * Servilio Afre Puentes <servi...@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 _______________________________________________ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch