/*
	Prototype of GCC's getopt():

	## include "unistd.h"
	int getopt(int argc, char * const argv[], const char *optstring)

	extern char *optarg;
	extern int optind, opterr, optopt;

	#define _GNU_SOURCE
	#include "getopt.h"

	struct option {
		const char *name;
		int has_arg;
		int *flag;
		int val;
	};

	int getopt_long(int argc, char * const argv[]
	 , const char *optstring
	 , const struct option * longopts, int *longindex)

	int getopt_long_only(int argc, char * const argv[]
	 , const char *optstring
	 , const struct option * longopts, int *longindex)


	optstring enumerates all valid option characters. If such option
	character is followed by a colon, this option requires an argument.
	"An argument" is either the text immediately following the option
	character or, if none, the next argv[] entry. The address of the
	argument is returned in 'optarg'.

	Option sign: '-' (single dash)
	A single dash without any following characters is considered a
	non-option argument.
	Two dashes stop the option scanning regardless of point 3) below.

	Characters are matched case-sensitively.

	Extensions:
	1) Two colons: optional argument --> the next argv[] entry is never
		returned, instead NULL is assigned to optarg.
	2) Semicolon: The argument of the option is actually a long option.
	3) getopt() re-orders argv[] that all option arguments are moved at
		front of the array. If the first character of optstring is '+'
		or the environment variable POSIX_CORRECT is set, the scan
		stops at the first non-option argument. If the first character is
		'-', non-option arguments are returned as arguments of a
		pseudo option with character code 1 (numerical one).

	On error, the current option character is assigned to optopt, but
	'?' is returned by getopt(); also, if opterr is non-zero, an
	error message is printed to stderr.

	getopt_long() also accepts long options preceeded by two dashes.
	Otherwise it acts as getopt(). The members of struct option mean:

	name: name of long option
	has_arg:	0: no argument
				1: required argument
				2: optional argument
	val: value of long option (see below)
	flag:	!= NULL: val is assgined to *flag; getopt_long() returns 0
			== NULL: val is returned by getopt_long()

	getopt_long_only() behaves like getopt_long(), but it tries to
	match both '-' and '--' options as long option, on failure '-'
	options are parsed as a single short option.

	======================

	Implementation differences:

	The semicolon modifier is not implemented.
	The '+' and '-' modifiers are not implemented. There is no POSIX_CORRECT.
	Argumented options optionally may be delimited from the
	argument with one argument sign, which is ignored.
	No <unistd.h>; instead always <getopt.h> is to be included.

	There is no external variable that points to the current character
	within argv[optind]. Doing so has pros and cons. But to allow that
	getopt() is used for user-prepared strings and may be interrupted
	and re-started anytime, another global variable is introduced:
	optcur. The NEXT character to be parsed by getopt() is *optcur. If
	optcur is equal to NULL, a first-time check of the argument string
	is performed; otherwise getopt() assumes that argv[optind] _is_ an
	option. Most importantly, optcur does not really belong to
	argv[optind], thus, during parsing an option string, optind can be
	modified by the user in order to "use up" the next command line
	arguments.
	However, when getopt() hits the end of the option string, thus
	*optcur == '\0', optind is incremented by one to advance to the
	next command line argument to be parsed.

	If getopt() finds a long option, it issues an error "long options
	not supported", then optopt := isOpt('-')? '-': '/', then
	proceeds with error return (see below).

	On error any getopt() assigns optcur to optarg, invalidates optcur to
	force to advance to the next argument next time getopt() is called, 
	and finally returns '?' (ERRCH).

	=======================

	Saving/initializing optcur and optind (and opterr) is appropriate to
	use getopt() multiple times even simultaneously.
	optopt and optarg just return further information about the 
	return value of last getopt().

*/

/*		Macros:
		GETOPT_LONG_ONLY: compiles getopt_long_only(), superceeds GETOPT_LONG
		GETOPT_LONG: compiles getopt_long()
		GETOPT_SILENT: no visual error reporting
		GETOPT_HELPSCREEN: if '?' is specified as option, hlpScreen() is
			invoked
		GETOPT_ERROR_WRAPPER: Instead of displaying an error message with
			"fprintf(stderr, )" it is passed to a function wrapper, which
			allows to localize SUPPL and/or use the message library.
		GETOPT_USE_DOS_NLS: Use DOS NLS to upcase characters
		GETOPT_UPCASE: Upcase characters read from argument string as
			short option; optopt remains non-upcased
		GETOPT_UPCASE_LONG: Compare long options case-insensitively
		GETOPT_LONG_ALWAYS_FULL: don't match for partial long options
		GETOPT_EMBEDDED_OPTIONS: Option signs embedded into options
			start a new option, e.g. "/opt1/opt2" is "/opt1" "/opt2"
			Options with a required argument followed by an
			embedded option sign span to the next argv[] item, e.g.:
			"/opt1/opt2:arg2" "arg1" is "/opt1:arg1" "/opt2:arg2"
		GETOPT_NEED_ARGSIGN: Short options need an argument sign,
			long options, too
		GETOPT_NO_ARG_SPANNING: Don't use next argv[] entry as argument
		GETOPT_LONG_NO_FLAG_MEMBER: Don't include "flag" into struct option
			This macro can/must be defined prior #include'ing "GETOPT.H".
		GETOPT_OPTSIGN_DASH: Support the dash as an option sign, but
			not as embedded option sign
		GETOPT_OPTSIGN_SLASH: Support the forward slash as an option sign
		GETOPT_ARGSIGN_EQUAL_SIGN: Support the equal sign as an
			argument sign
		GETOPT_ARGSIGN_COLON: Support the colon as an argument sign

 Debugging macros:
 		GETOPT_LOCAL_VARS: Defines the global variables locally
 		GETOPT_JUST_VERSION_NUMBER: Just the rcsid field
 */

#ifdef GETOPT_JUST_VERSION_NUMBER
static char const rcsid[] = 
	"$Id: GETOPT.SRC 1.2 2002/07/30 05:37:22 ska Exp ska $";
#else

#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "suppl.h"
#include "getopt.h"
#ifdef GETOPT_USE_DOS_NLS
	/* toUpper() and StriCmp() */
#include "dynstr.h"	
#endif
#ifdef GETOPT_ERROR_WRAPPER
#include "msgs.h"
#endif


#ifdef GETOPT_LONG_ONLY
#ifdef GETOPT_NO_FLAG_MEMBER
#define fct_name getopt_long_only_noFlag
#else
#define fct_name getopt_long_only
#endif
#ifndef GETOPT_LONG
#define GETOPT_LONG
#endif
#else
#ifdef GETOPT_LONG
#ifdef GETOPT_NO_FLAG_MEMBER
#define fct_name getopt_long_noFlag
#else
#define fct_name getopt_long
#endif
#else
#define fct_name getopt
#endif
#endif

#define ERRCH		'?'
#if 0 /* Unix defaults */
#define isOpt(c)	((c) == '-')
#define isArg(c)	((c) == '=')
#define isEmbedOpt(c)	(0)		/* the dash is no embedded option sign */
#endif

#ifndef HI_TECH_C
#define dcl_probe_ch	FLAG8 probe_ch;
#define probe(ch,ch1,ch2)	\
	(probe_ch = (ch), probe_ch == (ch1) || probe_ch == (ch2))
#else
	/* Don't know why, but Pacific HiTech C cannot create code for
		above statement */
#define dcl_probe_ch
static int probe(int ch, int ch1, int ch2)
{	return ch == ch1 || ch == ch2;	}
#endif

#ifdef GETOPT_OPTSIGN_SLASH
#ifdef GETOPT_OPTSIGN_DASH
#define NEED_PROBE_CHAR
#define isOpt(c)	probe((c), '/', '-')
	/* The dash may not be an embedded option sign */
#define isEmbedOpt(c)	((c) == '/')
#else
#define isOpt(c)	((c) == '/')
#define isEmbedOpt(c)	((c) == '/')
#define OPTSIGN '/'
#endif
#else
#define isOpt(c)	((c) == '-')
#endif
#ifndef OPTSIGN
#define OPTSIGN	'-'
#endif

#ifdef GETOPT_ARGSIGN_EQUAL_SIGN
#ifdef GETOPT_ARGSIGN_COLON
#define NEED_PROBE_CHAR
#define isArg(c)	probe((c), ':', '=')
#else
#define isArg(c)	((c) == '=')
#endif
#else
#define isArg(c)	((c) == ':')
#endif


#ifdef GETOPT_UPCASE
#ifdef GETOPT_USE_DOS_NLS
#define UPCASE(c) toUpper(c)
#else
#define UPCASE(c) toupper(c)
#endif
#else
#define UPCASE(c) (c)
#endif
#ifdef GETOPT_UPCASE_LONG
#ifdef GETOPT_USE_DOS_NLS
#define CMPLONG(a,b,l) MemiCmp(a, b, l)
#else
#define CMPLONG(a,b,l) memicmp(a, b, l)
#endif
#else
#define CMPLONG(a,b,l) memcmp(a, b, l)
#endif

#ifdef GETOPT_HELPSCREEN
extern void hlpScreen(void);
#endif

#if 0
struct option {
	const char *name;
	int has_arg;
#ifndef GETOPT_NO_FLAG_MEMBER
	int *flag;
#endif
	int val;
};
#endif

#ifdef GETOPT_LOCAL_VARS
char *optarg = 0;
int optopt = 0;
#ifndef GETOPT_SILENT
int opterr = 1;
#endif
int optind = 0;
const char *optcur = 0;
#endif


#ifndef GETOPT_ERROR_WRAPPER

#define Wsuppl_lArgOption(name)  fprintf(stderr							\
					 , "Option '%s' requires an argument.\n"			\
					 , name);
#define Wsuppl_argOption(name)  fprintf(stderr							\
					 , "Option '%c' requires an argument.\n"			\
					 , name);
#define Wsuppl_lOption(name)	 fprintf(stderr							\
					 , "Unknown option '%s'\n"							\
					 , name);
#define Wsuppl_option(name)	 fprintf(stderr								\
					 , "Unknown option '%c'\n"							\
					 , name);
#define Wsuppl_longOption()	 fprintf(stderr								\
					 , "Long options are not supported.\n");
#endif




#ifdef GETOPT_LONG
static int isLong(const char **p, const struct option * const lopts)
{
#ifdef NEED_PROBE_CHAR
	dcl_probe_ch
#endif
#ifndef GETOPT_LONG_ALWAYS_FULL
	int idx, matchIdx;
	const char *q, *arg;
	size_t len;

	assert(p && *p);

	q = arg = *p;
	while(*arg && !isArg(*arg)
#ifdef GETOPT_EMBEDDED_OPTIONS
	 && !isEmbedOpt(*arg)
#endif
	)
		++arg;
	assert((signed long)(arg - q) < (signed long)UINT_MAX);
	len = (size_t)(arg - q);

	for(matchIdx = -1, idx = 0; lopts[idx].name; ++idx)
		if(CMPLONG(lopts[idx].name, q, len) == 0) {
			if(len == strlen(lopts[idx].name)) {	/* full match */
				*p = arg;
				return idx;
			}
			if(matchIdx != -1)
				return -1;	/* Error long option matches two abbreviations */
			matchIdx = idx;	/* abbreviation match */
		}

	if(matchIdx != -1)
		*p = arg;

	return matchIdx;
#else
	int idx, ch;
	size_t len;

	assert(p && *p);

	for(idx = 0; lopts[idx].name; ++idx)
		if(CMPLONG(lopts[idx].name, *p, len = strlen(lopts[idx].name)) == 0)
			if((ch = (*p)[len]) == 0	/* completely used argument */
			 || isArg(ch)
#ifdef GETOPT_EMBEDDED_OPTIONS
		 	 || isEmbedOpt(ch)
#endif
		 	) {	/* lopt with argument */
				*p += len;
				return idx;
			}	/* else does not match completely */

	return -1;
#endif
}
#endif

int fct_name (int argc, const char * const * const argv, const char * const options
#ifdef GETOPT_LONG
	, const struct option * const lopts, int * const longindex
#endif
)
{
#ifdef GETOPT_LONG
	int idx;
#endif
#ifdef NEED_PROBE_CHAR
	dcl_probe_ch
#endif
	int ch, newoption;

	assert(argv);
#ifdef GETOPT_LONG
	assert(lopts);
#endif

	newoption = 0;

	while(1) {
		optopt = 0;
		optarg = 0;

		if(optind >= argc)		/* no unparsed argument left */
			break;

		if(!optcur) {
			optcur = argv[optind];
			if(!isOpt(*optcur)		/* non-option argument */
			 || !*++optcur)			/* just "-" --> no argument, too */
				break;
			newoption = 1;			 /* is an option */
		}

		if(newoption) {		/* New-argument check */
			if(*optcur == optcur[-1]) {		/* long option or full stop */
				if(!*++optcur)		/* just "--" --> stop */
					break;
#ifdef 	GETOPT_LONG
				if((idx = isLong(&optcur, lopts)) != -1) {	/* Long option */
					/* probe the argument */
#ifdef GETOPT_LONG_ONLY
longOption:
#endif
					if(lopts[idx].has_arg) {
						if(isArg(*optcur)) {
							/* Differently than single character options
								long options always require an argument
								sign, except if spanning onto next arg */
							optarg = (char *)optcur + 1;
							optcur = "";	/* will force ++optind next time */
						}
						if(lopts[idx].has_arg == 1 && !optarg) {
							/* required argument --> next argv[] entry */
#ifndef GETOPT_NO_ARG_SPANNING
							if(optind >= argc) {
#endif
#ifndef GETOPT_SILENT
								if(opterr)
									Wsuppl_lArgOption(lopts[idx].name);
#endif

								optarg = (char *)optcur;
								optcur = "";
								return ERRCH;
#ifndef GETOPT_NO_ARG_SPANNING
							}
							optarg = (char *)argv[++optind];
#endif
						}
					}
					if(longindex) *longindex = idx;
#ifndef GETOPT_LONG_NO_FLAG_MEMBER
					if(lopts[idx].flag) {
						*lopts[idx].flag = lopts[idx].val;
						return 0;
					}
#endif
					return lopts[idx].val;
				}

				if(opterr)
					Wsuppl_lOption(optcur);
#else
				/* No long option --> error */
				if(opterr)
					Wsuppl_longOption();

#endif
				optopt = OPTSIGN;
				optarg = (char *)optcur;
				optcur = "";
				return ERRCH;
			}
#ifdef GETOPT_LONG_ONLY
			if((idx = isLong(&optcur, lopts)) != -1)
				goto longOption;
#endif
			/* parse short options */
			newoption = 0;
		}

		ch = UPCASE(optopt = *optcur++);
		if(ch) {
			char *p;

#ifdef GETOPT_EMBEDDED_OPTIONS
			if(isEmbedOpt(ch)) {	/* ignore embedded option signs */
				newoption = 1;
				continue;
			}
#endif
			if(isArg(ch) || (p = strchr(options, ch)) == 0) {
				/* Unknown option */
#ifndef GETOPT_SILENT
				if(opterr)
#ifdef GETOPT_LONG_ONLY
					Wsuppl_lOption(optcur - 1);
#else
					Wsuppl_option(ch);
#endif
#endif
				optarg = (char *)optcur - 1;
#ifndef GETOPT_LONG_ONLY
						/* argument is invalid for getopt_long()
							exactly, one would skip to next embedded
							option */
				if(isArg(ch))		/* skip remaining portion */
#endif
					optcur = "";
				return ERRCH;
			}
			if(*++p == ':') {		/* need an argument */
				assert(optarg == 0);
				if(*optcur
#ifndef GETOPT_NEED_ARGSIGN
#ifdef GETOPT_EMBEDDED_OPTIONS
				  && !isEmbedOpt(*optcur)
#endif
#else
				  && isArg(*optcur)
#endif
				) {
					optarg = (char*)optcur
#ifdef GETOPT_NEED_ARGSIGN
						+ 1
#endif
					;
					optcur = "";	/* force ++optind next time */
				}

				if(optarg) {		/* some argument found */
#ifndef GETOPT_NEED_ARGSIGN
					if(isArg(*optarg))
						++optarg;
#endif
				} else if(*++p != ':') {	/* not optional? --> use next arg */
					/* Last character of argument or embedded option
						--> use next argument */
#ifndef GETOPT_NO_ARG_SPANNING
					if(optind >= argc) {
#endif
#ifndef GETOPT_SILENT
						if(opterr)
							Wsuppl_argOption(ch);
#endif
						optarg = (char *)optcur;
						optcur = "";
						return ERRCH;
#ifndef GETOPT_NO_ARG_SPANNING
					}
					optarg = (char *)argv[++optind];
#endif
				}
			}
			/* in case: no argument needed */
#ifdef GETOPT_LONG_ONLY
				/* long_only() requires that single character options
					appear solemnly in an option string */
			else if(*optcur
#ifdef GETOPT_EMBEDDED_OPTIONS
			 && !isEmbedOpt(*optcur)
#endif
			) {
				/* must be a single character --> no option */
#ifndef GETOPT_SILENT
				if(opterr)
					Wsuppl_lOption(optcur - 1);
#endif
				optarg = (char *)optcur;
				optcur = "";		/* force ++optind next time */
				return ERRCH;
			}
#endif
#ifdef GETOPT_HELPSCREEN
			if(ch == '?')
				hlpScreen();
#endif
			return ch;
		}

		/* End of option reached */
		optcur = 0;
		++optind;			/* advance to next argument */
	}

	return EOF;
}
#endif
