Browse Source

Added a functional version of getopt() and getopt_long(), ready for use

in our commands.
tags/v0.9.3
Michael Brown 17 years ago
parent
commit
be0cd1cddd
2 changed files with 355 additions and 0 deletions
  1. 280
    0
      src/core/getopt.c
  2. 75
    0
      src/include/getopt.h

+ 280
- 0
src/core/getopt.c View File

@@ -0,0 +1,280 @@
1
+/*
2
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+#include <stdint.h>
20
+#include <string.h>
21
+#include <vsprintf.h>
22
+#include <getopt.h>
23
+
24
+/** @file
25
+ *
26
+ * Parse command-line options
27
+ *
28
+ */
29
+
30
+/**
31
+ * Option argument
32
+ *
33
+ * This will point to the argument for the most recently returned
34
+ * option, if applicable.
35
+ */
36
+char *optarg;
37
+
38
+/**
39
+ * Current option index
40
+ *
41
+ * This is an index into the argv[] array.  When getopt() returns -1,
42
+ * @c optind is the index to the first element that is not an option.
43
+ */
44
+int optind = 1;
45
+
46
+/**
47
+ * Current option character index
48
+ *
49
+ * This is an index into the current element of argv[].
50
+ */
51
+static int nextchar = 0;
52
+
53
+/**
54
+ * Unrecognised option
55
+ *
56
+ * When an unrecognised option is encountered, the actual option
57
+ * character is stored in @c optopt.
58
+ */
59
+int optopt;
60
+
61
+/**
62
+ * Reset getopt() internal state
63
+ *
64
+ * Due to a limitation of the POSIX getopt() API, it is necessary to
65
+ * add a call to reset_getopt() before each set of calls to getopt()
66
+ * or getopt_long().  This arises because POSIX assumes that each
67
+ * process will parse command line arguments no more than once; this
68
+ * assumption is not valid within Etherboot.  We work around the
69
+ * limitation by arranging for execv() to call reset_getopt() before
70
+ * executing the command.
71
+ */
72
+void reset_getopt ( void ) {
73
+	optind = 1;
74
+	nextchar = 0;
75
+}
76
+
77
+/**
78
+ * Get option argument from argv[] array
79
+ *
80
+ * @v argc		Argument count
81
+ * @v argv		Argument list
82
+ * @ret argument	Option argument, or NULL
83
+ *
84
+ * Grab the next element of argv[], if it exists and is not an option.
85
+ */
86
+static const char * get_argv_argument ( int argc, char * const argv[] ) {
87
+	char *arg;
88
+
89
+	/* Don't overrun argv[] */
90
+	if ( optind >= argc )
91
+		return NULL;
92
+	arg = argv[optind];
93
+
94
+	/* If next argv element is an option, then it's not usable as
95
+	 * an argument.
96
+	 */
97
+	if ( *arg == '-' )
98
+		return NULL;
99
+
100
+	/** Consume this argv element, and return it */
101
+	optind++;
102
+	return arg;
103
+}
104
+
105
+/**
106
+ * Match long option
107
+ *
108
+ * @v argc		Argument count
109
+ * @v argv		Argument list
110
+ * @v opttext		Option text within current argv[] element
111
+ * @v longopt		Long option specification
112
+ * @ret option		Option to return from getopt()
113
+ * @ret matched		Found a match for this long option
114
+ */
115
+static int match_long_option ( int argc, char * const argv[],
116
+			       const char *opttext,
117
+			       const struct option *longopt, int *option ) {
118
+	size_t optlen;
119
+	const char *argument = NULL;
120
+
121
+	/* Compare option name */
122
+	optlen = strlen ( longopt->name );
123
+	if ( strncmp ( opttext, longopt->name, optlen ) != 0 )
124
+		return 0;
125
+
126
+	/* Check for inline argument */
127
+	if ( opttext[optlen] == '=' ) {
128
+		argument = &opttext[ optlen + 1 ];
129
+	} else if ( opttext[optlen] ) {
130
+		/* Long option with trailing garbage - no match */
131
+		return 0;
132
+	}
133
+
134
+	/* Consume this argv element */
135
+	optind++;
136
+
137
+	/* If we want an argument but don't have one yet, try to grab
138
+	 * the next argv element
139
+	 */
140
+	if ( ( longopt->has_arg != no_argument ) && ( ! argument ) )
141
+		argument = get_argv_argument ( argc, argv );
142
+
143
+	/* If we need an argument but don't have one, sulk */
144
+	if ( ( longopt->has_arg == required_argument ) && ( ! argument ) ) {
145
+		printf ( "Option \"%s\" requires an argument\n",
146
+			 longopt->name );
147
+		*option = ':';
148
+		return 1;
149
+	}
150
+
151
+	/* If we have an argument where we shouldn't have one, sulk */
152
+	if ( ( longopt->has_arg == no_argument ) && argument ) {
153
+		printf ( "Option \"%s\" takes no argument\n", longopt->name );
154
+		*option = ':';
155
+		return 1;
156
+	}
157
+
158
+	/* Store values and return success */
159
+	optarg = ( char * ) argument;
160
+	if ( longopt->flag ) {
161
+		*(longopt->flag) = longopt->val;
162
+		*option = 0;
163
+	} else {
164
+		*option = longopt->val;
165
+	}
166
+	return 1;
167
+}
168
+
169
+/**
170
+ * Match short option
171
+ *
172
+ * @v argc		Argument count
173
+ * @v argv		Argument list
174
+ * @v opttext		Option text within current argv[] element
175
+ * @v shortopt		Option character from option specification
176
+ * @ret option		Option to return from getopt()
177
+ * @ret matched		Found a match for this short option
178
+ */
179
+static int match_short_option ( int argc, char * const argv[],
180
+				const char *opttext, int shortopt,
181
+				enum getopt_argument_requirement has_arg,
182
+				int *option ) {
183
+	const char *argument = NULL;
184
+
185
+	/* Compare option character */
186
+	if ( *opttext != shortopt )
187
+		return 0;
188
+
189
+	/* Consume option character */
190
+	opttext++;
191
+	nextchar++;
192
+	if ( *opttext ) {
193
+		if ( has_arg != no_argument ) {
194
+			/* Consume remainder of element as inline argument */
195
+			argument = opttext;
196
+			optind++;
197
+			nextchar = 0;
198
+		}
199
+	} else {
200
+		/* Reached end of argv element */
201
+		optind++;
202
+		nextchar = 0;
203
+	}
204
+
205
+	/* If we want an argument but don't have one yet, try to grab
206
+	 * the next argv element
207
+	 */
208
+	if ( ( has_arg != no_argument ) && ( ! argument ) )
209
+		argument = get_argv_argument ( argc, argv );
210
+
211
+	/* If we need an argument but don't have one, sulk */
212
+	if ( ( has_arg == required_argument ) && ( ! argument ) ) {
213
+		printf ( "Option \"%c\" requires an argument\n", shortopt );
214
+		*option = ':';
215
+		return 1;
216
+	}
217
+
218
+	/* Store values and return success */
219
+	optarg = ( char * ) argument;
220
+	*option = shortopt;
221
+	return 1;
222
+}
223
+
224
+/**
225
+ * Parse command-line options
226
+ *
227
+ * @v argc		Argument count
228
+ * @v argv		Argument list
229
+ * @v optstring		Option specification string
230
+ * @v longopts		Long option specification table
231
+ * @ret longindex	Index of long option (or NULL)
232
+ * @ret option		Option found, or -1 for no more options
233
+ *
234
+ */
235
+int getopt_long ( int argc, char * const argv[], const char *optstring,
236
+		  const struct option *longopts, int *longindex ) {
237
+	const char *opttext = argv[optind];
238
+	const struct option *longopt;
239
+	int shortopt;
240
+	enum getopt_argument_requirement has_arg;
241
+	int option;
242
+
243
+	/* Check for end of options */
244
+	if ( *(opttext++) != '-' )
245
+		return -1;
246
+
247
+	/* Check for long options */
248
+	if ( *(opttext++) == '-' ) {
249
+		for ( longopt = longopts ; longopt->name ; longopt++ ) {
250
+			if ( ! match_long_option ( argc, argv, opttext,
251
+						   longopt, &option ) )
252
+				continue;
253
+			if ( longindex )
254
+				*longindex = ( longopt - longopts );
255
+			return option;
256
+		}
257
+		optopt = '?';
258
+		printf ( "Unrecognised option \"--%s\"\n", opttext );
259
+		return '?';
260
+	}
261
+
262
+	/* Check for short options */
263
+	if ( nextchar < 1 )
264
+		nextchar = 1;
265
+	opttext = ( argv[optind] + nextchar );
266
+	while ( ( shortopt = *(optstring++) ) ) {
267
+		has_arg = no_argument;
268
+		while ( *optstring == ':' ) {
269
+			has_arg++;
270
+			optstring++;
271
+		}
272
+		if ( match_short_option ( argc, argv, opttext, shortopt,
273
+					  has_arg, &option ) ) {
274
+			return option;
275
+		}
276
+	}
277
+	optopt = *opttext;
278
+	printf ( "Unrecognised option \"-%c\"\n", optopt );
279
+	return '?';
280
+}

+ 75
- 0
src/include/getopt.h View File

@@ -0,0 +1,75 @@
1
+#ifndef _GETOPT_H
2
+#define _GETOPT_H
3
+
4
+/** @file
5
+ *
6
+ * Parse command-line options
7
+ *
8
+ */
9
+
10
+#include <stddef.h>
11
+
12
+enum getopt_argument_requirement {
13
+	/** Option does not take an argument */
14
+	no_argument = 0,
15
+	/** Option requires an argument */
16
+	required_argument = 1,
17
+	/** Option may have an argument */
18
+	optional_argument = 2,
19
+};
20
+
21
+/** A long option, as used for getopt_long() */
22
+struct option {
23
+	/** Long name of this option */
24
+	const char *name;
25
+	/** Option takes an argument
26
+	 *
27
+	 * Must be one of @c no_argument, @c required_argument, or @c
28
+	 * optional_argument.
29
+	 */
30
+	int has_arg;
31
+	/** Location into which to store @c val, or NULL.
32
+	 *
33
+	 * See the description for @c val for more details.
34
+	 */
35
+	int *flag;
36
+	/** Value to return
37
+	 *
38
+	 * If @c flag is NULL, then this is the value that will be
39
+	 * returned by getopt_long() when this option is found, and
40
+	 * should therefore be set to the equivalent short option
41
+	 * character.
42
+	 *
43
+	 * If @c flag is non-NULL, then this value will be written to
44
+	 * the location pointed to by @flag, and getopt_long() will
45
+	 * return 0.
46
+	 */
47
+	int val;
48
+};
49
+
50
+extern char *optarg;
51
+extern int optind;
52
+extern int optopt;
53
+
54
+extern int getopt_long ( int argc, char * const argv[], const char *optstring,
55
+			 const struct option *longopts, int *longindex );
56
+
57
+/**
58
+ * Parse command-line options
59
+ *
60
+ * @v argv		Argument count
61
+ * @v argv		Argument list
62
+ * @v optstring		Option specification string
63
+ * @ret option		Option found, or -1 for no more options
64
+ *
65
+ * See getopt_long() for full details.
66
+ */
67
+static inline int getopt ( int argc, char * const argv[],
68
+			   const char *optstring ) {
69
+	static const struct option no_options[] = {
70
+		{ NULL, 0, NULL, 0 }
71
+	};
72
+	return getopt_long ( argc, argv, optstring, no_options, NULL );
73
+}
74
+
75
+#endif /* _GETOPT_H */

Loading…
Cancel
Save