0

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 :)

greg-449
  • 109,219
  • 232
  • 102
  • 145
  • Why not just `./someProgram -f opt1 -f opt2 -f opt3 -j opt -d` ? – KamilCuk Apr 28 '20 at 20:38
  • 1
    @KamilCuk, because If you call program like this ```./p -f /dir/dir/* -j opt -d``` it will not work. –  Apr 28 '20 at 20:40
  • 1
    Please don't vandalize your own posts. When you post here, you give SO the right to distribute the content under CC-by SA 4.0. Any vandalism will be reverted. – greg-449 Apr 29 '20 at 11:15

1 Answers1

0

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.)

rici
  • 234,347
  • 28
  • 237
  • 341
  • Can the decision to go to the end of the arguments and get the remaining option from there be considered as good design? Or get all the remaining options at the beginning, and then call getopt_long - this will be easier, I think. And thank you for your help :) –  Apr 28 '20 at 21:17
  • @hero1n: `getopt` and `getopt_long` are identical except that `getopt_long` handles long option names. They both permute by default. And personally, I think that you probably don't need the `-f` flag; just take all the arguments left after the options are finished. But that will depend on the precise nature of your utility. – rici Apr 28 '20 at 21:39
  • Solution not use flag at all, seems even better. Thank very much, appreciate your help :) –  Apr 28 '20 at 22:18