This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: getopt extension: dump options in standardized format


Good morning,

I would like to propose an extension to getopt(3) that would
enable a user to dump all available short and long options for
a program that calls getopt(3), and friends, in a standardized
format.

The diff follows far below (against glibc-2.3.2).

I occasionally need something like this for standard unix
utilities that I write shell-script wrappers for.
These wrappers need to do some preprocessing of the 'real'
program arguments (such as filenames), and skip past all options
and their arguments -- these are usually passed onto the unix
utility unprocessed and unchanged.

I first thought this could be achieved by just parsing off the
options and their arguments, without returning them to the caller,
and then dumping the 'real' program arguments indexed by optind.

But this seems simpler than it is, for it looks like all 'optional'
option-arguments are still preserved in the resulting char*-array
indexed by optind.
Strange, but I guess it makes sense -- the caller of getopt(3)
should prob. parse off those.

Fwiw, this was basically the diff (on getopt1.c) that I thought
would suffice:

***************
*** 71,85 ****
  getopt_long (argc, argv, options, long_options, opt_index)
       int argc;
       char *const *argv;
       const char *options;
       const struct option *long_options;
       int *opt_index;
  {
!   return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
  }
  
  /* Like getopt_long, but '-' as well as '--' can indicate a long option.
     If an option that starts with '-' (not '--') doesn't match a long option,
     but does match a short option, it is parsed as a short option
     instead.  */
  
--- 72,101 ----
  getopt_long (argc, argv, options, long_options, opt_index)
       int argc;
       char *const *argv;
       const char *options;
       const struct option *long_options;
       int *opt_index;
  {
! 	int c;
! 
! 	if (getenv( "GETOPT_DUMP" ))
! 	{
! 		char *const *s;
! 
! 		while ((c=_getopt_internal (argc, argv, options, long_options, opt_index, 0))!=-1)
! 			;
!  
! 		for ( s=&argv[optind]; s && *s; s++ )
! 			fprintf( stderr, "%s\n", *s );
! 
! 		return c;
! 	}
! 	else
! 		return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
  }
  
  /* Like getopt_long, but '-' as well as '--' can indicate a long option.
     If an option that starts with '-' (not '--') doesn't match a long option,
     but does match a short option, it is parsed as a short option
     instead.  */
  

Again, this doesn't quite get it for options with optional
arguments.

Now the following diff just presents the available options.  
Thus, /any/ program making use of getopt(3) et al. will dump
the short and long options available and exit if and when the
environment variable GETOPT_DUMP is set.

Quite a different approach, so if you want to program a shell-
script wrapper, you just need to give it some extra effort.

The output of a dump looks like this (in this case for du(1));
the numbers at end of lines are the getopt 'has_arg'-values
(0=no argument, 1=required argument, 2=optional argument):


$> GETOPT_DUMP=1 du

-a | --all                   0
-\x80 | --apparent-size      0
-B | --block-size            1
-b | --bytes                 0
-l | --count-links           0
-L | --dereference           0
-D | --dereference-args      0
-\x81 | --exclude            1
-X | --exclude-from          1
-h | --human-readable        0
-H | --si                    0
-k | --kilobytes             0
-\x82 | --max-depth          1
-m | --megabytes             0
-x | --one-file-system       0
-S | --separate-dirs         0
-s | --summarize             0
-c | --total                 0
-\x7e | --help               0
-\x7d | --version            0


I don't know if this idea is gonna fly, anyway I just thought
I'd toss it up.

Thanks for your time.


--<snip>--
*** getopt.c.orig	Tue Jan 28 23:23:44 2003
--- getopt.c	Thu Jul 24 00:29:11 2003
***************
*** 504,532 ****
--- 505,623 ----
  
     LONGIND returns the index in LONGOPT of the long-named option found.
     It is only valid when a long-named option has been found by the most
     recent call.
  
     If LONG_ONLY is nonzero, '-' as well as '--' can introduce
     long-named options.  */
  
+ #include <ctype.h>
+        
+ #define NO_ARGUMENT		0
+ #define REQUIRED_ARGUMENT	1
+ #define OPTIONAL_ARGUMENT	2
+ 
+ void dump_options( const char *options, const struct option *long_options )
+ {
+ 	const struct option *o;
+ 	const char *s;
+ 	int len=0;
+ 
+ 	/*
+ 	 * first find longest long-option name,
+ 	 * for nicely formatted ouput
+ 	 */
+ 	for ( o=long_options; o->name; o++ )
+ 	{
+ 		int l=0;
+ 	
+ 		if ((l=strlen( o->name ))>len)
+ 			len = l;
+ 	}
+ 
+ 	for ( s=options; *s; s++ )
+ 	{
+ 		int has_arg;
+ 		
+ 		if (*s==':' || *s=='+' || *s=='-')
+ 			continue;
+ 
+ 		for ( o=long_options; o->name; o++ )
+ 		{
+ 			if (o->flag==NULL && o->val==*s)
+ 				/*
+ 				 * here, skip options that have a long-option
+ 				 * equivalent; we will group long options and
+ 				 * these short-option equivalents in a moment.
+ 				 */
+ 				goto END;
+ 		}
+ 
+ 		if (*(s+1) && *(s+1)==':')
+ 		{
+ 			if (*(s+2) && *(s+2)==':')
+ 				has_arg = OPTIONAL_ARGUMENT;
+ 			else
+ 				has_arg = REQUIRED_ARGUMENT;
+ 		}
+ 		else
+ 			has_arg = NO_ARGUMENT;
+ 
+ 		/*
+ 		 * print the short-option
+ 		 */
+ 		fprintf( stderr, "-%c%*s%d\n", *s, len+9+2, " ", has_arg );
+ 
+ 		END:;
+ 	}
+ 
+ 	for ( o=long_options; o->name; o++ )
+ 	{
+ 		char options_str[64];
+ 		int l;
+ 		
+ 		if (o->flag==NULL && o->val)
+ 		{
+ 			if (isprint( o->val ))
+ 				l = sprintf( options_str, "-%c | --%s", o->val, o->name );
+ 			else
+ 				/*l = sprintf( options_str, "-%02d | --%s", o->val, o->name );*/
+ 				l = sprintf( options_str, "-\\x%02x | --%s", (unsigned char)o->val, o->name );
+ 		}
+ 		else
+ 			l = sprintf( options_str, "--%s", o->name );
+ 
+ 		/*
+ 		 * print the long-option and (if present) its short-option equivalent
+ 		 */
+ 		fprintf( stderr, "%s%*s%d\n", options_str, len-l+11+2, " ", o->has_arg );
+ 	}
+ }
+ 
  int
  _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
       int argc;
       char *const *argv;
       const char *optstring;
       const struct option *longopts;
       int *longind;
       int long_only;
  {
    int print_errors = opterr;
    if (optstring[0] == ':')
      print_errors = 0;
  
+   if (getenv( "GETOPT_DUMP" ))
+   {
+ 	/* dump all available short and long options */
+ 	dump_options( optstring, longopts );
+ 	return -1;
+   }
+ 
    if (argc < 1)
      return -1;
  
    optarg = NULL;
  
    if (optind == 0 || !__getopt_initialized)
      {
        if (optind == 0)
--<snap>--


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]