10.3. Interpreting program arguments
The loop used to examine the program arguments in the example above is
a common C idiom which you will see in many other programs. An additional
common idiom is to use ‘options’ to control the behaviour of the
program (these are also sometimes called switches or flags). Arguments which
start with a ‘-
’ are taken to introduce one or more
single-letter option indicators, which can be run together or provided
separately:
progname -abxu file1 file2 progname -a -b -x -u file1 file2
The idea is that each of the options selects a particular aspect from the
program's repertoire of features. An extension to that idea is to allow
options to take arguments; if the -x
option is specified to
take an argument, then this is how it might be used:
progname -x arg file1
so that the arg
argument is associated with the option. The
options
function below automates the processing of this style
of use, with the additional (common but preferably considered obsolescent)
support for the provision of option arguments immediately following the
option letter, as in:
progname -xarg file1
In either of the above cases, the options routine returns the character
‘x
’ and sets a global pointer, OptArg
, to
point to the value arg
.
To use this routine, a program must supply a list of valid option letters
in the form of a string; when a letter in this string is followed by
a ‘:
’ this indicates that the option letter is to be
followed by an argument. When the program is run, it is then simply
a question of repeatedly calling the options
routine until no
more option letters remain to be found.
It seems to be a fact of life that functions which scan text strings looking for various combinations or patterns within them end up being hard to read; if it's any consolation they aren't all that easy to write either. The code that implements the options is definitely one of the breed, although by no means one of the worst:
/* * options() parses option letters and option arguments from the argv list. * Succesive calls return succesive option letters which match one of * those in the legal list. Option letters may require option arguments * as indicated by a ':' following the letter in the legal list. * for example, a legal list of "ab:c" implies that a, b and c are * all valid options and that b takes an option argument. The option * argument is passed back to the calling function in the value * of the global OptArg pointer. The OptIndex gives the next string * in the argv[] array that has not already been processed by options(). * * options() returns -1 if there are no more option letters or if * double SwitchChar is found. Double SwitchChar forces options() * to finish processing options. * * options() returns '?' if an option not in the legal set is * encountered or an option needing an argument is found without an * argument following it. * */ #include <stdio.h> #include <string.h> static const char SwitchChar = '-'; static const char Unknown = '?'; int OptIndex = 1; /* first option should be argv[1] */ char *OptArg = NULL; /* global option argument pointer */ int options(int argc, char *argv[], const char *legal) { static char *posn = ""; /* position in argv[OptIndex] */ char *legal_index = NULL; int letter = 0; if(!*posn){ /* no more args, no SwitchChar or no option letter ? */ if((OptIndex >= argc) || (*(posn = argv[OptIndex]) != SwitchChar) || !*++posn) return -1; /* find double SwitchChar ? */ if(*posn == SwitchChar){ OptIndex++; return -1; } } letter = *posn++; if(!(legal_index = strchr(legal, letter))){ if(!*posn) OptIndex++; return Unknown; } if(*++legal_index != ':'){ /* no option argument */ OptArg = NULL; if(!*posn) OptIndex++; } else { if(*posn) /* no space between opt and opt arg */ OptArg = posn; else if(argc <= ++OptIndex){ posn = ""; return Unknown; } else OptArg = argv[OptIndex]; posn = ""; OptIndex++; } return letter; }Example 10.2