Provide more helpful error message in getopt
authorUlrich Drepper <drepper@gmail.com>
Sun, 15 May 2011 17:35:09 +0000 (13:35 -0400)
committerUlrich Drepper <drepper@gmail.com>
Sun, 15 May 2011 17:35:09 +0000 (13:35 -0400)
If provide with an ambiguous long option we now show all the possibilities.

ChangeLog
NEWS
posix/Makefile
posix/getopt.c
posix/tst-getopt_long1.c [new file with mode: 0644]

index 47ca10d..0c0aa6a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2011-05-15  Ulrich Drepper  <drepper@gmail.com>
 
+       [BZ #7101]
+       * posix/getopt.c (_getopt_internal_r): List all ambigious possibilities
+       when an incomplete long option is used.
+       * posix/tst-getopt_long1.c: New file.
+       * posix/Makefile (tests): Add tst-getopt_long1.
+
        [BZ #10138]
        * scripts/config.guess: Update from autoconf-2.68.
        * scripts/config.sub: Likewise.
diff --git a/NEWS b/NEWS
index 43c3a6c..7100e1d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,13 +9,13 @@ Version 2.14
 
 * The following bugs are resolved with this release:
 
-  386, 9730, 9732, 9809, 10138, 10149, 10157, 11257, 11258, 11487, 11532,
-  11578, 11653, 11668, 11724, 11901, 11945, 11947, 11952, 12052, 12083,
-  12158, 12178, 12200, 12346, 12393, 12420, 12432, 12445, 12449, 12453,
-  12454, 12460, 12469, 12489, 12509, 12510, 12511, 12518, 12527, 12541,
-  12545, 12551, 12582, 12583, 12587, 12597, 12601, 12611, 12625, 12626,
-  12631, 12650, 12653, 12655, 12660, 12681, 12685, 12711, 12713, 12714,
-  12717, 12723, 12724, 12734, 12738
+  386, 7101, 9730, 9732, 9809, 10138, 10149, 10157, 11257, 11258, 11487,
+  11532, 11578, 11653, 11668, 11724, 11901, 11945, 11947, 11952, 12052,
+  12083, 12158, 12178, 12200, 12346, 12393, 12420, 12432, 12445, 12449,
+  12453, 12454, 12460, 12469, 12489, 12509, 12510, 12511, 12518, 12527,
+  12541, 12545, 12551, 12582, 12583, 12587, 12597, 12601, 12611, 12625,
+  12626, 12631, 12650, 12653, 12655, 12660, 12681, 12685, 12711, 12713,
+  12714, 12717, 12723, 12724, 12734, 12738
 
 * The RPC implementation in libc is obsoleted.  Old programs keep working
   but new programs cannot be linked with the routines in libc anymore.
index 373e50b..e89f21e 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 1991-1999, 2000-2007, 2009, 2010 Free Software Foundation, Inc.
+# Copyright (C) 1991-2007, 2009, 2010, 2011 Free Software Foundation, Inc.
 # This file is part of the GNU C Library.
 
 # The GNU C Library is free software; you can redistribute it and/or
@@ -94,7 +94,7 @@ tests         := tstgetopt testfnm runtests runptests      \
                   tst-rfc3484-3 \
                   tst-getaddrinfo3 tst-fnmatch2 tst-cpucount tst-cpuset \
                   bug-getopt1 bug-getopt2 bug-getopt3 bug-getopt4 \
-                  bug-getopt5
+                  bug-getopt5 tst-getopt_long1
 xtests         := bug-ga2
 ifeq (yes,$(build-shared))
 test-srcs      := globtest
index 2746364..db89abf 100644 (file)
@@ -2,7 +2,7 @@
    NOTE: getopt is part of the C library, so if you don't know what
    "Keep this file name-space clean" means, talk to drepper@gnu.org
    before changing it!
-   Copyright (C) 1987-1996,1998-2004,2008,2009,2010
+   Copyright (C) 1987-1996,1998-2004,2008,2009,2010,2011
    Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -526,23 +526,28 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring,
                            || !strchr (optstring, argv[d->optind][1])))))
     {
       char *nameend;
+      unsigned int namelen;
       const struct option *p;
       const struct option *pfound = NULL;
+      struct option_list
+      {
+       const struct option *p;
+       struct option_list *next;
+      } *ambig_list = NULL;
       int exact = 0;
-      int ambig = 0;
       int indfound = -1;
       int option_index;
 
       for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
        /* Do nothing.  */ ;
+      namelen = nameend - d->__nextchar;
 
       /* Test all long options for either exact match
         or abbreviated matches.  */
       for (p = longopts, option_index = 0; p->name; p++, option_index++)
-       if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+       if (!strncmp (p->name, d->__nextchar, namelen))
          {
-           if ((unsigned int) (nameend - d->__nextchar)
-               == (unsigned int) strlen (p->name))
+           if (namelen == (unsigned int) strlen (p->name))
              {
                /* Exact match found.  */
                pfound = p;
@@ -560,35 +565,71 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring,
                     || pfound->has_arg != p->has_arg
                     || pfound->flag != p->flag
                     || pfound->val != p->val)
-             /* Second or later nonexact match found.  */
-             ambig = 1;
+             {
+               /* Second or later nonexact match found.  */
+               struct option_list *newp = alloca (sizeof (*newp));
+               newp->p = p;
+               newp->next = ambig_list;
+               ambig_list = newp;
+             }
          }
 
-      if (ambig && !exact)
+      if (ambig_list != NULL && !exact)
        {
          if (print_errors)
            {
+             struct option_list first;
+             first.p = pfound;
+             first.next = ambig_list;
+             ambig_list = &first;
+
 #if defined _LIBC && defined USE_IN_LIBIO
-             char *buf;
+             char *buf = NULL;
+             size_t buflen = 0;
 
-             if (__asprintf (&buf, _("%s: option '%s' is ambiguous\n"),
-                             argv[0], argv[d->optind]) >= 0)
+             FILE *fp = open_memstream (&buf, &buflen);
+             if (fp != NULL)
                {
-                 _IO_flockfile (stderr);
+                 fprintf (fp,
+                          _("%s: option '%s' is ambiguous; possibilities:"),
+                          argv[0], argv[d->optind]);
 
-                 int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
-                 ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+                 do
+                   {
+                     fprintf (fp, " '--%s'", ambig_list->p->name);
+                     ambig_list = ambig_list->next;
+                   }
+                 while (ambig_list != NULL);
 
-                 __fxprintf (NULL, "%s", buf);
+                 fputc_unlocked ('\n', fp);
 
-                 ((_IO_FILE *) stderr)->_flags2 = old_flags2;
-                 _IO_funlockfile (stderr);
+                 if (__builtin_expect (fclose (fp) != EOF, 1))
+                   {
+                     _IO_flockfile (stderr);
 
-                 free (buf);
+                     int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+                     ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+                     __fxprintf (NULL, "%s", buf);
+
+                     ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+                     _IO_funlockfile (stderr);
+
+                     free (buf);
+                   }
                }
 #else
-             fprintf (stderr, _("%s: option '%s' is ambiguous\n"),
+             fprintf (stderr,
+                      _("%s: option '%s' is ambiguous; possibilities:"),
                       argv[0], argv[d->optind]);
+             do
+               {
+                 fprintf (stderr, " '--%s'", ambig_list->p->name);
+                 ambig_list = ambig_list->next;
+               }
+             while (ambig_list != NULL);
+
+             fputc ('\n', stderr);
 #endif
            }
          d->__nextchar += strlen (d->__nextchar);
diff --git a/posix/tst-getopt_long1.c b/posix/tst-getopt_long1.c
new file mode 100644 (file)
index 0000000..e0ecd12
--- /dev/null
@@ -0,0 +1,62 @@
+static void do_prepare (void);
+#define PREPARE(argc, argv) do_prepare ()
+static int do_test (void);
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+
+static char *fname;
+
+
+static void
+do_prepare (void)
+{
+  if (create_temp_file ("tst-getopt_long1", &fname) < 0)
+    {
+      printf ("cannot create temp file: %m\n");
+      exit (1);
+    }
+}
+
+
+static const struct option opts[] =
+  {
+    { "one", no_argument, NULL, '1' },
+    { "two", no_argument, NULL, '2' },
+    { "one-one", no_argument, NULL, '3' },
+    { "four", no_argument, NULL, '4' },
+    { "onto", no_argument, NULL, '5' },
+    { NULL, 0, NULL, 0 }
+  };
+
+
+static int
+do_test (void)
+{
+  if (freopen (fname, "w+", stderr) == NULL)
+    {
+      printf ("freopen failed: %m\n");
+      return 1;
+    }
+
+  char *argv[] = { "program", "--on" };
+  int argc = 2;
+
+  int c = getopt_long (argc, argv, "12345", opts, NULL);
+  printf ("return value: %c\n", c);
+
+  rewind (stderr);
+  char *line = NULL;
+  size_t len = 0;
+  if (getline (&line, &len, stderr) < 0)
+    {
+      printf ("cannot read stderr redirect: %m\n");
+      return 1;
+    }
+  printf ("message = \"%s\"\n", line);
+
+  static const char expected[] = "\
+program: option '--on' is ambiguous; possibilities: '--one' '--onto' '--one-one'\n";
+
+  return c != '?' || strcmp (line, expected) != 0;
+}