Glibc: Argument Parsing using Argp

While writing a CLI application, Argument parsing is an important task. For parsing the CLI arguments in c, you can use Argp from Glibc (the Gnu standard C library).

To parse the command line arguments in a c program the procedure is as follows.

First of all we need to include argp.h.

argp.h is where the prototypes for all the APIs for argument parsing are found.

After doing that, we need to declare following variables;

const char *argp_program_version:

This is the version string.

for example:

const char *argp_program_version = "your-cool-app 1.0.0";

static char doc[]:

This is the documentation string.

for example:

static char doc[] = "documentation for your cool application :)";

const char *argp_program_bug_address:

This is the email address for reporting bugs.

for example:

const char *argp_program_bug_address = "<your e-mail address>";

static char args_doc[]:

These are the mandatory arguments that the program is expecting.

for example:

static char args_doc[] = "ARG1";

static struct argp_option options[]:

These are the options that can be used with your cli application.

For example if you want the usage for your cli application to look like

$ ./cli -o something

or

$ ./cli --option1 something

your struct argp_option struct will look like:

static struct argp_option options[] = {
    {"verbose", 'v', 0, 0, "Produce verbose output"},
    {"option1", 'o', "Option1", 0},
    {0}
};

This is essentially an array of struct argp_option.

struct argp_option

This structure specifies a single option that an argp parser understands, as well as how to parse and document that option. It has the following fields:

You can find more details about struct argp_option in the glibc official doc

struct arguments:

This structure is used to hold all of the arguments.

For example, for the options that we have used above, the struct arguments will look like:

struct arguments{
    int  verbose;
    char *args[1];
    char *option1;
};

A function to parse options and set the values in struct arguments.

The next thing is to initialize the struct arguments according to the options that we have specified in struct argp_option options[]. To do that we need to define a function which will parse one option at a time and depending upon the option flag, it will set the corresponding value in struct arguments.

Prototype for this function is:

static error_t parse_opt(int, char*, struct argp_state*);

this function take three inputs.

static error_t parse_opt(int key, char *arg, struct argp_state *state){

    struct arguments *arguments = state->input;
    switch(key){

        case 'v':
            arguments->verbose = 1;
            break;
        case 'o':
            arguments->option1 = arg;
            break;

        case ARGP_KEY_ARG:
            // Too many arguments, if your program expects only one argument.
            if(state->arg_num > 1)
                argp_usage(state);
            arguments->args[state->arg_num] = arg;
            break;

        case ARGP_KEY_END:
            // Not enough arguments. if your program expects exactly one argument.
            if(state->arg_num < 1)
                argp_usage(state);
            break;

        default:
            return ARGP_ERR_UNKNOWN;
    }

    return 0;
}     

Initialize struct argp argp:

Now the last thing remaining is to initialize struct argp, Where we need to use options, parse_opt, args_doc and doc that we have defined earlier.

For example:

static struct argp argp = {options, parse_opt, args_doc, doc};

Parse the arguments using argp_parse().

At this point we are all set to use Argp. Now We can use argp_parse() function for parsing the command line arguments.

// create a new struct to hold arguments.
struct arguments arguments;

// set the default values for all of the args.
arguments.verbose = 0;
arguments.option1 = "";

// parse the cli arguments.
argp_parse(&argp, argc, args, 0, 0, &arguments);

After doing this we have values for all of the arguments passed to the executable from command line, stored in the struct arguments.

The complete code for this example.

#include<stdlib.h>
#include<argp.h>

// need to mention a version string.
const char *argp_program_version = "your-cool-app 1.0.0";

// documentation string that will be displayed in the help section.
static char doc[] = "documentation for your cool application :)";

// email address for bug reporting.
const char *argp_program_bug_address = "<your e-mail address>";

// argument list for doc. This will be displayed on --help
static char args_doc[] = "ARG1";

// cli argument availble options.
static struct argp_option options[] = {
    {"verbose", 'v', 0, 0, "Produce verbose output"},
    {"option1", 'o', "Option1", 0},
    {0}
};


// define a struct to hold the arguments.
struct arguments{
    int  verbose;
    char *args[1];
    char *option1;
};


// define a function which will parse the args.
static error_t parse_opt(int key, char *arg, struct argp_state *state){

    struct arguments *arguments = state->input;
    switch(key){

        case 'v':
            arguments->verbose = 1;
            break;
        case 'o':
            arguments->option1 = arg;
            break;

        case ARGP_KEY_ARG:
        
            // Too many arguments.
            if(state->arg_num > 1)
                argp_usage(state);
            arguments->args[state->arg_num] = arg;
            break;

        case ARGP_KEY_END:
            // Not enough arguments.
            if(state->arg_num < 1)
                argp_usage(state);
            break;

        default:
            return ARGP_ERR_UNKNOWN;
    }

    return 0;
}


// initialize the argp struct. Which will be used to parse and use the args.
static struct argp argp = {options, parse_opt, args_doc, doc};


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

    // create a new struct to hold arguments.
    struct arguments arguments;

    // set the default values for all of the args.
    arguments.verbose = 0;
    arguments.option1 = "";
    arguments.option2 = "";

    // parse the cli arguments.
    argp_parse(&argp, argc, args, 0, 0, &arguments);

    printf("ARG1:    %s", arguments.args[0]);
    printf("\nVERBOSE: %s", arguments.verbose? "yes" : "no");
    printf("\nOption1: %s", arguments.option1);
    printf("\n");
}

To compile this code.

$ gcc -o argparsing argparsing.c

The output of this code:

$ ./argparsing --help
Usage: argparsing [OPTION...] ARG1
documentation for your cool application :)

  -o, --option1=Option1
  -v, --verbose              Produce verbose output
  -?, --help                 Give this help list
      --usage                Give a short usage message
  -V, --version              Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Report bugs to <your e-mail address>.

Output of the above code whe you pass arguments to it.

$ ./argparsing -o "option1" "this is trial argument"
ARG1:    this is trial argument
VERBOSE: no
Option1: option1