4

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.

1 Answers1

5

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.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • By the time the code has went through the work of scanning `argv[]` is should have already acquired all the arguments and each found argument can be immediately processed into the appropriate flags within the program. So no need to create a new array – user3629249 Mar 25 '18 at 23:44
  • @user3629249 The idea is to write a small amount of code to perform a simple substitution, and then leverage `getopt` to do the actual argument processing. – jxh Mar 26 '18 at 01:11
  • Can you tell me how to create a new argument vector? We can't just prepend to the corresponding `argv[i]`, can we? I mean, it would disturb the array? – J...S May 03 '18 at 16:10
  • @J...S: Yes, don't modify the strings in `argv[]`. Instead, allocate new strings for the ones you plan to change. You can overwrite the old entries with the new ones if you wish onto the original `argv[]`, but it might be more clear to instead create a `new_argv[]`. – jxh May 03 '18 at 17:09