getopt.c

posted by Thomas Kindler, 2006/05/27 00:31

/*  A lightweight command line option parser.
 
    Copyright (c)2002 by Thomas Kindler, thomas.kindler@gmx.de
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of
    the License, or (at your option) any later version. Read the
    full License at http://www.gnu.org/copyleft for more details.
*/
 
// include files -----
//
#include "getopt.h"
#include <string.h>
#include <stdio.h>
 
/**
 * Left-rotate [len] array entries by one entry.
 */
static void rotate(char *array[], int len)
{
  char *old  = array[0];
  for (int i=0; i<len-1; i++)
    array[i] = array[i+1];
  array[len-1] = old;
}
 
 
/**
 * Parse argv[] array for command line options.
 * The array is permuted, so that all recognized options
 * are moved to the end of the array. This has several
 * advantages, because it makes the option parsing
 * totally transparent to the calling program. See the
 * example for more details.
 *
 * \b Example: \code
 *   bool opt_boolflag;
 *   int  opt_inttest;
 *
 *   CmdOpt  options[] = {
 *     'b', "onoff",   NULL, &opt_boolflag, "this is an on/off flag",
 *     'i', "integer", "%d", &opt_test,     "integer scan test",
 *     NULL, NULL, NULL, NULL, NULL
 *   };
 *
 *   int main(int argc, char* argv[])
 *   {
 *     if (!(argc = getopt(argc, argv, options))) {
 *       getopt_help(options);
 *       return -1;
 *     }
 *     for (int i=0; i<argc; i++)
 *       printf("  %d: \"%s\"\n", i, argv[i]);
 *   }
 * \endcode
 *
 * \param   argc, argv   argument count and array of
 *          arguments as given to the main() function.
 * \param   options   pointer to options array, the
 *          last entry must have zeros or NULL's in all
 *          fields.
 * \return  number of remaining non-option arguments
 *          or 0 if there was an error.
 */
int getopt(int argc, char *argv[], const CmdOpt *options)
{
  int  current = 1;
  int  todo    = argc - 1;
 
  while (todo > 0) {
    const CmdOpt *opt  = NULL;
 
    if (!strcmp(argv[current], "--")) {
      // stop option parsing.
      //
      rotate(&argv[current], argc - current); todo--;
      return current+todo;
    } else if (!strncmp(argv[current], "--", 2)) {
      // search for long-option match
      //
      bool ambiguous = false;
      int  len = strlen(&argv[current][2]);
 
      for (const CmdOpt *o = options; o->param; o++) {
        if (!strcmp(&argv[current][2], o->longopt)) {
          // We found a direct, unambiguos match.
          //
          opt = o;
          ambiguous = false;
          break;
        }
        if (!strncmp(&argv[current][2], o->longopt, len)) {
          // We found a partial match. If there's more
          // than one match, the option is ambiguous.
          // (unless we also find a direct match).
          //
          if (opt == NULL)
            opt = o;
          else
            ambiguous = true;
        }
      }
      if (ambiguous) {
        fprintf(stderr, "ERROR: ambiguous option \"%s\".\n", argv[current]);
        return 0;
      }
      if (!opt) {
        fprintf(stderr, "ERROR: unknown option \"%s\".\n", argv[current]);
        return 0;
      }
    } else if (!strncmp(argv[current], "-", 1) ||
               !strncmp(argv[current], "/", 1)  ) {
      // search for short-option match
      //
      for (const CmdOpt *o = options; o->param; o++)
        if (argv[current][1] == o->shortopt && argv[current][2] == 0)
          opt = o;
 
      if (!opt) {
        fprintf(stderr, "ERROR: unknown option \"%s\".\n", argv[current]);
        return 0;
      }
    }
 
    if (opt) {
      // cool, we found an option.
      //
      if (opt->format) {
        // scan for format string..
        //
        if (current+1 >= argc) {
          fprintf(stderr, "ERROR: missing parameter for \"%s\".\n", argv[current]);
          return 0;
        }
        if (!sscanf(argv[current+1], opt->format, opt->param)) {
          fprintf(stderr, "ERROR: invalid parameter \"%s\" for \"%s\".\n",
                  argv[current+1], argv[current]);
          return 0;
        }
        rotate(&argv[current], argc - current); todo--;
        rotate(&argv[current], argc - current); todo--;
      } else {
        // no format string - set boolean value.
        //
        if (opt->param)
          *((bool*)opt->param) = true;
        rotate(&argv[current], argc - current); todo--;
      }
    } else {
      // skip non-option argument.
      //
      current++; todo--;
    }
  }
  return current;
}
 
 
/**
 * Print option help strings to stdout. The help texts
 * are generated from the options array.
 *
 * \param options  pointer to array of CmdOpt options.
 */
void getopt_help(const CmdOpt *options)
{
  int xpos = printf("Options : ");
  for (CmdOpt *o=options; o->param; o++) {
    if (xpos == 0)
      xpos = printf("          ");
    if (o->shortopt)
      xpos += printf("-%c, ", o->shortopt);
    else             
      xpos += printf("    ");
    
    if (o->longopt)
      xpos += printf("--%s", o->longopt);
 
    xpos += printf("  ");
    while (xpos < 25) {
      printf(" ");
      xpos++;
    }
    printf("%s\n", o->help);
    xpos = 0;
  }
}
/*  A lightweight command line option parser.
 
    Copyright (c)2002 by Thomas Kindler, thomas.kindler@gmx.de
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of
    the License, or (at your option) any later version. Read the
    full License at http://www.gnu.org/copyleft for more details.
*/
#ifndef GETOPT_H
#define GETOPT_H
 
/**
 * Command line option definition.
 */
typedef struct {
  char  shortopt;   ///< short option string, e.g. 'o'
  char *longopt;    ///< long option string, e.g. "--option"
  char *format;     ///< scanf format string, e.g. "%d"
                    ///< or NULL for boolean on/off switches   
  void *param;      ///< where to store the parameter
  char *help;       ///< where to store the parameter
} CmdOpt;
 
 
extern  int   getopt(int argc, char *argv[], const CmdOpt *options);
extern  void  getopt_help(const CmdOpt *options);
 
#endif
 
codesnippets/getopt.txt · Zuletzt geändert: 28.05.2006 22:57 von thomask