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