Alesfatalis
Alesfatalis

Reputation: 777

getopt_long() option with optional argument

I am trying to create a option with an optional argument using getopt_long().

Here is my code:

static struct option long_options[] = {
   {"help",   no_argument,      NULL, 'h'},
   {"debug",  no_argument,      NULL, 'd'},
   {"config", optional_argument, NULL, 'c'},
   {NULL, 0, NULL, 0}
};

while ((ch = getopt_long(argc, argv, "hdc::", long_options, NULL)) != -1) {
        // check to see if a single character or long option came through
        switch (ch) {
            case 'h':
                opt.help = true;
                break;
            case 'd':
                opt.debug = true;
                break;
            case 'c':
                printf("C-Option: %s\n", optarg);
                if (optarg != NULL) {
                    opt.configFile = optarg;
                }
                break;
        }
    }

I am trying to make the argument for -c optional so you can run the program with these options:

-c test.cfg --> optarg = "test.cfg"

-c --> optarg = null

If I set c:: than optarg is always null.

If I set c: than I get an error: option requires an argument -- 'c'

What did I wrong? Is there any other options I can set?

Upvotes: 3

Views: 4975

Answers (1)

ad absurdum
ad absurdum

Reputation: 21316

From the man pages:

If the option has an optional argument, it must be written directly after the option character if present.

Your code works as expected:

./a.out -c some_argument --> "C-Option: (null)"
./a.out -csome_argument  --> "C-Option: some_argument"
./a.out -c=some_argument --> "C-Option: =some_argument"

You might consider prepending optional argument names with '=', as in the above example. Here is another question that discusses the issue.

UPDATE

I saw a nice solution to this problem in the link to the other question referenced above. The idea is to check, following an option that takes an argument, to see if the next string in argv[] is an option. If not, it is assumed to be an argument. The argument is then processed, and optind is advanced accordingly. Here is how this idea can be applied to your problem:

case 'c':
            if (optarg == NULL && argv[optind] != NULL
                && argv[optind][0] != '-') {            // not an option
                printf("C-Option: %s\n", argv[optind]);
                opt.configFile = argv[optind];
                ++optind;
            } else {  // handle case of argument immediately after option
                printf("C-Option: %s\n", optarg);
                if (optarg != NULL) {
                    opt.configFile = optind;
            }
            break;

This code would result in the following output:

./a.out -csome_argument  --> "C-Option: some_argument"
./a.out -c some_argument --> "C-Option: some_argument"

Upvotes: 4

Related Questions