[comp.lang.c] getopt

everson@CompSci.Bristol.AC.UK (Phill Everson ) (06/13/89)

Following the getopt()/egetopt() discussions here in early 1988 ...

2 questions about getopt()/egetopt() ...

1) Is there any way of allowing getopt() to parse the following:
		foo -Wt name -x a ...
2) Or
		foo -x a b -y a ...

Obviously, I want to be able to combine the above. I suspect 2) is
possible with getopt() somehow, but for 1) it may be necessary to
modify egetopt(). Has anyone done this before or has anyone any ideas
about how it could be done?

I'd prefer email ...

Thanks in advance,

Phill Everson
Medical Imaging
Dept Comp Sci
University of Bristol, UK

davidsen@sungod.crd.ge.com (William Davidsen) (06/15/89)

I have a program which needs an option which may have either a default
value or a specific value. ie. "-c" or "-c4" would be typical. I would
like to use getopt to read the values.

The question: is there a tricky way to use the existing getopt, or
do I have to make an extended version? I would like to use the existing
interface if possible.

  In the meantime, I took a P.D. version and added another option
qualifier, #, indicating an optional numeric argument. The logic
requires that the numeric value follow the option inducer without a
space, as "-c2" but not "-c 2". I set optarg to NULL if no value is
specified. I renamed the procedure, obviously, to prevent calling the
common version if the software is ported. Since the character # could be
an option in some software, my new version is not a superset.

  I have seen references to "eoptarg" in this group, but can't find a
man entry for it on SunOS or Ultrix, so I assume it's either proprietary
or P.D. we don't have it.
	bill davidsen		(davidsen@crdos1.crd.GE.COM)
  {uunet | philabs}!crdgw1!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

karl@haddock.ima.isc.com (Karl Heuer) (06/16/89)

In article <782@crdgw1.crd.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
>I have a program which needs an option which may have either a default
>value or a specific value. ie. "-c" or "-c4" would be typical. I would
>like to use getopt to read the values.

This doesn't fit the Official Arg Passing Style, so even if you use a hook a
kludge into getopt(), it isn't guaranteed to always work.

>  In the meantime, I took a P.D. version and added another option
>qualifier, #, indicating an optional numeric argument.

Why restrict it to numeric?  I use optional non-numeric arguments all the
time.  If you're going to add the optional-argument hack, you might as well
let it return (in optarg) whatever string immediately followed the keyletter,
including the empty string in the case of "-c".  (No reason for it ever to
return NULL; that would make an unnecessary special case.)

>Since the character # could be an option in some software, my new version is
>not a superset.

"-#" is a not uncommon debugging option.  I think ";" is a better choice,
since it does half of what ":" does%, and because I don't know of any code
that uses "-;" as an option.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
________
% This pun works only if you pronounce them as "semicolon" and "colon".

sarantos@notecnirp.Princeton.EDU (Sarantos Kapidakis) (06/16/89)

Here is my version of getopt, which have much more flexibility
than the standard one, and can parse the arguments more than one
times.  Examples and documentation can be produced by different
preprocessor options:

#if defined(DOC)
The Getopt routine helps with finding the options from the command line.
It takes 7 arguments:
argc, argv: The argument counter and vector, as in the main program.
	The argv[0] is used as the program name, in case of error.
pargc, pargv: Pointers to (so as their values can be updated) the argument
	index and position in the current argument.
	In the usual usage, in the first call to this routine, *pargc will
	be 1, indicating that the next argument to be processed is the
	first argument (after the program name), and **pargv=`\0`,
	indicating we finished processing the previous argument.
	In any case, if **pargv != '\0', the next letter **pargv
	is considered as the next option, while otherwise, a '-'
	is expected to be found as the first character of the next
	argument is processed.
	Normally, the client never has to change *pargv, and *pargc.
par: If the option found has arguments, the first of these is stored in *par.
	If more arguments, say n were asked, argument i (1<i<=n) is accessed
	as agrv[(*pargc)-n-1+i].  This is easily understand from the fact that
	argv[*pargc] is always the next argument to be processed, so the last
	argument found was argv[(*pargc)-1], and so on.
	The first argument has a special treatment, because it may start from
	the middle of the real argument, while the others start always in the
	beginning.
	This variable is not changed if the option found has no arguments.
ostr1: The string containing the option letters, and possibly some of
	the control characters ':', '?' and '*'.
	The option-letters, followed by a '*' means that this
	option may have an argument adjacent to it. If no such
	argument exists, the routine consider the empty string
	as its argument (and never the next argument in the list).
	The option-letters, followed by a number of ':' or '?' means that
  	this option needs so many arguments. The first argument may
	be adjacent to the option letter, or a separate argument if
	if the option was the last character in that argument.
	*ppar is assigned the address of the first argument, or NULL
	if there are no arguments at all.
	The rest of the required arguments (if any), are the next
	arguments in the argv list, upto argv[(*pargc)-1].
	An error occurs if not enough arguments were found, when the
	':' was used (but not when the '*' was used).

	In any case, *pargc is always increased one for each expected
	argument, so that its difference from argc expresses the number
	of arguments still availiable (or missing for negative value).

	After returning from any of these cases(an option found followed
	by a ':', '?' or '*'), **pargv is always '\0', since the last
	argument was consumed entirelly.
ostr2: The string containing some additional option letters, in the same
	format as ostr1, and which are just going to be ignored and passed by,
	by the procedure. In most cases this string is the empty string.

	The routine returns the option found, or '?' for error
	and EOF for no option found.  Then the arguments to the
	program are from argv[*pargc] up to argv[argc-1].

example:
	we can call the following program as follows,
	or in many other combinations:
#endif
#if !(defined(DOC) || defined(DEFS) || defined(SHELL) ||\
			defined(PROGRAM) || defined(DATA))
/*FUNCTIONS*/
/* got this off net.sources, and improve it */
#include <stdio.h>

Getopt(argc, argv, /*INOUT*/ pargc, /*INOUT*/ pargv, /*OUT*/ ppar, ostr1, ostr2)
int	argc; /* argument counter */
char	**argv; /* argument vector */
int	*pargc; /* the &possition to be read next on the argument vector */
char	**pargv; /* the &possition to be read next on the current argument */
char	**ppar; /* any returned &value (string) for the option found */
char	*ostr1, *ostr2; /* the string of options */
{
	extern	char	*index();
	char	sop[2];
	char	*dummy;			/* dummy parameter - ignored options */
	register char	**pstr;		/* the real first parameter address */
	register char	*optr;		/* option letter list index */
	register int	ochar;		/* character checked for validity */
			/* update scanning pointer */
	for(;;){
	if (**pargv == '\0' && (*pargc >= argc || (*pargv=argv[*pargc]) == NULL
	|| **pargv != '-' || *++*pargv=='\0' || **pargv == '-' && (++*pargc))) {
			*pargv = "";
			return(EOF);
		}
	if ((ochar = *(*pargv)++) == ':' || ochar == '*' ||  ochar == '?' ||
		(ostr1==NULL || (pstr= ppar, optr=index(ostr1,ochar))==NULL) &&
		(ostr2==NULL || (pstr= &dummy, optr=index(ostr2,ochar))==NULL)) {
		if (**pargv=='\0') ++*pargc ;
		sop[0]=ochar; sop[1]='\0';
		wrong("illegal option: '%s'", sop);
		return('?');
	}
	if (*++optr != ':' && *optr != '?') {	/* don't need next argument */
		if (*optr=='*' && *(*pstr = *pargv)!='\0') *pargv="";
		if (**pargv=='\0') ++*pargc ;
	}
	else {				/* need argument */
		++*pargc;
		if (*(*pstr = *pargv) != '\0') *pargv = "";
		else	*pstr = (*pargc < argc)? argv[(*pargc)++]:
				(--optr, (char *) NULL);
		while(*++optr == ':' || *optr == '?')
			if (++*pargc > argc && *optr == ':') {
				sop[0]=ochar; sop[1]='\0';
				wrong("argument missing from option '%s'", sop);
				return('?');
			}
					
	}
	if (pstr==ppar) return(ochar);	/* dump back option letter */
	}
}
/*FUNCTIONS*/
#endif
#if defined(DOC) || defined(SHELL)
	a.out	-vvargument -a argumenttoa -P -x 1 2 3 file1 file2
	a.out	-aargumenttoa -PargumenttoP -v varg -x 1 2 3 file1 file2
	a.out	-a argumenttoa -x1 2 3 file1 file2
	a.out	-P file1 file2
#endif
#if defined(DOC) || defined(TEST) || defined(PROGRAM)
#if !defined(DOC)
#include <stdio.h>
#endif

char	*progname;
char	*opa="default aa", *opc=NULL, *opP=NULL;
char	*opx1=NULL, *opx2=NULL, *opx3=NULL;
char	*opy1=NULL, *opy2=NULL, *opy3=NULL;
int	opA=0;

main(argc,argv)
int	argc ;
char	**argv ;
{
	int	optc=1;
	char	*optr="", *par;
	int	c, i;
	progname=argv[0];
	while ((c=Getopt(argc, argv, &optc, &optr, &par,
		"Aa?P*c:x:::y:??", "v:"))!=EOF)
		switch (c) {
		/* 'v' will never be returned. It is just ignored */
		case'a':opa=par ;break;
			/* may have 1 argument */

		case'c':opc=par ;break;
		case'A':opA= !opA; break;
			/* needs no argument */
		case'P':opP=par;break;
			/* may have an argument (if -Parg) or may not (if -P) */
		case'x':opx1=par; opx2=argv[optc-2]; opx3=argv[optc-1]; break;
			/* needs 3 arguments */
		case'y':opy1=par;
			if (optc-argc < 2) opy2=argv[optc-2];
			if (optc-argc < 1) opy3=argv[optc-1]; break;
			/* needs 1 argument and may have up to 3 arguments */
		default:exit(2);
		}
	for(i=0 ; i<argc ; i++) printf("%s%c", argv[i], (i==argc-1)?'\n':' ');
	printf("%c: %s\n", 'A', opA? "true": "false");
	if (opP != NULL)printf("%c: %s\n", 'P', opP);
	if (opc != NULL)printf("%c: %s\n", 'c', opc);
	if (opa != NULL)printf("%c: %s\n", 'a', opa);
	if (opx1 != NULL)printf("%c: %s %s %s\n", 'x', opx1, opx2, opx3);
	if (opy1 != NULL)printf("%c: %s", 'y', opy1);
	if (opy2 != NULL)printf(" %s", opy2);
	if (opy3 != NULL)printf(" %s", opy3);
	if (opy1 != NULL)printf("\n");
	printf("optc=%d, argc=%d\n file arguments:", optc, argc);
	while(optc < argc) printf("%s ", argv[optc++]);
	printf("\noptc=%d, argc=%d\n", optc, argc);
	exit(0);
}

#include "error.c"
#endif

davidsen@sungod.crd.ge.com (William Davidsen) (06/16/89)

  I would like to thank all the people who posted or mailed comments
about my getopt question. I am incorporating some of those ideas and
will be posting a source and man page for the result. I am using the
character ? for the optional argument character. Since getopt returns ?
to indicate an unknown option, there won't be any existing programs
using it, and the extended getopt will work with existing programs.

  I guess anything with a man page should be posted to sources.misc
rather than alt.sources, so look for it soon.
	bill davidsen		(davidsen@crdos1.crd.GE.COM)
  {uunet | philabs}!crdgw1!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

jmm@eci386.uucp (John Macdonald) (06/19/89)

In article <13730@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
}In article <782@crdgw1.crd.ge.com> davidsen@crdos1.UUCP (bill davidsen) writes:
}>I have a program which needs an option which may have either a default
}>value or a specific value. ie. "-c" or "-c4" would be typical. I would
}>like to use getopt to read the values.
}
}This doesn't fit the Official Arg Passing Style, so even if you use a hook a
}kludge into getopt(), it isn't guaranteed to always work.
}
} [...]
}
}"-#" is a not uncommon debugging option.  I think ";" is a better choice,
}since it does half of what ":" does%, and because I don't know of any code
}that uses "-;" as an option.
}
}Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
}________
}% This pun works only if you pronounce them as "semicolon" and "colon".

Surely it would be better (except for punning purposes) to use -? to indicate
an optional argument.  Getopt precludes having a useful ? option since the
return of ? indicates an error occurred.  The ? is also suggests that the
option is "conditionally present".

bobmon@iuvax.cs.indiana.edu (RAMontante) (06/21/89)

jmm@eci386.UUCP (John Macdonald) <1989Jun19.153426.21238@eci386.uucp> :
-}
-}"-#" is a not uncommon debugging option.  I think ";" is a better choice,
-}since it does half of what ":" does%, and because I don't know of any code
-}that uses "-;" as an option.
-}
-}Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
-}________
-}% This pun works only if you pronounce them as "semicolon" and "colon".
-
-Surely it would be better (except for punning purposes) to use -? to indicate
-an optional argument.  Getopt precludes having a useful ? option since the
-return of ? indicates an error occurred.  The ? is also suggests that the
-option is "conditionally present".


I believe that some command-line interpreters (viz., csh?) will attempt
to expand `?' as a wildcard unless it's escaped, at which point using it
becomes a nuisance.  I'm not too comfortable with `;' and `:', either,
as some CLI, somewhere, may treat these as statement delimiters or
something.  Letters seem like the safest bet to me....