Search code examples
cargumentsgetoptoptional-argumentsgetopt-long

getopt order of argv when passing multiple arguments to one option


I'm trying to pass multiple arguments to one option. For example,

(1) ./someProgram -f opt1 opt2 opt3 -j opt -d.

(2) ./someProgram -f /dir/dir/* -j opt -d.

My settings for getopt_long looks like this.

const char *short_opt = "hp:j:o:f:";
    struct option long_opt[] =
            {
                    {"help",      no_argument,       NULL, 'h'},
                    {"journal",   required_argument, NULL, 'j'},
                    {"partition", required_argument, NULL, 'p'},
                    {"output",    required_argument, NULL, 'o'},
                    {"file",      required_argument, NULL, 'f'},
                    {NULL, 0,                        NULL, 0}
            };

I tried to simplify main code just so it doesn't look overwhelming. I left parts where I interact with getopt_long variables.

while ((arg = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
        switch (arg) {
            case -1:
            case 0:        
                break;
            ...
            case 'j':
                if (optarg) {
                    setSomeVar; //And nothing else, only set var and break.
                }
                break;
            case 'f':
                if (optarg) {
                    index = optind - 1;
                    while (index < argc) {
                        nextOpt = strdup(argv[index]);
                        index++;
                        if (nextOpt[0] != '-') {
                            callFunc(nextOpt);
                        }
                        else {
                            break;
                        }
                    }
                    optind = index - 1;
                }
                else {
                    fprintf(stderr, "...\n");
                    exit(EXIT_FAILURE);
                }
                break;
            case ':':
            case '?':
                fprintf(stderr, "...");
                return (EXIT_FAILURE);

            default:
                fprintf(stderr, "...", argv[0], arg);
                return (EXIT_FAILURE);
        };

If I call my program like below, everything is as expected.

First input

./prog -f /dir/dir/* - (60 files) or ./prog -f file1 file2 file3
info about file1
info about file2
... and so on

If I will add another option to the end.

Second input

./prog -f file1 file2 file3 file4 -j smth
info about1 file1
action with -j smth. **End.**

I tried to list all arguments in the begining, with the second input like this:

for (int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
}

And result was as expected(for second input)

-f, file1,file... -j,print

Again I included this for loop in my switch, under 'f' case. And using second input, I saw that alredy 3 argument was -j, and 4th was option for j, and only after that there were all other files. Output was like you already can guess,

file1, -j, print, file2, file3,file4

My question is why this is happening? Or how should I fix it, or were do I need to look? Also I tried to improve my code looking at several similar questions answered here, but it looks like Im already following all the advice.

Parsing command line options with multiple arguments [getopt?]

C getopt multiple value

Get several values for the same option [duplicate]

Solution by passing all argument in quotes, doesn't work for me, because user may want to redirect input from ls, for example, or pass directrory with * at the end.

Thank you :)


Solution

  • This is about the sixth paragraph in man getopt (for the Gnu version, which you are evidently using):

    By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end. Two other modes are also implemented. If the first character of optstring is '+' or the environment variable POSIXLY_CORRECT is set, then option processing stops as soon as a nonoption argument is encountered. If the first character of optstring is '-', then each nonoption argv-element is handled as if it were the argument of an option with character code 1. (This is used by programs that were written to expect options and other argv-elements in any order and that care about the ordering of the two.) The special argument "--" forces an end of option-scanning regardless of the scanning mode.

    I would think that you could make your code work using either of the non-default modes, but I'd go with the Posix standard one for portability.

    (Although I would actually encourage you to not use a non-standard utility argument convention. Options should take one argument; it's easy enough to repeat an option over a list if that's what you want.)