Introduction to argtable 1.2.

Stewart Heitmann

August 1999


Legal notice

The argtable library and accompanying documentation is copyright © 1998, 1999 Stewart Heitmann (Stewart.Heitmann@tip.csiro.au)

Argtable is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU Library General Public License for more details.

You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


Overview

Argtable is a freely available programmer's library for parsing the command line arguments of any C/C++ program. It allows the programmer to specify the desired format of the command line arguments in one or more statically defined arrays known as argument tables. Each row of an argument table specifies the data type of an expected argument and nominates a user-defined program variable as storage for the incoming argument value. If arguments require default values, then these too are specified in the argument table.
Once an argument table has been established, parsing the command line is simply a matter of calling an argtable library function which attempts to resolve the program's argv[] arguments with the entries of the argument table. If successful, the command line arguments are now known to be valid and their values are ready and available for use in their nominated program variables. If, on the other hand, the arguments could not be successfully resolved then the program has the choice of trying again with an alternative argument table (for programs with multiple usages), or it can flag the error to the user and terminate. When argtable does detect an error in the command line it automatically generates the appropriate error message strings for you, so error reporting is usually just a matter of printing these strings to stdout or stderr.
Argtable also provides some auxiliary functions that allow the programmer to generate plain text descriptions of the individual arguments and their overall usage syntax directly from an argument table. These make it easy to generate useful on-line help facilities that are guaranteed to be up to date with your code.
Perhaps best of all, argtable is simple to use; it only has seven functions.

Styles of command line arguments.

Argtable supports two styles of command line arguments, tagged and untagged.

Tagged arguments are identified by a prefix tag, as in -o file or title:mystuff. The tag enables these arguments to appear anywhere on the command line, and in any order. The format of the tags is completely general; they appear exactly as defined in the argument table, without being restricted to any particular formatting style or identifying character traits. The programmer may implement any style of argument tag desired, including such common styles as -title mystuff, title:mystuff, --title mystuff, or title=mystuff.
Untagged arguments on the other hand have no prefix; they are identified strictly by their ordering on the command line.
The two styles of arguments may be freely mixed, whereupon the tagged arguments are always processed first, leaving any remaining (untagged) arguments to be scanned from left to right.

The value given to an argument may be of type integer, double, string, or boolean. Doubles may be given in either floating point or scientific notation, and strings may be either quoted or unquoted. Booleans will accept any of the keywords on, off, yes, no, true, or false and yield an integer value of 0 (negative) or 1 (affirmative) accordingly.
A special argument type called literal is also provided; it yields an integer value according to the presence or absence of a given string literal on the command line. It is useful for specifying unparameterised command line switches such as -verbose and -help.

Optional arguments and default values.

Arguments may be assigned default values that take effect when no matching command line argument could be found. When you specify a default value for an argument you are, in effect, declaring that argument as being optional. Arguments without defaults are, by definition, regarded as mandatory arguments.

Supported platforms

Argtable conforms to ansi C requirements and should compile on any standard ansi C compiler. To date, it has been successfully compiled on:

Where to get it

Argtable is available under the /pub/linux/devel/ directory of the ftp archives at the Metalab site of the University of North Carolina. You can go straight to http://metalab.unc.edu/pub/linux/devel/ but you are encouraged to try one of the Metalab mirror sites closest to you.

Similar packages

Here are some similar packages that I am aware of. Apologies for any I may have omitted.


How argtable works

By way of example, lets say we have a program called myprog and we wish to implement the following command line usage syntax for it:

myprog [-tit <title>] grad:gradient [y-int]
   -tit <title>    your title
   grad:gradient   line gradient
   y-int           y-intercept

Our program will use a single argument table to do this. All argument tables are defined as an array of arg_rec structs, with each array element pertaining to a single command line argument. The arg_rec struct is defined in argtable.h as:

typedef enum {arg_int=0,arg_dbl,arg_str,arg_bool,arg_lit} arg_type;
typedef struct
   {
   const char *tagstr;       /* argument tag string */
   const char *argname;      /* argument name string */
   arg_type    argtype;      /* argument data type */
   void       *valueptr;     /* ptr to user storage location */
   const char *defaultstr;   /* default value, as a string */
   const char *argdescrip;   /* argument description string */
   } arg_rec;

The following code fragment shows how the argument table is defined statically within the code. Defining the tables statically is a programming convenience but not a requirement; the table could equally well have been dynamically allocated and initialized at run time. Notice that I also chose to define the argument table within the main() block because that's the only place where it is used so there is no need to promote it to a higher namespace. However you may define it in the global namespace if you prefer.

int main(int argc, char **argv)
   {
   static char str[50];
   static double grad;
   static int c;
   arg_rec argtable[] =
      {
      {"-tit ", "<title>",  arg_str, str,   "noname", "\t\t your title"},
      {"grad:", "gradient", arg_dbl, &grad, NULL,     "\t line gradient"},
      {NULL,    "y-int",    arg_int, &c,    "0",      "\t\t y-intercept"}
      };
   const size_t narg = sizeof(argtable)/sizeof(arg_rec);
   ...
  }

Our argument table has three rows, one for each command line argument -tit <title>, grad:gradient, and y-int. The behaviour of the argument parsing is governed entirely by the contents of the various fields (columns) of the argument table. Lets step through each field one by one.

Having defined the argument table, we can now use it to parse the command line arguments in argv[]. There are several ways to do this, but the simplest is to use the arg_scanargv() library function.

int arg_scanargv(int argc, char** argv, arg_rec *argtable, int n, char* CmdLine, char* ErrMsg, char* ErrMark);

This function takes as input the command line arguments in argv[] (there are argc of them) and a pointer to the argument table in argtable (which has n rows). It proceeds to scan the argv[] arguments (skipping argv[0]) and tries to resolve them with the entries given in the argument table. If this can be done successfully then all argument values are written into the program variables as designated by the argument table and the function returns 1. If not, the function returns 0 to indicate the failure.

The three string pointers CmdLine, ErrMsg, and ErrMark refer to user defined string buffers which arg_scanargv() uses to return information about the parsing. They are optional parameters for arg_scanargv() in the sense that they may be given as NULL if you do not wish to use them. Into the first string buffer, *CmdLine, is written a copy of the command line arguments from argv[] concatenated into a single line string with the arguments seprated by spaces. This string is written before the parsing commences, so it will always be present whether or not the parsing was succsessful. The CmdLine string is useful when the calling program wants to echo the command line to stdout or stderr, say after an error has occurred. The string buffers ErrMsg and ErrMark are only used when arg_scanargv() detects an error in the command line. When this happens, an error message describing the reason for the failure is written into *ErrMsg. The ErrMark string is somewhat related to CmdLine; it contains a string of tilde characters which have been formatted in such a way as to highlight the exact location of the error in the command line when it is displayed directly below the *CmdLine string (assuming a fixed width font is used).

The code fragment below demonstrates the use of the arg_scanargv() function. It presumes that argc, argv, argtable, and narg are as defined in the example above and are accessable in this namespace.

{
char cmdline[200], errmsg[200], errmark[200];

if (!arg_scanargv(argc,argv,argtable,narg,cmdline,errmsg,errmark))
   {
   /*-- arg error occurred, print error message and exit --*/
   printf("ERROR: %s\n", cmdline);
   printf("       %s %s\n", errmark, errmsg);
   return 1;
   }

/*-- only get here if the arguments were scanned successfully --*/

}

And here are some examples of the console output that this code produces when arg_scanargv() returns zero.

$ myprog grad:oops
ERROR: myprog grad:oops
                   ^ invalid grad:gradient argument
$ myprog grad:13 nope
ERROR: myprog grad:13 nope
                      ^^^^ unexpected argument
$ myprog grad:13 99 uh oh
ERROR: myprog grad:13 99 uh oh
                         ^^ unexpected argument

There are two more functions from the argtable library of interest to us, namely, arg_syntax() and arg_glossary(). These functions generate plain text descriptions of the argument syntax as well as descriptions of the individual arguments and are useful for writing out online help text to the console

const char* arg_syntax(const arg_rec* argtable, int n);
const char* arg_glossary(const arg_rec* argtable, int n, const char* prefix);

The arg_syntax() function returns a pointer to an internal string buffer that contains a plain text description of the usage syntax of the argument table it was passed. The string comprises a space separated list of the tag and name strings of each argument table entries concatenated into a single line string. Optional command line arguments are automatically enclosed in square brackets. Calling arg_syntax() on our example argument table would return the string:

[-tit <title>] grad:gradient [y-int]

The calling program would ordinarily prepend this with the program name from argv[0] to get the full usage syntax.

The arg_glossary() function is similar, except it generates a multi-line text string with one argument per line. Each line includes the argument's tag, its name string, and its description string as given in the argument table. Arguments that have a NULL description string are omitted. Each line of the glossary string is prefixed with the string given in the prefix parameter; it useful for indenting each line of the string. Calling arg_glossary() with our example argument table results in the following multi-line string:

-tit <title> your title
grad:gradient line gradient
y-int y-intercept

Lets return to our example program and put it all together in its entirety.
Our program, when executed without any arguments (argc==1), will display the argument usage syntax and a description of the individual arguments on stdout, then exit. When given a valid set of arguments, it will display the resulting argument values as they are stored in the local program variables. Otherwise, it echoes the erroneous command line together with an appropriate error message to stdout and terminates with error code 1.

#include "argtable.h"

int main(int argc, char **argv)
   {
   static char str[50];
   static double grad;
   static int c;
   arg_rec argtable[] =
      {
      {"-tit ", "<title>",  arg_str, str,   "noname", "\t\t your title"},
      {"grad:", "gradient", arg_dbl, &grad, NULL,     "\t line gradient"},
      {NULL,    "y-int",    arg_int, &c,    "0",      "\t\t y-intercept"}
      };
   const size_t narg = sizeof(argtable)/sizeof(arg_rec);

   /*-- process the command line args --*/
   if (argc==1)
      {
      /*-- display program usage and exit. --*/
      printf("Usage: %s %s\n", argv[0], arg_syntax(argtable,narg));
      printf("%s\n",arg_glossary(argtable,narg,"  "));
      return 0;
      }
   else
      {
      /*-- scan command line arguments from argv[] --*/
      char cmdline[200], errmsg[200], errmark[200];
      if (!arg_scanargv(argc, argv, argtable, narg, cmdline, errmsg, errmark))
         {
         /*-- arg error occurred, print error message and exit --*/
         printf("ERROR: %s\n", cmdline);
         printf("       %s %s\n", errmark, errmsg);
         return 1;
         }
      }

   /*-- get here only if command line args ok --*/
   printf("title: \"%s\"\n",str);
   printf("gradient: %f\n",grad);
   printf("y-intercept: %d\n",c);

   return 0;
   }

Here are some results of running myprog with various command line arguments.

$ myprog
Usage: myprog [-tit <title>] grad:gradient [y-int]
  -tit <title>     your title
  grad:gradient    line gradient
  y-int            y-intercept

$ myprog grad:10
title: "noname"
gradient: 10.000000
y-intercept: 0

$ myprog 7 grad:1.234
title: "noname"
gradient: 1.234000
y-intercept: 7

$ myprog -tit "hello world" grad:3.33 11
title: "hello world"
gradient: 3.330000
y-intercept: 11

Man pages

For more information on any of the argtable library functions, consult the unix man pages that come supplied with the library. They are provided in both nroff and html format. The functions are summarized below:


arg_catargs()

Creates a single line string of space separated arguments by concatenating copies of the argv[] arguments.

arg_dump()

Prints the contents of an argument table. This is useful for debugging

arg_glossary()

Generates a multi-line glossary string from an argument table.

arg_record()

Returns an arg_rec struct pre-filled with a set of given values.

arg_scanargv()

Scans arguments from argv[] and parses them according to a given argument table.

arg_scanargstr()

Scans arguments from a single line string and parses them acording to a given argument table.

arg_syntax()

Generates a single line string describing an argument table's usage syntax.

arg_typestr

A static array of strings defining the default names assigned to different argument types.



Installing argtable

Having untarred the argtable distribution file (argtable-1.2.tar), detailed instructions on how to install the package can be found in the argtable/INSTALL text file. However in most cases all you need do is:


History & Release Notes


From the author

Argtable is maintained by me, Stewart Heitmann. It is a tool I have felt has been long overdue in the C/C++ world, and having written it I hope that you will find it as useful as I do. If you have any comments or suggestions for improvements please email me at Stewart.Heitmann@tip.csiro.au, I'd love to hear from you. Happy parsing!