Applied a modified version of holger's regparm patches.
[people/adir/gpxe.git] / src / core / getopt.c
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 <stdio.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;
45
46 /**
47  * Current option character index
48  *
49  * This is an index into the current element of argv[].
50  */
51 int nextchar;
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  * Get option argument from argv[] array
63  *
64  * @v argc              Argument count
65  * @v argv              Argument list
66  * @ret argument        Option argument, or NULL
67  *
68  * Grab the next element of argv[], if it exists and is not an option.
69  */
70 static const char * get_argv_argument ( int argc, char * const argv[] ) {
71         char *arg;
72
73         /* Don't overrun argv[] */
74         if ( optind >= argc )
75                 return NULL;
76         arg = argv[optind];
77
78         /* If next argv element is an option, then it's not usable as
79          * an argument.
80          */
81         if ( *arg == '-' )
82                 return NULL;
83
84         /** Consume this argv element, and return it */
85         optind++;
86         return arg;
87 }
88
89 /**
90  * Match long option
91  *
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
98  */
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;
104
105         /* Compare option name */
106         optlen = strlen ( longopt->name );
107         if ( strncmp ( opttext, longopt->name, optlen ) != 0 )
108                 return 0;
109
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;
116         }
117
118         /* Consume this argv element */
119         optind++;
120
121         /* If we want an argument but don't have one yet, try to grab
122          * the next argv element
123          */
124         if ( ( longopt->has_arg != no_argument ) && ( ! argument ) )
125                 argument = get_argv_argument ( argc, argv );
126
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;
133         }
134
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;
140         }
141
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;
149         }
150         return 1;
151 }
152
153 /**
154  * Match short option
155  *
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
162  */
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;
168
169         /* Compare option character */
170         if ( *opttext != shortopt )
171                 return 0;
172
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;
182                 }
183         } else {
184                 /* Reached end of argv element */
185                 optind++;
186                 nextchar = 0;
187         }
188
189         /* If we want an argument but don't have one yet, try to grab
190          * the next argv element
191          */
192         if ( ( has_arg != no_argument ) && ( ! argument ) )
193                 argument = get_argv_argument ( argc, argv );
194
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;
200         }
201
202         /* Store values and return success */
203         optarg = ( char * ) argument;
204         *option = shortopt;
205         return 1;
206 }
207
208 /**
209  * Parse command-line options
210  *
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
217  *
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().
221  */
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;
229
230         /* Check for end of argv array */
231         if ( optind >= argc )
232                 return -1;
233
234         /* Check for end of options */
235         if ( *(opttext++) != '-' )
236                 return -1;
237
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;
247                 }
248                 optopt = '?';
249                 printf ( "Unrecognised option \"--%s\"\n", opttext );
250                 return '?';
251         }
252
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++;
262                 }
263                 if ( match_short_option ( argc, argv, opttext, shortopt,
264                                           has_arg, &option ) ) {
265                         return option;
266                 }
267         }
268         optopt = *opttext;
269         printf ( "Unrecognised option \"-%c\"\n", optopt );
270         return '?';
271 }