| /**************************************************************************** |
| * libs/libc/unistd/lib_getopt_common.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <assert.h> |
| #include <stdbool.h> |
| #include <string.h> |
| |
| #include "unistd.h" |
| |
| /**************************************************************************** |
| * 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 |
| * |
| * Description: |
| * Handle one long option |
| * |
| ****************************************************************************/ |
| |
| static int getopt_long_option(FAR struct getopt_s *go, |
| FAR char * const argv[], |
| FAR const struct option *longopts, |
| FAR int *longindex) |
| { |
| int ndx; |
| int ret; |
| |
| /* The option list may not be NULL in this context */ |
| |
| if (longopts == NULL) |
| { |
| goto errout; |
| } |
| |
| /* Search the list of long options for a matching name. |
| * The last element of the option array must be filled with zeroes. |
| */ |
| |
| for (ndx = 0; longopts[ndx].name != NULL; ndx++) |
| { |
| 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 |
| * argument provided in the same argv entry like |
| * --option=argument? |
| */ |
| |
| if (terminator != NULL) |
| { |
| /* Skip over the option + argument */ |
| |
| go->go_optptr = NULL; |
| go->go_optind++; |
| |
| switch (longopts[ndx].has_arg) |
| { |
| case no_argument: |
| |
| /* But no argument is expected! */ |
| |
| go->go_optarg = NULL; |
| return '?'; |
| |
| case optional_argument: |
| case required_argument: |
| |
| /* Return the required argument */ |
| |
| go->go_optarg = terminator; |
| break; |
| |
| default: |
| goto errout; |
| } |
| } |
| 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; |
| |
| case optional_argument: |
| /* Check if there is a following argument and if that |
| * following argument is another option. |
| */ |
| |
| next = argv[go->go_optind + 1]; |
| if (next == NULL || next[0] == '-') |
| { |
| go->go_optptr = NULL; |
| go->go_optarg = NULL; |
| go->go_optind++; |
| break; |
| } |
| |
| /* Fall through and treat as a required option */ |
| |
| case required_argument: |
| |
| /* Verify that the required argument is present */ |
| |
| next = argv[go->go_optind + 1]; |
| if (next == NULL || next[0] == '-') |
| { |
| go->go_optptr = NULL; |
| go->go_optarg = NULL; |
| go->go_optind++; |
| return '?'; |
| } |
| |
| /* Return the required argument */ |
| |
| go->go_optptr = NULL; |
| go->go_optarg = next; |
| go->go_optind += 2; |
| break; |
| |
| default: |
| goto errout; |
| } |
| } |
| |
| /* Setup return value. |
| * |
| * 'val' is the value to return on success, or to load into the |
| * variable pointed to by flag. |
| * |
| * 'flag' specifies how results are returned for a long option. If |
| * flag is NULL, then getopt_long() returns val. Otherwise, |
| * getopt_long() returns 0, and flag points to a variable which is |
| * set to val if the option is found, but left unchanged if the |
| * option is not found. |
| */ |
| |
| if (longopts[ndx].flag != NULL) |
| { |
| *(longopts[ndx].flag) = longopts[ndx].val; |
| ret = OK; |
| } |
| else |
| { |
| ret = longopts[ndx].val; |
| } |
| |
| /* If longindex is not NULL, it points to a variable which is |
| * set to the index of the long option relative to longopts. |
| */ |
| |
| if (longindex != NULL) |
| { |
| *longindex = ndx; |
| } |
| |
| return ret; |
| } |
| } |
| |
| /* This option is not in the list of valid options */ |
| |
| go->go_optopt = *go->go_optptr; |
| return '?'; |
| |
| errout: |
| |
| /* Restore the initial, uninitialized state */ |
| |
| go->go_binitialized = false; |
| return ERROR; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: getopt_common |
| * |
| * Description: |
| * |
| * getopt_common() is the common, internal implementation of getopt(), |
| * getopt_long(), and getopt_long_only(). |
| * |
| * getopt() parses command-line arguments. Its arguments argc and argv |
| * are the argument count and array as passed to the main() function on |
| * program invocation. An element of argv that starts with '-' is an |
| * option element. The characters of this element (aside from the initial |
| * '-') are option characters. If getopt() is called repeatedly, it |
| * returns successively each of the option characters from each of the |
| * option elements. |
| * |
| * If getopt() finds another option character, it returns that character, |
| * updating the external variable optind and a static variable nextchar so |
| * that the next call to getopt() can resume the scan with the following |
| * option character or argv-element. |
| * |
| * If there are no more option characters, getopt() returns -1. Then optind |
| * is the index in argv of the first argv-element that is not an option. |
| * |
| * The 'optstring' argument is a string containing the legitimate option |
| * characters. If such a character is followed by a colon, this indicates |
| * that the option requires an argument. If an argument is required for an |
| * option so getopt() places a pointer to the following text in the same |
| * argv-element, or the text of the following argv-element, in optarg. |
| * |
| * The getopt_long() function works like getopt() except that it also |
| * accepts long options, started with two dashes. (If the program accepts |
| * only long options, then optstring should be specified as an empty |
| * string (""), not NULL.) Long option names may be abbreviated if the |
| * abbreviation is unique or is an exact match for some defined option |
| * |
| * getopt_long_only() is like getopt_long(), but '-' as well as "--" can |
| * indicate a long option. If an option that starts with '-' (not "--") |
| * doesn't match a long option, but does match a short option, it is |
| * parsed as a short option instead. |
| * |
| * NOTES: |
| * 1. opterr is not supported and this implementation of getopt() never |
| * printfs error messages. |
| * 2. getopt is NOT threadsafe! |
| * 3. This version of getopt() does not reset global variables until |
| * -1 is returned. As a result, your command line parsing loops |
| * must call getopt() repeatedly and continue to parse if other |
| * errors are returned ('?' or ':') until getopt() finally returns -1. |
| * (You can also set optind to -1 to force a reset). |
| * 4. Standard getopt() permutes the contents of argv as it scans, so that |
| * eventually all the nonoptions are at the end. This implementation |
| * does not do this. |
| * |
| * Returned Value: |
| * If an option was successfully found, then getopt() returns the option |
| * character. If all command-line options have been parsed, then getopt() |
| * returns -1. If getopt() encounters an option character that was not |
| * in optstring, then '?' is returned. If getopt() encounters an option |
| * with a missing argument, then the return value depends on the first |
| * character in optstring: if it is ':', then ':' is returned; otherwise |
| * '?' is returned. |
| * |
| ****************************************************************************/ |
| |
| int getopt_common(int argc, FAR char * const argv[], |
| FAR const char *optstring, |
| FAR const struct option *longopts, |
| FAR int *longindex, |
| enum getopt_mode_e mode) |
| { |
| int ret; |
| |
| /* Get thread-specific getopt() variables */ |
| |
| FAR struct getopt_s *go = getoptvars(); |
| if (go == NULL) |
| { |
| return '?'; |
| } |
| |
| /* Verify input parameters. */ |
| |
| if (argv != NULL) |
| { |
| FAR char *optchar; |
| int noarg_ret = '?'; |
| |
| /* The initial value of optind is 1. If getopt() is called again in |
| * the program, optind must be reset to some value <= 1. |
| */ |
| |
| if (go->go_optind < 1 || !go->go_binitialized) |
| { |
| go->go_optarg = NULL; |
| go->go_optind = 1; /* Skip over the program name */ |
| go->go_optopt = '?'; |
| go->go_optptr = NULL; /* Start at the beginning of the first argument */ |
| go->go_binitialized = true; /* Now we are initialized */ |
| } |
| |
| /* Are we resuming in the middle, or at the end of a string of |
| * arguments? optptr == NULL means that we are started at the |
| * beginning of argv[optind]; *optptr == \0 means that we are |
| * starting at the beginning of optind+1 |
| */ |
| |
| while (!go->go_optptr || !*go->go_optptr) |
| { |
| /* We need to start at the beginning of the next argv. Check if we |
| * need to increment optind |
| */ |
| |
| if (go->go_optptr) |
| { |
| /* Yes.. Increment it and check for the case where where we |
| * have processed everything in the argv[] array. |
| */ |
| |
| go->go_optind++; |
| } |
| |
| /* Check for the end of the argument list */ |
| |
| go->go_optptr = go->go_optind < argc ? argv[go->go_optind] : NULL; |
| if (!go->go_optptr) |
| { |
| /* There are no more arguments, we are finished */ |
| |
| go->go_binitialized = false; |
| return ERROR; |
| } |
| |
| /* We are starting at the beginning of argv[optind]. In this case, |
| * the first character must be '-' |
| */ |
| |
| if (*go->go_optptr != '-') |
| { |
| /* The argument does not start with '-', we are finished */ |
| |
| go->go_binitialized = false; |
| return ERROR; |
| } |
| |
| /* Skip over the '-' */ |
| |
| go->go_optptr++; |
| } |
| |
| /* Special case handling of "-" and "-:" */ |
| |
| if (!*go->go_optptr) |
| { |
| /* We'll fix up optptr the next time we are called */ |
| |
| go->go_optopt = '\0'; |
| return '?'; |
| } |
| |
| /* Handle the case of "-:" */ |
| |
| if (*go->go_optptr == ':') |
| { |
| go->go_optopt = ':'; |
| go->go_optptr++; |
| return '?'; |
| } |
| |
| /* go->go_optptr now points at the next option and it is not something |
| * crazy. Possibilities: |
| * |
| * FORM APPLICABILITY |
| * -o getopt(), getopt_long_only() |
| * -o reqarg getopt(), getopt_long_only() |
| * -o optarg getopt_long_only() |
| * -option getopt_long_only() |
| * -option reqarg getopt_long_only() |
| * -option optarg getopt_long_only() |
| * --option getopt_long(), getopt_long_only() |
| * --option reqarg getopt_long(), getopt_long_only() |
| * --option optarg getopt_long(), getopt_long_only() |
| * |
| * Where: |
| * o - Some short option |
| * option - Some long option |
| * reqarg - A required argument |
| * optarg - An optional argument |
| */ |
| |
| /* Check for --option forms or -option forms */ |
| |
| if (GETOPT_HAVE_LONG(mode)) |
| { |
| /* Handle -option and --option forms. */ |
| |
| if (*go->go_optptr == '-') |
| { |
| /* Skip over the second '-' */ |
| |
| go->go_optptr++; |
| |
| /* And parse the long option */ |
| |
| ret = getopt_long_option(go, argv, longopts, longindex); |
| if (ret == '?') |
| { |
| /* Skip over the unrecognized long option. */ |
| |
| go->go_optind++; |
| go->go_optptr = NULL; |
| } |
| |
| return ret; |
| } |
| |
| /* The -option form is only valid in getop_long_only() mode and |
| * must be distinguished from the -o case forms. |
| */ |
| |
| else if (GETOPT_HAVE_LONGONLY(mode)) |
| { |
| /* A special case is that the option is of a form like |
| * -o but is represented as a single character long option. |
| * In that case, getopt_long_option() will fail with '?' and, |
| * if it is a single character option, we can just fall |
| * through to the short option logic. |
| */ |
| |
| ret = getopt_long_option(go, argv, longopts, longindex); |
| if (ret != '?') |
| { |
| /* Return success or ERROR */ |
| |
| return ret; |
| } |
| |
| /* Check for single character option. |
| * |
| * REVISIT: There is no way to distinguish a sequence of |
| * short arguments like -abc (meaning -a -b -c) from a single |
| * long argument (like "abc"). I am not sure of the correct |
| * behavior in this case. While supported for getopt(), I do |
| * not think that the first interpretation is standard. |
| */ |
| |
| else if (go->go_optptr == NULL || go->go_optptr[1] != '\0') |
| { |
| /* Skip over the unrecognized long option. */ |
| |
| go->go_optind++; |
| go->go_optptr = NULL; |
| return ret; |
| } |
| } |
| } |
| |
| /* Check if the option is in the list of valid short options. |
| * In long option modes, opstring may be NULL. However, that is |
| * an error in any case here because we have not found any |
| * long options. |
| */ |
| |
| if (optstring == NULL) |
| { |
| /* Not an error with getopt_long() */ |
| |
| if (GETOPT_HAVE_LONG(mode)) |
| { |
| /* Return '?'. optptr is reset to the next argv entry, |
| * discarding everything else that follows in the argv string |
| * (which could be another single character command). |
| */ |
| |
| DEBUGASSERT(go->go_optptr != NULL); |
| |
| go->go_optopt = *go->go_optptr; |
| go->go_optptr = NULL; |
| go->go_optind++; |
| return '?'; |
| } |
| else |
| { |
| /* Restore the initial, uninitialized state, and return an |
| * error. |
| */ |
| |
| go->go_binitialized = false; |
| return ERROR; |
| } |
| } |
| |
| /* If the first character of opstring s ':', then ':' is in the event |
| * of a missing argument. Otherwise '?' is returned. |
| */ |
| |
| if (*optstring == ':') |
| { |
| noarg_ret = ':'; |
| optstring++; |
| } |
| |
| /* Check if the option appears in 'optstring' */ |
| |
| DEBUGASSERT(go->go_optptr != NULL); |
| |
| optchar = strchr(optstring, *go->go_optptr); |
| if (!optchar) |
| { |
| /* No this character is not in the list of valid options */ |
| |
| go->go_optopt = *go->go_optptr; |
| go->go_optptr++; |
| return '?'; |
| } |
| |
| /* Yes, the character is in the list of valid options. Does it have a |
| * required argument? |
| */ |
| |
| if (optchar[1] != ':') |
| { |
| /* No, no arguments. Just return the character that we found */ |
| |
| go->go_optptr++; |
| return *optchar; |
| } |
| |
| /* Yes, it may have an argument. Is the argument immediately after |
| * the command in this same argument? |
| */ |
| |
| if (go->go_optptr[1] != '\0') |
| { |
| /* Yes, return a pointer into the current argument */ |
| |
| go->go_optarg = &go->go_optptr[1]; |
| go->go_optind++; |
| go->go_optptr = NULL; |
| return *optchar; |
| } |
| |
| /* No.. is there an argument in the next value of argv[] ? */ |
| |
| if (argv[go->go_optind + 1] && *argv[go->go_optind + 1] != '-') |
| { |
| /* Yes.. return that */ |
| |
| go->go_optarg = argv[go->go_optind + 1]; |
| go->go_optind += 2; |
| go->go_optptr = NULL; |
| return *optchar; |
| } |
| |
| /* No argument was supplied */ |
| |
| go->go_optptr = NULL; |
| go->go_optarg = NULL; |
| go->go_optopt = *optchar; |
| go->go_optind++; |
| |
| /* Two colons means that the argument is optional. */ |
| |
| return (optchar[2] == ':') ? *optchar : noarg_ret; |
| } |
| |
| /* Restore the initial, uninitialized state, and return an error. */ |
| |
| go->go_binitialized = false; |
| return ERROR; |
| } |