about summary refs log tree commit diff
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@gmail.com>2011-05-15 13:35:09 -0400
committerUlrich Drepper <drepper@gmail.com>2011-05-15 13:35:09 -0400
commitbd25564e1e98910ed69043ed6a6f884ce60e5780 (patch)
tree76b63ae4d281a28b7a00956504eb8ea13e8e2118
parentbac102db9293f3f619c319312e05dfeb7051a7ad (diff)
downloadglibc-bd25564e1e98910ed69043ed6a6f884ce60e5780.tar.gz
glibc-bd25564e1e98910ed69043ed6a6f884ce60e5780.tar.xz
glibc-bd25564e1e98910ed69043ed6a6f884ce60e5780.zip
Provide more helpful error message in getopt
If provide with an ambiguous long option we now show all the possibilities.
-rw-r--r--ChangeLog6
-rw-r--r--NEWS14
-rw-r--r--posix/Makefile4
-rw-r--r--posix/getopt.c79
-rw-r--r--posix/tst-getopt_long1.c62
5 files changed, 137 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog
index 47ca10d25e..0c0aa6a2ff 100644
--- 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 43c3a6cd73..7100e1dbcb 100644
--- 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.
diff --git a/posix/Makefile b/posix/Makefile
index 373e50b0a9..e89f21e5b3 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -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
diff --git a/posix/getopt.c b/posix/getopt.c
index 2746364fc7..db89abf6a7 100644
--- a/posix/getopt.c
+++ b/posix/getopt.c
@@ -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
index 0000000000..e0ecd12190
--- /dev/null
+++ b/posix/tst-getopt_long1.c
@@ -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;
+}