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 initialization 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);
}