Chapter 26. Parsing Command-Line Options

Most Linux programs allow the user to specify command-line options. Such options perform a wide variety of functions but are fairly uniform in their syntax. Short options consist of a - character followed by a single alphanumeric character. Long options, common in GNU utilities, consist of two - characters followed by a string made up of letters, numbers, and hyphens. Either type of option may be followed by an argument. A space separates a short option from its arguments; either a space or an = separates a long option from an argument.

There are many ways of parsing command-line options. The most popular method is parsing the argv array by hand. The library functions getopt() and getopt_long() provide some assistance for option parsing. getopt() is provided by many Unix implementations, but it supports only short options. The getopt_long() function is available on Linux and allows automated parsing of both short and long options.[1]

A library called popt exists specifically for option parsing. It includes a number of advantages over the getopt() functions.

  • It does not make use of global variables, which allows it to be used when multiple passes are needed to parse argv.

  • It can parse an arbitrary array of argv-style elements. This allows popt to be used for parsing command-line-style strings from any source.

  • Many argument types can be parsed by the library without any extra code in the application.

  • It provides a standard method of option aliasing. Programs that use popt can easily allow users to add new command-line options, which are defined as combinations of already-existing options. This allows the user to define new, complex behaviors or change the default behaviors of existing options.

  • There is a straightforward mechanism for allowing libraries to parse some options while the main application parses other options.

  • It can automatically generate a usage message summarizing the options a program understands, as well as a more detailed help message.

  • Common error messages are provided by the library.

Like getopt_long(), the popt library supports short and long style options.

The popt library is highly portable and should work on any POSIX platform. The latest version is available from ftp://ftp.rpm.org/pub/rpm/. There are some features of popt that we do not discuss in this chapter, but the popt man page is quite good and contains documention on the features we have skipped.

It may be redistributed under either the GNU General Public License or the GNU Library General Public License, at the distributor’s discretion.

The Option Table

Defining the Options

Applications provide popt with information on their command-line options through an array of struct poptOption structures.

#include <popt.h>

struct poptOption {
    const char * longName; /* may be NULL */
    char shortName;        /* may be '' */
    int argInfo;
    void * arg;            /* depends on argInfo */
    int val;               /* 0 means do not return, just update flag */
    char * descrip;        /* optional description of the option */
    char * argDescrip;     /* optional argument description */
};

Each member of the table defines a single option that may be passed to the program. Long and short options are considered a single option that may occur in two different forms. The first two members, longName and shortName, define the names of the option; the first is a long name, and the latter is a single character.

The argInfo member tells popt what type of argument is expected after the argument. If no option is expected, POPT_ARG_NONE should be used. The rest of the valid values are summarized in Table 26.1.[2]

Table 26.1. popt Argument Types

Value

Description

arg Type

POPT_ARG_NONE

No argument is expected

int

POPT_ARG_STRING

No type checking should be performed

char *

POPT_ARG_INT

An integer argument is expected

int

POPT_ARG_LONG

A long integer is expected

long

POPT_ARG_FLOAT

A float integer is expected

float

POPT_ARG_DOUBLE

A double integer is expected

double

POPT_ARG_VAL

No argument is expected (see text)

int

The next element, arg, allows popt to update program variables automatically when the option is used. If arg is NULL, it is ignored and popt takes no special action. Otherwise, it should point to a variable of the type indicated in the right-most column of Table 26.1.

If the option takes no argument (argInfo is POPT_ARG_NONE), the variable pointed to by arg is set to one when the option is used. If the option does take an argument, the variable that arg points to is updated to reflect the value of the argument. Any string is acceptable for POPT_ARG_STRING arguments, but POPT_ARG_INT, POPT_ARG_LONG, POPT_ARG_FLOAT, and POPT_ARG_DOUBLE arguments are converted to the appropriate type, and an error is returned if the conversion fails.

If POPT_ARG_VAL is used, then no argument is expected. Instead, popt copies the integer value stored in the val member into the address pointed to by arg. This is useful when a program has a set of mutually exclusive arguments, and the last one specified wins. By specifying different val values for each option, having each option’s arg member point to the same integer, and specifying POPT_ARG_VAL for each one, it is easy to know which of those options was the last one specified. If you need to give an error if more than one of the options was given, this does not work.

The val member specifies the value popt’s parsing function should return when the option is encountered, unless POPT_ARG_VAL is used. If it is zero, the parsing function continues parsing with the next command-line argument rather than return.

The last two members are optional, and should be set to NULL if they are not needed.[3] The first of these, descrip, is a string describing the option. It is used by popt when it generates a help message describing all of the options available. The descrip member provides a sample argument for the option that is also used to display help. Help message generation is described on pages 606-608.

The final structure in the table should have all the pointer values set to NULL and all the arithmetic values set to 0, marking the end of the table.

Let’s look at how an option table would be defined for a common application. Here is the options table for a simple version of grep utility:[4]

const char * pattern = NULL;
int mode = MODE_REGEXP;
int ignoreCase = 0;
int maxCount = -1;

struct poptOption optionsTable[] = {
    { "extended-regexp", 'E', POPT_ARG_VAL, &mode, MODE_EXTENDED,
        "pattern for match is an extended regular expression", NULL },
    { "fixed-strings", 'F', POPT_ARG_VAL, &mode, MODE_FIXED,
        "pattern for match is a basic string (not a "
        "regular expression)", NULL },
    { "basic-regexp", 'G', POPT_ARG_VAL, &mode, MODE_REGEXP,
        "pattern for match is a basic regular expression" },
    { "ignore-case", 'i', POPT_ARG_NONE, &ignoreCase, 0,
        "perform case insensitive search", NULL },
    { "max-count", 'm', POPT_ARG_INT, &maxCount, 0,
        "terminate after N matches", "N" },
    { "regexp", 'e', POPT_ARG_STRING, &pattern, 0,
        "regular expression to search for", "pattern" },
    { NULL, '', POPT_ARG_NONE, NULL, 0, NULL, NULL }
};

The retry argument does not take an argument, so popt sets the retry variable to one if --retry is specified. The bytes and lines options both take integer arguments, which are stored in the identically named variables. The final option, follow, may be either the literal name or descriptor. The followType variable is set to point to whatever value is given on the command line, and needs to be checked for correctness. By setting it to point to "descriptor" initially, a useful default value is provided.

Nesting Option Tables

Some libraries provide implementation of some common command-line options. For example, one of the original X Windows toolkits handled the -geometry and -display options for applications, giving most X Windows programs a standard set of command-line options for controlling basic behaviors. Unfortunately, there is not an easy way of doing this. Passing argc and argv to an initialization function in the library lets the library handle the appropriate options, but the application has to know to ignore those options when it parses argv. To prevent this problem, XtAppInitialize() took argc and argv as parameters and returned new values for each of them with the options handled by the library removed. This approach, while workable, becomes cumbersome as the number of libraries increases.

To address this, popt allows the nesting of option tables. This lets libraries define the options they want to handle (which could include further nesting), and the main program can provide those options by nesting the libraries option table within its own.

An option table that is going to be nested is defined just like any other one. To include it in another table, a new option is created with empty longName and shortName options. The argInfo field must be set to POPT_ARG_INCLUDE_TABLE and the arg member points to the table to be nested. Here is an example of an option table that includes another:

struct poptOption nestedArgs[] = {
     { "option1", 'a', POPT_ARG_NONE, NULL, 'a' },
     { "option2", 'b', POPT_ARG_NONE, NULL, 'b' },
     { NULL, '', POPT_ARG_NONE, NULL, 0 }
};

struct poptOption mainArgs[] = {
     { "anoption", 'o', POPT_ARG_NONE, NULL, 'o' },
     { NULL, '', POPT_ARG_INCLUDE_TABLE, nestedArgs, 0 },
     { NULL, '', POPT_ARG_NONE, NULL, 0 }
};

In this example, the application ends up providing three options, --option1, --option2, and --anoption. A more complete example of nested option tables appears on pages 610-612.

Using the Option Table

Creating a Context

popt can interleave the parsing of multiple command-line sets. It does this by keeping all the state information for a particular set of command-line arguments in a poptContext data structure, an opaque type that should not be modified outside the popt library.

New popt contexts are created by poptGetContext().

#include <popt.h>

poptContext poptGetContext(char * name, int argc, const char ** argv,
                           struct poptOption * options, int flags);

The first parameter, name, is used for alias handling and in help messages, and should be the name of the application whose options are being parsed. The next two arguments specify the command-line arguments to parse. These are generally passed to poptGetContext() exactly as they were passed to the program’s main() function.[5] The options parameter points to the table of command-line options, which was described in the previous section. The final parameter, flags, modifies how options are parsed, and consists of the following flags (which may be logically OR’ed together):

POPT_CONTEXT_KEEP_FIRST

Normally, popt ignores the value in argv[0] as it is typically the name of the program being run rather than a command-line argument. Specifying this flag causes popt to treat argv[0] as an option.

POPT_CONTEXT_POSIXMEHARDER

Strict POSIX compliance requires that all options occur before extra command-line parameters. For example, according to POSIX, rm -f file1 file2 would force the files file1 and file2 to be removed, while rm file1 file -f would cause normal removal of the three files file1, file2, and -f. Most Linux programs ignore this particular convention, so popt does not use this rule by default. This flag tells popt to parse options according to this convention.[6]

A poptContext keeps track of which options have already been parsed and which remain, among other things. If a program wishes to restart option processing of a set of arguments, it can reset the poptContext by passing the context as the sole argument to poptResetContext().

When argument processing is complete, the process should free the popt-Context as it contains dynamically allocated components. The poptFreeContext() function takes a poptContext as its sole argument and frees the resources the context is using.

Here are the prototypes of both poptResetContext() and poptFreeContext():

#include <popt.h>

void poptFreeContext(poptContext con);
void poptResetContext(poptContext con);

Parsing the Command Line

After an application has created a poptContext, it may begin parsing arguments. The poptGetNextOpt() performs the actual argument parsing.

#include <popt.h>

int poptGetNextOpt(poptContext con);

Taking the context as its sole argument, this function parses the next command-line argument found. After finding the next argument in the option table, the function fills in the object pointed to by the option table entry’s arg pointer if it is not NULL. If the val entry for the option is nonzero, the function then returns that value. Otherwise, poptGetNextOpt() continues on to the next argument.

poptGetNextOpt() returns -1 when the final argument has been parsed, and other negative values when errors occur. This makes it a good idea to keep the val elements in the option table greater than zero.

If all of the command-line options are handled through arg pointers, command-line parsing is reduced to the following line of code:

rc = poptGetNextOpt(poptcon);

Many applications require more complex command-line parsing than this, however, and use the following structure:

while ((rc = poptGetNextOpt(poptcon)) > 0) {
    switch (rc) {
        /* specific arguments are handled here */
    }
}

When returned options are handled, the application needs to know the value of any arguments that were specified after the option. There are two ways to discover them. One is to ask popt to fill in a variable with the value of the option through the option table’s arg elements. The other is to use poptGetOptArg().

#include <popt.h>

char * poptGetOptArg(poptContext con);

This function returns the argument given for the final option returned by poptGetNextOpt(), or it returns NULL if no argument was specified.

Leftover Arguments

Many applications take an arbitrary number of command-line arguments, such as a list of file names. When popt encounters an argument that does not begin with a -, it assumes it is such an argument and adds it to a list of leftover arguments. Three functions allow applications to access such arguments:

char * poptGetArg(poptContext con);

This function returns the next leftover argument and marks it as processed.

char * poptPeekArg(poptContext con);

The next leftover argument is returned but not marked as processed. This allows an application to look ahead into the argument list, without modifying the list.

char ** poptGetArgs(poptContext con);

All the leftover arguments are returned in a manner identical to argv. The final element in the returned array points to NULL, indicating the end of the arguments.

Automatic Help Messages

One of the benefits of using popt is its ability to generate help and usage messages automatically. Help messages list each command-line option along with a reasonably detailed description of that option, while usage messages provide a concise list of the available options without any descriptive text. popt provides a function to create each type of message.

#include <popt.h>

void poptPrintHelp(poptContext con, FILE * f, int flags);
void poptPrintUsage(poptContext con, FILE * f, int flags);

These two functions behave almost identically, writing the appropriate type of message to the file f. The flags argument is not currently used by either function, and should be set to zero for compatibility with future versions of popt.

Since the help message is normally provided by the --help option, and the usage message by the --usage option, popt provides an easy way of adding those two options to your program. The POPT_AUTOHELP macro can be used in the option table to add these options,[7] which displays the appropriate messages on STDOUT and exit with a return code of 0.[8] This example shows the option table in grep.c with the single line we need to add to the option table for grep to enable automatic help text:

 95:     struct poptOption optionsTable[] = {
 96:             { "extended-regexp", 'E', POPT_ARG_VAL,
 97:               &mode, MODE_EXTENDED,
 98:               "pattern for match is an extended regular "
 99:               "expression" },
100:             { "fixed-strings", 'F', POPT_ARG_VAL,
101:               &mode, MODE_FIXED,
102:               "pattern for match is a basic string (not a "
103:               "regular expression)", NULL },
104:             { "basic-regexp", 'G', POPT_ARG_VAL,
105:               &mode, MODE_REGEXP,
106:               "pattern for match is a basic regular expression" },
107:             { "ignore-case", 'i', POPT_ARG_NONE, &ignoreCase, 0,
108:               "perform case insensitive search", NULL },
109:             { "max-count", 'm', POPT_ARG_INT, &maxCount, 0,
110:               "terminate after N matches", "N" },
111:             { "regexp", 'e', POPT_ARG_STRING, &pattern, 0,
112:               "regular expression to search for", "pattern" },
113:             POPT_AUTOHELP
114:             { NULL, '', POPT_ARG_NONE, NULL, 0, NULL, NULL }
115:     };

Here is what the help message generated by this table looks like:

Usage: grep [OPTION...]
  -E,  --extended-regexp      pattern for match is an extended regular
expression
  -F, --fixed-strings        pattern for match is a basic string (not a
regular
                            expression)
  -G, --basic-regexp      pattern for match is a basic regular expression
   -i, --ignore-case        perform case insensitive search
   -m, --max-count=N        terminate after N matches
   -e, --regexp=pattern     regular expression to search for

Help options:
  -?, --help               Show this help message
  --usage                  Display brief usage message

While this looks quite good, it is not quite right. The first line does not mention that the command expects file names on the command line. The [OPTION...] text that does appear there is popt’s default, and this can be changed to be more descriptive through the poptSetOtherOptionHelp() function.

#include <popt.h>

poptSetOtherOptionHelp(poptContext con, const char * text);

The context is the first parameter and the second specifies the text that should appear after the program’s name. Adding this call

poptSetOtherOptionHelp(optCon, "<pattern> <file list>");

changes the first line of our help message to

Usage: grep <pattern> <file list>

which is much nicer.

The final nicety of help message generation is how nested tables are handled. Look again at the help message for our grep program; the help options are in their own section of the help message. When a POPT_ARG_INCLUDE_TABLE option table entry provides a descrip member, that string is used as a description for all of the options in the nested table, and those options are displayed in their own section of the help message (like the help options for tail). If the descrip is NULL, the options for the nested table are displayed with the options from the main table rather than in their own section.

Occasionally, programs provide options that probably should not be used; they may be there for support with legacy applications or designed for testing only. Automatic help message generation for that option can be suppressed by logically OR’ing POPT_ARGFLAG_DOC_HIDDEN with the arg member of the the struct poptOption that defines that option.

Using Callbacks

We have shown two ways of handling options with popt: having an option returned by poptGetNextOpt() and having variables changed automatically when options are present. Unfortunately, neither of these are satisfactory for nested tables. Returning options defined by a nested table for the application to handle is obviously unworkable as nested tables are designed to prevent the application from having to know about options provided by a library. Setting variables does not work very well either as it is not clear what variables would get set. Using global variables is often a bad idea, and there are no local variables available for the library to use as the parsing happens from the main application, not from a library. To provide flexible option handling in nested applications, popt provides a callback facility.

Each table can define its own callback function, which overrides normal processing for the options defined in that table. Instead, the callback function gets called for each option that is found. Options defined in other option tables (including tables nested inside of a table defining a callback) get handled using the normal rules, unless those other tables define their own callbacks.

Callbacks can be defined only in the first entry in an option table. When that entry defines a callback, the argInfo member is POPT_ARG_CALLBACK and arg points to the callback function. The descrip member can be any pointer value, and it is passed to the callback each time it is invoked, allowing any arbitrary data to be accessed by the callback. All of the other members of struct poptOption should be zero or NULL.

Callbacks can be called at three points during option processing: before processing starts, when an option in the table for that callback is found, and when processing is complete. This gives libraries the opportunity to initialize any structures they need to (including the data defined by the descrip member) and to perform any housekeeping that may need to occur when processing is completed (perhaps cleaning up dynamic memory allocated for the descrip member). They are always called when options are found, but the option table needs to specify that they should be called in the other two places. To do this, POPT_CBFLAG_PRE or POPT_CBFLAG_POST (or both) should be logically OR’ed with the POPT_ARG_CALLBACK value set in the arg member of the structure specifying the callback.

Here is the prototype that should be used to define the callback function:

void callback(poptContext con, enum poptCallbackReason reason,
    const struct poptOption * opt, const char * arg,
    const void * data);

The first option is the context that is being parsed when the callback gets invoked. The next option is POPT_CALLBACK_REASON_PRE if option processing has not yet begun, POPT_CALLBACK_REASON_POST if option processing is finished, or POPT_CALLBACK_REASON_OPTION if an option in the table for this callback has been found. If it is the latter, the opt argument points to the option table entry for the option that was found, and the arg argument points to the string that defines the argument for the option. If a nonstring argument is expected, the callback is responsible for checking the type and converting the argument. The last parameter to the callback, data, is the value of the descrip field in the option table entry that sets up the callback.

Here is an example of a library that uses a nested popt table and callbacks to parse some command-line options. The data structure is initialized before the command-line parsing begins, and the final values are displayed afterward.

 1: /* popt-lib.c */
 2:
 3: #include <popt.h>
 4: #include <stdlib.h>
 5:
 6: struct params {
 7:     int height, width;
 8:     char * fg, * bg;
 9: };
10:
11: static void callback(poptContext con,
12:                      enum poptCallbackReason reason,
13:                      const struct poptOption * opt,
14:                      const char * arg,
15:                      const void * data);
16:
17: /* Store the parsed variables here. A global is not normally
18:    preferred, but it is simpler. */
19: struct params ourParam;
20:
21: struct poptOption libTable[] = {
22:     { NULL, '',
23:       POPT_ARG_CALLBACK | POPT_CBFLAG_PRE | POPT_CBFLAG_POST,
24:       callback, '', (void *) &ourParam, NULL },
25:     { "height", 'h', POPT_ARG_STRING, NULL, '', NULL, NULL },
26:     { "width", 'w', POPT_ARG_STRING, NULL, '', NULL, NULL },
27:     { "fg", 'f', POPT_ARG_STRING, NULL, '', NULL, NULL },
28:     { "bg", 'b', POPT_ARG_STRING, NULL, '', NULL, NULL },
29:     { NULL, '', POPT_ARG_NONE, NULL, '', NULL, NULL }
30: };
31:
32: static void callback(poptContext con,
33:                      enum poptCallbackReason reason,
34:                      const struct poptOption * opt,
35:                      const char * arg,
36:                      const void * data) {
37:     struct params * p = (void *) data;
38:     char * chptr = NULL;
39:
40:     if (reason == POPT_CALLBACK_REASON_PRE) {
41:         p->height = 640;
42:         p->width = 480;
43:         p->fg = "white";
44:         p->bg = "black";
45:     } else if (reason == POPT_CALLBACK_REASON_POST) {
46:         printf("using height %d width %d fg %s bg %s
",
47:                p->height, p->width, p->fg, p->bg);
48:
49:     } else {
50:         switch (opt->shortName) {
51:             case 'h': p->height = strtol(arg, &chptr, 10); break;
52:             case 'w': p->width = strtol(arg, &chptr, 10); break;
53:             case 'f': p->fg = (char *) arg; break;
54:             case 'b': p->bg = (char *) arg; break;
55:         }
56:
57:         if (chptr && *chptr) {
58:             fprintf(stderr, "numeric argument expected for %s
",
59:                     opt->longName);
60:             exit(1);
61:         }
62:     }
63: }
64:

Programs that want to provide these command-line arguments need to include a single extra line in their popt table. Normally, this line would be a macro provided by a header file (like POPT_AUTOHELP is implemented), but for simplicity we just explicitly listed the line for this example.

 1: /* popt-nest.c */
 2:
 3: #include <popt.h>
 4:
 5: /* this would normally be declared in a header file */
 6: extern struct poptOption libTable[];
 7:
 8: int main(int argc, const char * argv[]) {
 9:     poptContext optCon;
10:     int rc;
11:     struct poptOption options[] = {
12:         { "app1", '', POPT_ARG_NONE, NULL, '' },
13:         { NULL, '', POPT_ARG_INCLUDE_TABLE, libTable,
14:           '', "Nested:", },
15:         POPT_AUTOHELP
16:         { NULL, '', POPT_ARG_NONE, NULL, '' }
17:     };
18:
19:     optCon = poptGetContext("popt-nest", argc, argv, options, 0);
20:
21:     if ((rc = poptGetNextOpt(optCon)) < -1) {
22:         fprintf(stderr, "%s: %s
",
23:                 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
24:                 poptStrerror(rc));
25:         return 1;
26:     }
27:
28:     return 0;
29: }

Error Handling

All of the popt functions that can return errors return integers. When an error occurs, a negative error code is returned. Table 26.2 summarizes the error codes that occur. Here is a more detailed discussion of each error:

POPT_ERROR_NOARG

An option that requires an argument was specified on the command line, but no argument was given. This can be returned only by poptGetNextOpt().

POPT_ERROR_BADOPT

An option was specified in argv but is not in the option table. This error can be returned only from poptGetNextOpt().

POPT_ERROR_OPTSTOODEEP

A set of option aliases is nested too deeply. Currently, popt follows options only 10 levels deep to prevent infinite recursion. Only poptGetNextOpt() can return this error.

POPT_ERROR_BADQUOTE

A parsed string has a quotation mismatch (such as a single quotation mark). poptParseArgvString(), poptReadConfigFile(), or poptReadDefaultConfig() can return this error.

POPT_ERROR_BADNUMBER

A conversion from a string to a number (int or long) failed due to the string’s containing nonnumeric characters. This occurs when poptGetNextOpt() processes an argument of type POPT_ARG_INT or POPT_ARG_LONG.

POPT_ERROR_OVERFLOW

A string-to-number conversion failed because the number was too large or too small. Like POPT_ERROR_BADNUMBER, this error can occur only when poptGetNextOpt() processes an argument of type POPT_ARG_INT or POPT_ARG_LONG.

POPT_ERROR_ERRNO

A system call returned with an error, and errno still contains the error from the system call. Both poptReadConfigFile() and poptReadDefaultConfig() can return this error.

Table 26.2. popt Errors

Error

Description

POPT_ERROR_NOARG

An argument is missing for an option.

POPT_ERROR_BADOPT

An option’s argument could not be parsed.

POPT_ERROR_OPTSTOODEEP

Option aliasing is nested too deeply.

POPT_ERROR_BADQUOTE

Quotations do not match.

POPT_ERROR_BADNUMBER

An option could not be converted to a number.

POPT_ERROR_OVERFLOW

A given number was too big or too small.

Two functions are available to make it easy for applications to provide good error messages.

const char * poptStrerror(const int error);

This function takes a popt error code and returns a string describing the error, just as with the standard strerror() function.

char * poptBadOption(poptContext con, int flags);

If an error occurred during poptGetNextOpt(), this function returns the option that caused the error. If the flags argument is set to POPT_BADOPTION_NOALIAS, the outermost option is returned. Otherwise, flags should be zero, and the option that is returned may have been specified through an a lias.

These two functions make popt error handling trivial for most applications. When an error is detected from most of the functions, an error message is printed along with the error string from poptStrerror(). When an error occurs during argument parsing, code similar to the following displays a useful error message:

fprintf(stderr, "%s: %s
",
        poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
        poptStrerror(rc));

Option Aliasing

One of the primary benefits of using popt over getopt() is the ability to use option aliasing. This lets the user specify options that popt expands into other options when they are specified. If the standard grep program made use of popt, users could add a --text option that expanded to -i -n -E -2 to let them more easily find information in text files.

Specifying Aliases

Aliases are normally specified in two places: /etc/popt and the .popt file in the user’s home directory (found through the HOME environment variable). Both files have the same format, an arbitrary number of lines formatted like this:

appname alias newoption expansion

The appname is the name of the application, which must be the same as the name parameter passed to poptGetContext(). This allows each file to specify aliases for multiple programs. The alias keyword specifies that an alias is being defined; currently, popt configuration files support only aliases, but other abilities may be added in the future. The next option is the option that should be aliased, and it may be either a short or a long option. The rest of the line specifies the expansion for the alias. It is parsed similarly to a shell command, which allows ,", and ' to be used for quoting. If a backslash is the final character on a line, the next line in the file is assumed to be a logical continuation of the line containing the backslash, just as in shell.

The following entry would add a --text option to the grep command, as suggested at the beginning of this section:

grep alias --text -i -n -E -2

Enabling Aliases

An application must enable alias expansion for a poptContext before calling poptGetNextOpt() for the first time. There are three functions that define aliases for a context.

int poptReadDefaultConfig(poptContext con, int flags);

This function reads aliases from /etc/popt and the .popt file in the user’s home directory. Currently, flags should be zero, as it is provided only for future expansion.

int poptReadConfigFile(poptContext con, char * fn);

The file specified by fn is opened and parsed as a popt configuration file. This allows programs to use program-specific configuration files.

int poptAddAlias(poptContext con, struct poptAlias alias, int
flags);

Occasionally, programs want to specify aliases without having to read them from a configuration file. This function adds a new alias to a context. The flags argument should be zero, as it is currently reserved for future expansion. The new alias is specified as a struct poptAlias, which is defined as

struct poptAlias {
    char * longName;              /* may be NULL */
    char shortName;               /* may be '' */
    int argc;
    char ** argv;                 /* must be free()able */
};

The first two elements, longName and shortName, specify the option that is aliased. The final two, argc and argv, define the expansion to use when the aliases option is encountered.

Parsing Argument Strings

Although popt is usually used for parsing arguments already divided into an argv-style array, some programs need to parse strings that are formatted identically to command lines. To facilitate this, popt provides a function that parses a string into an array of string, using rules similar to normal shell parsing.

#include <popt.h>

int poptParseArgvString(char * s, int * argcPtr, char *** argvPtr);

The string s is parsed into an argv-style array. The integer pointed to by the second parameter, argcPtr, contains the number of elements parsed, and the pointer pointed to by the final parameter is set to point to the newly created array. The array is dynamically allocated and should be free() ed when the application is finished with it.

The argvPtr created by poptParseArgvString() is suitable to pass directly to poptGetContext().[9]

Handling Extra Arguments

Some applications implement the equivalent of option aliasing but need to do so through special logic. The poptStuffArgs() function allows an application to insert new arguments into the current poptContext.

#include <popt.h>

int poptStuffArgs(poptContext con, char ** argv);

The passed argv must have a NULL pointer as its final element. When poptGetNextOpt() is next called, the “stuffed” arguments are the first to be parsed. popt returns to the normal arguments once all the stuffed arguments have been exhausted.

Sample Application

Several of the examples in other chapters of this book use popt for option processing. A simple implementation of grep is on pages 562-567 and robin appears on pages 355-363. Both provide good examples of how popt is used in most applications.

RPM, a popular Linux package management program, makes heavy use of popt’s features. Many of its command-line arguments are implemented through popt aliases, which makes RPM an excellent example of how to take advantage of the popt library.[10]. For more information on RPM, see http://www.rpm.org.

Logrotate is a program to help manage a system’s log files. It provides a simpler example of using popt than RPM does, and is included with most Linux distributions.



[1] The glibc library also provides the argp library, which provides yet another alternative for option parsing.

[2] getopt() connoisseurs will note that argInfo is the only required member of struct poptOption that is not directly analogous to a member in the getopt_long() argument table. The similarity between the two allows for easy transitions from getopt_long() to popt.

[3] Note that the C language clears all remaining members of a structure that is given a partial default value when it is declared, making this happen automatically in most cases.

[4] The full source code for this example is on pages 562-567 in Chapter 23.

[5] A common mistake is to define argv as char ** rather than const char **, which is more correct. The prototype for poptGetContext() causes a compiler warning if argv is not defined in the correct manner.

[6] The “posix me harder” terminology is popular among Linux applications when some (often unpleasant) strict POSIX compatibility mode is provided.

[7] It also adds a -? option that is the same as --help.

[8] The POPT_AUTOHELP macro expands to add a nested option table that defines the new options and a callback that implements the options.

[9] This is often a good time to use POPT_CONTEXT_KEEP_FIRST.

[10] popt was originally implemented for RPM, and many of RPM’s query options are implemented as simple popt macros.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset