[alt.sources] getoptx - an extended getopt

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

  I recently was installing a new version of PD diff and noted that if
the -c option was used the argument had to be specified. This appeared
to be a limitation of getopt, so I took the PD version which came with
smail2.5 and changed it to accept optional arguments to options.

  Thanks to the people who responded to my initial posting on this. My
first idea was to use ? to indicate an optional argument, but I am
assurred that -? is a fairly common way of requesting help, so I changed
to *.

Here's my getopt, called getoptx although I think it's a valid superset
of the existing version. This is all PD, and I invite and encourage
anyone to include it with their distribution. I was able to convert a
number of programs to getopt usage with new use of the new option.
Enjoy!

#!/bin/sh
# shar:	Shell Archiver  (v1.24)
#
#	Run the following text with /bin/sh to create:
#	  getoptx.c
#	  getoptst.c
#	  getoptx.3
#	  getoptx.mak
#
echo "x - extracting getoptx.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > getoptx.c &&
X/*
X**	@(#)getopt.c	2.5 (smail) 9/15/87
X*/
X
X/*
X *  NOTE: this source has been hacked to allow * in the optstring,
X *  meaning "an optional argument which must follow the option
X *  character without a blank." Read the comments following with 
X *  that in mind.
X *    bill davidsen (davidsen@crdos1.crd.ge.com) June 1989
X */
X
X#define OptArgFlg '*'
X
X/*
X * Here's something you've all been waiting for:  the AT&T public domain
X * source for getopt(3).  It is the code which was given out at the 1985
X * UNIFORUM conference in Dallas.  I obtained it by electronic mail
X * directly from AT&T.  The people there assure me that it is indeed
X * in the public domain.
X * 
X * There is no manual page.  That is because the one they gave out at
X * UNIFORUM was slightly different from the current System V Release 2
X * manual page.  The difference apparently involved a note about the
X * famous rules 5 and 6, recommending using white space between an option
X * and its first argument, and not grouping options that have arguments.
X * Getopt itself is currently lenient about both of these things White
X * space is allowed, but not mandatory, and the last option in a group can
X * have an argument.  That particular version of the man page evidently
X * has no official existence, and my source at AT&T did not send a copy.
X * The current SVR2 man page reflects the actual behavor of this getopt.
X * However, I am not about to post a copy of anything licensed by AT&T.
X */
X
X/* for SysV use strchr instead of index */
X#ifdef USG
X#define index	strchr
X#define rindex	strrchr
X#endif
X
X/*LINTLIBRARY*/
X#define NULL	0
X#define EOF	(-1)
X#define ERR(s, c)	if(opterr){\
X	extern int write();\
X	char errbuf[2];\
X	errbuf[0] = c; errbuf[1] = '\n';\
X	(void) write(2, argv[0], (unsigned)strlen(argv[0]));\
X	(void) write(2, s, (unsigned)strlen(s));\
X	(void) write(2, errbuf, 2);}
X
Xextern char *index();
X
Xint	opterr = 1;
Xint	optind = 1;
Xint	optopt;
Xchar	*optarg;
X
Xint
Xgetoptx(argc, argv, opts)
Xint	argc;
Xchar	**argv, *opts;
X{
X	static int sp = 1;
X	register int c, ch;
X	register char *cp;
X
X	if(sp == 1)
X		if(optind >= argc ||
X		   argv[optind][0] != '-' || argv[optind][1] == '\0')
X			return(EOF);
X		else if(strcmp(argv[optind], "--") == NULL) {
X			optind++;
X			return(EOF);
X		}
X
X	optopt = c = argv[optind][sp];
X	if(c == ':' || c == OptArgFlg || (cp=index(opts, c)) == NULL) {
X		ERR(": illegal option -- ", c);
X		if(argv[optind][++sp] == '\0') {
X			optind++;
X			sp = 1;
X		}
X		return('?');
X	}
X	if((ch = *++cp) == OptArgFlg) {
X		if(argv[optind][sp+1] != '\0')
X			optarg = &argv[optind++][sp+1];
X		else {
X			optarg = "";
X			optind++;
X			sp = 1;
X		}
X	}
X	else if(ch == ':') {
X		if(argv[optind][sp+1] != '\0')
X			optarg = &argv[optind++][sp+1];
X		else if(++optind >= argc) {
X			ERR(": option requires an argument -- ", c);
X			sp = 1;
X			return('?');
X		} else
X			optarg = argv[optind++];
X		sp = 1;
X	} else {
X		if(argv[optind][++sp] == '\0') {
X			sp = 1;
X			optind++;
X		}
X		optarg = NULL;
X	}
X	return(c);
X}
SHAR_EOF
chmod 0644 getoptx.c || echo "restore of getoptx.c fails"
echo "x - extracting getoptst.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > getoptst.c &&
X#include <stdio.h>
X
Xextern char *optarg;
Xextern int opterr, optind;
X
Xmain(argc, argv)
X  int argc;
X  char *argv[];
X{
X  int ch;
X
X  while ((ch = getoptx(argc, argv, "abc:xD*n?")) != EOF) {
X    switch (ch) {
X    case 'c':
X      printf("option c required arg: %s\n", optarg);
X      break;
X    case 'D':
X      if (*optarg) {
X        printf("option D optional arg: %s\n", optarg);
X      }
X      else {
X        printf("option D no argument\n");
X      }
X      break;
X    case 'a':
X    case 'b':
X    case 'x':
X      printf("option %c\n", ch);
X      break;
X    case 'n':
X      printf("Error reporting disabled\n");
X      opterr = 0;
X      break;
X    default:
X      printf("Get unknown option! Usage follows.\n");
X    }
X  }
X  
X  for (ch = optind; ch < argc; ch++) {
X    printf("Arg[%2d]: %s\n", ch, argv[ch]);
X  }
X  
X  exit(0);
X}
SHAR_EOF
chmod 0644 getoptst.c || echo "restore of getoptst.c fails"
echo "x - extracting getoptx.3 (Text)"
sed 's/^X//' << 'SHAR_EOF' > getoptx.3 &&
X.TH GETOPTX 3 local
X.SH NAME
Xgetoptx - decode command line options
X.SH SYNOPSIS
X.B "char getoptx(argc, argv, optstring)
X  int argc;
X  char *argv[];
X  char *optstring;
X  extern int optind, opterr;
X  extern char *optarg;
X.SH DESCRIPTION
X.I getoptx
Xreturns a character which is either (a) one of the known option characters
Xspecified in
X.IR optstring ,
X(b) a question mark character (?) if the option is unknown or
Xincorrectly specified, or
X.B EOF
Xif all options have been returned. If the caller has specified that an
Xoption requires or accepts an argument, the string value of the argument
Xis returned in
X.IR optarg .
XIn the case of an optional argument
X.I optarg
Xmay point to an empty ("") string.
X.SS Format of optstring
X.I optstring
Xcontains the values of the option characters which are recognized. In
Xaddition any character may be followed by a colon (:) is it requires an
Xargument, or an asterisk (*) if it accepts an optional argument.
X.SS Format of the command line
XThe command line parsed by
X.I getoptx
Xconsists of the command name, followed by zero or more options separated
Xby blanks, followed by arguments to the command. The options are ended
Xby any of (a) the end of data on the command line, (b) a token which
Xdoes not start with a hyphen (-), or (c) the special token --.
X.SS Using ? as a valid option
XAlthough
X.I getoptx
Xreturns the ? as an indication of an unknown option, or one used
Xwithout a required argument, the ? may be included in the
X.I optstr
Xso that ? becomes a known option. The effect is that specifying any
Xinvalid option will generate an error message, while using -? as an
Xoption does not. In either case a ? returned from
X.I getoptx
Xcan be used to display a usage message.
X.br
X.ne 1.5i
X.SH EXAMPLES
X.tr ~
X cval = getoptx(argc, argv, "al:sD*");
X
XThe options 'a' and 's' are returned without arguments. The option 'l'
Xrequires an argument, which may be part of the same command line token
Xas the option (as:~-l40) or as the following token (as:~-c~40). The
Xoption 'D' may be immediately followed by an argument, and if there is
Xno argument it must be the last option in the token.
X.br
X.ne 1.5i
X.SH WARNINGS
XA warning message is displayed on
X.B stderr
Xand the character ? is returned is either (a) an option character not in
X.I optstr
Xis found, or (b) a known option which requires an argument does not have
Xone. If the variable
X.I opterr
Xis set to zero the warning message will not be displayed.
X.br
X.ne 1.5i
X.SH SEE ALSO
X.SH LIMITATIONS
XThe characters colon (:) and asterisk (*) may not be option characters.
X.br
X.ne 1.5i
X.SH AUTHOR
XMan page by Bill Davidsen (davidsen@crdos1.crd.ge.com). Original getopt
Xsource attributed to AT&T public domain version from 1985 UNIFORUM,
Xchanges by Bill Davidsen. All original work by Bill Davidsen is hereby
Xplaced in the public domain.
SHAR_EOF
chmod 0666 getoptx.3 || echo "restore of getoptx.3 fails"
echo "x - extracting getoptx.mak (Text)"
sed 's/^X//' << 'SHAR_EOF' > getoptx.mak &&
X# makefile for extended getopt program
X#
X# make via "make -f getoptx.mak"
X
X# define USG if V&, SysIII, SysV or other non-BSD
XUSGOPT	= -DUSG
X
X.c.o:
X	$(CC) $(USGOPT) $< -c
X
Xtest:	getoptst suite
X
Xgetoptst: getoptst.o getoptx.o
X	$(CC) -o getoptst getoptst.o getoptx.o
X
Xsuite:
X	@echo; echo "Test1: (one valid option)"
X	./getoptst -a "just a"
X	@echo; echo "Test2: (option withy arg and args to program)"
X	./getoptst -xc40 -a x "c inline arg 40" "a follows"
X	@echo; echo "Test3: (arg to option after whitespace)"
X	./getoptst -c 40 "c arg 40"
X	@echo; echo "Test4: (optional arg to -D not given)"
X	./getoptst -D -a "options D and a"
X	@echo; echo "Test5: (optional arg to -D given)"
X	./getoptst -Dokay -x "D arg okay" x
X	@echo; echo "Test6: (-- ends options, -c arg to prog)"
X	./getoptst -c"last arg" -- -c "-c arg not option"
X	@echo; echo "Test7: (invalid option m)"
X	-./getoptst -m
X	@echo; echo "Test8: (missing arg to option c)"
X	-./getoptst -c
X	@echo; echo "Test9: (explicit ? option)"
X	-./getoptst -?
X	@echo; echo "Test10: (use -n to disable warning)"
X	-./getoptst -n -Q
X
X# subsitute your favorite shar here or on the make line, as
X# make shar "SHAR=myshar -options"
X
XSHAR	= shar
Xshar:
X	$(SHAR) getoptx.c getoptst.c getoptx.3 getoptx.mak > getoptx.shar
SHAR_EOF
chmod 0644 getoptx.mak || echo "restore of getoptx.mak fails"
exit 0

	bill davidsen		(davidsen@crdos1.crd.GE.COM)
  {uunet | philabs}!crdgw1!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me