How do I use getopt_long to parse multiple arguments? - c++

#include <iostream>
#include <getopt.h>
#define no_argument 0
#define required_argument 1
#define optional_argument 2
int main(int argc, char * argv[])
{
std::cout << "Hello" << std::endl;
const struct option longopts[] =
{
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"stuff", required_argument, 0, 's'},
{0,0,0,0},
};
int index;
int iarg=0;
//turn off getopt error message
opterr=1;
while(iarg != -1)
{
iarg = getopt_long(argc, argv, "s:vh", longopts, &index);
switch (iarg)
{
case 'h':
std::cout << "You hit help" << std::endl;
break;
case 'v':
std::cout << "You hit version" << std::endl;
break;
case 's':
std::cout << "You hit stuff" << std::endl;
if(optarg)
std::cout << "Your argument(s): " << optarg << std::endl;
break;
}
}
std::cout << "GoodBye!" << std::endl;
return 0;
}
Desired output:
./a.out --stuff someArg1 someArg2
Hello
You hit stuff
Your agument(s): someArg1 someArg2
GoodBye!

getopt returns -1 when all option args have been processed. The --stuff is recognized as an option that takes an argument, in this case someArg1. The someArg2 arg does not start with - or --, so it is not an option. By default, this will be permuted to the end of argv. After getopt returns -1, all non-option args will be in argv from optind to argc-1:
while (iarg != -1) {
iarg = getopt_long(argc, argv, "s:vh", longopts, &index);
// ...
}
for (int i = optind; i < argc; i++) {
cout << "non-option arg: " << argv[i] << std::endl;
}
If you add a single - to the start of optstring, getopt will return 1 (not '1') and point optarg to the non-option parameter:
while (iarg != -1) {
iarg = getopt_long(argc, argv, "-s:vh", longopts, &index);
switch (iarg)
{
// ...
case 1:
std::cout << "You hit a non-option arg:" << optarg << std::endl;
break;
}
}

In the line ./a.out --stuff someArg1 someArg2 the shell interprets three arguments to a.out. You want the shell to interpret "someArg1 someArg2" as one argument - so put the words in quotes:
./a.out --stuff "someArg1 someArg2"

I'm working on windows, so I had to compile getopt and getopt_long from this excellent source
I modified getopt_long.c (below) to accommodate two input arguments. I didn't bother with the more general case of multiple arguments since that would require more (and cleaner) rework than I had time/need for. The second argument is placed in another global, "optarg2".
If you don't need to compile getopt from source, Frank's answer above is more elegant.
extern char * optarg2
.
.
.
int getopt_long(nargc, nargv, options, long_options, index)
{
.
.
.
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument ||
long_options[match].has_arg == two_req_arguments) {
if (has_equal)
optarg = has_equal;
else
optarg = nargv[optind++];
if (long_options[match].has_arg == two_req_arguments) {
optarg2 = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument ||
long_options[match].has_arg == two_req_arguments)
&& (optarg == NULL)) {
/*
* Missing argument, leading :
* indicates no error should be generated
*/
if ((opterr) && (*options != ':'))
(void)fprintf(stderr,
"%s: option requires an argument -- %s\n",
__progname(nargv[0]), current_argv);
return (BADARG);
}
if ((long_options[match].has_arg == two_req_arguments)
&& (optarg2 == NULL)) {
/*
* Missing 2nd argument, leading :
* indicates no error should be generated
*/
if ((opterr) && (*options != ':'))
(void)fprintf(stderr,
"%s: option requires 2nd argument -- %s\n",
__progname(nargv[0]), current_argv);
return (BADARG);
}
You'll also need to add a define in getopt.h for "two_required_args" or "multiple_args" as you see fit.
edit: I'm bad at markdown

optarg points to "someArg1" and argv[optind] is "someArg2" if it exists and is not an option. You can simply use it and then consume it by incrementing optind.
case 's':
std::cout << "You hit stuff" << std::endl;
if (optind < argc && argv[optind][0] != '-') {
std::cout << "Your argument(s): " << optarg << argv[optind] << std::endl;
optind++;
} else {
printusage();
}
break;
Note this can work for an arbitrary number of arguments:
case 's':
std::cout << "You hit stuff." << std::endl;
std::cout << "Your arguments:" std::endl << optarg << std::endl;
while (optind < argc && argv[optind][0] != '-') {
std::cout << argv[optind] << std::endl;
optind++;
}
break;
c++ getopt-long command-line-arguments

Related

Parsing C++ command line arguments with optional parameters using optionparser.h

All the examples in the optionparser.h (Lean Mean C++ Option Parser) file look to not use any parameters to options. And I readily admit I'm not a brilliant C++ programmer. I just wand to have some options that take a numeric parameter.
Something like [--help --count_to 5 --jump 3 --noecho].
Here is a clip from the file on usage:
enum optionIndex { UNKNOWN, HELP, PLUS, SKIP1, SKIP2};
const option::Descriptor usage[] =
{
{UNKNOWN, 0, "", "",option::Arg::None, "USAGE: MyPgm [options]\n\n" "Options:" },
{HELP, 0,"h", "help",option::Arg::None, " --help -h \tPrint usage and exit." },
{PLUS, 0,"p","plus",option::Arg::None, " --plus, -p \tIncrement count." },
{SKIP1, 0,"","nodcs",option::Arg::None, " --nodcs, \tMy Option1" },
{SKIP2, 0,"","noacs",option::Arg::None, " --noacs, \tMy Option2" },
};
int main(int argc, char* argv[])
{
argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present
option::Stats stats(usage, argc, argv);
option::Option options[stats.options_max], buffer[stats.buffer_max];
option::Parser parse(usage, argc, argv, options, buffer);
if (parse.error())
return 1;
if (options[HELP] || argc == 0) {
option::printUsage(std::cout, usage);
return 0;
}
std::cout << "--plus count: " <<
options[PLUS].count() << "\n";
for (option::Option* opt = options[UNKNOWN]; opt; opt = opt->next())
std::cout << "Unknown option: " << opt->name << "\n";
for (int i = 0; i < parse.nonOptionsCount(); ++i)
std::cout << "Non-option #" << i << ": " << parse.nonOption(i) << "\n";
}
I see that option::Arg can be NONE or Optional, but I am at a loss exactly
how to reference an optional argument. I am also certain that this code can do much more than I am asking it to do at this time.

GNU argp "too few arguments"

My program is supposed to take two required arguments, and three optional arguments, like follows
ATE <input file> <output file> [--threads] [--bass] [--treble]
(note, I haven't figured out how to take <required> arguments yet, so input and output file is defined in the code as -i input_file and -o output_file)
I'm using the GNU library argp to parse the command line arguments, my file is based off the third example.
I run my program using the following command
$ ./ATE -i input_file.pcm -o output_file.pcm
Too few arguments!
Usage: ATE [OPTION...]
-p AMOUNT_OF_THREADS -b BASS_INTENSITY -t TREBLE_INTENSITY
input_file.pcm output_file.pcm
Try `ATE --help' or `ATE --usage' for more information.
threads: 2, bass: 4, treble: 4
opening file input.pcm
RUNNING!
done, saving to out.pcm
When running my program, I get "too few arguments", even though argp succesfully parsed the input and output option, as you can see in the output.
Printing out the number of arguments in parse_opt, cout << state->arg_num << endl; gives me 0's at every call.
The code is a little long, but it's completely self-contained so you can compile it to see for yourself.
commands.cpp
using namespace std;
#include <stdlib.h>
#include <argp.h>
#include <iostream>
#include <string>
#include <errno.h>
struct arguments {
string input_file;
string output_file;
int threads;
int bass;
int treble;
};
static char doc[] = "Parallequaliser - a multithreaded equaliser application written in c++";
static char args_doc[] = "-p AMOUNT_OF_THREADS -b BASS_INTENSITY -t TREBLE_INTENSITY input_file.pcm output_file.pcm";
static struct argp_option options[] = {
{"input_file", 'i', "IN_FILE", 0, "an input file in pcm format"},
{"output_file", 'o', "OUT_FILE", 0, "an output file in pcm format"},
{"threads", 'p', "AMOUNT_OF_THREADS", OPTION_ARG_OPTIONAL, "amount of threads, min 2"},
{"bass", 'b', "BASS_INTENSITY", OPTION_ARG_OPTIONAL, "bass intensity, from 0 to 7"},
{"treble", 't', "TREBLE_INTENSITY", OPTION_ARG_OPTIONAL, "treble intensity, from 0 to 7"},
{0}
};
static error_t parse_opt (int key, char *arg, struct argp_state *state) {
struct arguments *arguments = (struct arguments *) state->input;
switch (key) {
case 'p':
if (arg == NULL) {
arguments->threads = 4;
} else {
arguments->threads = strtol(arg, NULL, 10);
}
break;
case 'b':
if (arg == NULL) {
arguments->bass = 4;
} else {
arguments->bass = strtol(arg, NULL, 10);
}
break;
case 't':
if (arg == NULL) {
arguments->treble = 4;
} else {
arguments->treble = strtol(arg, NULL, 10);
}
break;
case 'i':
if (arg == NULL) {
cout << "You forgot to specify the input file using the -i input_file.pcm option" << endl;
} else {
arguments->input_file = (string) arg;
}
break;
case 'o':
if (arg == NULL) {
cout << "You forgot to specify the out file using the -i output_file.pcm option" << endl;
} else {
arguments->output_file = (string) arg;
}
break;
case ARGP_KEY_ARG:
cout << "Key arg... " << key << endl;
if (state->arg_num > 5){
cout << "Too many arguments!" << endl;
argp_usage(state);
}
break;
case ARGP_KEY_END:
if (state->arg_num < 2){
cout << "Too few arguments!" << endl;
argp_usage(state);
}
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, args_doc, doc };
int main (int argc, char **argv) {
struct arguments arguments;
arguments.threads = 2;
arguments.bass = 4;
arguments.treble = 4;
argp_parse(&argp, argc, argv, ARGP_NO_EXIT, 0, &arguments);
cout << "threads: " << arguments.threads << ", bass: " << arguments.bass << ", treble: " << arguments.treble << endl;
cout << "opening file " << arguments.input_file << endl;
cout << "RUNNING!" << endl;
cout << "done, saving to " << arguments.output_file << endl;
return 0;
}
The options don't count as "arguments" for the context of the argp parser.
When running ./ATE -i input_file.pcm -o output_file.pcm, you have "too few arguments" because you reach ARGP_KEY_END, the end of the arguments, with no arguments left. arg_num represents the "stand-alone" arguments : the number of ARGP_KEY_ARG arguments that have been processed. You don't have any.
To make sure you have the two required arguments as you initialy wanted, check that you don't reach ARGP_KEY_END without having seen two arguments (like you are already doing : the too few arguments would mean you don't have your two filenames). The case ARGP_KEY_ARG is where you get the values of the arguments.

Segmentation Fault with `getopt`

I have a function which handles arguments two three global variables.
It works fine with program -s3, but if I put a space between the s and the argument, I get a segmentation fault even though I'm using atoi to remove whitespace.
Here is the code:
bool handleArgs(int argc, char *argv[])
{
int arg;
bool rtVal = true;
while (true)
{
static struct option long_options[] =
{
{"steps", optional_argument, 0, 's'},
{"walks", optional_argument, 0, 'w'},
{"dimensions", optional_argument, 0, 'd'},
{nullptr, 0, 0, 0}
};
int option_index = 0;
arg = getopt_long (argc, argv, "s::w::d::",long_options, &option_index);
if(arg == -1)
{
break;
}
switch(arg)
{
case 0:
std::cout << long_options[option_index].name << std::endl;
if (optarg)
std::cout << " with arg " << optarg << std::endl;
break;
case 's':
std::cout << "option -s with value " << atoi(optarg) << std::endl;
break;
case 'w':
std::cout << "option -w with value " << atoi(optarg) << std::endl;
break;
case 'd':
std::cout << "option -d with value " << atoi(optarg) << std::endl;
break;
case '?':
/* getopt_long already printed an error message. */
rtVal = false;
break;
default:
rtVal = false;
}
}
return rtVal;
}
In your handler for -s, you don't check for optarg being 0. But you specify two colons after s in your option string: (from man 3 getopt):
Two colons mean an option takes an optional arg; if there is text in the current argv-element (i.e., in the same word as the option name itself, for example, "-oarg"), then it is returned in optarg, otherwise optarg is set to zero. This is a GNU extension.
When the shell starts your program after the invocation program -s 3, it provides three elements in the argv vector:
0: program
1: -s
2: 3
Normally, getopt would interpret this identically to the invocation program -s3, and it's hard to see a reason to change this behaviour. However, gnu helpfully provides you with such an option, allowing you to interpret program -s 3 as a -s option without an argument and a positional argument 3. Once you go down this road, you must check whether optarg is 0 before attempting to use it.
I suspect that you didn't really want to enable this gnu extension. There are very few applications which will benefit from it.

Case Statement does not execute

This is a growing source of irritation for me at the moment, when I press the corresponding button for the cases (they're initialized above) they don't actually execute and I'm stuck in the menu.
I'm sure this is ridiculously simple and I'm just not seeing it.
Edit: Added more, upon request
const int POKER = 1;
const int EVAL = 2;
const int EXIT = 3;
const char FIVE_CARD = 'a';
const char TEXAS = 'b';
const char OMAHA = 'c';
const char SEVEN_CARD = 'd';
const char GO_BACK = 'e';
const char MENU[] = "\nPlease choose an option from the following:\n"
"1) Play Poker\n2) Set Evaluation Method\n3) Quit\n: ";
const char POKER_MENU[] = "\nPlease choose your game:\n"
"a) 5 Card Draw\nb) Texas Hold 'Em\nc) Omaha High\n"
"d) 7 Card Stud\ne) Go back\n: ";
int main()
{
int choice = 0;
char poker_choice;
do
{
choice = askForInt(MENU, EXIT, POKER);
switch(choice)
{
case POKER :
do
{
choice = askForChar(POKER_MENU, GO_BACK, FIVE_CARD);
switch(poker_choice)
{
case FIVE_CARD :
std::cout << "Not implemented yet" << std::endl;
break;
case TEXAS :
std::cout << "Not implemented yet" << std::endl;
break;
case OMAHA :
std::cout << "Not implemented yet" << std::endl;
break;
case SEVEN_CARD :
std::cout << "Not implemented yet" << std::endl;
break;
case GO_BACK :
break;
}
}while(poker_choice != GO_BACK);
case EVAL :
std::cout << "Not implemented yet" << std::endl;
break;
case EXIT :
break;
}
}while(choice != EXIT);
choice = askForChar(POKER_MENU, GO_BACK, FIVE_CARD);
should be
poker_choice = askForChar(POKER_MENU, GO_BACK, FIVE_CARD);
Since you mentioned this is inside a method,
There are few things to check here;
Once inside the method, just print poker_choice and see if your the value is getting passed correctly.
Check if all the cases FIVE_CARD, TEXAS are declared as constants of the same data type.
Your error seems to be on this line:
choice = askForChar(POKER_MENU, GO_BACK, FIVE_CARD);
You test poker_choice in your switch but you assign the value to choice.
It should be:
poker_choice = askForChar(POKER_MENU, GO_BACK, FIVE_CARD);
// ^^^^^^
switch(poker_choice)
// ...

getopt fails to detect missing argument for option

I have a program which takes various command line arguments. For the sake of simplification, we will say it takes 3 flags, -a, -b, and -c, and use the following code to parse my arguments:
int c;
while((c = getopt(argc, argv, ":a:b:c")) != EOF)
{
switch (c)
{
case 'a':
cout << optarg << endl;
break;
case 'b':
cout << optarg << endl;
break;
case ':':
cerr << "Missing option." << endl;
exit(1);
break;
}
}
note: a, and b take parameters after the flag.
But I run into an issue if I invoke my program say with
./myprog -a -b parameterForB
where I forgot parameterForA, the parameterForA (represented by optarg) is returned as -b and parameterForB is considered an option with no parameter and optind is set to the index of parameterForB in argv.
The desired behavior in this situation would be that ':' is returned after no argument is found for -a, and Missing option. is printed to standard error. However, that only occurs in the event that -a is the last parameter passed into the program.
I guess the question is: is there a way to make getopt() assume that no options will begin with -?
See the POSIX standard definition for getopt. It says that
If it [getopt] detects a missing
option-argument, it shall return the
colon character ( ':' ) if the first
character of optstring was a colon, or
a question-mark character ( '?' )
otherwise.
As for that detection,
If the option was the last character in the string pointed to by
an element of argv, then optarg shall
contain the next element of argv, and
optind shall be incremented by 2. If
the resulting value of optind is
greater than argc, this indicates a
missing option-argument, and getopt()
shall return an error indication.
Otherwise, optarg shall point to the string following the option
character in that element of argv, and
optind shall be incremented by 1.
It looks like getopt is defined not to do what you want, so you have to implement the check yourself. Fortunately, you can do that by inspecting *optarg and changing optind yourself.
int c, prev_ind;
while(prev_ind = optind, (c = getopt(argc, argv, ":a:b:c")) != EOF)
{
if ( optind == prev_ind + 2 && *optarg == '-' ) {
c = ':';
-- optind;
}
switch ( …
If you are working in C++, boost::program_option is my recommendation to parse command line argument:
Boost::program_options library
Full disclosure: I'm no expert on this matter.
Would this example from gnu.org be of use? It seems to handle the '?' character in cases where an expected argument was not supplied:
while ((c = getopt (argc, argv, "abc:")) != -1)
switch (c)
{
case 'a':
aflag = 1;
break;
case 'b':
bflag = 1;
break;
case 'c':
cvalue = optarg;
break;
case '?':
if (optopt == 'c')
fprintf (stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr,
"Unknown option character `\\x%x'.\n",
optopt);
return 1;
default:
abort ();
}
update: Perhaps the following would work as a fix?
while((c = getopt(argc, argv, ":a:b:c")) != EOF)
{
if (optarg[0] == '-')
{
c = ':';
}
switch (c)
{
...
}
}
As an alternative for Boost-free projects, I have a simple header-only C++ wrapper for getopt (under The BSD 3-Clause License): https://github.com/songgao/flags.hh
Taken from example.cc in the repo:
#include "Flags.hh"
#include <cstdint>
#include <iostream>
int main(int argc, char ** argv) {
uint64_t var1;
uint32_t var2;
int32_t var3;
std::string str;
bool b, help;
Flags flags;
flags.Var(var1, 'a', "var1", uint64_t(64), "This is var1!");
flags.Var(var2, 'b', "var2", uint32_t(32), "var2 haahahahaha...");
flags.Var(var3, 'c', "var3", int32_t(42), "var3 is signed!", "Group 1");
flags.Var(str, 's', "str", std::string("Hello!"), "This is a string, and the description is too long to fit in one line and has to be wrapped blah blah blah blah...", "Group 1");
flags.Bool(b, 'd', "bool", "this is a bool variable", "Group 2");
flags.Bool(help, 'h', "help", "show this help and exit", "Group 3");
if (!flags.Parse(argc, argv)) {
flags.PrintHelp(argv[0]);
return 1;
} else if (help) {
flags.PrintHelp(argv[0]);
return 0;
}
std::cout << "var1: " << var1 << std::endl;
std::cout << "var2: " << var2 << std::endl;
std::cout << "var3: " << var3 << std::endl;
std::cout << "str: " << str << std::endl;
std::cout << "b: " << (b ? "set" : "unset") << std::endl;
return 0;
}
There are quite a few different versions of getopt around, so even if you can get it to work for one version, there will probably be at least five others for which your workaround will break. Unless you have an overwhelming reason to use getopt, I'd consider something else, such as Boost.Program_options.