1 /*
2 * Copyright (c) 2017 - 2020, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <assert.h>
8
9 #include "win_posix.h"
10
11 /*
12 * This variable is set by getopt to the index of the next element of the
13 * argv array to be processed. Once getopt has found all of the option
14 * arguments, you can use this variable to determine where the remaining
15 * non-option arguments begin. The initial value of this variable is 1.
16 */
17 int optind = 1;
18
19 /*
20 * If the value of this variable is nonzero, then getopt prints an error
21 * message to the standard error stream if it encounters an unknown option
22 * default character or an option with a missing required argument.
23 * If you set this variable to zero, getopt does not print any messages,
24 * but it still returns the character ? to indicate an error.
25 */
26 const int opterr; /* = 0; */
27 /* const because we do not implement error printing.*/
28 /* Not initialised to conform with the coding standard. */
29
30 /*
31 * When getopt encounters an unknown option character or an option with a
32 * missing required argument, it stores that option character in this
33 * variable.
34 */
35 int optopt; /* = 0; */
36
37 /*
38 * This variable is set by getopt to point at the value of the option
39 * argument, for those options that accept arguments.
40 */
41 char *optarg; /* = 0; */
42
43 enum return_flags {
44 RET_ERROR = -1,
45 RET_END_OPT_LIST = -1,
46 RET_NO_PARAM = '?',
47 RET_NO_PARAM2 = ':',
48 RET_UNKNOWN_OPT = '?'
49 };
50
51 /*
52 * Common initialisation on entry.
53 */
54 static
getopt_init(void)55 void getopt_init(void)
56 {
57 optarg = (char *)0;
58 optopt = 0;
59 /* optind may be zero with some POSIX uses.
60 * For our purposes we just change it to 1.
61 */
62 if (optind == 0)
63 optind = 1;
64 }
65
66 /*
67 * Common handling for a single letter option.
68 */
69 static
getopt_1char(int argc,char * const argv[],const char * const opstring,const int optchar)70 int getopt_1char(int argc,
71 char *const argv[],
72 const char *const opstring,
73 const int optchar)
74 {
75 size_t nlen = (opstring == 0) ? 0 : strlen(opstring);
76 size_t loptn;
77
78 for (loptn = 0; loptn < nlen; loptn++) {
79 if (optchar == opstring[loptn]) {
80 if (opstring[loptn + 1] == ':') {
81 /* Option has argument */
82 if (optind < argc) {
83 /* Found argument. */
84 assert(argv != 0);
85 optind++;
86 optarg = argv[optind++];
87 return optchar;
88 }
89 /* Missing argument. */
90 if (opstring[loptn + 2] == ':') {
91 /* OK if optional "x::". */
92 optind++;
93 return optchar;
94 }
95 /* Actual missing value. */
96 optopt = optchar;
97 return ((opstring[0] == ':')
98 ? RET_NO_PARAM2
99 : RET_NO_PARAM);
100 }
101 /* No argument, just return option char */
102 optind++;
103 return optchar;
104 }
105 }
106 /*
107 * If getopt finds an option character in argv that was not included in
108 * options, ... it returns '?' and sets the external variable optopt to
109 * the actual option character.
110 */
111 optopt = optchar;
112 return RET_UNKNOWN_OPT;
113 }
114
getopt(int argc,char * argv[],char * opstring)115 int getopt(int argc,
116 char *argv[],
117 char *opstring)
118 {
119 int result = RET_END_OPT_LIST;
120 size_t argn = 0;
121 size_t nlen = strlen(opstring);
122
123 getopt_init();
124 /* If we have an argument left to play with */
125 if ((argc > optind) && (argv != 0)) {
126 const char *arg = (const char *)argv[optind];
127
128 if ((arg != 0) && (arg[0] == '-'))
129 result = getopt_1char(argc, argv, opstring, arg[1]);
130 }
131
132 return result;
133 }
134
135 /*
136 * Match an argument value against an option name.
137 * Note that we only match over the shorter length of the pair, to allow
138 * for abbreviation or say --match=value
139 * Long option names may be abbreviated if the abbreviation is unique or an
140 * exact match for some defined option. This function does not check that the
141 * abbreviations are unique and should be handled by the caller.
142 * A long option may take a parameter, of the form --opt=param or --opt param.
143 */
144 static
optmatch(const char * argval,const char * optname)145 int optmatch(const char *argval, const char *optname)
146 {
147 int result = 0;
148
149 while ((result == 0) && (*optname != 0) && (*argval != 0))
150 result = (*argval++) - (*optname++);
151 return result;
152 }
153
154 /* Handling for a single long option. */
155 static
getopt_1long(const int argc,char * const argv[],const struct option * const longopts,const char * const optname,int * const indexptr)156 int getopt_1long(const int argc,
157 char *const argv[],
158 const struct option *const longopts,
159 const char *const optname,
160 int *const indexptr)
161 {
162 int result = RET_UNKNOWN_OPT;
163 size_t loptn = 0;
164 bool match_found = false;
165
166 /*
167 * Long option names may be abbreviated if the abbreviation
168 * is unique or an exact match for some defined option.
169 * To handle this:
170 * - First search for an exact match.
171 * - If exact match was not found search for a abbreviated match.
172 * By doing this an incorrect option selection can be avoided.
173 */
174
175 /* 1. Search for an exact match. */
176 while (longopts[loptn].name != NULL) {
177 if (strcmp(optname, longopts[loptn].name) == 0) {
178 match_found = true;
179 break;
180 }
181 ++loptn;
182 }
183
184 /* 2. If exact match was not found search for a abbreviated match. */
185 if (!match_found) {
186 loptn = 0;
187 while (longopts[loptn].name != NULL) {
188 if (optmatch(optname, longopts[loptn].name) == 0) {
189 match_found = true;
190 break;
191 }
192 ++loptn;
193 }
194 }
195
196 if (match_found) {
197 /* We found a match. */
198 result = longopts[loptn].val;
199 if (indexptr != 0) {
200 *indexptr = loptn;
201 }
202 switch (longopts[loptn].has_arg) {
203 case required_argument:
204 if ((optind + 1) >= argc) {
205 /* Missing argument. */
206 optopt = result;
207 return RET_NO_PARAM;
208 }
209 /* Fallthrough to get option value. */
210
211 case optional_argument:
212 if ((argc - optind) > 0) {
213 /* Found argument. */
214 optarg = argv[++optind];
215 }
216 /* Fallthrough to handle flag. */
217
218 case no_argument:
219 optind++;
220 if (longopts[loptn].flag != 0) {
221 *longopts[loptn].flag = result;
222 result = 0;
223 }
224 break;
225
226 }
227 return result;
228 }
229
230 /*
231 * If getopt finds an option character in argv that was not included
232 * in options, ... it returns '?' and sets the external variable
233 * optopt to the actual option character.
234 */
235 return RET_UNKNOWN_OPT;
236 }
237
238 /*
239 * getopt_long gets the next option argument from the argument list
240 * specified by the argv and argc arguments. Options may be either short
241 * (single letter) as for getopt, or longer names (preceded by --).
242 */
getopt_long(int argc,char * argv[],const char * shortopts,const struct option * longopts,int * indexptr)243 int getopt_long(int argc,
244 char *argv[],
245 const char *shortopts,
246 const struct option *longopts,
247 int *indexptr)
248 {
249 int result = RET_END_OPT_LIST;
250
251 getopt_init();
252 /* If we have an argument left to play with */
253 if ((argc > optind) && (argv != 0)) {
254 const char *arg = argv[optind];
255
256 if ((arg != 0) && (arg[0] == '-')) {
257 if (arg[1] == '-') {
258 /* Looks like a long option. */
259 result = getopt_1long(argc,
260 argv,
261 longopts,
262 &arg[2],
263 indexptr);
264 } else {
265 result = getopt_1char(argc,
266 argv,
267 shortopts,
268 arg[1]);
269 }
270 }
271 }
272 return result;
273 }
274
275 /*
276 * getopt_long_only gets the next option argument from the argument list
277 * specified by the argv and argc arguments. Options may be either short
278 * or long as for getopt_long, but the long names may have a single '-'
279 * prefix too.
280 */
getopt_long_only(int argc,char * argv[],const char * shortopts,const struct option * longopts,int * indexptr)281 int getopt_long_only(int argc,
282 char *argv[],
283 const char *shortopts,
284 const struct option *longopts,
285 int *indexptr)
286 {
287 int result = RET_END_OPT_LIST;
288
289 getopt_init();
290 /* If we have an argument left to play with */
291 if ((argc > optind) && (argv != 0)) {
292 const char *arg = argv[optind];
293
294 if ((arg != 0) && (arg[0] == '-')) {
295 if (arg[1] == '-') {
296 /* Looks like a long option. */
297 result = getopt_1long(argc,
298 argv,
299 longopts,
300 &arg[2],
301 indexptr);
302 } else {
303 result = getopt_1long(argc,
304 argv,
305 longopts,
306 &arg[1],
307 indexptr);
308 if (result == RET_UNKNOWN_OPT) {
309 result = getopt_1char(argc,
310 argv,
311 shortopts,
312 arg[1]);
313 }
314 }
315 }
316 }
317 return result;
318 }
319