1

I am looking for a way to pass multiple arguments for a single option using argp. I want to invoke my command line tool this way:

./foo -l user1 user2 user3 -name bar

Here, I want user1, user2, and user3 to be passed as an argument to the -l option. Since argp only accepts 1 argument per option, how do I achieve this?

Currently, I have the following structure:

struct arguments arguments;
arguments.lists = "";
arguments.name = "";
argparse (&argp, argc, argv, 0, 0, &arguments);

So, after parsing option -l correctly, argument.lists should be "user1 user2 user3" instead of just "user1". Thanks

Neuron
  • 5,141
  • 5
  • 38
  • 59
shank22
  • 159
  • 2
  • 12
  • See http://stackoverflow.com/questions/9642732/parsing-command-line-arguments which describes several alternatives (`getopt`, `argp`) as well as this posting about difficulties with `getopt` http://stackoverflow.com/questions/1052746/getopt-does-not-parse-optional-arguments-to-parameters – Richard Chambers Sep 29 '15 at 04:41

3 Answers3

2

The standard syntax on the command line is:

./foo -l user1 -l user2 -l user3 -name bar

Most command line parsing libraries will understand this.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Hi, I tried that but the option (-l) gets overwritten with the last known option. Not sure why. – shank22 Sep 29 '15 at 01:58
  • I see. In this case, to support the most standard syntax you may need to use a different parser, such as good old `getopt` which makes it straightforward to support the above. – John Zwinck Sep 29 '15 at 02:10
0

How the arguments are presented in the argv[] array will depend on the command shell being used. Most command shells follow the generally accepted standard that command line arguments are separated by spaces and if you want to specify an argument with spaces it must be quoted.

So ./foo -l user1 user2 user3 -name foo is a command line that would be provided to the C run time as seven arguments, the first is the name of the program with the remaining six arguments being -l, user1, user2, user3, -name, and foo.

If at the command line you entered ./foo -l "user1 user2 user3" -name foo where the list of users (user1, user2, and user3) are in a quoted string, the command line would be provided to the C run time as five arguments, the first is the name of the program with the remaining four arguments being -l, user1 user2 user3 (the quotes are normally removed by the command shell and all are in the same argument), -name, and foo.

Back in the old days of UNIX C command line utilities we would do something like the following for simple command line parsing. The following source does the work that a command line class might do. This was done with C++ but in a C style.

Naturally since this was thrown together, it is not very robust as for instance more than 30 arguments for a particular command line option would cause problems.

// arglist.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>

/*
 *  argument options accepted are as follows:
 *    -l  -> indicates a user list of one or more user names follows
 *    -name  -> indicates a name for the list follows
 *
 *  arguments without a leading dash are considered to be option changes
 *  and arguments without a leading dash are considered to be arguments
 *  for the last option.
**/
typedef struct {
    int  index;
    int  argListIndex[30];
} argList;

const int indexOptionUnknown = 0;
const int indexOptionL = 1;
const int indexOptionName = 2;

int main(int argc, char * argv[])
{
    // the index into the argv[] array. zeroth element of argve[] is ignored.
    argList myList[3] = {0};
    int     iMyList = 0;

    // we skip the first argument which is the name of our program
    // we then process the remaining command line arguments which the C runtime
    // has provided as a series of char strings.
    for (int i = 1; i < argc; i++) {
        if (*argv[i] == '-') {
            // this is an argument option.  figure out which one.
            if (strcmp (argv[i], "-l") == 0) {
                iMyList = indexOptionL;
            } else if (strcmp (argv[i], "-name") == 0) {
                iMyList = indexOptionName;
            } else {
                // unknown option so we will ignore it.
                iMyList = indexOptionUnknown;
            }
        } else {
            // this is an argument for the last option found
            if (iMyList > indexOptionUnknown) {
                myList[iMyList].argListIndex[ myList[iMyList].index ]  = i;
                myList[iMyList].index++;
            }
        }
    }

    std::cout << "option -l, count of arguments " << myList[indexOptionL].index << std::endl;
    for (int i = 0; i < myList[indexOptionL].index; i++) {
        std::cout << "   " << i << " is " << argv[myList[indexOptionL].argListIndex[i]] << std::endl;
    }

    std::cout << "option -name, count of arguments " << myList[indexOptionName].index << std::endl;
    for (int i = 0; i < myList[indexOptionName].index; i++) {
        std::cout << "   " << i << " is " << argv[myList[indexOptionName].argListIndex[i]] << std::endl;
    }

    return 0;
}

The output for an argument lines such as pgm -l user1 user2 user3 -name foo is:

option -l, count of arguments 3
   0 is user1
   1 is user2
   2 is user3
option -name, count of arguments 1
   0 is foo

And a command line such as pgm -l user1 -l user2 -l user3 -name foo would be the same output. What this parser does is to build a list of the arguments on the command line for a particular option.

If the command line such as pgm -l "user1 user2 user3" -name foo is provided the output is changed due to the quoted argument and the result is:

option -l, count of arguments 1
   0 is user1  user2 user3
option -name, count of arguments 1
   0 is foo
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • Thanks, that was useful. However, I would really like to use argp since it has built-in code to display the options usage and other checks. – shank22 Sep 29 '15 at 03:59
-1

Not sure if this is the right way, but I was able to do this as follows:

if (strlen(arguments->lists) != 0) {
    char *temp;
    sprintf(temp, " %s", arg);
    strcat(arguments->lists, temp);
}
else {
    arguments->lists = arg;
}

This way, if a an extra arg gets passed for the option -l or --lists, it gets appended as required.

shank22
  • 159
  • 2
  • 12