Next: , Previous: , Up: Making Genparse Files   [Contents][Index]

2.2 Adding Command Line Options

From the previous section’s mycopy1, we will now add command line option parsing with getopt_long () to create mycopy2. Along the way we’ll add a small number of useful features.

/* mycopy2.c */

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

int main (int argc, char *argv[])
{
  int c, i;
  FILE *fp;
  extern char *optarg;
  extern int optind;
  int option_index = 0;
  char ch;
  int help, iterations;
  int errflg = 0;

  static struct option long_options[] =
  {
    {"help", no_argument, NULL, 'h'},
    {"iterations", required_argument, NULL, 'i'},
    {NULL, 0, NULL, 0}
  };

  help = 0;
  iterations = 1;

  while ((ch = getopt_long (argc, argv, "hi:", long_options, 
			    &option_index)) != EOF)
    {
      switch (ch)
        {
	case 'h':
          help = 1;
          break;

        case 'i':
          iterations = atoi (optarg);
          if (iterations < 0)
            {
              fprintf (stderr, "error: iterations must be >= 0\n");
              errflg ++;
            }
          break;

        default:
          errflg++;

	}
    } /* while */

  if (errflg || help)
    {
      printf ("usage: %s [ -i ] <file>\n", argv[0]);
      printf ("  [ -h ] [ --help ] Print help screen \n");
      printf ("  [ -i ] [ --iterations ] Number of times to \
output <file>\n");
      exit (1);
    }

  if (optind >= argc)
    fp = stdin;
  else
    fp = fopen (argv[optind],"r");

  for (i = 0; i < iterations; i++)
    {
      while ((c = fgetc (fp)) != EOF)
	fputc (c, stdout);
      rewind (fp);
    }
  
  fclose (fp);
  return 0;
}

This program performs the same function as mycopy1 but does so in a more flexible and reliable fashion. Two command line options are supported, -h and -i. When -h, or its long form, --help, appears on the command line, all other options are ignored and a usage message is displayed. The -i option allows the user to specify the number of iterations, as discussed above. It also has a long form, --iterations. The number of iterations defaults to 1 if -i does not appear on the command line, and the program prevents negative values from being specified with -i.

The usage message is a useful way of summarizing a program’s options without needed a man or info page. There are three ways that the usage message for mycopy2 will be displayed:

A nice feature of getopt () and getopt_long () is that they will rearrange the command line within argv so that all non-options follow all of the options1. The external variable optind is set to point to the first non-option in the re-arranged argv. Thus, by comparing optind to argc, we can determine whether or not an input file has been specified. If there is no input file on the command line, we can redirect mycopy2 to use stdin.

While mycopy2 is an improvement over mycopy1, there are a number of drawbacks to using getopt () in all of your programs. The most obvious is time. In mycopy2, two-thirds (about 50 lines) of the code does the command line parsing, and only two options are supported. If there we’re 10 options, the command line parsing code could easily reach 200 lines or more. Additionally, since each option’s parameter may need to be checked for validity and assigned to a variable, this becomes a tedious process that can be error prone.

Observing the source code for mycopy2, it becomes clear that, like any repetitive task, the command line parsing code follows a number of simple patterns. If these patterns can be abstracted and generalized so that the user can indicate option use in a concise format, most, if not all, of the parsing code can be automatically generated. With this thought in mind, we turn our attention to Genparse.


Footnotes

(1)

argv[0] is not rearranged - it remains in its place.


Next: , Previous: , Up: Making Genparse Files   [Contents][Index]