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