twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

getopt.c (8053B)


      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 }