[PATCH v4 1/5] command-line-arguments.[ch]: new argument parsing framework for notmuch.
authorDavid Bremner <david@tethera.net>
Wed, 7 Dec 2011 19:26:35 +0000 (15:26 +2000)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:40:39 +0000 (09:40 -0800)
da/dcab90dda1154c3deee50bfa1cd742e96c5f42 [new file with mode: 0644]

diff --git a/da/dcab90dda1154c3deee50bfa1cd742e96c5f42 b/da/dcab90dda1154c3deee50bfa1cd742e96c5f42
new file mode 100644 (file)
index 0000000..f4dfa6b
--- /dev/null
@@ -0,0 +1,342 @@
+Return-Path: <bremner@pivot.cs.unb.ca>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 26153429E32\r
+       for <notmuch@notmuchmail.org>; Wed,  7 Dec 2011 11:26:58 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -2.3\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-2.3 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id g-KeGB45L3ta for <notmuch@notmuchmail.org>;\r
+       Wed,  7 Dec 2011 11:26:56 -0800 (PST)\r
+Received: from tempo.its.unb.ca (tempo.its.unb.ca [131.202.1.21])\r
+       (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id 01C83429E3B\r
+       for <notmuch@notmuchmail.org>; Wed,  7 Dec 2011 11:26:49 -0800 (PST)\r
+Received: from convex-new.cs.unb.ca ([131.202.13.154])\r
+       by tempo.its.unb.ca (8.13.8/8.13.8) with ESMTP id pB7JQhbj021338;\r
+       Wed, 7 Dec 2011 15:26:46 -0400\r
+Received: from bremner by convex-new.cs.unb.ca with local (Exim 4.72)\r
+       (envelope-from <bremner@pivot.cs.unb.ca>)\r
+       id 1RYN8d-0004Pg-SY; Wed, 07 Dec 2011 15:26:43 -0400\r
+From: David Bremner <david@tethera.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH v4 1/5] command-line-arguments.[ch]: new argument parsing\r
+       framework for notmuch.\r
+Date: Wed,  7 Dec 2011 15:26:35 -0400\r
+Message-Id: <1323285999-16870-2-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 1.7.5.4\r
+In-Reply-To: <1323285999-16870-1-git-send-email-david@tethera.net>\r
+References: <87iplso9c9.fsf@zancas.localnet>\r
+       <1323285999-16870-1-git-send-email-david@tethera.net>\r
+Cc: David Bremner <bremner@debian.org>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Wed, 07 Dec 2011 19:26:58 -0000\r
+\r
+From: David Bremner <bremner@debian.org>\r
+\r
+As we noticed when Jani kindly converted things to getopt_long, much\r
+of the work in argument parsing in notmuch is due to the the key-value\r
+style arguments like --format=(raw|json|text).\r
+\r
+The framework here provides positional arguments, simple switches,\r
+and --key=value style arguments that can take a value being an integer,\r
+a string, or one of a set of keywords.\r
+---\r
+ Makefile.local           |    1 +\r
+ command-line-arguments.c |  155 ++++++++++++++++++++++++++++++++++++++++++++++\r
+ command-line-arguments.h |   80 ++++++++++++++++++++++++\r
+ notmuch-client.h         |    1 +\r
+ 4 files changed, 237 insertions(+), 0 deletions(-)\r
+ create mode 100644 command-line-arguments.c\r
+ create mode 100644 command-line-arguments.h\r
+\r
+diff --git a/Makefile.local b/Makefile.local\r
+index 15e6d88..28e371a 100644\r
+--- a/Makefile.local\r
++++ b/Makefile.local\r
+@@ -295,6 +295,7 @@ clean:\r
+ distclean: clean\r
\r
+ notmuch_client_srcs =         \\r
++      command-line-arguments.c\\r
+       debugger.c              \\r
+       gmime-filter-reply.c    \\r
+       gmime-filter-headers.c  \\r
+diff --git a/command-line-arguments.c b/command-line-arguments.c\r
+new file mode 100644\r
+index 0000000..3aa87aa\r
+--- /dev/null\r
++++ b/command-line-arguments.c\r
+@@ -0,0 +1,155 @@\r
++#include <assert.h>\r
++#include <string.h>\r
++#include <stdio.h>\r
++#include "error_util.h"\r
++#include "command-line-arguments.h"\r
++\r
++/*\r
++  Search the array of keywords for a given argument, assigning the\r
++  output variable to the corresponding value.  Return FALSE if nothing\r
++  matches.\r
++*/\r
++\r
++static notmuch_bool_t\r
++_process_keyword_arg (const notmuch_opt_desc_t *arg_desc, const char *arg_str) {\r
++\r
++    notmuch_keyword_t *keywords = arg_desc->keywords;\r
++\r
++    while (keywords->name) {\r
++      if (strcmp (arg_str, keywords->name) == 0) {\r
++          if (arg_desc->output_var) {\r
++              *((int *)arg_desc->output_var) = keywords->value;\r
++          }\r
++          return TRUE;\r
++      }\r
++      keywords++;\r
++    }\r
++    fprintf (stderr, "unknown keyword: %s\n", arg_str);\r
++    return FALSE;\r
++}\r
++\r
++/*\r
++   Search for the {pos_arg_index}th position argument, return FALSE if\r
++   that does not exist.\r
++*/\r
++\r
++notmuch_bool_t\r
++parse_position_arg (const char *arg_str, int pos_arg_index,\r
++                  const notmuch_opt_desc_t *arg_desc) {\r
++\r
++    int pos_arg_counter = 0;\r
++    while (arg_desc->opt_type != NOTMUCH_OPT_END){\r
++      if (arg_desc->opt_type == NOTMUCH_OPT_POSITION) {\r
++          if (pos_arg_counter == pos_arg_index) {\r
++              if (arg_desc->output_var) {\r
++                  *((const char **)arg_desc->output_var) = arg_str;\r
++              }\r
++              return TRUE;\r
++          }\r
++          pos_arg_counter++;\r
++      }\r
++      arg_desc++;\r
++    }\r
++    return FALSE;\r
++}\r
++\r
++/*\r
++ * Search for a non-positional (i.e. starting with --) argument matching arg,\r
++ * parse a possible value, and assign to *output_var\r
++ */\r
++\r
++notmuch_bool_t\r
++parse_option (const char *arg,\r
++            const notmuch_opt_desc_t *options) {\r
++\r
++    assert(arg);\r
++    assert(options);\r
++\r
++    arg += 2;\r
++\r
++    const notmuch_opt_desc_t *try = options;\r
++    while (try->opt_type != NOTMUCH_OPT_END) {\r
++      if (try->name && strncmp (arg, try->name, strlen (try->name)) == 0) {\r
++          char next = arg[strlen (try->name)];\r
++          const char *value= arg+strlen(try->name)+1;\r
++\r
++          char *endptr;\r
++\r
++          /* Everything but boolean arguments (switches) needs a\r
++           * delimiter, and a non-zero length value\r
++           */\r
++\r
++          if (try->opt_type != NOTMUCH_OPT_BOOLEAN) {\r
++              if (next != '=' && next != ':') return FALSE;\r
++              if (value[0] == 0) return FALSE;\r
++          } else {\r
++              if (next != 0) return FALSE;\r
++          }\r
++\r
++          if (try->output_var == NULL)\r
++              INTERNAL_ERROR ("output pointer NULL for option %s", try->name);\r
++\r
++          switch (try->opt_type) {\r
++          case NOTMUCH_OPT_KEYWORD:\r
++              return _process_keyword_arg (try, value);\r
++              break;\r
++          case NOTMUCH_OPT_BOOLEAN:\r
++              *((notmuch_bool_t *)try->output_var) = TRUE;\r
++              return TRUE;\r
++              break;\r
++          case NOTMUCH_OPT_INT:\r
++              *((int *)try->output_var) = strtol (value, &endptr, 10);\r
++              return (*endptr == 0);\r
++              break;\r
++          case NOTMUCH_OPT_STRING:\r
++              *((const char **)try->output_var) = value;\r
++              return TRUE;\r
++              break;\r
++          case NOTMUCH_OPT_POSITION:\r
++          case NOTMUCH_OPT_END:\r
++          default:\r
++              INTERNAL_ERROR ("unknown or unhandled option type %d", try->opt_type);\r
++              /*UNREACHED*/\r
++          }\r
++      }\r
++      try++;\r
++    }\r
++    fprintf (stderr, "Unrecognized option: --%s\n", arg);\r
++    return FALSE;\r
++}\r
++\r
++/* See command-line-arguments.h for description */\r
++int\r
++parse_arguments (int argc, char **argv,\r
++               const notmuch_opt_desc_t *options, int opt_index) {\r
++\r
++    int pos_arg_index = 0;\r
++    notmuch_bool_t more_args = TRUE;\r
++\r
++    while (more_args && opt_index < argc) {\r
++      if (strncmp (argv[opt_index],"--",2) != 0) {\r
++\r
++          more_args = parse_position_arg (argv[opt_index], pos_arg_index, options);\r
++\r
++          if (more_args) {\r
++              pos_arg_index++;\r
++              opt_index++;\r
++          }\r
++\r
++      } else {\r
++\r
++          if (strlen (argv[opt_index]) == 2)\r
++              return opt_index+1;\r
++\r
++          more_args = parse_option (argv[opt_index], options);\r
++          if (more_args) {\r
++              opt_index++;\r
++          } else {\r
++              opt_index = -1;\r
++          }\r
++\r
++      }\r
++    }\r
++\r
++    return opt_index;\r
++}\r
+diff --git a/command-line-arguments.h b/command-line-arguments.h\r
+new file mode 100644\r
+index 0000000..af8b1ce\r
+--- /dev/null\r
++++ b/command-line-arguments.h\r
+@@ -0,0 +1,80 @@\r
++#ifndef NOTMUCH_OPTS_H\r
++#define NOTMUCH_OPTS_H\r
++\r
++#include "notmuch.h"\r
++\r
++enum notmuch_opt_type {\r
++    NOTMUCH_OPT_END = 0,\r
++    NOTMUCH_OPT_BOOLEAN,      /* --verbose              */\r
++    NOTMUCH_OPT_INT,          /* --frob=8               */\r
++    NOTMUCH_OPT_KEYWORD,      /* --format=raw|json|text */\r
++    NOTMUCH_OPT_STRING,               /* --file=/tmp/gnarf.txt  */\r
++    NOTMUCH_OPT_POSITION      /* notmuch dump pos_arg   */\r
++};\r
++\r
++/*\r
++ * Describe one of the possibilities for a keyword option\r
++ * 'value' will be copied to the output variable\r
++ */\r
++\r
++typedef struct notmuch_keyword {\r
++    const char *name;\r
++    int value;\r
++} notmuch_keyword_t;\r
++\r
++/*\r
++ * Describe one option.\r
++ *\r
++ * First two parameters are mandatory.\r
++ *\r
++ * name is mandatory _except_ for positional arguments.\r
++ *\r
++ * arg_id is currently unused, but could define short arguments.\r
++ *\r
++ * keywords is a (possibly NULL) pointer to an array of keywords\r
++ */\r
++typedef struct notmuch_opt_desc {\r
++    enum notmuch_opt_type opt_type;\r
++    void *output_var;\r
++    const char *name;\r
++    int  arg_id;\r
++    struct notmuch_keyword *keywords;\r
++} notmuch_opt_desc_t;\r
++\r
++\r
++/*\r
++  This is the main entry point for command line argument parsing.\r
++\r
++  Parse command line arguments according to structure options,\r
++  starting at position opt_index.\r
++\r
++  All output of parsed values is via pointers in options.\r
++\r
++  Parsing stops at -- (consumed) or at the (k+1)st argument\r
++  not starting with -- (a "positional argument") if options contains\r
++  k positional argument descriptors.\r
++\r
++  Returns the index of first non-parsed argument, or -1 in case of error.\r
++\r
++*/\r
++int\r
++parse_arguments (int argc, char **argv, const notmuch_opt_desc_t *options, int opt_index);\r
++\r
++/*\r
++ * If the argument parsing loop provided by parse_arguments is not\r
++ * flexible enough, then the user might be interested in the following\r
++ * routines, but note that the API to parse_option might have to\r
++ * change. See command-line-arguments.c for descriptions of these\r
++ * functions.\r
++ */\r
++\r
++notmuch_bool_t\r
++parse_option (const char *arg, const notmuch_opt_desc_t* options);\r
++\r
++notmuch_bool_t\r
++parse_position_arg (const char *arg,\r
++                  int position_arg_index,\r
++                  const notmuch_opt_desc_t* options);\r
++\r
++\r
++#endif\r
+diff --git a/notmuch-client.h b/notmuch-client.h\r
+index b50cb38..703f856 100644\r
+--- a/notmuch-client.h\r
++++ b/notmuch-client.h\r
+@@ -238,4 +238,5 @@ notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,\r
+ notmuch_bool_t\r
+ debugger_is_active (void);\r
\r
++#include "command-line-arguments.h"\r
+ #endif\r
+-- \r
+1.7.5.4\r
+\r