--- /dev/null
+Return-Path: <bremner@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 B4EFC429E31\r
+ for <notmuch@notmuchmail.org>; Thu, 1 Dec 2011 22:42:14 -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 xO-yl8ov3Bok for <notmuch@notmuchmail.org>;\r
+ Thu, 1 Dec 2011 22:42:14 -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 5A7CF429E38\r
+ for <notmuch@notmuchmail.org>; Thu, 1 Dec 2011 22:42:13 -0800 (PST)\r
+Received: from rocinante.cs.unb.ca (m3.kereda.com [207.194.238.3] (may be\r
+ forged)) (authenticated bits=0)\r
+ by tempo.its.unb.ca (8.13.8/8.13.8) with ESMTP id pB26g5lX010488\r
+ (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NO);\r
+ Fri, 2 Dec 2011 02:42:07 -0400\r
+Received: from bremner by rocinante.cs.unb.ca with local (Exim 4.76)\r
+ (envelope-from <bremner@tesseract.cs.unb.ca>)\r
+ id 1RWMou-00038i-Ob; Thu, 01 Dec 2011 22:42:04 -0800\r
+From: David Bremner <david@tethera.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: [RFC Patch v2 1/3] notmuch-opts.[ch]: new argument parsing framework\r
+ for notmuch.\r
+Date: Thu, 1 Dec 2011 22:41:52 -0800\r
+Message-Id: <1322808114-11854-1-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 1.7.5.4\r
+In-Reply-To: <1322702130-26068-3-git-send-email-bremner@debian.org>\r
+References: <1322702130-26068-3-git-send-email-bremner@debian.org>\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: Fri, 02 Dec 2011 06:42:14 -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
+In this version I implement Austin Clements' suggestion of basing the\r
+main API on taking pointers to output variables.\r
+---\r
+ Makefile.local | 1 +\r
+ notmuch-opts.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
+ notmuch-opts.h | 50 +++++++++++++++++++++++\r
+ 3 files changed, 171 insertions(+), 0 deletions(-)\r
+ create mode 100644 notmuch-opts.c\r
+ create mode 100644 notmuch-opts.h\r
+\r
+diff --git a/Makefile.local b/Makefile.local\r
+index c94402b..6606be8 100644\r
+--- a/Makefile.local\r
++++ b/Makefile.local\r
+@@ -303,6 +303,7 @@ notmuch_client_srcs = \\r
+ notmuch-count.c \\r
+ notmuch-dump.c \\r
+ notmuch-new.c \\r
++ notmuch-opts.c \\r
+ notmuch-reply.c \\r
+ notmuch-restore.c \\r
+ notmuch-search.c \\r
+diff --git a/notmuch-opts.c b/notmuch-opts.c\r
+new file mode 100644\r
+index 0000000..a8c5223\r
+--- /dev/null\r
++++ b/notmuch-opts.c\r
+@@ -0,0 +1,120 @@\r
++#include <assert.h>\r
++#include <string.h>\r
++#include <stdio.h>\r
++#include "error_util.h"\r
++#include "notmuch-opts.h"\r
++\r
++/*\r
++ Search the list 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
++ if (arg_str[0] != ':' && arg_str[0] != '=') {\r
++ return FALSE;\r
++ }\r
++\r
++ /* skip delimiter */\r
++ 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
++ 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, const notmuch_opt_desc_t *arg_desc) {\r
++\r
++ int pos_arg_counter = 0;\r
++ while (arg_desc->name){\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
++notmuch_opt_result_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->name) {\r
++ if (strncmp (arg, try->name, strlen(try->name)) == 0) {\r
++\r
++ switch (try->opt_type) {\r
++ case NOTMUCH_OPT_KEYWORD:\r
++ return _process_keyword_arg (try, arg+strlen(try->name));\r
++ break;\r
++ case NOTMUCH_OPT_BOOLEAN:\r
++ return TRUE;\r
++ break;\r
++ case NOTMUCH_OPT_POSITION:\r
++ case NOTMUCH_OPT_NULL:\r
++ default:\r
++ INTERNAL_ERROR ("unknown or unhandled option type %d", try->opt_type);\r
++ /*UNREACHED*/\r
++ }\r
++ }\r
++ try++;\r
++ }\r
++ return FALSE;\r
++}\r
++\r
++int\r
++notmuch_parse_args (int argc, char **argv, 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
++ opt_index++;\r
++ }\r
++ }\r
++\r
++ return opt_index;\r
++}\r
+diff --git a/notmuch-opts.h b/notmuch-opts.h\r
+new file mode 100644\r
+index 0000000..75d65cb\r
+--- /dev/null\r
++++ b/notmuch-opts.h\r
+@@ -0,0 +1,50 @@\r
++#ifndef NOTMUCH_OPTS_H\r
++#define NOTMUCH_OPTS_H\r
++\r
++#include "notmuch.h"\r
++\r
++enum notmuch_opt_type {\r
++ NOTMUCH_OPT_NULL = 0,\r
++ NOTMUCH_OPT_BOOLEAN,\r
++ NOTMUCH_OPT_KEYWORD,\r
++ NOTMUCH_OPT_POSITION\r
++};\r
++\r
++typedef enum notmuch_opt_result {\r
++ NOTMUCH_OPT_ERROR = -2,\r
++ NOTMUCH_OPT_GOT_POS = -1,\r
++ NOTMUCH_OPT_STOP = 0,\r
++ NOTMUCH_OPT_SUCCESS = 1\r
++} notmuch_opt_result_t;\r
++\r
++typedef struct notmuch_keyword {\r
++ const char *name;\r
++ int value;\r
++} notmuch_keyword_t;\r
++\r
++typedef struct notmuch_opt_desc {\r
++ const char *name;\r
++ int arg_id;\r
++ enum notmuch_opt_type opt_type;\r
++ struct notmuch_keyword *keywords;\r
++ void *output_var;\r
++} notmuch_opt_desc_t;\r
++\r
++typedef struct notmuch_opt {\r
++ int arg_id;\r
++ int keyword_id;\r
++ const char *string;\r
++} notmuch_opt_t;\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
++int\r
++notmuch_parse_args(int argc, char **argv, const notmuch_opt_desc_t *options, int opt_index);\r
++\r
++#endif\r
+-- \r
+1.7.5.4\r
+\r