about summary refs log tree commit diff
path: root/posix/getopt.c
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 /posix/getopt.c
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.
Diffstat (limited to 'posix/getopt.c')
-rw-r--r--posix/getopt.c79
1 files changed, 60 insertions, 19 deletions
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);