1 /** @file getopt_long.c
2 ** @brief getopt_long - Definition
3 ** @author Andrea Vedaldi
4 **/
5
6 /*
7 Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
8 All rights reserved.
9
10 This file is part of the VLFeat library and is made available under
11 the terms of the BSD license (see the COPYING file).
12 */
13
14 /**
15 @file getopt_long.h
16 @brief getopt_long
17 @author Andrea Vedaldi
18
19 This is a drop-in replacament of GNU getopt_long meant to be used
20 on platforms that do not support such functionality.
21 **/
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26
27 #include "generic.h"
28 #include "getopt_long.h"
29
30 int opterr = 1 ;
31 int optind = 1 ;
32 int optopt ;
33 char * optarg ;
34 int optreset ;
35
36 #define BADCH '?'
37 #define BADARG ':'
38 #define EEND -1
39 #define EMSG ""
40
41 /** @brief Parse long options (BSD style)
42 ** @param argc number of arguments.
43 ** @param argv pointer to the vector of arguments.
44 ** @param optstring list of abbreviated options
45 ** @param longopts list of long options.
46 ** @param longindex index of current option in @a longopts.
47 ** @return the code of the next option.
48 **
49 ** This function extract long and short options from the argument
50 ** list @a argv of @a argc entries.
51 **
52 ** A short options sequence is introduced by a single dash character
53 ** @c -. Each short option is described by a single character in the
54 ** string @a optstring, possibly followed by a @c : character to
55 ** denote a (mandatory) argument of the short option. A short option
56 ** with an argument cannot appear in the middle of a short option
57 ** sequence, but only at the end.
58 **
59 ** A long option is introduced by a double dash @c --. Each long
60 ** option is described by an instance of the ::option structure in
61 ** the @a longopts table (the last entry must be filled with zeroes
62 ** to denote the end).
63 **
64 ** Illegal options and missing arguments cause the function to skip
65 ** the option and return '?'. If ::opterr is @c true (default), the
66 ** function prints an error message to @a stderr. Finally, if @a
67 ** optstring has a leading @c :, then error messages are suppressed
68 ** and a missing argument causes @a : to be returned.
69 **
70 ** @remark The function is currently <em>not</em> thread safe.
71 **/
72
73 VL_EXPORT int
getopt_long(int argc,char * const argv[],const char * optstring,const struct option * longopts,int * longindex)74 getopt_long(int argc, char *const argv[],
75 const char *optstring,
76 const struct option * longopts,
77 int *longindex)
78 {
79 static char *place = EMSG; /* option letter processing */
80 static int optbegin = 0 ;
81 static int optend = 0 ;
82 char *oli; /* option letter list index */
83 int has_colon = 0 ;
84 int ret_val = 0 ;
85
86 /*
87 A semicolon at the beginning of optstring has a special meaning.
88 If we find one, we annote and remove it.
89 */
90 has_colon = optstring && optstring[0] == ':' ;
91 if (has_colon) ++ optstring ;
92
93 /*
94 Here we are either processing a short option sequence or
95 we start processing a new option. This is indicated by optreset.
96 */
97
98 if (optreset || *place == '\0') {
99
100 /* ---------------------------------------------------------------
101 * Look for next short/long option
102 * ------------------------------------------------------------ */
103 optreset = 0 ;
104
105 /* no more arguments ? */
106 if (optind >= argc) {
107 place = EMSG ;
108 return -1 ;
109 }
110
111 /* next argument that may hold an option */
112 optbegin = optind ;
113
114 /* ---------------------------------------------------------------
115 * Look for an option to parse
116 * ------------------------------------------------------------ */
117
118 parse_option_at_optbegin :
119
120 /* place points to the candidate option */
121 place = argv [optbegin] ;
122
123 /* an option is introduced by '-' */
124 if (place [0] != '-') {
125 /* this argument is not an option: try next argument */
126 ++ optbegin ;
127 if (optbegin >= argc) {
128 /* no more arguments to look for options */
129 place = EMSG ;
130 return -1 ;
131 }
132 goto parse_option_at_optbegin ;
133 }
134
135 /* consume leading `-' */
136 ++ place ;
137
138 /* assume the option is composed of one argument only */
139 optend = optbegin + 1 ;
140
141 /* assume no argument */
142 optarg = 0 ;
143
144 /* ---------------------------------------------------------------
145 * option `--'
146 * ------------------------------------------------------------ */
147
148 /* this special option (void long option) ends the option processing */
149 if (place[0] &&
150 place[0] == '-' &&
151 place[1] == '\0') {
152
153 optind = optend ;
154 place = EMSG ;
155 ret_val = -1 ;
156 goto done_option ;
157 }
158
159 /* ---------------------------------------------------------------
160 * long option
161 * ------------------------------------------------------------ */
162
163 if (place[0] &&
164 place[0] == '-' &&
165 place[1] ) {
166
167 size_t namelen ;
168 int i ;
169
170 /* consume second `-' */
171 ++ place ;
172
173 /* count characters before `=' */
174 namelen = strcspn(place, "=") ;
175
176 /* scan longopts for this option */
177 for (i = 0 ; longopts[i].name != NULL ; ++ i) {
178
179 if (strlen ( longopts[i].name) == namelen &&
180 strncmp (place, longopts[i].name, namelen) == 0 ) {
181
182 /* save back long option index */
183 if (longindex) *longindex = i ;
184
185 /* process long option argument */
186 if (longopts[i].has_arg == required_argument ||
187 longopts[i].has_arg == optional_argument) {
188
189 /* --option=value style */
190 if (place[namelen] == '=') {
191 optarg = place + namelen + 1 ;
192 }
193
194 /* --option value style (only required_argument) */
195 else if (longopts[i].has_arg == required_argument) {
196 /* missing argument ? */
197 if (optbegin >= argc - 1) {
198 if (! has_colon && opterr)
199 fprintf(stderr,
200 "%s: option requires an argument -- %s\n",
201 argv[0], place);
202 place = EMSG ;
203 ret_val = has_colon ? BADARG : BADCH ;
204 goto done_option ;
205 }
206 optarg = argv [optend] ;
207 ++ optend ;
208 }
209 }
210
211 /* determine return value */
212 if (longopts[i].flag == NULL) {
213 ret_val = longopts[i].val ;
214 }
215 else {
216 *longopts[i].flag = longopts[i].val;
217 ret_val = 0 ;
218 }
219
220 /* mark sequence closed */
221 place = EMSG ;
222 goto done_option ;
223 } /* if match */
224
225 } /* scan longoptions */
226
227 /* no matching option found */
228 if (! has_colon && opterr)
229 fprintf(stderr,
230 "%s: illegal option -- %s\n", argv[0], place) ;
231 place = EMSG ;
232 ret_val = BADCH ;
233 goto done_option ;
234 }
235 } /* end new option */
236
237 /* -----------------------------------------------------------------
238 * Finish short option sequence
239 * -------------------------------------------------------------- */
240 optopt = (int) *place++ ;
241
242 /* search charcater in option list */
243 oli = strchr(optstring, optopt);
244
245 /* short option not found */
246 if (!oli) {
247
248 if (! has_colon && opterr)
249 fprintf(stderr,
250 "%s: illegal option -- %c\n",
251 argv[0], optopt);
252
253 if (*place) {
254 /* more short options in the list */
255 return BADCH ;
256 }
257
258 else {
259 /* error occured as last option in the list */
260 place = EMSG ;
261 ret_val = BADCH ;
262 goto done_option ;
263 }
264 } /* end short option not found */
265
266 if (oli[1] != ':') {
267 /* short option with no argument */
268
269 if (*place) {
270 /* more short options in the list */
271 return optopt ;
272 }
273 else {
274 /* last option in the list */
275 place = EMSG ;
276 ret_val = optopt ;
277 goto done_option ;
278 }
279
280 } else {
281 /* short option with argument */
282
283 /* -ovalue style */
284 if (*place) {
285 optarg = place ;
286 place = EMSG ;
287 ret_val = optopt ;
288 goto done_option ;
289 }
290 /* -o value style: missing argument */
291 else if (optbegin >= argc - 1) {
292 if (! has_colon && opterr)
293 fprintf(stderr,
294 "%s: option requires an argument -- %c\n",
295 argv[0], optopt);
296 place = EMSG ;
297 ret_val = has_colon ? BADARG : BADCH ;
298 goto done_option ;
299 }
300
301 /* -o value style: process argument */
302 optarg = argv [optend] ;
303 ++ optend ;
304 place = EMSG ;
305 ret_val = optopt ;
306 goto done_option ;
307 } /* short with argument */
308
309 done_option :
310 {
311 int pos = optend - optbegin ; /* n of circular shifts */
312 int c = pos ;
313
314 while (c --) {
315 int i ;
316 char *tmp = argv [optend - 1] ;
317 for (i = optend - 1 ; i > optind ; -- i) {
318 ((char**)argv) [i] = argv [i-1] ;
319 }
320 ((char**)argv) [optind] = tmp ;
321 }
322 optind += pos ;
323 }
324
325 return ret_val ;
326 }
327