initCommon(); $template->displayHeader(); ?>

15.2. The Power of popt

Popt provides a powerful command-line processing library, allowing the rpm command to handle a variety of options in a very flexible way. You can use popt alone as a library in its own right, or use it combined with the rpm library to handle command-line options like those of the rpm command.

At its most basic, popt processes the command-line arguments to a C program, traditionally called argc and argv, into an option table that describes and contains all the option values.

The main advantage popt has over simpler libraries such as getopt lies in the ability to handle complex arguments and to define aliases. The rpm command supports three different behaviors for the –i option, depending on the context (install a package, get information on a package as part of a query, and perform the install stage of a source RPM, as part of rpmbuild).

The popt library supports both traditional UNIX short options such as –U and the longer options common for GNU programs, especially on Linux, such as --upgrade. For the popt library, you can define both short and long variants for each option. In addition, command-line options may be individual flags, such as –v for verbose, or options that expect one or more data values as arguments, such as –f, which requires a file name.

15.2.1. Popt aliases

One of the most powerful features of popt is the ability to define aliases. A popt alias allows you to define one command-line option as an alias for a set of options. As its simplest, the rpm command-line options --upgrade and –U refer to the same action. You could define one as an alias for the other.

With rpm, the file /usr/lib/rpm/rpmpopt-4.1 (for RPM version 4.1) defines over 400 lines of popt aliases to configure the rpm command-line options. For example:

Rpm alias –requires --qf \

"[%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]" \

--POPTdesc=$"list capabilities required by package(s)"

This example defines rpm --requires as really a query using the --qf or --queryformat options covered in Chapter 5.

Cross Reference

See Chapter 21 for more on defining popt aliases.

15.2.2. Programming with popt

To use popt in your programs, you need to fill in a table of options and then call poptGetContext. The poptGetContext function parses the command-line options and returns a poptContext, an opaque data type that you need to pass as a parameter to a number of popt functions. The poptContext holds the state of your command-line processing. This allows you to call the popt library with multiple sets of arguments. Each set will have an associate poptContext to keep all the data separate.

The basic poptGetContext function signature follows:

poptContext poptGetContext (const char * name,

int argc,

const char ** argv,

const struct poptOption * options,

int flags );

All the popt functions require the popt.h include file:

#include <popt.h>

The flags should be a bitmask of any options you require, including those listed in Table 16-5.

Table 16-5 Flags for poptGetContext

Flag

Meaning

POPT_CONTEXT_NO_EXEC

Ignore executable expansions

POPT_CONTEXT_KEEP_FIRST

Treat argv[0], the command name, as an option

POPT_CONTEXT_POSIXMEHARDER

Do not allow options to follow arguments

When done with a poptContext, you should free it by calling poptFreeContext:

poptContext poptFreeContext(poptContext context);

The call to poptFreeContext frees up the memory allocated for the context.

Note

You can also fill in a poptContext from settings in a file with poptReadConfigFile:

int poptReadConfigFile(poptContext context,

const char * file_name);

15.2.2.1. Filling in the Options Table

You need to pass in a table that defines all the possible options. This table is an array of structures, where each structure defines one option. The format for a single option follows:

struct poptOption {

const char * longName;

char shortName;

int argInfo;

void * arg;

int val;

const char * descrip;

const char * argDescrip;

};

Going through this structure, the longName defines the long version of the option, such as "upgrade" for --upgrade. The shortName defines the short, one-character option, such as 'U' for an option of -U. You can place a null character, '\0', to specify no short option. With the rpm command, the --rebuilddb option has only a long name and not a short name, for example.

Note

The longName is not preceded by the double minus sign. Similarly, the shortName is not preceded by the single minus sign.

The descrip field holds a short description of the option and the argDescrip field holds a description of the types of values it expects, or NULL if this option expects no values.

The argInfo field holds a flag that tells the popt library how to treat the option. At the very least, you need to define the type of the option. You can also define special processing flags. Table 16-6 lists the argument types in the options table.

Table 16-6 Popt option table argInfo argument types

Type

Value

Meaning

POPT_ARG_NONE

0

No argument data, just the option such as -v

POPT_ARG_STRING

1

arg treated as string

POPT_ARG_INT

2

arg treated as int

POPT_ARG_LONG

3

arg treated as long

POPT_ARG_INCLUDE_TABLE

4

arg points to a table

POPT_ARG_CALLBACK

5

arg points to a callback function

POPT_ARG_INTL_DOMAIN

6

sets translation domain

POPT_ARG_VAL

7

use value of val field for arg

POPT_ARG_FLOAT

8

arg treated as float

POPT_ARG_DOUBLE

9

arg treated as double

Use these constants, from the include file popt.h, in place of the actual numbers.

Depending on the type you define in the argInfo field, popt will interpret the generic pointer field, arg, in different ways. Using a pointer allows the popt library to automatically update your program variables based on the command-line option settings.

Note

You can pass NULL for the arg field. In this case, the popt library will not set any values for you.

The POPT_ARG_NONE type indicates that this option has no argument. For example, the -v verbose option has no data. On the other hand, the POPT_ARG_STRING type indicates that the user should provide a string. For example, the -f option to the rpm command is expected to include a string argument, the name of the file to look up.

Note

If the argInfo argument type is POPT_ARG_NONE, the popt library will set arg to 1 if the option is present on the command line. You should pass a pointer to an int if you want this set for you.

15.2.2.2. Popt Callbacks

The POPT_ARG_CALLBACK type indicates that the arg field holds a function pointer to a callback function of the following type:

typedef void (*poptCallbackType) (poptContext con,

enum poptCallbackReason reason,

const struct poptOption * opt,

const char * arg,

const void * data);

The callback reason will be one of the following enum values:

enum poptCallbackReason {

POPT_CALLBACK_REASON_PRE = 0,

POPT_CALLBACK_REASON_POST = 1,

POPT_CALLBACK_REASON_OPTION = 2

};

The data field holds the value of the descrip field in the poptOption entry. You can cheat and stuff a pointer to arbitrary data into this field.

The callback function is most useful if you are using nested option tables. You can place your processing code for the nested options into a callback.

15.2.2.3. Special Option Table Flags

In addition to the types in Table 16-6, you can also define special bit flags that define extra processing information for each option. Combine these bit flags with the type values using a logical OR operation:

*The POPT_ARGFLAG_ONEDASH flag allows the longName to be used with one or two dashes, such as -upgrade or --upgrade.

*For bitmask options, the POPT_ARGFLAG_OR, POPT_ARGFLAG_NOR, POPT_ARGFLAG_AND, POPT_ARGFLAG_NAND, and POPT_ARGFLAG_XOR type flags tell the popt library to apply the given operation, OR, NOR, AND, NAND, or XOR, to the value if set. The POPT_ARGFLAG_NOT flag tells the popt library to negate the value first.

*You can also use the macros POPT_BIT_SET to set a bit and POPT_BIT_CLR to clear a bit.

*The POPT_ARGFLAG_OPTIONAL flag indicates that the argument value is optional.

*The POPT_ARGFLAG_DOC_HIDDEN flag tells popt to hide this option when displaying the help documentation. In other words, this is an internal option.

*The rarely used POPT_ARGFLAG_STRIP flag tells popt to consume an option and ignore it. This option is rarely used.

*The POPT_ARGFLAG_SHOW_DEFAULT flag tells popt to show the initial value of the argument for this option as a default when displaying a help message.

15.2.2.4. Magic Options

With RPM programs, developers usually round out the option table with three special options: POPT_AUTOALIAS, POPT_AUTOHELP, and POPT_TABLEEND. The POPT_AUTOALIAS option sets up a table of aliases:

#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \

0, "Options implemented via popt alias/exec:", NULL },

This option refers to the table, poptAliasOptions. You can use the POPT_ARG_INCLUDE_TABLE argInfo type to include another table of options. These options get filled in from popt aliases. In addition, within RPM programs, another table, rpmcliAllPoptTable, holds a set of options common to all RPM programs.

The POPT_AUTOHELP option supports standard help options. The POPT_AUTOHELP macro adds in automatic support for -?, --help, and --usage options.

#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \

0, "Help options:", NULL },

The POPT_TABLEEND option defines an empty option to mark the end of the table. You must include an empty option to end the table, and POPT_TABLEEND makes this easy.

#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }

Note

The code in Listing 16-3, in the "Running a Popt Example" section later in this chapter, shows a full option table.

15.2.2.5. Parsing the Command-Line Options

Once you have set up a poptGetContext, you need to iterate over all the command-line parameters. To do this, call poptGetNextOpt:

int poptGetNextOpt(poptContext context);

If an error occurs, poptGetNextOpt returns a negative error code. If the context is at the end of the options, poptGetNextOpt returns –1. Table 16-7 lists the error codes:

Table 16-7 Error codes from poptGetNextOpt

Code

Meaning

POPT_ERROR_NOARG

Option requires an argument, but it is missing

POPT_ERROR_BADOPT

Argument could not be parsed

POPT_ERROR_OPTSTOODEEP

Aliases are nested too deeply

POPT_ERROR_BADQUOTE

Start and end quotation marks don't match

POPT_ERROR_BADNUMBER

Argument could not be converted to a number

POPT_ERROR_OVERFLOW

Argument number was too big or too small

POPT_ERROR_ERRNO

A system call returned an error in errno

15.2.2.6. Walking Through the Command-Line Options

In normal circumstances, poptGetNextOpt parses all the options and returns –1. If your needs are simple, you can use the pointers to the variables passed in the options table, described previously. If you need some special processing for options not handled by popt, that is, options of type POPT_ARG_NONE, then poptGetNextOpt returns the single-character option.

In this case, you can call poptGetNextOpt in a while loop. For example:

while ((option = poptGetNextOpt(context) ) {

/* Do something... */

}

Inside your while loop, you can call poptGetOptArg to get the value of the argument:

char * poptGetOptArg(poptContext context);

You can restart the processing of the options by calling poptResetContext:

void poptResetContext(poptContext context);

The popt system is just looking for arguments that start with a dash, -. In most command-line applications, you may have a number of extra arguments at the end, such as a list of file names. The popt library doesn’t process these, but can provide them to you.

Call poptGetArg to return the next extra argument:

char * poptGetArg(poptContext context);

Keep calling this function until it returns NULL.

Call poptPeekArg to look at the next argument but not mark it as being processed:

char * poptPeekArg(poptContext context);

Or, you can get the whole list of extra arguments by calling poptGetArgs:

char ** poptGetArgs(poptContext context);

15.2.3. Handling Errors

Inside your while loop processing the command-line arguments, you can call poptBadOption to get the option that was bad, and poptStrerror to look up the error message associated with the error.

For poptBadOption, you need to pass in the context, and a bitmask of flags. Normally, pass 0 for no flags or POPT_BADOPTION_NOALIAS, which tells popt to return the actual option, not a value defined in an alias. This makes poptBadOption return the option closest to, if not exactly the same as, what the user entered, which makes for better error reporting.

The poptBadOption function signature follows:

char * poptBadOption(poptContext context, int flags);

Pass the error number returned by poptGetOptArg to poptStrerror to get the standard error message for that option:

const char * poptStrerror(const int error_code);

You can combine these and print out an error with code like the following:

fprintf( stderr, "Error with option [%s]\n %s",

poptBadOption(context, POPT_BADOPTION_NOALIAS),

poptStrerror(error_code);

To print out a usage message, call poptPrintUsage:

void poptPrintUsage(poptContext context,

FILE *output,

int flags);

This function prints out the usage help information, which is a useful function when the user has called a program with incomplete or wrong options.

15.2.4. Running a popt example

Pulling this all together, you can use the popt1.c program, in Listing 16-3, as an example for using popt to process command-line options.

Listing 16-3: popt1.c

/* Processes command-line options. */

#include <stdio.h>

#include <stdlib.h>

#include <popt.h>

/* Data values for the options. */

static int intVal = 55;

static int print = 0;

static char* stringVal;

void callback(poptContext context,

enum poptCallbackReason reason,

const struct poptOption * option,

const char * arg,

const void * data)

{

switch(reason)

{

case POPT_CALLBACK_REASON_PRE:

printf("\t Callback in pre setting\n"); break;

case POPT_CALLBACK_REASON_POST:

printf("\t Callback in post setting\n"); break;

case POPT_CALLBACK_REASON_OPTION:

printf("\t Callback in option setting\n"); break;

}

}

/* Set up a table of options. */

static struct poptOption optionsTable[] = {

{ (const) "int", (char) 'i', POPT_ARG_INT, (void*) &intVal, 0,

(const) "follow with an integer value", (const) "2, 4, 8, or 16" },

{ "callback", '\0', POPT_ARG_CALLBACK|POPT_ARGFLAG_DOC_HIDDEN,

&callback, 0, NULL, NULL },

{ (const) "file", (char) 'f', POPT_ARG_STRING, (void*) &stringVal, 0,

(const) "follow with a file name", NULL },

{ (const) "print", (char) 'p', POPT_ARG_NONE, &print, 0,

(const) "send output to the printer", NULL },

POPT_AUTOALIAS

POPT_AUTOHELP

POPT_TABLEEND

};

int main(int argc, char *argv[]) {

poptContext context = poptGetContext(

(const char*) "popt1",

argc,

argv,

(const struct poptOption* ) &optionsTable,

0);

int option = poptGetNextOpt(context);

printf("option = %d\n", option);

/* Print out option values. */

printf("After processing, options have values:\n");

printf("\t intVal holds %d\n", intVal);

printf("\t print flag holds %d\n", print);

printf("\t stringVal holds [%s]\n", stringVal);

poptFreeContext(context);

exit(0);

}

This example defines a callback but otherwise uses the simplest case for processing the command-line options. This program lets the popt library simply set the values into the option table. In most cases, you should avoid more complex command-line processing.

To compile popt programs, you just need the popt library. For example:

gcc -I/usr/include/rpm -o popt1 popt1.c -lpopt

When you run this program, try out the different options. For example, when you set all the options, you’ll see output like the following:

$ ./popt1 -i 42 --print -f filename1

Callback in option setting

Callback in option setting

Callback in post setting

option = -1

After processing, options have values:

intVal holds 42

print flag holds 1

stringVal holds [filename1]

This command used two short options and one long. You can mix and match short and long options, as shown following:

$ ./popt1 --int 42 -p --file filename1

Callback in option setting

Callback in option setting

Callback in post setting

option = -1

After processing, options have values:

intVal holds 42

print flag holds 1

stringVal holds [filename1]

This example used a short option for print, -p, and long options for the other two options. The popt library also provides handy help and usage messages, using the option table macro POPT_AUTOALIAS. To get a help message, use --help or -?:

$ ./popt1 --help

Usage: popt1 [OPTION...]

-i, --int=2, 4, 8, or 16 follow with an integer value

-f, --file=STRING follow with a file name

-p, --print send output to the printer

Options implemented via popt alias/exec:

Help options:

-?, --help Show this help message

--usage Display brief usage message

Notice how the help descriptions from the options table are used.

Note

With some shells, especially the tcsh shell, you need to wrap a -? In single quotes. For example:

$ ./popt1 '-?'

The usage message is shorter, and you also get it for free:

$ ./popt1 --usage

Usage: popt1 [-i|--int 2, 4, 8, or 16] [-f|--file STRING] [-p|--print]

[-?|--help] [--usage]

All in all, the popt library provides a handy library for processing command-line options and aliases, covered in Chapter 21.

15.2.5. Handling rpm command-line options

The RPM C library makes extensive use of popt for processing command-line arguments. Functions that set up the RPM library, such as rpmcliInit, which sets up the RPM command-line environment, require a table of poptOption entries that define the command-line options for your program.

To create a simple program that handles the standard rpm command-line options, set up the following options table:

static struct poptOption optionsTable[] = {

{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,

"Common options for all rpm modes and executables:",

NULL },

POPT_AUTOALIAS

POPT_AUTOHELP

POPT_TABLEEND

};

Then, initialize your program with a call to rpmcliInit:

poptContext rpmcliInit(int argc, char *const argv[],

struct poptOption * optionsTable);

When you call rpmcliInit, it will set up all the variables for the standard rpm command-line options.

For example, to see if the verbose flag is turned on, call rpmIsVerbose:

int rpmIsVerbose();

When you are done with a program that called rpmcliInit, call rpmcliFini to clean up the global data:

poptContext rpmcliFini(poptContext context);

The call to rpmcliFini returns NULL.

displayFooter('$Date: 2005/11/02 19:30:06 $'); ?>