github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/external/glfw/deps/getopt.c (about)

     1  /* Copyright (c) 2012, Kim Gräsman
     2   * All rights reserved.
     3   *
     4   * Redistribution and use in source and binary forms, with or without
     5   * modification, are permitted provided that the following conditions are met:
     6   *  * Redistributions of source code must retain the above copyright notice,
     7   *    this list of conditions and the following disclaimer.
     8   *  * Redistributions in binary form must reproduce the above copyright notice,
     9   *    this list of conditions and the following disclaimer in the documentation
    10   *    and/or other materials provided with the distribution.
    11   *  * Neither the name of Kim Gräsman nor the names of contributors may be used
    12   *    to endorse or promote products derived from this software without specific
    13   *    prior written permission.
    14   *
    15   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    16   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    17   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    18   * ARE DISCLAIMED. IN NO EVENT SHALL KIM GRÄSMAN BE LIABLE FOR ANY DIRECT,
    19   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    20   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    21   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    22   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    23   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    24   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    25   */
    26  
    27  #include "getopt.h"
    28  
    29  #include <stddef.h>
    30  #include <string.h>
    31  
    32  const int no_argument = 0;
    33  const int required_argument = 1;
    34  const int optional_argument = 2;
    35  
    36  char* optarg;
    37  int optopt;
    38  /* The variable optind [...] shall be initialized to 1 by the system. */
    39  int optind = 1;
    40  int opterr;
    41  
    42  static char* optcursor = NULL;
    43  
    44  /* Implemented based on [1] and [2] for optional arguments.
    45     optopt is handled FreeBSD-style, per [3].
    46     Other GNU and FreeBSD extensions are purely accidental.
    47  
    48  [1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html
    49  [2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
    50  [3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE
    51  */
    52  int getopt(int argc, char* const argv[], const char* optstring) {
    53    int optchar = -1;
    54    const char* optdecl = NULL;
    55  
    56    optarg = NULL;
    57    opterr = 0;
    58    optopt = 0;
    59  
    60    /* Unspecified, but we need it to avoid overrunning the argv bounds. */
    61    if (optind >= argc)
    62      goto no_more_optchars;
    63  
    64    /* If, when getopt() is called argv[optind] is a null pointer, getopt()
    65       shall return -1 without changing optind. */
    66    if (argv[optind] == NULL)
    67      goto no_more_optchars;
    68  
    69    /* If, when getopt() is called *argv[optind]  is not the character '-',
    70       getopt() shall return -1 without changing optind. */
    71    if (*argv[optind] != '-')
    72      goto no_more_optchars;
    73  
    74    /* If, when getopt() is called argv[optind] points to the string "-",
    75       getopt() shall return -1 without changing optind. */
    76    if (strcmp(argv[optind], "-") == 0)
    77      goto no_more_optchars;
    78  
    79    /* If, when getopt() is called argv[optind] points to the string "--",
    80       getopt() shall return -1 after incrementing optind. */
    81    if (strcmp(argv[optind], "--") == 0) {
    82      ++optind;
    83      goto no_more_optchars;
    84    }
    85  
    86    if (optcursor == NULL || *optcursor == '\0')
    87      optcursor = argv[optind] + 1;
    88  
    89    optchar = *optcursor;
    90  
    91    /* FreeBSD: The variable optopt saves the last known option character
    92       returned by getopt(). */
    93    optopt = optchar;
    94  
    95    /* The getopt() function shall return the next option character (if one is
    96       found) from argv that matches a character in optstring, if there is
    97       one that matches. */
    98    optdecl = strchr(optstring, optchar);
    99    if (optdecl) {
   100      /* [I]f a character is followed by a colon, the option takes an
   101         argument. */
   102      if (optdecl[1] == ':') {
   103        optarg = ++optcursor;
   104        if (*optarg == '\0') {
   105          /* GNU extension: Two colons mean an option takes an
   106             optional arg; if there is text in the current argv-element
   107             (i.e., in the same word as the option name itself, for example,
   108             "-oarg"), then it is returned in optarg, otherwise optarg is set
   109             to zero. */
   110          if (optdecl[2] != ':') {
   111            /* If the option was the last character in the string pointed to by
   112               an element of argv, then optarg shall contain the next element
   113               of argv, and optind shall be incremented by 2. If the resulting
   114               value of optind is greater than argc, this indicates a missing
   115               option-argument, and getopt() shall return an error indication.
   116  
   117               Otherwise, optarg shall point to the string following the
   118               option character in that element of argv, and optind shall be
   119               incremented by 1.
   120            */
   121            if (++optind < argc) {
   122              optarg = argv[optind];
   123            } else {
   124              /* If it detects a missing option-argument, it shall return the
   125                 colon character ( ':' ) if the first character of optstring
   126                 was a colon, or a question-mark character ( '?' ) otherwise.
   127              */
   128              optarg = NULL;
   129              optchar = (optstring[0] == ':') ? ':' : '?';
   130            }
   131          } else {
   132            optarg = NULL;
   133          }
   134        }
   135  
   136        optcursor = NULL;
   137      }
   138    } else {
   139      /* If getopt() encounters an option character that is not contained in
   140         optstring, it shall return the question-mark ( '?' ) character. */
   141      optchar = '?';
   142    }
   143  
   144    if (optcursor == NULL || *++optcursor == '\0')
   145      ++optind;
   146  
   147    return optchar;
   148  
   149  no_more_optchars:
   150    optcursor = NULL;
   151    return -1;
   152  }
   153  
   154  /* Implementation based on [1].
   155  
   156  [1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
   157  */
   158  int getopt_long(int argc, char* const argv[], const char* optstring,
   159    const struct option* longopts, int* longindex) {
   160    const struct option* o = longopts;
   161    const struct option* match = NULL;
   162    int num_matches = 0;
   163    size_t argument_name_length = 0;
   164    const char* current_argument = NULL;
   165    int retval = -1;
   166  
   167    optarg = NULL;
   168    optopt = 0;
   169  
   170    if (optind >= argc)
   171      return -1;
   172  
   173    if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0)
   174      return getopt(argc, argv, optstring);
   175  
   176    /* It's an option; starts with -- and is longer than two chars. */
   177    current_argument = argv[optind] + 2;
   178    argument_name_length = strcspn(current_argument, "=");
   179    for (; o->name; ++o) {
   180      if (strncmp(o->name, current_argument, argument_name_length) == 0) {
   181        match = o;
   182        ++num_matches;
   183      }
   184    }
   185  
   186    if (num_matches == 1) {
   187      /* If longindex is not NULL, it points to a variable which is set to the
   188         index of the long option relative to longopts. */
   189      if (longindex)
   190        *longindex = (int) (match - longopts);
   191  
   192      /* If flag is NULL, then getopt_long() shall return val.
   193         Otherwise, getopt_long() returns 0, and flag shall point to a variable
   194         which shall be set to val if the option is found, but left unchanged if
   195         the option is not found. */
   196      if (match->flag)
   197        *(match->flag) = match->val;
   198  
   199      retval = match->flag ? 0 : match->val;
   200  
   201      if (match->has_arg != no_argument) {
   202        optarg = strchr(argv[optind], '=');
   203        if (optarg != NULL)
   204          ++optarg;
   205  
   206        if (match->has_arg == required_argument) {
   207          /* Only scan the next argv for required arguments. Behavior is not
   208             specified, but has been observed with Ubuntu and Mac OSX. */
   209          if (optarg == NULL && ++optind < argc) {
   210            optarg = argv[optind];
   211          }
   212  
   213          if (optarg == NULL)
   214            retval = ':';
   215        }
   216      } else if (strchr(argv[optind], '=')) {
   217        /* An argument was provided to a non-argument option.
   218           I haven't seen this specified explicitly, but both GNU and BSD-based
   219           implementations show this behavior.
   220        */
   221        retval = '?';
   222      }
   223    } else {
   224      /* Unknown option or ambiguous match. */
   225      retval = '?';
   226    }
   227  
   228    ++optind;
   229    return retval;
   230  }