mrQWERTY
mrQWERTY

Reputation: 4149

Issue with options order in getopt

I am having trouble with getopt. In my program, I wish to run the program with the following options: -l -w -n -b. n and b are optional, however the program must have either l or w, not both. Also, if w is invoked, it must be followed by an argument. Also, the order should be l or w first, followed by n or b, whose order does not matter.

Here are examples of valid arguments:

./prog -l -n -b
./prog -w argument -b -n
./prog -l -n
./prog -l -b

These examples are not valid:

./prog -l -w argument
./prog -n -l
./prog -b -l

Based on these requirements, I having having trouble using getopt. Of course I can use a myriad of if else statements, but that would be ugly and unnecessarily complicated.

Specifically, I am having trouble with the order of the arguments and making -l and -w options an either or relationship.

Here is what I have so far, although it is not much because of these uncertainties.

while ((option = getopt(argc, argv,"lw:nb:")) != -1) {
  switch(option) {
  case'l': ...  // I haven't wrote anything here yet
  case'w': ...
  case'n': ...
  case'b': ...
  case '?': ...
  default: ...
  }
}

Upvotes: 1

Views: 2612

Answers (1)

indiv
indiv

Reputation: 17856

First, getopt makes sure that w has an argument. That's the point of the : in your getopt string. If -b does not need an argument, then you should remove the : after it.

Second, I don't think verifying argument conditions is as bad as you're thinking it's going to be. You should already have variables to keep track of your options, like this:

int opt_l = 0;
int opt_w = 0;
char *arg_w = 0;
int opt_n = 0;
int opt_b = 0;

You should also have a function that prints out the usage and a helpful error message to the user, like this:

int exit_usage(const char *err)
{
    if( err ) { printf("Bad arguments: %s\n", err); }
    printf("Usage: ...\n");
    exit(1);
}

Then it's a simple one-line check to verify your preconditions. If a precondition fails, then print an error and show the usage info to your user. You don't have to cram the precondition check into one line, but it tends to be easier to read a list of preconditions when they're all one line each. Like this:

while ((option = getopt(argc, argv, "lw:nb")) != -1)
{
    switch(option)
    {
    case 'l':
        if( opt_w != 0 ) { exit_usage("Cannot combine -l with -w"); }
        ++opt_l;
        break;
    case 'w':
        if( opt_l != 0 ) { exit_usage("Cannot combine -l with -w"); }
        arg_w = optarg;
        ++opt_w;
        break;
    case 'n':
        if( opt_l == 0 && opt_w == 0 ) { exit_usage("-n requires -l or -w first"); }
        ++opt_n;
        break;
    case 'b':
        if( opt_l == 0 && opt_w == 0 ) { exit_usage("-n requires -l or -w first"); }
        ++opt_b;
        break;
    case '?':
        exit_usage(NULL);
        break;
    default:
        break;
    }
}

Upvotes: 2

Related Questions