Search code examples
c++cgetoptgetopt-long

getopt_long acting weirdly


I'm writing some code for parsing the command line input. The way I use getopt_long is as follows:

int c = 0; 
static struct option long_options[] =  
{ 
    {"mode",        1,  NULL,   'm'}, 
    {"help",        0,  NULL,   'h'}, 
    {0,             0,  0,      0} 
}; 
while ((c = getopt_long(argc, argv, "mh", long_options, NULL))!=-1) 
{ 
    switch(c) 
    { 
        case 0: 
        { 
            cerr<<"Usage: ./program <-m> <-h>"<<endl; 
            exit(1); 
            break; 
        } 
        case 'm': 
        { 
            if (!strcmp(optarg, "small"))
                mode = 0;
            else if (!strcmp(optarg, "medium"))
                mode = 1;
            else if (!strcmp(optarg, "large"))
                mode = 2;
            else{
                cerr<<"Invalid mode "<<optarg<<endl;
                exit(1);
            }
            break; 
        } 
        case 'h': 
        { 
            cerr<<"See man page for help."<<endl;
            exit(0); 
        } 
        default: 
        { 
            cerr<<"Unrecognized argument!"<<endl; 
            exit(1); 
        } 
    } 
}

I tested the following:

1)

./program

The program doesn't enter the while-loop. Variable c is inspected to be -1.

2)

./program -h

Works well.

3)

./program -m small

The program exit with Segmentation Fault throwing from strcmp().

Thanks for any help.


Solution

  • Here is an example how to parse options with getopt_long() and correctly handle its return values, such as end of options, missing arguments and unknown options:

    struct Options
    {
        std::string username = "guest";
    
        void parse_command_line(int ac, char** av) try {
            enum {
                  HELP
                , USER
            };
    
            // This array must be in the same order as the enum.
            option const options[] = {
                  {"help",            no_argument, nullptr, HELP}
                , {"username",  required_argument, nullptr, USER}
                , {}
            };
    
            ::opterr = 0;
            for(int c; -1 != (c = getopt_long(ac, av, ":h", options, nullptr));) {
                switch(c) {
                // both short and long option
                case 'h':
                case HELP:
                    usage(av, EXIT_SUCCESS);
                    break;
    
                // only long option
                case USER:
                    username = ::optarg; // 
                    break;
    
                case ':': // missing argument
                    throw Exception("--%s: an argument required", options[::optopt].name);
    
                case '?': // unknown option
                    throw Exception("%s: unknown option", av[optind - 1]);
                }
            }
    
        }
        catch(std::exception& e) {
            fprintf(stderr, "error: %s\n", e.what());
            usage(av, EXIT_FAILURE);
        }
    };
    

    Note, that it is not necessary to have a corresponding short option for each long option.