[comp.lang.c] Using getopt to parse multi-argument options

roy@phri.UUCP (Roy Smith) (01/15/89)

[not strictly a C question, but comp.lang.c seems to be the best place]

	I'm writing a program which will take an optional range, in the
style of graph(1)'s "-x xmin xmax".  How do I tell getopt to parse
something like that?  I suppose I could diddle with optind to make getopt
skip the next argument, but that hardly seems kosher.  The obvious way to
do this would be to have getopt support a syntax like "-r::" which means
the "-r" flag has *two* arguments after it but it's too late now.
-- 
Roy Smith, System Administrator
Public Health Research Institute
{allegra,philabs,cmcl2,rutgers}!phri!roy -or- phri!roy@uunet.uu.net
"The connector is the network"

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/16/89)

In article <3652@phri.UUCP> roy@phri.UUCP (Roy Smith) writes:
>	I'm writing a program which will take an optional range, in the
>style of graph(1)'s "-x xmin xmax".  How do I tell getopt to parse
>something like that?

You don't.  getopt() was written to support the Command Syntax
Standard, and that usage is a violation of the Standard.

henry@utzoo.uucp (Henry Spencer) (01/16/89)

In article <3652@phri.UUCP> roy@phri.UUCP (Roy Smith) writes:
>	I'm writing a program which will take an optional range, in the
>style of graph(1)'s "-x xmin xmax".  How do I tell getopt to parse
>something like that? ...

You can't.  The only getopt-compatible way is to require the user to
say "-x 'xmin xmax'" instead, and have your code pull the single argument
apart.  This is what the AT&T syntax standard mandates (with the further
flourish that tabs and commas must work as separators, not just spaces,
as I recall).

Nobody has ever pretended that getopt or the syntax standard could handle
all the old weird programs that hack their arguments in their own strange
ways.  From personal experience, I can testify that life is a lot better
when everything uses getopt, even if this means some aggravation over
backward compatibility.  Just having things consistent is a huge win.
-- 
"God willing, we will return." |     Henry Spencer at U of Toronto Zoology
-Eugene Cernan, the Moon, 1972 | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

bobmon@iuvax.cs.indiana.edu (RAMontante) (01/17/89)

henry@utzoo.uucp (Henry Spencer):
-
-ways.  From personal experience, I can testify that life is a lot better
-when everything uses getopt	[ ... ]

Spoken like a true author of the standard getopt program!  :-)

(yes, i use it on my msdos box, so it must be standard)

alanf%smile@Sun.COM (Alan Fargusson @ peace with the world) (01/18/89)

In article <1989Jan16.023712.29002@utzoo.uucp>, henry@utzoo.uucp (Henry Spencer) writes:
> In article <3652@phri.UUCP> roy@phri.UUCP (Roy Smith) writes:
> >	I'm writing a program which will take an optional range, in the
> >style of graph(1)'s "-x xmin xmax".  How do I tell getopt to parse
> >something like that? ...
> 
> You can't.  The only getopt-compatible way is to require the user to
> say "-x 'xmin xmax'" instead, and have your code pull the single argument
> apart.  This is what the AT&T syntax standard mandates (with the further
> flourish that tabs and commas must work as separators, not just spaces,
> as I recall).
> 

I think the best way to do this would be to have the user type "-x xmin,xmax",
and parse the 'xmin,xmax' yourself.  This is what some AT&T software does.
- - - - - - - - - - - - - - - - - - - - -
Alan Fargusson		Sun Microsystems
alanf@sun.com		..!sun!alanf

levy@ttrdc.UUCP (Daniel R. Levy) (01/18/89)

In article <1989Jan16.023712.29002@utzoo.uucp>, henry@utzoo.uucp (Henry Spencer) writes:
> In article <3652@phri.UUCP> roy@phri.UUCP (Roy Smith) writes:
> >	I'm writing a program which will take an optional range, in the
> >style of graph(1)'s "-x xmin xmax".  How do I tell getopt to parse
> >something like that? ...
> 
> You can't.  The only getopt-compatible way is to require the user to
> say "-x 'xmin xmax'" instead, and have your code pull the single argument
> apart.  This is what the AT&T syntax standard mandates (with the further
> flourish that tabs and commas must work as separators, not just spaces,
> as I recall).

Actually, what Roy had in mind IS possible.  Yes it violates the syntax
standard, but if one is masochistic enough to WANT that, it's doable through
suitable abuse of optind.  This permits argument sequences of the form
"-x <xmin> <xmax>" or even "-bundledoptionlettersx<xmin> <xmax>" for the
truly warped of mind:

main(argc,argv)
char **argv;
{
	...
	int c;
	extern char *optarg;
	extern int optind;
	double xmin, xmax, atof();
	void exit(), usage();
	...

	while ((c=getopt(argc,argv,"x:abcdefgh")) != EOF) {
		switch(c) {
		case 'a':	/* ordinary options */
		...
		case 'h':	/* etc. */
		case 'x':	if (optind == argc || !isanumber(optarg) ||
					!isanumber(argv[optind]) {
					usage();
					exit(1);
				} else {
					xmin=atof(optarg);
					xmax=atof(argv[optind]);
					optind++; /* skip the next argument */
				}
				break;
		...
		}
	}
	...
}

This will only work for compiled code; I know of no way that getopt(1) can
be made to do something similar in a shell script.
-- 
Daniel R. Levy             UNIX(R) mail:  att!ttbcad!levy
AT&T Bell Laboratories
5555 West Touhy Avenue     Any opinions expressed in the message above are
Skokie, Illinois  60077    mine, and not necessarily AT&T's.

henry@utzoo.uucp (Henry Spencer) (01/20/89)

In article <3141@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>Actually, what Roy had in mind IS possible.  Yes it violates the syntax
>standard, but if one is masochistic enough to WANT that, it's doable through
>suitable abuse of optind...

This is not portable; it assumes a specific implementation of getopt.
Unless things have changed, the getopt(3) documentation makes no promise
that user changes to optind will be reflected back into getopt's innards.

It probably works in the existing implementations, but that's not the
same thing.
-- 
Allegedly heard aboard Mir: "A |     Henry Spencer at U of Toronto Zoology
toast to comrade Van Allen!!"  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

levy@ttrdc.UUCP (Daniel R. Levy) (01/21/89)

In article <1989Jan19.192946.15825@utzoo.uucp>, henry@utzoo.uucp (Henry Spencer) writes:
< In article <3141@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
< >Actually, what Roy had in mind IS possible.  Yes it violates the syntax
< >standard, but if one is masochistic enough to WANT that, it's doable through
< >suitable abuse of optind...
< 
< This is not portable; it assumes a specific implementation of getopt.
< Unless things have changed, the getopt(3) documentation makes no promise
< that user changes to optind will be reflected back into getopt's innards.

Literally taken, you are quite right.  I am reading between the lines of
the man page and making an educated guess.  Can you suggest a reason for
the statement "Because optind is external, it is normally initialized to
zero automatically before the first call to getopt," other than the
implication that optind's value will influence the behavior of getopt?
I mean, why else should we care what optind is prior to at least one usage
of getopt?

But, supposing we take your objection at face value, that we can't count on
user changes in optind being reflected into the internal workings of getopt.
O.K., how about mucking with argv and argc instead of with optind:

	...
	int i;
	...
	while (...getopt...) {
	...
	case 'x':
		... /* validate the presence of two numeric arguments */ ...
		xmin=atof(optarg);
		xmax=atof(argv[optind]);
		argv++;
		argc--;
		break;
		...
	}

The documentation doesn't say that we must give each call to getopt() the same
argc and the same argv does it now?  Huh? Huh? :-)
-- 
Daniel R. Levy             UNIX(R) mail:  att!ttbcad!levy
AT&T Bell Laboratories
5555 West Touhy Avenue     Any opinions expressed in the message above are
Skokie, Illinois  60077    mine, and not necessarily AT&T's.

henry@utzoo.uucp (Henry Spencer) (01/22/89)

In article <3146@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>< Unless things have changed, the getopt(3) documentation makes no promise
>< that user changes to optind will be reflected back into getopt's innards.
>
>Literally taken, you are quite right.  I am reading between the lines of
>the man page and making an educated guess.  Can you suggest a reason for
>the statement "Because optind is external, it is normally initialized to
>zero automatically before the first call to getopt," other than the
>implication that optind's value will influence the behavior of getopt?

There is a clear implication here that the initial value matters.  There
is no similar statement about subsequent values.  If one wants portable
software, one is not allowed to make educated guesses about the software;
one is entitled to trust only properties that are explicitly documented.

>But, supposing we take your objection at face value, that we can't count on
>user changes in optind being reflected into the internal workings of getopt.
>O.K., how about mucking with argv and argc instead of with optind...
>The documentation doesn't say that we must give each call to getopt() the same
>argc and the same argv does it now?  Huh? Huh? :-)

No, it doesn't.  It's also kind of vague on what might happen.  I fear it
comes under the same heading:  "behavior not documented, may vary between
implementations".
-- 
Allegedly heard aboard Mir: "A |     Henry Spencer at U of Toronto Zoology
toast to comrade Van Allen!!"  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

john@frog.UUCP (John Woods) (01/24/89)

In article <3146@ttrdc.UUCP>, levy@ttrdc.UUCP (Daniel R. Levy) writes:
N>In article <1989Jan19.192946.15825@utzoo.uucp>, henry@utzoo.uucp (Henry Spencer) writes:
e>< This is not portable; it assumes a specific implementation of getopt.
c>< Unless things have changed, the getopt(3) documentation makes no promise
r>< that user changes to optind will be reflected back into getopt's innards.
o>Literally taken, you are quite right.  I am reading between the lines of
m>the man page and making an educated guess.  Can you suggest a reason for
a>the statement "Because optind is external, it is normally initialized to
n>zero automatically before the first call to getopt," other than the
c>implication that optind's value will influence the behavior of getopt?
y>I mean, why else should we care what optind is prior to at least one usage
 >of getopt?

Interesting.  The SVID (Issue 2, Volume 1) says "The external variable optind
is initialized to 1 before the first call to the function getopt."  No
statement of cause-and-effect, and a different initializer from your manual
page.  This also does not imply that optind influences getopt(), it merely
implies that optind always has a reasonable value.

If you really have to have a program which doesn't use getopt() argument
semantics, why bang on getopt() to fool it into doing what you want?  At the
very worst, writing your own getopt() with your own semantic variations can't
be all that hard (I wrote one a long time ago in a small amount of time, and
I bet Henry's version didn't take all that long, either).  That way, you can
make sure that the next version of your libraries don't surprise you, and you
have real documentation for what your preferred argument semantics are ("Use
the Source, Luke!").  The real motivation for putting getopt() in the library
isn't saving people gobs of coding time, it was to make having a uniform
command syntax temptingly inviting.
-- 
John Woods, Charles River Data Systems, Framingham MA, (508) 626-1101
...!decvax!frog!john, john@frog.UUCP, ...!mit-eddie!jfw, jfw@eddie.mit.edu

Presumably this means that it is vital to get the wrong answers quickly.
		Kernighan and Plauger, The Elements of Programming Style