Anup Agarwal
Anup Agarwal

Reputation: 88

when long option is given as argument to short option - getopt_long

I was learning how to accept command line arguments using getopt_long function, I made 2 long options 'filename' (required arg) and 'clear' (no arg) and 2 short args 'a' (with arg) and 'b' (no arg) when i executed:

$ ./a.out -a --filename=test.txt

instead of showing 'a' has no arg it shows the optarg for 'a' is: --filename=text.txt and skips the filename long option Any work around for this?

My code is:

#include <iostream>
#include <getopt.h>

using namespace std;



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

    static struct option long_options[] = {
        {"filename",1,0,0},
        {"clear",0,0,0},
        {NULL,0,0,0}
    };

    int op,option_index = 0;
    string filename;
    while((op = getopt_long(argc,argv,"a:b",long_options,&option_index))!=-1){
        switch (op){
            case 0:
                switch(option_index){
                    case 0:
                        filename = optarg;
                        cout<<filename<<endl;
                        break;
                    case 1:
                        cout<<"clear is yes\n";
                        break;
                    default:
                        cout<<"Please enter valid long option\n";
                        break;
                }break;
            case 'a':
                cout<<"a is set as "<<optarg<<endl;
                //cout<<optarg<<endl;
                break;
            case 'b':
                cout<<"b is set"<<endl;
                    break;
            default:
                cout<<"Please enter valid Arguments"<<endl;
                break;
        }
    }
    cout<<"\n\n";

    return 0;
}

Upvotes: 1

Views: 1661

Answers (2)

Michael Burr
Michael Burr

Reputation: 340208

One way is to look at the argument to -a and see if it's really an option. If so, handle the error and set optind -= 1 so that further processing by getopt_long() will treat the option that incorrectly followed -a as an option. Not particularly elegant, but it works.

Here's an example:

#include <iostream>
#include <getopt.h>

#include <string.h>

using namespace std;


int str_startswith( char const* s, char const* prefix)
{
    return strncmp(prefix, s, strlen(prefix)) == 0;
}


bool is_option(char const* arg)
{
    int result = 0;

    result = result || str_startswith( arg, "--filename=");
    result = result || (strcmp( arg, "--clear") == 0);
    result = result || (strcmp( arg, "-b") == 0);

    return result;
}



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

    static struct option long_options[] = {
        {"filename",1,0,0},
        {"clear",0,0,0},
        {NULL,0,0,0}
    };

    int op,option_index = 0;
    string filename;
    while((op = getopt_long(argc,argv,"a:b",long_options,&option_index))!=-1){
        switch (op){
            case 0:
                switch(option_index){
                    case 0:
                        filename = optarg;
                        cout << "filename is: " <<filename<<endl;
                        break;
                    case 1:
                        cout<<"clear is yes\n";
                        break;
                    default:
                        cout<<"Please enter valid long option\n";
                        break;
                }break;
        case 'a':
                if (is_option(optarg)) {
                    cout << "-a option requires an argument" << endl;
                    optind -= 1; // put the option back into consideration for getopt_long()
                }
                else {
                    cout << "a is set as " << optarg << endl;
                }
                break;
            case 'b':
                cout<<"b is set"<<endl;
                    break;
            default:
                cout<<"Please enter valid Arguments"<<endl;
                break;
        }
    }
    cout<<"\n\n";

    return 0;
}

One way I'd look to improve this is to have the function that checks if an argument is potentially an option is to have it look through he same structures as getopt_long() (long_options[] and the string argument to getopt_long()) instead of hard-coding it like in my quick-n-dirty example.

In fact, since this is the second time in a few weeks I've come across this question on SO (I can't find the other one), it might pay to create a wrapper for getopt_long() and friends that provides a helper function do exactly this. Maybe I'll work on that later this weekend...

Upvotes: 0

Anup Agarwal
Anup Agarwal

Reputation: 88

I found a better Answer, my friend told me this.

I could directly use :: instead of : that means 'a' requires an optional argument so getopt_long will check whether the arg is an option or not if there is no arg or arg is an option 0 is returned which I can handle separately and if 'a' has non option arg is there that situation is handled normally.

The final code stands as:

#include <iostream>
#include <getopt.h>

using namespace std;



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

    static struct option long_options[] = {
        {"filename",1,0,0},
        {"clear",0,0,0},
        {NULL,0,0,0}
    };

    int op,option_index = 0;
    string filename;
    while((op = getopt_long(argc,argv,"a::b",long_options,&option_index))!=-1){
        switch (op){
            case 0:
                switch(option_index){
                    case 0:
                        filename = optarg;
                        cout<<filename<<endl;
                        break;
                    case 1:
                        cout<<"clear is yes\n";
                        break;
                    default:
                        cout<<"Please enter valid long option\n";
                        break;
                }break;
            case 'a':
                if(optarg)
                    cout<<"a is set as "<<optarg<<endl;
                else 
                    cout<<"a needs an argument"<<endl;
                //cout<<optarg<<endl;
                break;
            case 'b':
                cout<<"b is set"<<endl;
                    break;
            default:
                cout<<"Please enter valid Arguments"<<endl;
                break;
        }
    }
    cout<<"\n\n";

    return 0;
}

There is no need of hard-coding anything in this.

Upvotes: 2

Related Questions