[PATCH v2] cli: make the command line parser's errors more informative.
authorMark Walters <markwalters1009@gmail.com>
Tue, 5 Jun 2012 14:36:36 +0000 (15:36 +0100)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:47:37 +0000 (09:47 -0800)
4e/7ef8ac83c4723e53af951fb8a87337f3d4fb96 [new file with mode: 0644]

diff --git a/4e/7ef8ac83c4723e53af951fb8a87337f3d4fb96 b/4e/7ef8ac83c4723e53af951fb8a87337f3d4fb96
new file mode 100644 (file)
index 0000000..75eb63c
--- /dev/null
@@ -0,0 +1,216 @@
+Return-Path: <markwalters1009@gmail.com>\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 B1028431FB6\r
+       for <notmuch@notmuchmail.org>; Tue,  5 Jun 2012 07:36:44 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0.201\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0.201 tagged_above=-999 required=5\r
+       tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,\r
+       FREEMAIL_ENVFROM_END_DIGIT=1, FREEMAIL_FROM=0.001,\r
+       RCVD_IN_DNSWL_LOW=-0.7] 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 uV8m17Ibeigq for <notmuch@notmuchmail.org>;\r
+       Tue,  5 Jun 2012 07:36:42 -0700 (PDT)\r
+Received: from mail-wg0-f45.google.com (mail-wg0-f45.google.com\r
+ [74.125.82.45])       (using TLSv1 with cipher RC4-SHA (128/128 bits))        (No client\r
+ certificate requested)        by olra.theworths.org (Postfix) with ESMTPS id\r
+ 8C2CD431FAE   for <notmuch@notmuchmail.org>; Tue,  5 Jun 2012 07:36:42 -0700\r
+ (PDT)\r
+Received: by wgbdt14 with SMTP id dt14so4367607wgb.2\r
+       for <notmuch@notmuchmail.org>; Tue, 05 Jun 2012 07:36:39 -0700 (PDT)\r
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113;\r
+       h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references;\r
+       bh=HWxzz8H+vobZV9ooepheMwydwZagFl56U1Ylacl7IMo=;\r
+       b=NZw9goUOlmoSQD9T4S57D54wU5K5A4c2dtBiNfX9ZgB8iZ6IB0eof/CFXbSg/N6Bcd\r
+       x93LF3eIfq3XkBkrwyOx/NrogiQDOfZymXhqYrrsSNustB93r4INq1J+Ln8Hz/g0zRN5\r
+       655mMI/MT8WRbcNuXIcwC3acDbpaw6pnU7dcUcBn+P9BAVBAuyP9n3pHyZD9sJxG0x2E\r
+       +HihLvZ0WWBbzGhcmUxceA4//YEI/p4YF3CuTgZQVeUKHLdEpkUj3hoEnkt4W+7L20j8\r
+       YJzj4V8vJQkhKBdjxpdbeoP5268QAeRS7tk/YK7hglNi/pzBa0Cavaxd91T6IalzzDqg\r
+       8mSQ==\r
+Received: by 10.216.206.164 with SMTP id l36mr14126494weo.154.1338906999766;\r
+       Tue, 05 Jun 2012 07:36:39 -0700 (PDT)\r
+Received: from localhost (94-192-233-223.zone6.bethere.co.uk.\r
+ [94.192.233.223])     by mx.google.com with ESMTPS id\r
+ m20sm27402051wie.6.2012.06.05.07.36.37        (version=TLSv1/SSLv3 cipher=OTHER);\r
+       Tue, 05 Jun 2012 07:36:38 -0700 (PDT)\r
+From: Mark Walters <markwalters1009@gmail.com>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH v2] cli: make the command line parser's errors more\r
+       informative.\r
+Date: Tue,  5 Jun 2012 15:36:36 +0100\r
+Message-Id: <1338906996-18720-1-git-send-email-markwalters1009@gmail.com>\r
+X-Mailer: git-send-email 1.7.9.1\r
+In-Reply-To: <87oboxakus.fsf@qmul.ac.uk>\r
+References: <87oboxakus.fsf@qmul.ac.uk>\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: Tue, 05 Jun 2012 14:36:44 -0000\r
+\r
+Previously, the cli parser was a little erratic in what errors it\r
+reported and would fail silently in many cases (for example, when no\r
+argument was passed to an integer option). This was particularly\r
+annoying as the user could not (easily) tell whether the command\r
+failed or just there were no search results.\r
+\r
+This patch tries to make the handling consistent and return a helpful\r
+error message in all cases.\r
+---\r
+ command-line-arguments.c |   76 ++++++++++++++++++++++++++++++----------------\r
+ 1 files changed, 50 insertions(+), 26 deletions(-)\r
+\r
+diff --git a/command-line-arguments.c b/command-line-arguments.c\r
+index b0a0dab..bf9aeca 100644\r
+--- a/command-line-arguments.c\r
++++ b/command-line-arguments.c\r
+@@ -15,7 +15,7 @@ _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, char next, const char\r
\r
+     const notmuch_keyword_t *keywords = arg_desc->keywords;\r
\r
+-    if (next == 0) {\r
++    if (next == '\0') {\r
+       /* No keyword given */\r
+       arg_str = "";\r
+     }\r
+@@ -29,17 +29,17 @@ _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, char next, const char\r
+       }\r
+       keywords++;\r
+     }\r
+-    if (next != 0)\r
+-      fprintf (stderr, "unknown keyword: %s\n", arg_str);\r
++    if (next != '\0')\r
++      fprintf (stderr, "Unknown keyword argument \"%s\" for option \"%s\".\n", arg_str, arg_desc->name);\r
+     else\r
+-      fprintf (stderr, "option %s needs a keyword\n", arg_desc->name);\r
++      fprintf (stderr, "Option \"%s\" needs a keyword argument.\n", arg_desc->name);\r
+     return FALSE;\r
+ }\r
\r
+ static notmuch_bool_t\r
+ _process_boolean_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) {\r
\r
+-    if (next == 0) {\r
++    if (next == '\0') {\r
+       *((notmuch_bool_t *)arg_desc->output_var) = TRUE;\r
+       return TRUE;\r
+     }\r
+@@ -51,9 +51,43 @@ _process_boolean_arg (const notmuch_opt_desc_t *arg_desc, char next, const char\r
+       *((notmuch_bool_t *)arg_desc->output_var) = TRUE;\r
+       return TRUE;\r
+     }\r
++    fprintf (stderr, "Unknown argument \"%s\" for (boolean) option \"%s\".\n", arg_str, arg_desc->name);\r
+     return FALSE;\r
+ }\r
\r
++static notmuch_bool_t\r
++_process_int_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) {\r
++\r
++    char *endptr;\r
++    if (next == '\0' || arg_str[0] == '\0') {\r
++      fprintf (stderr, "Option \"%s\" needs an integer argument.\n", arg_desc->name);\r
++      return FALSE;\r
++    }\r
++\r
++    *((int *)arg_desc->output_var) = strtol (arg_str, &endptr, 10);\r
++    if (*endptr == '\0')\r
++      return TRUE;\r
++\r
++    fprintf (stderr, "Unable to parse argument \"%s\" for option \"%s\" as an integer.\n",\r
++           arg_str, arg_desc->name);\r
++    return FALSE;\r
++}\r
++\r
++static notmuch_bool_t\r
++_process_string_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) {\r
++\r
++    if (next == '\0') {\r
++      fprintf (stderr, "Option \"%s\" needs a string argument.\n", arg_desc->name);\r
++      return FALSE;\r
++    }\r
++    if (arg_str[0] == '\0') {\r
++      fprintf (stderr, "String argument for option \"%s\" must be non-empty.\n", arg_desc->name);\r
++      return FALSE;\r
++    }\r
++    *((const char **)arg_desc->output_var) = arg_str;\r
++    return TRUE;\r
++}\r
++\r
+ /*\r
+    Search for the {pos_arg_index}th position argument, return FALSE if\r
+    that does not exist.\r
+@@ -93,26 +127,19 @@ parse_option (const char *arg,\r
\r
+     arg += 2;\r
\r
+-    const notmuch_opt_desc_t *try = options;\r
+-    while (try->opt_type != NOTMUCH_OPT_END) {\r
++    const notmuch_opt_desc_t *try;\r
++    for (try = options; try->opt_type != NOTMUCH_OPT_END; try++) {\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. Boolean\r
+-           * arguments may take an optional =true or =false value.\r
+-           */\r
+-          if (next != '=' && next != ':' && next != 0) return FALSE;\r
+-          if (next == 0) {\r
+-              if (try->opt_type != NOTMUCH_OPT_BOOLEAN &&\r
+-                  try->opt_type != NOTMUCH_OPT_KEYWORD)\r
+-                  return FALSE;\r
+-          } else {\r
+-              if (value[0] == 0) return FALSE;\r
+-          }\r
++          /* If we have not reached the end of the argument\r
++             (i.e. the next character is not a space or delimiter)\r
++             then the argument could still match a longer option\r
++             name later in the option table.\r
++          */\r
++          if (next != '=' && next != ':' && next != '\0')\r
++              continue;\r
\r
+           if (try->output_var == NULL)\r
+               INTERNAL_ERROR ("output pointer NULL for option %s", try->name);\r
+@@ -125,12 +152,10 @@ parse_option (const char *arg,\r
+               return _process_boolean_arg (try, next, value);\r
+               break;\r
+           case NOTMUCH_OPT_INT:\r
+-              *((int *)try->output_var) = strtol (value, &endptr, 10);\r
+-              return (*endptr == 0);\r
++              return _process_int_arg (try, next, value);\r
+               break;\r
+           case NOTMUCH_OPT_STRING:\r
+-              *((const char **)try->output_var) = value;\r
+-              return TRUE;\r
++              return _process_string_arg (try, next, value);\r
+               break;\r
+           case NOTMUCH_OPT_POSITION:\r
+           case NOTMUCH_OPT_END:\r
+@@ -139,7 +164,6 @@ parse_option (const char *arg,\r
+               /*UNREACHED*/\r
+           }\r
+       }\r
+-      try++;\r
+     }\r
+     fprintf (stderr, "Unrecognized option: --%s\n", arg);\r
+     return FALSE;\r
+-- \r
+1.7.9.1\r
+\r