egetopt.c -- Extended 'getopt'.

A while back, a public-domain version of getopt() was posted to the
net.  A bit later, a gentleman by the name of Keith Bostic made some
enhancements and reposted it.

In recent weeks (i.e., early-to-mid 1988) there has been a sometimes
heated discussion in comp.lang.c about the merits and drawbacks of
getopt(), especially with regard to its handling of '?'.

In light of this, I have taken Mr. Bostic's public-domain getopt()
and have made some changes that I hope will be considered to be
improvements.  I call this routine 'egetopt' ("Extended getopt").
The default behavior of this routine is the same as that of getopt(),
but it has some optional features that make it more useful.  These
options are controlled by the settings of some global variables.
By not setting any of these extra global variables, you will have
the same functionality as getopt(), which should satisfy those
purists who believe getopt() is perfect and can never be improved.
If, on the other hand, you are someone who isn't satisfied with the
status quo, egetopt() may very well give you the added capabilities
you want.

egetopt() behaves like getopt() with the following added capabilities:

--	The '?' which gets returned when there is an unrecognized option
	is now stored in a global integer called 'optbad', and the caller
	can set this value to anything.  The initial value in 'optbad' is
    	'?', which means that the default behavior is just like that of
    	getopt().  For example, If you want egetopt() to return '~'
    	instead of '?' when it sees an invalid option, put the following
    	lines in your code before egetopt() gets called:

		extern int optbad;
		optbad = (int)'~';

--	Options can begin with characters other than just '-'.  There
	is now a global character pointer called 'optstart'.  It points
	to a string which consists of a list of characters which can
	be used to begin options.  The initial string that 'optstart'
	points to is "-", so the default behavior is like that of
    	getopt().  For example, if you want to allow both '+' and '-'
    	as option delimiters, put the following lines in your code
    	before egetopt() gets called:

		extern char *optstart;
		optstart = "-+";

--	Now that there's a choice of the characters that can precede options
	it's desirable to let the caller know what character begins a
	given option.  In egetopt(), the global integer 'optchar' will
	now contain the character that begins a given option, or 0 if
	there was an error.  Just put the following line in your code
	and you can check the value of 'optchar' after each call to
	egetopt():

		extern int optchar;

--	The old getopt() writes error messages to file descriptor 2
	(or to stderr, depending on your implementation).  In egetopt(),
	you can change this file descriptor to be anything you want.
	The global integer 'opterrfd' contains the file descriptor
	to use for writing error messages.  As you might have guessed,
	this variable is initialized to 2.  As an example, if you want
	your egetopt() errors to go to the file "egetopt.errs", do
    	something similar to the following before calling egetopt():

		extern int opterrfd;

		FILE *eout = fopen("egetopt.errs", "w");

		if (eout == (FILE)NULL) {
			/* error condition/
			...
			exit(1);
		}

		opterrfd = fileno(eout);

--	Some implementations of getopt() allow you to set the global
	integer 'opterr' to control whether error output is printed:
	it is initialized to 1, which enables error output (as does
	any non-zero value); setting it to 0 disables error output.
	In egetopt(), 'opterr' is treated the same way.

--	The old getopt() forces you to use ':' in the string of option
	letters to show that a given option takes an argument.  There is
	now a global integer called 'optneed' which contains this value,
	so you can change it to something else if you want.  As you might
    	have suspected, 'optneed' is initialized to ':'.

	In addition, something that I always found annoying about the old
	getopt() is its inability to handle non-mandatory option arguments.
	For example, if an option called 'd' was specified as taking
	an argument to the program 'foo', you'd get the following
	results when invoking 'foo' in different ways:

	1)	foo -dABC -x ...

			getopt() return:	'd'
			optarg:			"ABC"

	2)	foo -dABC -x ...

			getopt() return:	'd'
			optarg:			"ABC"

	3)	foo -d -x ...

		A)	getopt() return:	'd'
			optarg:			"-x"

	In the case of number 3, sometimes one would prefer to get ...

		B)	getopt() return:	'd'
			optarg:			NULL

	This would allow "-x" to be handled as another option in the next
	call.  In the old getopt(), you can get the 3B behavior by testing
	the first character of 'optarg' and decrementing 'optind' if this
	character is '-'.  However, since I am enhancing the routine
    	anyway, I decided to build in the ability to have either the 3A
    	or the 3B behavior.

	Since this behavior isn't always desired, I have added another
	global integer called 'optmaybe' which optionally allows you to
	control whether an option with an argument will get treated as number
	3A or as number 3B above.  It is used similarly to 'optneed'.  It is
	initialized to 0, meaning that behavior 3B is impossible in the
	default case.  The following example shows how 'optneed' and
	'optmaybe' can be used:

    	    	extern int optneed;
       	    	extern int optmaybe;

    	    	optneed = (int)'!';	/* use '!' instead of ':'/
		optmaybe = (int)'%';	/* use '%' for optional arguments/

		...

		while ((c = egetopt(argc, argv, "abc!d%x")) != EOF) ...

	In this example, options 'a', 'b', and 'x' take no arguments,
	option 'c' takes a mandatory argument, and option 'd' takes
	a non-mandatory argument.  If this is contained in program 'foo',
    	you'll get the following behavior when you run it:

		foo -a -cABC -dXYZ -d -x -c -b ...

			egetopt() return:	'a'
			optarg:			NULL

			egetopt() return:	'c'
			optarg:			"ABC"

			egetopt() return:	'd'
			optarg:			"XYZ"

	>>>>>>>>>>	egetopt() return:	'd'
	>>>>>>>>>>	optarg:			NULL
	>> NOTE >>
	>>>>>>>>>>	egetopt() return:	'x'
	>>>>>>>>>>	optarg:			NULL

			egetopt() return:	'c'
			optarg:			"-b"

			...

	Remember that 'optneed' is initialized to ':' and 'optmaybe'
	is initialized to 0.  This causes behavior identical to that
	of getopt() unless you specifically override it.

Since the default behavior of egetopt() is the same as that of getopt(),
there is no reason why you can't rename this routine to getopt() and
use it in place of the original.  I gave it a new name so as not to
offend those of you who believe that getopt() is perfect and should
never have any new features added to it.

The code was originally posted to the net as getopt.c by ...

	Keith Bostic
	ARPA: keith@seismo 
	UUCP: seismo!keith

Current version: added enhancements and comments, reformatted code.

	Lloyd Zusman
	Master Byte Software
	Los Gatos, California
	Internet:	ljz@fx.com
	UUCP:		...!ames!fxgrp!ljz

    	May, 1988
