[sundance] Add reset completion check
[gpxe.git] / src / core / getopt.c
blob6de412bb8a3424a68c370cd1440344fe9347c8eb
1 /*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
19 #include <stdint.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <getopt.h>
24 /** @file
26 * Parse command-line options
30 /**
31 * Option argument
33 * This will point to the argument for the most recently returned
34 * option, if applicable.
36 char *optarg;
38 /**
39 * Current option index
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.
44 int optind;
46 /**
47 * Current option character index
49 * This is an index into the current element of argv[].
51 int nextchar;
53 /**
54 * Unrecognised option
56 * When an unrecognised option is encountered, the actual option
57 * character is stored in @c optopt.
59 int optopt;
61 /**
62 * Get option argument from argv[] array
64 * @v argc Argument count
65 * @v argv Argument list
66 * @ret argument Option argument, or NULL
68 * Grab the next element of argv[], if it exists and is not an option.
70 static const char * get_argv_argument ( int argc, char * const argv[] ) {
71 char *arg;
73 /* Don't overrun argv[] */
74 if ( optind >= argc )
75 return NULL;
76 arg = argv[optind];
78 /* If next argv element is an option, then it's not usable as
79 * an argument.
81 if ( *arg == '-' )
82 return NULL;
84 /** Consume this argv element, and return it */
85 optind++;
86 return arg;
89 /**
90 * Match long option
92 * @v argc Argument count
93 * @v argv Argument list
94 * @v opttext Option text within current argv[] element
95 * @v longopt Long option specification
96 * @ret option Option to return from getopt()
97 * @ret matched Found a match for this long option
99 static int match_long_option ( int argc, char * const argv[],
100 const char *opttext,
101 const struct option *longopt, int *option ) {
102 size_t optlen;
103 const char *argument = NULL;
105 /* Compare option name */
106 optlen = strlen ( longopt->name );
107 if ( strncmp ( opttext, longopt->name, optlen ) != 0 )
108 return 0;
110 /* Check for inline argument */
111 if ( opttext[optlen] == '=' ) {
112 argument = &opttext[ optlen + 1 ];
113 } else if ( opttext[optlen] ) {
114 /* Long option with trailing garbage - no match */
115 return 0;
118 /* Consume this argv element */
119 optind++;
121 /* If we want an argument but don't have one yet, try to grab
122 * the next argv element
124 if ( ( longopt->has_arg != no_argument ) && ( ! argument ) )
125 argument = get_argv_argument ( argc, argv );
127 /* If we need an argument but don't have one, sulk */
128 if ( ( longopt->has_arg == required_argument ) && ( ! argument ) ) {
129 printf ( "Option \"%s\" requires an argument\n",
130 longopt->name );
131 *option = ':';
132 return 1;
135 /* If we have an argument where we shouldn't have one, sulk */
136 if ( ( longopt->has_arg == no_argument ) && argument ) {
137 printf ( "Option \"%s\" takes no argument\n", longopt->name );
138 *option = ':';
139 return 1;
142 /* Store values and return success */
143 optarg = ( char * ) argument;
144 if ( longopt->flag ) {
145 *(longopt->flag) = longopt->val;
146 *option = 0;
147 } else {
148 *option = longopt->val;
150 return 1;
154 * Match short option
156 * @v argc Argument count
157 * @v argv Argument list
158 * @v opttext Option text within current argv[] element
159 * @v shortopt Option character from option specification
160 * @ret option Option to return from getopt()
161 * @ret matched Found a match for this short option
163 static int match_short_option ( int argc, char * const argv[],
164 const char *opttext, int shortopt,
165 enum getopt_argument_requirement has_arg,
166 int *option ) {
167 const char *argument = NULL;
169 /* Compare option character */
170 if ( *opttext != shortopt )
171 return 0;
173 /* Consume option character */
174 opttext++;
175 nextchar++;
176 if ( *opttext ) {
177 if ( has_arg != no_argument ) {
178 /* Consume remainder of element as inline argument */
179 argument = opttext;
180 optind++;
181 nextchar = 0;
183 } else {
184 /* Reached end of argv element */
185 optind++;
186 nextchar = 0;
189 /* If we want an argument but don't have one yet, try to grab
190 * the next argv element
192 if ( ( has_arg != no_argument ) && ( ! argument ) )
193 argument = get_argv_argument ( argc, argv );
195 /* If we need an argument but don't have one, sulk */
196 if ( ( has_arg == required_argument ) && ( ! argument ) ) {
197 printf ( "Option \"%c\" requires an argument\n", shortopt );
198 *option = ':';
199 return 1;
202 /* Store values and return success */
203 optarg = ( char * ) argument;
204 *option = shortopt;
205 return 1;
209 * Parse command-line options
211 * @v argc Argument count
212 * @v argv Argument list
213 * @v optstring Option specification string
214 * @v longopts Long option specification table
215 * @ret longindex Index of long option (or NULL)
216 * @ret option Option found, or -1 for no more options
218 * Note that the caller must arrange for reset_getopt() to be called
219 * before each set of calls to getopt_long(). In Etherboot, this is
220 * done automatically by execv().
222 int getopt_long ( int argc, char * const argv[], const char *optstring,
223 const struct option *longopts, int *longindex ) {
224 const char *opttext = argv[optind];
225 const struct option *longopt;
226 int shortopt;
227 enum getopt_argument_requirement has_arg;
228 int option;
230 /* Check for end of argv array */
231 if ( optind >= argc )
232 return -1;
234 /* Check for end of options */
235 if ( *(opttext++) != '-' )
236 return -1;
238 /* Check for long options */
239 if ( *(opttext++) == '-' ) {
240 for ( longopt = longopts ; longopt->name ; longopt++ ) {
241 if ( ! match_long_option ( argc, argv, opttext,
242 longopt, &option ) )
243 continue;
244 if ( longindex )
245 *longindex = ( longopt - longopts );
246 return option;
248 optopt = '?';
249 printf ( "Unrecognised option \"--%s\"\n", opttext );
250 return '?';
253 /* Check for short options */
254 if ( nextchar < 1 )
255 nextchar = 1;
256 opttext = ( argv[optind] + nextchar );
257 while ( ( shortopt = *(optstring++) ) ) {
258 has_arg = no_argument;
259 while ( *optstring == ':' ) {
260 has_arg++;
261 optstring++;
263 if ( match_short_option ( argc, argv, opttext, shortopt,
264 has_arg, &option ) ) {
265 return option;
268 optopt = *opttext;
269 printf ( "Unrecognised option \"-%c\"\n", optopt );
270 return '?';