Search code examples
cbashc99getoptgetopt-long

How to get the value of an option which begins with '+' character?


I am writing an option parser for a bash-like shell I develop.

Nevertheless, to be compatible with bash options, I must read some options which begin with a '+', like this:

./42sh +O autocd [...]

(The manual page says these options will passed to the builtin shopt which sets the values of settings).

The problem is that getopt_long() function only returns the options which begin with a - or --, if they are not alone. If they are, bash counsider them respectively as an alias for standard input and a end of options marker.

How I could get this type of options with getopt_long() ? Have I to parse these options by myself ?

EDIT : according to the @jxh response and the man 3 getopt page, I discovered that getopt and getopt_long arranges the **argv parameters array to move all arguments that don't seem to be valid options - from their point of view - at the end. So, I wrote the following code after the usual code which gets the normal options (all suggestions and remarks greatly appreciated):

EDIT2 : fixed memory leak due to strdup() at each iteration of the loop.

for(; optind < argc; ++optind)
{
    const char *argv_copy = strdup(argv[optind]);
    if (argv_copy[0] == '+' && argv_copy[1] == 'O')
    {
        /* A deactivation parameter have been just found ! */
        if (handle_shopt_options(shell_options,
                                 argv[optind + 1],
                                 DISABLE) == EXIT_FAILURE)
        {
            usage(argv[optind]);
            free_shell_options(shell_options);
            shell_options = NULL;
        }
            ++optind;
    }
    free(argv_copy);
    argv_copy = NULL;
}

Some explanations:

  • optind is the argv index which tells us which argument will be parsed at the next pass. Since we parsed all the getopt() point-of-view valid arguments, and since getopt() moves all the non-options at the end, we will parse all remaining arguments, included the ones which interest us.
  • argv[argc] == NULL : this trick is used to know where is the end of the arguments list, so it is useless to parse the argument when optind == argc.
  • I am not comfortable to play directly with the current argv value, so I preferred to copy it in a new string, but I am probably wrong, to be verfied.

Some remarks:

  • getopt_long() will be available only if _GNU_SOURCE macro is defined.
  • strdup() will be available only if macro _XOPEN_SOURCE >= 500 or macro _POSIX_C_SOURCE >= 200809L.

Solution

  • As you have noted in your research, you cannot use getopt_long to parse options that begin with +.

    As a workaround, you can scan argv[] yourself, and then create a new argument vector that substitutes --plus- in front of every argument you think is a + style option in the original argv[]. This new array should be parseable by getopt_long.