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