1 /*
2 Copyright (C) 1997 Gregory Pietsch
3
4 [These files] are hereby placed in the public domain without restrictions. Just
5 give the author credit, don't claim you wrote it or prevent anyone else from
6 using it.
7 */
8
9 /****************************************************************************
10
11 getopt.c - Read command line options
12
13 AUTHOR: Gregory Pietsch
14 CREATED Fri Jan 10 21:13:05 1997
15
16 DESCRIPTION:
17
18 The getopt() function parses the command line arguments. Its arguments argc
19 and argv are the argument count and array as passed to the main() function
20 on program invocation. The argument optstring is a list of available option
21 characters. If such a character is followed by a colon (`:'), the option
22 takes an argument, which is placed in optarg. If such a character is
23 followed by two colons, the option takes an optional argument, which is
24 placed in optarg. If the option does not take an argument, optarg is NULL.
25
26 The external variable optind is the index of the next array element of argv
27 to be processed; it communicates from one call to the next which element to
28 process.
29
30 The getopt_long() function works like getopt() except that it also accepts
31 long options started by two dashes `--'. If these take values, it is either
32 in the form
33
34 --arg=value
35
36 or
37
38 --arg value
39
40 It takes the additional arguments longopts which is a pointer to the first
41 element of an array of type GETOPT_LONG_OPTION_T. The last element of the
42 array has to be filled with NULL for the name field.
43
44 The longind pointer points to the index of the current long option relative
45 to longopts if it is non-NULL.
46
47 The getopt() function returns the option character if the option was found
48 successfully, `:' if there was a missing parameter for one of the options,
49 `?' for an unknown option character, and EOF for the end of the option list.
50
51 The getopt_long() function's return value is described in the header file.
52
53 The function getopt_long_only() is identical to getopt_long(), except that a
54 plus sign `+' can introduce long options as well as `--'.
55
56 The following describes how to deal with options that follow non-option
57 argv-elements.
58
59 If the caller did not specify anything, the default is REQUIRE_ORDER if the
60 environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
61
62 REQUIRE_ORDER means don't recognize them as options; stop option processing
63 when the first non-option is seen. This is what Unix does. This mode of
64 operation is selected by either setting the environment variable
65 POSIXLY_CORRECT, or using `+' as the first character of the optstring
66 parameter.
67
68 PERMUTE is the default. We permute the contents of ARGV as we scan, so that
69 eventually all the non-options are at the end. This allows options to be
70 given in any order, even with programs that were not written to expect this.
71
72 RETURN_IN_ORDER is an option available to programs that were written to
73 expect options and other argv-elements in any order and that care about the
74 ordering of the two. We describe each non-option argv-element as if it were
75 the argument of an option with character code 1. Using `-' as the first
76 character of the optstring parameter selects this mode of operation.
77
78 The special argument `--' forces an end of option-scanning regardless of the
79 value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause
80 getopt() and friends to return EOF with optind != argc.
81
82 COPYRIGHT NOTICE AND DISCLAIMER:
83
84 Copyright (C) 1997 Gregory Pietsch
85
86 This file and the accompanying getopt.h header file are hereby placed in the
87 public domain without restrictions. Just give the author credit, don't
88 claim you wrote it or prevent anyone else from using it.
89
90 Gregory Pietsch's current e-mail address:
91 gpietsch@comcast.net
92 ****************************************************************************/
93
94 /* include files */
95 #include <stdio.h>
96 #include <stdlib.h>
97 #include <string.h>
98 #ifndef GETOPT_H
99 #include "getopt.h"
100 #endif
101
102 namespace crashpad {
103
104 /* macros */
105
106 /* types */
107 typedef enum GETOPT_ORDERING_T
108 {
109 PERMUTE,
110 RETURN_IN_ORDER,
111 REQUIRE_ORDER
112 } GETOPT_ORDERING_T;
113
114 /* globally-defined variables */
115 char *optarg = NULL;
116 int optind = 0;
117 int opterr = 1;
118 int optopt = '?';
119
120 /* functions */
121
122 /* reverse_argv_elements: reverses num elements starting at argv */
123 static void
reverse_argv_elements(char ** argv,int num)124 reverse_argv_elements (char **argv, int num)
125 {
126 int i;
127 char *tmp;
128
129 for (i = 0; i < (num >> 1); i++)
130 {
131 tmp = argv[i];
132 argv[i] = argv[num - i - 1];
133 argv[num - i - 1] = tmp;
134 }
135 }
136
137 /* permute: swap two blocks of argv-elements given their lengths */
138 static void
permute(char ** argv,int len1,int len2)139 permute (char **argv, int len1, int len2)
140 {
141 reverse_argv_elements (argv, len1);
142 reverse_argv_elements (argv, len1 + len2);
143 reverse_argv_elements (argv, len2);
144 }
145
146 /* is_option: is this argv-element an option or the end of the option list? */
147 static int
is_option(char * argv_element,int only)148 is_option (char *argv_element, int only)
149 {
150 return ((argv_element == NULL)
151 || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
152 }
153
154 /* getopt_internal: the function that does all the dirty work */
155 static int
getopt_internal(int argc,char ** argv,char * shortopts,GETOPT_LONG_OPTION_T * longopts,int * longind,int only)156 getopt_internal (int argc, char **argv, char *shortopts,
157 GETOPT_LONG_OPTION_T * longopts, int *longind, int only)
158 {
159 GETOPT_ORDERING_T ordering = PERMUTE;
160 static size_t optwhere = 0;
161 size_t permute_from = 0;
162 int num_nonopts = 0;
163 int optindex = 0;
164 size_t match_chars = 0;
165 char *possible_arg = NULL;
166 int longopt_match = -1;
167 int has_arg = -1;
168 char *cp = NULL;
169 int arg_next = 0;
170
171 /* first, deal with silly parameters and easy stuff */
172 if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))
173 return (optopt = '?');
174 if (optind >= argc || argv[optind] == NULL)
175 return EOF;
176 if (strcmp (argv[optind], "--") == 0)
177 {
178 optind++;
179 return EOF;
180 }
181 /* if this is our first time through */
182 if (optind == 0) {
183 optind = 1;
184 optwhere = 1;
185 }
186
187 /* define ordering */
188 if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))
189 {
190 ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
191 shortopts++;
192 }
193 else
194 ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
195
196 /*
197 * based on ordering, find our next option, if we're at the beginning of
198 * one
199 */
200 if (optwhere == 1)
201 {
202 switch (ordering)
203 {
204 case PERMUTE:
205 permute_from = optind;
206 num_nonopts = 0;
207 while (!is_option (argv[optind], only))
208 {
209 optind++;
210 num_nonopts++;
211 }
212 if (argv[optind] == NULL)
213 {
214 /* no more options */
215 optind = (int)permute_from;
216 return EOF;
217 }
218 else if (strcmp (argv[optind], "--") == 0)
219 {
220 /* no more options, but have to get `--' out of the way */
221 permute (argv + permute_from, num_nonopts, 1);
222 optind = (int)(permute_from + 1);
223 return EOF;
224 }
225 break;
226 case RETURN_IN_ORDER:
227 if (!is_option (argv[optind], only))
228 {
229 optarg = argv[optind++];
230 return (optopt = 1);
231 }
232 break;
233 case REQUIRE_ORDER:
234 if (!is_option (argv[optind], only))
235 return EOF;
236 break;
237 }
238 }
239 /* we've got an option, so parse it */
240
241 /* first, is it a long option? */
242 if (longopts != NULL
243 && (strncmp (argv[optind], "--", 2) == 0
244 || (only && argv[optind][0] == '+')) && optwhere == 1)
245 {
246 /* handle long options */
247 if (strncmp (argv[optind], "--", 2) == 0)
248 optwhere = 2;
249 longopt_match = -1;
250 possible_arg = strchr (argv[optind] + optwhere, '=');
251 if (possible_arg == NULL)
252 {
253 /* no =, so next argv might be arg */
254 match_chars = strlen (argv[optind]);
255 possible_arg = argv[optind] + match_chars;
256 match_chars = match_chars - optwhere;
257 }
258 else
259 match_chars = (possible_arg - argv[optind]) - optwhere;
260 for (optindex = 0; longopts[optindex].name != NULL; optindex++)
261 {
262 if (strncmp (argv[optind] + optwhere,
263 longopts[optindex].name, match_chars) == 0)
264 {
265 /* do we have an exact match? */
266 if (match_chars == strlen (longopts[optindex].name))
267 {
268 longopt_match = optindex;
269 break;
270 }
271 /* do any characters match? */
272 else
273 {
274 if (longopt_match < 0)
275 longopt_match = optindex;
276 else
277 {
278 /* we have ambiguous options */
279 if (opterr)
280 fprintf (stderr, "%s: option `%s' is ambiguous "
281 "(could be `--%s' or `--%s')\n",
282 argv[0],
283 argv[optind],
284 longopts[longopt_match].name,
285 longopts[optindex].name);
286 return (optopt = '?');
287 }
288 }
289 }
290 }
291 if (longopt_match >= 0)
292 has_arg = longopts[longopt_match].has_arg;
293 }
294 /* if we didn't find a long option, is it a short option? */
295 if (longopt_match < 0 && shortopts != NULL)
296 {
297 cp = strchr (shortopts, argv[optind][optwhere]);
298 if (cp == NULL)
299 {
300 /* couldn't find option in shortopts */
301 if (opterr)
302 fprintf (stderr,
303 "%s: invalid option -- `-%c'\n",
304 argv[0], argv[optind][optwhere]);
305 optwhere++;
306 if (argv[optind][optwhere] == '\0')
307 {
308 optind++;
309 optwhere = 1;
310 }
311 return (optopt = '?');
312 }
313 has_arg = ((cp[1] == ':') ? ((cp[2] == ':') ? optional_argument
314 : required_argument)
315 : no_argument);
316 possible_arg = argv[optind] + optwhere + 1;
317 optopt = *cp;
318 }
319 /* get argument and reset optwhere */
320 arg_next = 0;
321 switch (has_arg)
322 {
323 case optional_argument:
324 if (*possible_arg == '=')
325 possible_arg++;
326 if (*possible_arg != '\0')
327 {
328 optarg = possible_arg;
329 optwhere = 1;
330 }
331 else
332 optarg = NULL;
333 break;
334 case required_argument:
335 if (*possible_arg == '=')
336 possible_arg++;
337 if (*possible_arg != '\0')
338 {
339 optarg = possible_arg;
340 optwhere = 1;
341 }
342 else if (optind + 1 >= argc)
343 {
344 if (opterr)
345 {
346 fprintf (stderr, "%s: argument required for option `", argv[0]);
347 if (longopt_match >= 0)
348 fprintf (stderr, "--%s'\n", longopts[longopt_match].name);
349 else
350 fprintf (stderr, "-%c'\n", *cp);
351 }
352 optind++;
353 return (optopt = ':');
354 }
355 else
356 {
357 optarg = argv[optind + 1];
358 arg_next = 1;
359 optwhere = 1;
360 }
361 break;
362 case no_argument:
363 if (longopt_match < 0)
364 {
365 optwhere++;
366 if (argv[optind][optwhere] == '\0')
367 optwhere = 1;
368 }
369 else
370 optwhere = 1;
371 optarg = NULL;
372 break;
373 }
374
375 /* do we have to permute or otherwise modify optind? */
376 if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)
377 {
378 permute (argv + permute_from, num_nonopts, 1 + arg_next);
379 optind = (int)(permute_from + 1 + arg_next);
380 }
381 else if (optwhere == 1)
382 optind = optind + 1 + arg_next;
383
384 /* finally return */
385 if (longopt_match >= 0)
386 {
387 if (longind != NULL)
388 *longind = longopt_match;
389 if (longopts[longopt_match].flag != NULL)
390 {
391 *(longopts[longopt_match].flag) = longopts[longopt_match].val;
392 return 0;
393 }
394 else
395 return longopts[longopt_match].val;
396 }
397 else
398 return optopt;
399 }
400
401 int
getopt(int argc,char ** argv,char * optstring)402 getopt (int argc, char **argv, char *optstring)
403 {
404 return getopt_internal (argc, argv, optstring, NULL, NULL, 0);
405 }
406
407 int
getopt_long(int argc,char ** argv,const char * shortopts,const GETOPT_LONG_OPTION_T * longopts,int * longind)408 getopt_long (int argc, char **argv, const char *shortopts,
409 const GETOPT_LONG_OPTION_T * longopts, int *longind)
410 {
411 return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0);
412 }
413
414 int
getopt_long_only(int argc,char ** argv,const char * shortopts,const GETOPT_LONG_OPTION_T * longopts,int * longind)415 getopt_long_only (int argc, char **argv, const char *shortopts,
416 const GETOPT_LONG_OPTION_T * longopts, int *longind)
417 {
418 return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1);
419 }
420
421 } // namespace crashpad
422
423 /* end of file GETOPT.C */
424