win_posix.c 7.58 KB
Newer Older
1
/*
2
 * Copyright (c) 2017 - 2020, Arm Limited and Contributors. All rights reserved.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>

#include "win_posix.h"

/*
 * This variable is set by getopt to the index of the next element of the
 * argv array to be processed. Once getopt has found all of the option
 * arguments, you can use this variable to determine where the remaining
 * non-option arguments begin. The initial value of this variable is 1.
 */
int optind = 1;

/*
 * If the value of this variable is nonzero, then getopt prints an error
 * message to the standard error stream if it encounters an unknown option
 * default character or an option with a missing required argument.
 * If you set this variable to zero, getopt does not print any messages,
 * but it still returns the character ? to indicate an error.
 */
const int opterr; /* = 0; */
/* const because we do not implement error printing.*/
/* Not initialised to conform with the coding standard. */

/*
 * When getopt encounters an unknown option character or an option with a
 * missing required argument, it stores that option character in this
 * variable.
 */
int optopt;	/* = 0; */

/*
 * This variable is set by getopt to point at the value of the option
 * argument, for those options that accept arguments.
 */
char *optarg;	/* = 0; */

enum return_flags {
	RET_ERROR = -1,
	RET_END_OPT_LIST = -1,
	RET_NO_PARAM = '?',
	RET_NO_PARAM2 = ':',
	RET_UNKNOWN_OPT = '?'
};

/*
 * Common initialisation on entry.
 */
static
void getopt_init(void)
{
	optarg = (char *)0;
	optopt = 0;
	/* optind may be zero with some POSIX uses.
	 * For our purposes we just change it to 1.
	 */
	if (optind == 0)
		optind = 1;
}

/*
 * Common handling for a single letter option.
 */
static
int getopt_1char(int argc,
		 char *const argv[],
		 const char *const opstring,
		 const int optchar)
{
	size_t nlen = (opstring == 0) ? 0 : strlen(opstring);
	size_t loptn;

	for (loptn = 0; loptn < nlen; loptn++) {
		if (optchar == opstring[loptn]) {
			if (opstring[loptn + 1] == ':') {
				/* Option has argument */
				if (optind < argc) {
					/* Found argument. */
					assert(argv != 0);
					optind++;
					optarg = argv[optind++];
					return optchar;
				}
				/* Missing argument. */
				if (opstring[loptn + 2] == ':') {
					/* OK if optional "x::". */
					optind++;
					return optchar;
				}
				/* Actual missing value. */
				optopt = optchar;
				return ((opstring[0] == ':')
					? RET_NO_PARAM2
					: RET_NO_PARAM);
			}
			/* No argument, just return option char */
			optind++;
			return optchar;
		}
	}
	/*
	 * If getopt finds an option character in argv that was not included in
	 * options, ... it returns '?' and sets the external variable optopt to
	 * the actual option character.
	 */
	optopt = optchar;
	return RET_UNKNOWN_OPT;
}

int getopt(int argc,
	   char *argv[],
	   char *opstring)
{
	int result = RET_END_OPT_LIST;
	size_t argn = 0;
	size_t nlen = strlen(opstring);

	getopt_init();
	/* If we have an argument left to play with */
	if ((argc > optind) && (argv != 0)) {
		const char *arg = (const char *)argv[optind];

		if ((arg != 0) && (arg[0] == '-'))
			result = getopt_1char(argc, argv, opstring, arg[1]);
	}

	return result;
}

/*
 * Match an argument value against an option name.
 * Note that we only match over the shorter length of the pair, to allow
 * for abbreviation or say --match=value
 * Long option names may be abbreviated if the abbreviation is unique or an
140
141
 * exact match for some defined option. This function does not check that the
 * abbreviations are unique and should be handled by the caller.
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
 * A long option may take a parameter, of the form --opt=param or --opt param.
*/
static
int optmatch(const char *argval, const char *optname)
{
	int result = 0;

	while ((result == 0) && (*optname != 0) && (*argval != 0))
		result = (*argval++) - (*optname++);
	return result;
}

/* Handling for a single long option. */
static
int getopt_1long(const int argc,
		 char *const argv[],
		 const struct option *const longopts,
		 const char *const optname,
		 int *const indexptr)
{
	int result = RET_UNKNOWN_OPT;
	size_t loptn = 0;
164
	bool match_found = false;
165

166
167
168
169
170
171
172
173
	/*
	 * Long option names may be abbreviated if the abbreviation
	 * is unique or an exact match for some defined option.
	 * To handle this:
	 * - First search for an exact match.
	 * - If exact match was not found search for a abbreviated match.
	 * By doing this an incorrect option selection can be avoided.
	 */
174

175
176
177
178
179
180
181
182
	/* 1. Search for an exact match. */
	while (longopts[loptn].name != NULL) {
		if (strcmp(optname, longopts[loptn].name) == 0) {
			match_found = true;
			break;
		}
		++loptn;
	}
183

184
185
186
187
188
189
	/* 2. If exact match was not found search for a abbreviated match. */
	if (!match_found) {
		loptn = 0;
		while (longopts[loptn].name != NULL) {
			if (optmatch(optname, longopts[loptn].name) == 0) {
				match_found = true;
190
				break;
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
			}
			++loptn;
		}
	}

	if (match_found) {
		/* We found a match. */
		result = longopts[loptn].val;
		if (indexptr != 0) {
			*indexptr = loptn;
		}
		switch (longopts[loptn].has_arg) {
		case required_argument:
			if ((optind + 1) >= argc) {
				/* Missing argument. */
				optopt = result;
				return RET_NO_PARAM;
			}
			/* Fallthrough to get option value. */
210

211
212
213
214
		case optional_argument:
			if ((argc - optind) > 0) {
				/* Found argument. */
				optarg = argv[++optind];
215
			}
216
217
218
219
220
221
222
223
224
225
			/* Fallthrough to handle flag. */

		case no_argument:
			optind++;
			if (longopts[loptn].flag != 0) {
				*longopts[loptn].flag = result;
				result = 0;
			}
			break;

226
		}
227
		return result;
228
	}
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
	/*
	 * If getopt finds an option character in argv that was not included
	 * in options, ... it returns '?' and sets the external variable
	 * optopt to the actual option character.
	 */
	return RET_UNKNOWN_OPT;
}

/*
 * getopt_long gets the next option argument from the argument list
 * specified by the argv and argc arguments.  Options may be either short
 * (single letter) as for getopt, or longer names (preceded by --).
 */
int getopt_long(int argc,
		char *argv[],
		const char *shortopts,
		const struct option *longopts,
		int *indexptr)
{
	int result = RET_END_OPT_LIST;

	getopt_init();
	/* If we have an argument left to play with */
	if ((argc > optind) && (argv != 0)) {
		const char *arg = argv[optind];

		if ((arg != 0) && (arg[0] == '-')) {
			if (arg[1] == '-') {
				/* Looks like a long option. */
				result = getopt_1long(argc,
						      argv,
						      longopts,
						      &arg[2],
						      indexptr);
			} else {
				result = getopt_1char(argc,
						      argv,
						      shortopts,
						      arg[1]);
			}
		}
	}
	return result;
}

/*
 * getopt_long_only gets the next option argument from the argument list
 * specified by the argv and argc arguments.  Options may be either short
 * or long as for getopt_long, but the long names may have a single '-'
 * prefix too.
 */
int getopt_long_only(int argc,
		     char *argv[],
		     const char *shortopts,
		     const struct option *longopts,
		     int *indexptr)
{
	int result = RET_END_OPT_LIST;

	getopt_init();
	/* If we have an argument left to play with */
	if ((argc > optind) && (argv != 0)) {
		const char *arg = argv[optind];

		if ((arg != 0) && (arg[0] == '-')) {
			if (arg[1] == '-') {
				/* Looks like a long option. */
				result = getopt_1long(argc,
						      argv,
						      longopts,
						      &arg[2],
						      indexptr);
			} else {
				result = getopt_1long(argc,
						      argv,
						      longopts,
						      &arg[1],
						      indexptr);
				if (result == RET_UNKNOWN_OPT) {
					result = getopt_1char(argc,
							      argv,
							      shortopts,
							      arg[1]);
				}
			}
		}
	}
	return result;
}