]> git.sesse.net Git - pistorm/blob - raylib/external/glfw/deps/getopt.c
Update raylib files and Makefile for Pi 4 testing
[pistorm] / raylib / external / glfw / deps / getopt.c
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 }