*****Listing 3*****

/* cmd_opts.c, c\lib\src,  (c) 1989 Scott D. Maley
May be freely used, as long as copyright notice is preserved

cmd_options(argc, argv, option)

int	*argc;		-- pointer to command line arg count
char	*argv[];	-- pointer to array of pointers to 
			command line arguments
struct	options option[]; -- structure array defining valid 
			options

This is a function to process command line options (or
switches). The full set of command line arguments is passed
to the routine via argc and argv.  Every option switch
encountered that is a valid match for a switch specified in
the option array is counted, removed from argv, and the
pointer to it's associated value (if any) is moved to
the optv array.  A count of switches which are not valid
matches of any option is returned, and those switches are
left in argv.

A switch's value may be contiguous with it, or be separated
from it by white-space (e.g. -svalue, -s value).  White-
space is commonly blanks and tabs, but may also include
commas in some C implementations.  This routine doesn't care.
The C runtime initilization routine which runs before main()
is entered parses the command line into tokens (which the
elements of argv point to), based on it's definition of
white-space.

The structure "options" is used to define what this routine
will parse:

struct	options
{
	char	s;	  -- The option (switch) letter
	int	arg_flg;  -- indicates if an arg is required
	char	**poptv[];-- pointer to option value vector
			  -- NULL, if none expected
} ;

The third argument to this routine, option, is an array of
the options structures.  The end of this array is signaled
with s == 0.

This routine returns:
	 0 - if all switches encountered were valid options.
	-n - Negative of the count of invalid (e.g. no
	value followed the switch when one was expected, or
	a value was contiguous 	with the switch, but none
	was expected) switches encountered.  N also includes
	a count of switches that were expected, but not
	encountered in argv.

	It also sets arg_flg to indicate how many of each
	switch encountered.

	
	Sample use:
	----------
	
	#include <stdio.h>
	#include "cmd_opts.h"

	main(argc, argv)
	int	argc;
	char	*argv[];
	{
		char	s, *farg[] *marg[];
		static struct options sw[] =
			{'a', 0, NULL,	-- optional, no value
			 'f', 0, &farg,	-- optional, w/ value
			 'm', 1, &marg,	-- required, w/ value
			   0, 0, NULL};

		if (cmd_options( & argc, argv, sw) < 0)
		{
			--- error, handle it here
			---
		}
		---
		--- continue with rest of program
		---
	}

*-- History:
* 30 Jan 89 SDM (TASC)		No need to calloc optv, we
*			can work entirely within argv (plus
*			a temp pointer).
* 27 Jan 89 SDM (TASC)		Handle multiple instances
*			of a switch.
*				Retain everything not
*			specified in opts in argv, and set
*			argc accordingly.
* 20 Jan 89 S.D. Maley (TASC)	Initial implementation.
*-- End History
*/

#include <stdio.h>
#include "cmd_opts.h"


#define EOS '\0'

#define MoveOptFromArg(optv,argv,i,argc) \
   {char *temp;\
    temp= argv[i];\
    RemoveArg(argv,i,argc);\
    (optv)--;\
    optv[0]= temp;\
   }

#define RemoveArg(argv,i,argc) \
   {int j;\
    (argc)--;\
    for(j=i;j<argc;j++) argv[j]=argv[j+1];\
   }

#define SWFLG '-'
#define SwChr *(argv[i]+1)
#define SwMatch	(*argv[i] == SWFLG && opts[j].s == SwChr)
#define SwValContig (*(argv[i]+2) != EOS)
#define SwValNext (i+1 < *argc && *argv[i+1] != SWFLG)


cmd_options(argc, argv, opts)
   int     *argc;
   char          *argv[];
   struct options       opts[];
{
   int   i,j, njth, stat;
   char   **optv;      /* equivalent to: *optv[] */

   optv = argv + *argc;   /* work from back to front */

   /*-- Transfer options from argv to optv
   * -- and check against expectations
   */
   stat = 0;

   for (j = 0; opts[j].s != 0; j++)
   {
      njth = 0;

      for (i = *argc - 1; i > 0; i--)
      {  /* back to front, we build optv */
         if (SwMatch)
         {
            if (opts[j].poptv == NULL)
            { /* no arg value desired */
               if (SwValContig)
                  continue;   /* next i */
               else
                  RemoveArg(argv,i,*argc);
            } else { /* A value is desired */
               if (SwValContig)
               {
                  argv[i] += 2;   /* past "-'opt_char'" */
                  MoveOptFromArg(optv, argv,i,*argc);
               } else if (SwValNext)
               { /*-- pick up value from next arg */
                  RemoveArg(argv,i,*argc);
                  MoveOptFromArg(optv, argv,i,*argc);
               } else
                  continue;   /* next i */
            }
            njth++;      /* only count valid switches */
         } /* if SwMatch */
      } /* for i */

      if (opts[j].poptv != NULL)
         *opts[j].poptv= optv;   /* point to option value vector */
      if (opts[j].arg_flg > njth )
         stat -= opts[j].arg_flg - njth ;  /* not enough */
      opts[j].arg_flg = njth;

   } /* for j */

   for (i= 1; i < *argc; i++)
      if (*argv[i] == SWFLG)
         stat--;      /* a switch we couldn't handle */
   return(stat);
}
