From 7de43596da923e83d064d6bfff27d5d0dab62792 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 4 Apr 2021 08:55:26 -0600 Subject: [PATCH] getopt_long(): Add support for required argument format The Linux man page requires that the getopt_long() and getopt_long_only() functions accept arguments to options in a form like: --option=argument This PR adds that missing functionality. This change effects only getopt_long() and getopt_long_only() Tested on a simulator NSH configuration with a modified version of the getopt() test in apps/testing/ostest. --- libs/libc/unistd/lib_getopt_common.c | 172 +++++++++++++++++++++------ 1 file changed, 133 insertions(+), 39 deletions(-) diff --git a/libs/libc/unistd/lib_getopt_common.c b/libs/libc/unistd/lib_getopt_common.c index 2d76ecfcb3..5c89b49ca5 100644 --- a/libs/libc/unistd/lib_getopt_common.c +++ b/libs/libc/unistd/lib_getopt_common.c @@ -42,6 +42,60 @@ * Prive Functions ****************************************************************************/ +/**************************************************************************** + * Name: compare_long_option + * + * Description: + * Compare a command argument with a long option, handling the cases: + * + * --option: Any argument is in the next argv entry + * --option=argument: The argument is in the same argv entry + * + ****************************************************************************/ + +static int compare_long_option(FAR const char *cmdarg, + FAR const char *optname, + FAR const char **argument) +{ + int result; + + *argument = NULL; + + for (; ; ) + { + int rawchar = *cmdarg++; + int optchar = *optname++; + int cmdchar; + + /* The command line option may terminate with either '\0' or '='. */ + + cmdchar = rawchar; + if (cmdchar == '=') + { + cmdchar = '\0'; + } + + /* Perform the comparison */ + + result = cmdchar - optchar; + if (result != 0 || cmdchar == '\0') + { + /* If the '=' is the real terminator, then return the argument + * that follows the '=' + */ + + if (rawchar == '=') + { + *argument = cmdarg; + } + + break; + } + } + + return result; +} + /**************************************************************************** * Name: getopt_long_option * @@ -71,64 +125,104 @@ static int getopt_long_option(FAR struct getopt_s *go, for (ndx = 0; longopts[ndx].name != NULL; ndx++) { - if (strcmp(go->go_optptr, longopts[ndx].name) == 0) + FAR char *terminator = NULL; + + if (compare_long_option(go->go_optptr, longopts[ndx].name, + (FAR const char **)&terminator) == 0) { /* Found the option with the matching name. Does it have an - * required argument? And optional argument? + * argument provided in the same argv entry like + * --option=argument? */ - switch (longopts[ndx].has_arg) + if (terminator != NULL) { - FAR char *next; + /* Skip over the option + argument */ - case no_argument: - /* No, no arguments. Just return the option that we - * found. - */ + go->go_optptr = NULL; + go->go_optind++; - go->go_optptr = NULL; - go->go_optind++; - break; + switch (longopts[ndx].has_arg) + { + case no_argument: - case optional_argument: - /* Check if there is a following argument and if that - * following argument is another option. - */ + /* But no argument is expected! */ - next = argv[go->go_optind + 1]; - if (next == NULL || next[0] == '-') - { - go->go_optptr = NULL; go->go_optarg = NULL; + return '?'; + + case optional_argument: + case required_argument: + + /* Return the required argument */ + + go->go_optarg = terminator; + break; + + default: + goto errout; + break; + } + } + else + { + /* Does the option have a required argument in the next argv + * entry? An optional argument? + */ + + switch (longopts[ndx].has_arg) + { + FAR char *next; + + case no_argument: + /* No, no arguments. Just return the argument that we + * found. + */ + + go->go_optptr = NULL; go->go_optind++; break; - } - /* Fall through and treat as a required option */ + case optional_argument: + /* Check if there is a following argument and if that + * following argument is another option. + */ - case required_argument: + next = argv[go->go_optind + 1]; + if (next == NULL || next[0] == '-') + { + go->go_optptr = NULL; + go->go_optarg = NULL; + go->go_optind++; + break; + } - /* Verify that the required option is present */ + /* Fall through and treat as a required option */ - next = argv[go->go_optind + 1]; - if (next == NULL || next[0] == '-') - { - go->go_optptr = NULL; - go->go_optarg = NULL; - go->go_optind++; - return '?'; - } + case required_argument: - /* Return the required option */ + /* Verify that the required argument is present */ - go->go_optptr = NULL; - go->go_optarg = next; - go->go_optind += 2; - break; + next = argv[go->go_optind + 1]; + if (next == NULL || next[0] == '-') + { + go->go_optptr = NULL; + go->go_optarg = NULL; + go->go_optind++; + return '?'; + } - default: - goto errout; - break; + /* Return the required argument */ + + go->go_optptr = NULL; + go->go_optarg = next; + go->go_optind += 2; + break; + + default: + goto errout; + break; + } } /* Setup return value.