1/*
2 * Copyright (c) 2017, 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 */
17int 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 */
26const 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 */
35int 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 */
41char *optarg;	/* = 0; */
42
43enum 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 */
54static
55void 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 */
69static
70int 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
115int 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.
141 * A long option may take a parameter, of the form --opt=param or --opt param.
142*/
143static
144int optmatch(const char *argval, const char *optname)
145{
146	int result = 0;
147
148	while ((result == 0) && (*optname != 0) && (*argval != 0))
149		result = (*argval++) - (*optname++);
150	return result;
151}
152
153/* Handling for a single long option. */
154static
155int getopt_1long(const int argc,
156		 char *const argv[],
157		 const struct option *const longopts,
158		 const char *const optname,
159		 int *const indexptr)
160{
161	int result = RET_UNKNOWN_OPT;
162	size_t loptn = 0;
163
164	while (longopts[loptn].name != 0) {
165		if (optmatch(optname, longopts[loptn].name) == 0) {
166			/* We found a match. */
167			result = longopts[loptn].val;
168			if (indexptr != 0)
169				*indexptr = loptn;
170			switch (longopts[loptn].has_arg) {
171			case required_argument:
172				if ((optind + 1) >= argc) {
173					/* Missing argument. */
174					optopt = result;
175					return RET_NO_PARAM;
176				}
177				/* Fallthrough to get option value. */
178
179			case optional_argument:
180				if ((argc - optind) > 0) {
181					/* Found argument. */
182					optarg = argv[++optind];
183				}
184				/* Fallthrough to handle flag. */
185
186			case no_argument:
187				optind++;
188				if (longopts[loptn].flag != 0) {
189					*longopts[loptn].flag = result;
190					result = 0;
191				}
192				break;
193
194			}
195			return result;
196		}
197		++loptn;
198	}
199	/*
200	 * If getopt finds an option character in argv that was not included
201	 * in options, ... it returns '?' and sets the external variable
202	 * optopt to the actual option character.
203	 */
204	return RET_UNKNOWN_OPT;
205}
206
207/*
208 * getopt_long gets the next option argument from the argument list
209 * specified by the argv and argc arguments.  Options may be either short
210 * (single letter) as for getopt, or longer names (preceded by --).
211 */
212int getopt_long(int argc,
213		char *argv[],
214		const char *shortopts,
215		const struct option *longopts,
216		int *indexptr)
217{
218	int result = RET_END_OPT_LIST;
219
220	getopt_init();
221	/* If we have an argument left to play with */
222	if ((argc > optind) && (argv != 0)) {
223		const char *arg = argv[optind];
224
225		if ((arg != 0) && (arg[0] == '-')) {
226			if (arg[1] == '-') {
227				/* Looks like a long option. */
228				result = getopt_1long(argc,
229						      argv,
230						      longopts,
231						      &arg[2],
232						      indexptr);
233			} else {
234				result = getopt_1char(argc,
235						      argv,
236						      shortopts,
237						      arg[1]);
238			}
239		}
240	}
241	return result;
242}
243
244/*
245 * getopt_long_only gets the next option argument from the argument list
246 * specified by the argv and argc arguments.  Options may be either short
247 * or long as for getopt_long, but the long names may have a single '-'
248 * prefix too.
249 */
250int getopt_long_only(int argc,
251		     char *argv[],
252		     const char *shortopts,
253		     const struct option *longopts,
254		     int *indexptr)
255{
256	int result = RET_END_OPT_LIST;
257
258	getopt_init();
259	/* If we have an argument left to play with */
260	if ((argc > optind) && (argv != 0)) {
261		const char *arg = argv[optind];
262
263		if ((arg != 0) && (arg[0] == '-')) {
264			if (arg[1] == '-') {
265				/* Looks like a long option. */
266				result = getopt_1long(argc,
267						      argv,
268						      longopts,
269						      &arg[2],
270						      indexptr);
271			} else {
272				result = getopt_1long(argc,
273						      argv,
274						      longopts,
275						      &arg[1],
276						      indexptr);
277				if (result == RET_UNKNOWN_OPT) {
278					result = getopt_1char(argc,
279							      argv,
280							      shortopts,
281							      arg[1]);
282				}
283			}
284		}
285	}
286	return result;
287}
288