Search code examples
linuxparsingcommand-linegetopt-long

getopt_long not working as expected


I have the following code

#include <stdio.h>     
#include <getopt.h>       

int main(int argc, char* argv[]){
    const struct option longopts[]={
        {"one", required_argument, 0, '1'},
        {"two", required_argument, 0, '2'},
        {"three", required_argument, 0, '3'},
        {"four", required_argument, 0, '4'},
        {"five", required_argument, 0, '5'},
        {0,0,0,0}
    };

    const char* shortopts="1:2:3:4:5:";
    int c;
    c = -1;
    for(;;){
        int optind = 0;
        c = getopt_long(argc, argv, shortopts, longopts, &optind);
        if(c<0)
            break;
        switch(c){
            case 0:
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
                fprintf(stdout, "----------------------------------------\n");
                fprintf(stdout, "c = %c\n", c);
                fprintf(stdout, "optindd = %d\n", optind);
                fprintf(stdout, "val = %c, \n", longopts[optind].val);
                fprintf(stdout, "name = %s\n", longopts[optind].name);
                fprintf(stdout, "optarg = %s\n", optarg);
                break;
        }
    }

}

Input:

./a.out --one 1 -2 two --three 3 -4 four --five 5           

Expected output:
I want to print members of the struct option (name and val) when its corresponding shortopt/longopt is encountered.

The above code prints the following with some unexpected outputs :

----------------------------------------
c = 1
optindd = 0
val = 1, 
name = one
optarg = 1
---------------------------------------- 
c = 2
optindd = 0           // expected 1
val = 1,         // expected 2      
name = one        // expected two
optarg = two
----------------------------------------
c = 3
optindd = 2
val = 3, 
name = three
optarg = 3
---------------------------------------- 
c = 4
optindd = 0          // expected 3
val = 1,        // expected 4
name = one       // expected four
val = four
----------------------------------------
c = 5
optindd = 4
val = 5, 
name = five
val = 5

I am using Ubuntu 14.04.


Solution

  • The longindex return argument to getopt_long is only set for long options, not for short options. There is no way for getopt_long to know which long option corresponds to a given short option (although the correspondence may seem obvious to you). If a short option is found, the longindex variable is unchanged, so you should initialize to a value you can recognized (like -1) rather than initializing it to 0.

    By the way, optind is a global variable maintained by getopt, which you will need in order to process positional arguments once you have finished with the flags. It's unwise (though legal) to cover this variable with a local variable with the same name; it is both confusing for readers and awkward when you need the value.

    None of that helps you identify the long option corresponding to a given short option. That is totally your responsibility, if you feel you need that information. You could, for example, search the longopts structure for a val corresponding to the short option found; you would then have to deal with the possibilities that a given short option occurs zero or more than one times in that structure.

    As a simple example, instead of:

    int optindex = 0;
    c = getopt_long(argc, argv, shortopts, longopts, &optindex);
    

    you could do something like:

    int optindex = -1;
    c = getopt_long(argc, argv, shortopts, longopts, &optindex);
    if (optindex == -1) {
      for (optindex = 0; longopts[optindex].name, ++optindex) {
        if (longopts[optindex].val == c) break;
      }
      if (longopts[optindex].name == NULL) {
        // the short option was not found; do something
      }
    }