Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 2425F40DF1C for ; Sat, 20 Nov 2010 06:20:28 -0800 (PST) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -1.999 X-Spam-Level: X-Spam-Status: No, score=-1.999 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001] autolearn=ham Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id QdYsUFJ0lty8 for ; Sat, 20 Nov 2010 06:20:17 -0800 (PST) Received: from mail-ww0-f45.google.com (mail-ww0-f45.google.com [74.125.82.45]) by olra.theworths.org (Postfix) with ESMTP id B683940DF1B for ; Sat, 20 Nov 2010 06:20:16 -0800 (PST) Received: by wwi18 with SMTP id 18so2534526wwi.2 for ; Sat, 20 Nov 2010 06:20:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:received:date:message-id :subject:from:to:content-type; bh=K1obo4pj9/feYT1gxWJBJmdKYg3vHUk/OlL/TH+7PVE=; b=rJeeievScTWIJ4EssCeZo8N8VxqdbQ6yeaY/G9DUPdEPAZMAwaAQ2eS4PKwg9v29PV /kNwDeeHJOyvLQOD5pdLKv2uf3HjkYF9QqIevq/XKBFPamcvrGBqmu+vEVyM7MCsUDEm KVB3ZQnLQwOKcKsgftzXDpmXgx8aBXCp6N/RM= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:content-type; b=b+QzKjZDTlSNErnznrwrggdNwP+i4uQuhQXWHHuKZ03IYd2mE+rHsfYbKprZeHtWk9 Xu5NG9QDKYOrYOMP8+aHTX4qFe3VqKZhTqDPOuJPtUzVpuU8dYHgRqx/Z9+jn06JH3w0 wd266hYCWnKzui+5AE9q1XwNpNSEjY34zdv88= MIME-Version: 1.0 Received: by 10.216.231.82 with SMTP id k60mr2127209weq.88.1290262814773; Sat, 20 Nov 2010 06:20:14 -0800 (PST) Received: by 10.216.166.195 with HTTP; Sat, 20 Nov 2010 06:20:14 -0800 (PST) Date: Sat, 20 Nov 2010 09:20:14 -0500 Message-ID: Subject: [PATCH] Implement a simple read-eval-print loop. From: servilio To: notmuch Content-Type: text/plain; charset=ISO-8859-1 X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 20 Nov 2010 14:20:28 -0000 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 +#include +#include + +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 < * Keith Packard + * Servilio Afre Puentes */ +#include + +#include + +#include +#include +#include + #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]
. [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