Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding `option long_options[]` when using `getopt_long`

I am trying to learn to use getopt_long. From wikipedia, I see the code

#include <stdio.h>     /* for printf */
#include <stdlib.h>    /* for exit */
#include <getopt.h>    /* for getopt_long; POSIX standard getopt is in unistd.h */
int main (int argc, char **argv) {
    int c;
    int digit_optind = 0;
    int aopt = 0, bopt = 0;
    char *copt = 0, *dopt = 0;
    static struct option long_options[] = {
        {"add", 1, 0, 0},
        {"append", 0, 0, 0},
        {"delete", 1, 0, 0},
        {"verbose", 0, 0, 0},
        {"create", 1, 0, 'c'},
        {"file", 1, 0, 0},
        {NULL, 0, NULL, 0}
    };
    int option_index = 0;
    while ((c = getopt_long(argc, argv, "abc:d:012",
                 long_options, &option_index)) != -1) {
        int this_option_optind = optind ? optind : 1;
        switch (c) {
        case 0:
            printf ("option %s", long_options[option_index].name);
            if (optarg)
                printf (" with arg %s", optarg);
            printf ("\n");
            break;
        case '0':
        case '1':
        case '2':
            if (digit_optind != 0 && digit_optind != this_option_optind)
              printf ("digits occur in two different argv-elements.\n");
            digit_optind = this_option_optind;
            printf ("option %c\n", c);
            break;
        case 'a':
            printf ("option a\n");
            aopt = 1;
            break;
        case 'b':
            printf ("option b\n");
            bopt = 1;
            break;
        case 'c':
            printf ("option c with value '%s'\n", optarg);
            copt = optarg;
            break;
        case 'd':
            printf ("option d with value '%s'\n", optarg);
            dopt = optarg;
            break;
        case '?':
            break;
        default:
            printf ("?? getopt returned character code 0%o ??\n", c);
        }
    }
    if (optind < argc) {
        printf ("non-option ARGV-elements: ");
        while (optind < argc)
            printf ("%s ", argv[optind++]);
        printf ("\n");
    }
    exit (0);
}

I don't quite understand the option long_options[] object.

First column

I think the first "column" of long_options[] should be the long flag (whatever follows --) the user have used in the command line.

Second column

I would have thought the second column should contain only no_argument, required_argument, or optional_argument but instead of that I see 0 and 1.

Third column

I don't understand the third column.

Fourth column and maximum number of flags

The fourth column is the unique identifier one uses in the switch statement. However, if the unique identifier can only be a single character, then are we limited to all lower case letters (26) + all uppercase letters (26) + the numbers (10) + eventually some special characters for a total of a bit more than 62 different arguments max? Is this a limitation of getopt? If I am mistaken, then how can one indicate more than two characters to identify a flag in the third argument to getopt_long (""abc:d:012"")

I suppose the last row of option long_options[] is for when getopt returns -1 and therefore does not really matter as long as it exists.

like image 982
Remi.b Avatar asked Dec 15 '22 03:12

Remi.b


1 Answers

The struct option array is precisely defined in man getopt_long [Note 1] from which I excerpt:

longopts is a pointer to the first element of an array of struct option declared in <getopt.h> as

   struct option {
       const char *name;
       int         has_arg;
       int        *flag;
       int         val;
   };

The meanings of the different fields are:

name is the name of the long option.

has_arg is: no_argument (or 0) if the option does not take an argument; required_argument (or 1) if the option requires an argument; or optional_argument (or 2) if the option takes an optional argument.

flag specifies how results are returned for a long option. If flag is NULL, then getopt_long() returns val. (For example, the calling program may set val to the equivalent short option character.) Otherwise, getopt_long() returns 0, and flag points to a variable which is set to val if the option is found, but left unchanged if the option is not found.

val is the value to return, or to load into the variable pointed to by flag.

The last element of the array has to be filled with zeros.

So, you would normally use a symbolic constant for the second element (has_arg), but the manpage allows you to use 0, 1 or 2, presumably for backwards-compatibility. (Wikipedia should use the symbolic constants, IMHO, but that's between Wikipedia and its editors.)

getopt_long returns an int, not a char. If the flag (third) field is NULL (or, equivalently, 0), then the val (fourth) field will be returned, and that can be anything which fits into an int. A character certainly fits into an int, so you could return the equivalent short option character (as noted in the manpage) but you are under no obligation to do so. getopt also returns an int, but since it always returns an option character (or an error indication), there are a large number of int values which it will never return. [Note 2]

If the third field is not NULL, it is expected to point to a variable of type int into which getopt_long will store the val value. That could be used, for instance, for boolean flags:

enum FROBNICATE { FROB_UNSET = -1, FROB_NO = 0, FROB_YES = 1 };
/* ... */

/* This is conceptually an enum, but `getopt_long` expects an int */
int frob_flag = FROB_UNSET;

struct option long_opts = {
  /* ... */
  {"frobnicate", no_argument, &frob_flag, FROB_YES},
  {"unfrobnicated", no_argument, &frob_flag, FROB_NO},
  /* ... */
  {NULL, 0, NULL, 0}
};

/* Loop over arguments with getopt_long;
   In the switch statement, you can ignore the returned value
   0 because the action has been fully realized by setting the
   value of a flag variable.
 */

if (frob_flag == FROB_UNSET)
  frob_flag = get_default_frobnication();

As indicated by the manpage, the last entry in the array must be all zeros (or NULL for the pointer members). That is needed so that getopt_long knows where the array ends.

Notes

  1. You probably have the manpages installed on your system, in which case you can simply type man getopt_long to see the documentation for getopt_long. That should work for any standard C library function, any Gnu libc function, and, in general, any C library function for which you have installed the -doc package. (Highly recommended.) On the whole, you should try a manpage first before looking at Wikipedia, because the manpage will be the documentation for the version of the library function actually installed on your system.

  2. The fact that a function returns a given datatype does not imply that it might return any possible value of that datatype.

like image 56
rici Avatar answered Jan 07 '23 02:01

rici