Search code examples
cgetoptgetopt-long

is it possible to use getopt_long to parse arrays of strings similar to command line arguments in a C program?


I am aware that getopt should be used to parse command line arguments, and not strings.

However, I am confused by the fact that if I pass it an array of strings that "looks like" the argv variable, getopt_long seems to work, but only the first time that I call it. For the second, and all the subsequent times, that I call it it ignores any arguments and returns the default ones.

The reason I am doing this is that I am turning an application that used to take command line arguments in an "interactive" version of it that asks the user for some arguments, then does some actions based on those arguments, then asks for more arguments and so on.

The following code is a minimal example that reproduces the error

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

struct input_parameters{
    char * d; // data set
};

int solver_help(int argc, char* const argv[], struct input_parameters * p)
{
  int c;

  p->d = "default name";

  while (1)
  {
      static struct option long_options[] =
      {
          {"data",  required_argument,     0, 'd'},
          {0, 0, 0, 0}
      };
      /* getopt_long stores the option index here. */
      int option_index = 0;

      c = getopt_long (argc, argv, "d:",
                       long_options, &option_index);

      /* Detect the end of the options. */
      if (c == -1)
        break;

      switch (c)
      {
          case 'd': p->d = optarg;
              break;
          default:
              printf("wrong option specification\n");
              exit(-1);
      }
  }

  return 0 ;


}    


int main () {

    int argc_first = 3;
    char * const argv_first[] = { "getopt_test", "--data", "first" };
    struct input_parameters first_input;
    solver_help(argc_first, argv_first, &first_input);

    printf("I think the data is: %s\n", first_input.d);

    int argc_second = 3;
    char * const argv_second[] = { "getopt_test","--data", "second" };
    struct input_parameters second_input;
    solver_help(argc_second, argv_second, &second_input);

    printf("I think the data is: %s\n", second_input.d);

    return 0;
}

The output of this code is

I think the data is: first
I think the data is: default name

Solution

  • From the manual page for getopt:

    In order to use getopt() to evaluate multiple sets of arguments, or to evaluate a single set of arguments multiple times, the variable optreset must be set to 1 before the second and each additional set of calls to getopt(), and the variable optind must be reinitialized.

    Modifying your code slightly gives you the desired results:

    #include <stdio.h>
    #include <stdlib.h>
    #include <getopt.h>
    
    struct input_parameters {
        char * d; // data set
    };
    
    int solver_help(int argc, char* const argv[], struct input_parameters * p)
    {
        p->d = "default name";
    
        static struct option long_options[] =
        {
            {"data",  required_argument,     0, 'd'},
            {0, 0, 0, 0}
        };
    
        int option_index = 0;
    
        optreset = 1;     /*  ADD THIS  */
        optind = 1;       /*  ADD THIS  */
    
        int c = getopt_long(argc, argv, "d:",
                            long_options, &option_index);
    
        switch ( c )
        {
            case -1:
                break;
    
            case 'd':
                p->d = optarg;
                break;
    
            default:
                printf("in default case...\n");
                printf("wrong option specification\n");
                exit(EXIT_FAILURE);
        }
    
        return 0;
    }    
    
    int main(void) {
    
        int argc_first = 3;
        char * const argv_first[] = { "getopt_test", "--data", "first" };
        struct input_parameters first_input;
        solver_help(argc_first, argv_first, &first_input);
    
        printf("I think the data is: %s\n", first_input.d);
    
        int argc_second = 3;
        char * const argv_second[] = { "getopt_test","--data", "second" };
        struct input_parameters second_input;
        solver_help(argc_second, argv_second, &second_input);
    
        printf("I think the data is: %s\n", second_input.d);
    
        return 0;
    }
    

    with output:

    paul@horus:~/src/sandbox$ ./go
    I think the data is: first
    I think the data is: second
    paul@horus:~/src/sandbox$