1 /****************************************************************************
2
3 getopt.c - Read command line options
4
5 AUTHOR: Gregory Pietsch
6 CREATED Fri Jan 10 21:13:05 1997
7
8 DESCRIPTION:
9
10 The getopt() function parses the command line arguments. Its arguments argc
11 and argv are the argument count and array as passed to the main() function
12 on program invocation. The argument optstring is a list of available option
13 characters. If such a character is followed by a colon (`:'), the option
14 takes an argument, which is placed in optarg. If such a character is
15 followed by two colons, the option takes an optional argument, which is
16 placed in optarg. If the option does not take an argument, optarg is NULL.
17
18 The external variable optind is the index of the next array element of argv
19 to be processed; it communicates from one call to the next which element to
20 process.
21
22 The getopt_long() function works like getopt() except that it also accepts
23 long options started by two dashes `--'. If these take values, it is either
24 in the form
25
26 --arg=value
27
28 or
29
30 --arg value
31
32 It takes the additional arguments longopts which is a pointer to the first
33 element of an array of type struct option. The last element of the array
34 has to be filled with NULL for the name field.
35
36 The longind pointer points to the index of the current long option relative
37 to longopts if it is non-NULL.
38
39 The getopt() function returns the option character if the option was found
40 successfully, `:' if there was a missing parameter for one of the options,
41 `?' for an unknown option character, and EOF for the end of the option list.
42
43 The getopt_long() function's return value is described in the header file.
44
45 The function getopt_long_only() is identical to getopt_long(), except that a
46 plus sign `+' can introduce long options as well as `--'.
47
48 The following describes how to deal with options that follow non-option
49 argv-elements.
50
51 If the caller did not specify anything, the default is REQUIRE_ORDER if the
52 environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
53
54 REQUIRE_ORDER means don't recognize them as options; stop option processing
55 when the first non-option is seen. This is what Unix does. This mode of
56 operation is selected by either setting the environment variable
57 POSIXLY_CORRECT, or using `+' as the first character of the optstring
58 parameter.
59
60 PERMUTE is the default. We permute the contents of ARGV as we scan, so that
61 eventually all the non-options are at the end. This allows options to be
62 given in any order, even with programs that were not written to expect this.
63
64 RETURN_IN_ORDER is an option available to programs that were written to
65 expect options and other argv-elements in any order and that care about the
66 ordering of the two. We describe each non-option argv-element as if it were
67 the argument of an option with character code 1. Using `-' as the first
68 character of the optstring parameter selects this mode of operation.
69
70 The special argument `--' forces an end of option-scanning regardless of the
71 value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause
72 getopt() and friends to return EOF with optind != argc.
73
74 2012-08-26: Tried to make the error handling more sus4-like. The functions
75 return a colon if getopt() and friends detect a missing argument and the
76 first character of shortopts/optstring starts with a colon (`:'). If getopt()
77 and friends detect a missing argument and shortopts/optstring does not start
78 with a colon, the function returns a question mark (`?'). If it was a missing
79 argument to a short option, optopt is set to the character in question. The
80 colon goes after the ordering character (`+' or `-').
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 #ifndef HAVE_GETOPT
95
96 /* include files */
97 #include <stdio.h>
98 #include <stdlib.h>
99 #include <string.h>
100 #define __need_getopt_newlib
101 #include <getopt.h>
102
103 /* macros */
104
105 /* types */
106 typedef enum GETOPT_ORDERING_T
107 {
108 PERMUTE,
109 RETURN_IN_ORDER,
110 REQUIRE_ORDER
111 } GETOPT_ORDERING_T;
112
113 /* globally-defined variables */
114 char *optarg = 0;
115 int optind = 0;
116 int opterr = 1;
117 int optopt = '?';
118
119 /* static variables */
120 static int optwhere = 0;
121 static int permute_from = 0;
122 static int num_nonopts = 0;
123
124 /* functions */
125
126 /* reverse_argv_elements: reverses num elements starting at argv */
127 static void
reverse_argv_elements(char ** argv,int num)128 reverse_argv_elements (char **argv, int num)
129 {
130 int i;
131 char *tmp;
132
133 for (i = 0; i < (num >> 1); i++)
134 {
135 tmp = argv[i];
136 argv[i] = argv[num - i - 1];
137 argv[num - i - 1] = tmp;
138 }
139 }
140
141 /* permute: swap two blocks of argv-elements given their lengths */
142 static void
permute(char * const argv[],int len1,int len2)143 permute (char *const argv[], int len1, int len2)
144 {
145 reverse_argv_elements ((char **) argv, len1);
146 reverse_argv_elements ((char **) argv, len1 + len2);
147 reverse_argv_elements ((char **) argv, len2);
148 }
149
150 /* is_option: is this argv-element an option or the end of the option list? */
151 static int
is_option(char * argv_element,int only)152 is_option (char *argv_element, int only)
153 {
154 return ((argv_element == 0)
155 || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
156 }
157
158 /* read_globals: read the values from the globals into a getopt_data
159 structure */
160 static void
read_globals(struct getopt_data * data)161 read_globals (struct getopt_data *data)
162 {
163 data->optarg = optarg;
164 data->optind = optind;
165 data->opterr = opterr;
166 data->optopt = optopt;
167 data->optwhere = optwhere;
168 data->permute_from = permute_from;
169 data->num_nonopts = num_nonopts;
170 }
171
172 /* write_globals: write the values into the globals from a getopt_data
173 structure */
174 static void
write_globals(struct getopt_data * data)175 write_globals (struct getopt_data *data)
176 {
177 optarg = data->optarg;
178 optind = data->optind;
179 opterr = data->opterr;
180 optopt = data->optopt;
181 optwhere = data->optwhere;
182 permute_from = data->permute_from;
183 num_nonopts = data->num_nonopts;
184 }
185
186 /* getopt_internal: the function that does all the dirty work
187 NOTE: to reduce the code and RAM footprint this function uses
188 fputs()/fputc() to do output to stderr instead of fprintf(). */
189 static int
getopt_internal(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind,int only,struct getopt_data * data)190 getopt_internal (int argc, char *const argv[], const char *shortopts,
191 const struct option *longopts, int *longind, int only,
192 struct getopt_data *data)
193 {
194 GETOPT_ORDERING_T ordering = PERMUTE;
195 int optindex = 0;
196 size_t match_chars = 0;
197 char *possible_arg = 0;
198 int longopt_match = -1;
199 int has_arg = -1;
200 char *cp = 0;
201 int arg_next = 0;
202 int initial_colon = 0;
203
204 /* if this is our first time through */
205 if (data->optind <= 0)
206 {
207 data->optind = 1;
208 data->optwhere = 1;
209 data->permute_from = 0;
210 data->num_nonopts = 0;
211 }
212
213 /* first, deal with silly parameters and easy stuff */
214 if (argc == 0 || argv == 0 || (shortopts == 0 && longopts == 0)
215 || data->optind >= argc || argv[data->optind] == 0)
216 return EOF;
217
218 if (strcmp (argv[data->optind], "--") == 0)
219 {
220 data->optind++;
221 return EOF;
222 }
223
224 /* define ordering */
225 if (shortopts != 0 && (*shortopts == '-' || *shortopts == '+'))
226 {
227 ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
228 shortopts++;
229 }
230 else
231 ordering = (getenv ("POSIXLY_CORRECT") != 0) ? REQUIRE_ORDER : PERMUTE;
232
233 /* check for initial colon in shortopts */
234 if (shortopts != 0 && *shortopts == ':')
235 {
236 ++shortopts;
237 initial_colon = 1;
238 }
239
240 /*
241 * based on ordering, find our next option, if we're at the beginning of
242 * one
243 */
244 if (data->optwhere == 1)
245 {
246 switch (ordering)
247 {
248 default: /* shouldn't happen */
249 case PERMUTE:
250 data->permute_from = data->optind;
251 data->num_nonopts = 0;
252 while (!is_option (argv[data->optind], only))
253 {
254 data->optind++;
255 data->num_nonopts++;
256 }
257 if (argv[data->optind] == 0)
258 {
259 /* no more options */
260 data->optind = data->permute_from;
261 return EOF;
262 }
263 else if (strcmp (argv[data->optind], "--") == 0)
264 {
265 /* no more options, but have to get `--' out of the way */
266 permute (argv + data->permute_from, data->num_nonopts, 1);
267 data->optind = data->permute_from + 1;
268 return EOF;
269 }
270 break;
271 case RETURN_IN_ORDER:
272 if (!is_option (argv[data->optind], only))
273 {
274 data->optarg = argv[data->optind++];
275 return (data->optopt = 1);
276 }
277 break;
278 case REQUIRE_ORDER:
279 if (!is_option (argv[data->optind], only))
280 return EOF;
281 break;
282 }
283 }
284 /* End of option list? */
285 if (argv[data->optind] == 0)
286 return EOF;
287
288 /* we've got an option, so parse it */
289
290 /* first, is it a long option? */
291 if (longopts != 0
292 && (memcmp (argv[data->optind], "--", 2) == 0
293 || (only && argv[data->optind][0] == '+')) && data->optwhere == 1)
294 {
295 /* handle long options */
296 if (memcmp (argv[data->optind], "--", 2) == 0)
297 data->optwhere = 2;
298 longopt_match = -1;
299 possible_arg = strchr (argv[data->optind] + data->optwhere, '=');
300 if (possible_arg == 0)
301 {
302 /* no =, so next argv might be arg */
303 match_chars = strlen (argv[data->optind]);
304 possible_arg = argv[data->optind] + match_chars;
305 match_chars = match_chars - data->optwhere;
306 }
307 else
308 match_chars = (possible_arg - argv[data->optind]) - data->optwhere;
309 for (optindex = 0; longopts[optindex].name != 0; ++optindex)
310 {
311 if (memcmp
312 (argv[data->optind] + data->optwhere, longopts[optindex].name,
313 match_chars) == 0)
314 {
315 /* do we have an exact match? */
316 if (match_chars == strlen (longopts[optindex].name))
317 {
318 longopt_match = optindex;
319 break;
320 }
321 /* do any characters match? */
322 else
323 {
324 if (longopt_match < 0)
325 longopt_match = optindex;
326 else
327 {
328 /* we have ambiguous options */
329 if (data->opterr)
330 {
331 fputs (argv[0], stderr);
332 fputs (": option `", stderr);
333 fputs (argv[data->optind], stderr);
334 fputs ("' is ambiguous (could be `--", stderr);
335 fputs (longopts[longopt_match].name, stderr);
336 fputs ("' or `--", stderr);
337 fputs (longopts[optindex].name, stderr);
338 fputs ("')\n", stderr);
339 }
340 return (data->optopt = '?');
341 }
342 }
343 }
344 }
345 if (longopt_match >= 0)
346 has_arg = longopts[longopt_match].has_arg;
347 }
348
349 /* if we didn't find a long option, is it a short option? */
350 if (longopt_match < 0 && shortopts != 0)
351 {
352 cp = strchr (shortopts, argv[data->optind][data->optwhere]);
353 if (cp == 0)
354 {
355 /* couldn't find option in shortopts */
356 if (data->opterr)
357 {
358 fputs (argv[0], stderr);
359 fputs (": invalid option -- `-", stderr);
360 fputc (argv[data->optind][data->optwhere], stderr);
361 fputs ("'\n", stderr);
362 }
363 data->optwhere++;
364 if (argv[data->optind][data->optwhere] == '\0')
365 {
366 data->optind++;
367 data->optwhere = 1;
368 }
369 return (data->optopt = '?');
370 }
371 has_arg = ((cp[1] == ':')
372 ? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG);
373 possible_arg = argv[data->optind] + data->optwhere + 1;
374 data->optopt = *cp;
375 }
376
377 /* get argument and reset data->optwhere */
378 arg_next = 0;
379 switch (has_arg)
380 {
381 case OPTIONAL_ARG:
382 if (*possible_arg == '=')
383 possible_arg++;
384 data->optarg = (*possible_arg != '\0') ? possible_arg : 0;
385 data->optwhere = 1;
386 break;
387 case REQUIRED_ARG:
388 if (*possible_arg == '=')
389 possible_arg++;
390 if (*possible_arg != '\0')
391 {
392 data->optarg = possible_arg;
393 data->optwhere = 1;
394 }
395 else if (data->optind + 1 >= argc)
396 {
397 if (data->opterr)
398 {
399 fputs (argv[0], stderr);
400 fputs (": argument required for option `-", stderr);
401 if (longopt_match >= 0)
402 {
403 fputc ('-', stderr);
404 fputs (longopts[longopt_match].name, stderr);
405 data->optopt = initial_colon ? ':' : '\?';
406 }
407 else
408 {
409 fputc (*cp, stderr);
410 data->optopt = *cp;
411 }
412 fputs ("'\n", stderr);
413 }
414 data->optind++;
415 return initial_colon ? ':' : '\?';
416 }
417 else
418 {
419 data->optarg = argv[data->optind + 1];
420 arg_next = 1;
421 data->optwhere = 1;
422 }
423 break;
424 default: /* shouldn't happen */
425 case NO_ARG:
426 if (longopt_match < 0)
427 {
428 data->optwhere++;
429 if (argv[data->optind][data->optwhere] == '\0')
430 data->optwhere = 1;
431 }
432 else
433 data->optwhere = 1;
434 data->optarg = 0;
435 break;
436 }
437
438 /* do we have to permute or otherwise modify data->optind? */
439 if (ordering == PERMUTE && data->optwhere == 1 && data->num_nonopts != 0)
440 {
441 permute (argv + data->permute_from, data->num_nonopts, 1 + arg_next);
442 data->optind = data->permute_from + 1 + arg_next;
443 }
444 else if (data->optwhere == 1)
445 data->optind = data->optind + 1 + arg_next;
446
447 /* finally return */
448 if (longopt_match >= 0)
449 {
450 if (longind != 0)
451 *longind = longopt_match;
452 if (longopts[longopt_match].flag != 0)
453 {
454 *(longopts[longopt_match].flag) = longopts[longopt_match].val;
455 return 0;
456 }
457 else
458 return longopts[longopt_match].val;
459 }
460 else
461 return data->optopt;
462 }
463
464 int
getopt(int argc,char * const argv[],const char * optstring)465 getopt (int argc, char *const argv[], const char *optstring)
466 {
467 struct getopt_data data;
468 int r;
469
470 read_globals (&data);
471 r = getopt_internal (argc, argv, optstring, 0, 0, 0, &data);
472 write_globals (&data);
473 return r;
474 }
475
476 int
getopt_long(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind)477 getopt_long (int argc, char *const argv[], const char *shortopts,
478 const struct option *longopts, int *longind)
479 {
480 struct getopt_data data;
481 int r;
482
483 read_globals (&data);
484 r = getopt_internal (argc, argv, shortopts, longopts, longind, 0, &data);
485 write_globals (&data);
486 return r;
487 }
488
489 int
getopt_long_only(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind)490 getopt_long_only (int argc, char *const argv[], const char *shortopts,
491 const struct option *longopts, int *longind)
492 {
493 struct getopt_data data;
494 int r;
495
496 read_globals (&data);
497 r = getopt_internal (argc, argv, shortopts, longopts, longind, 1, &data);
498 write_globals (&data);
499 return r;
500 }
501
502 int
__getopt_r(int argc,char * const argv[],const char * optstring,struct getopt_data * data)503 __getopt_r (int argc, char *const argv[], const char *optstring,
504 struct getopt_data *data)
505 {
506 return getopt_internal (argc, argv, optstring, 0, 0, 0, data);
507 }
508
509 int
__getopt_long_r(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind,struct getopt_data * data)510 __getopt_long_r (int argc, char *const argv[], const char *shortopts,
511 const struct option *longopts, int *longind,
512 struct getopt_data *data)
513 {
514 return getopt_internal (argc, argv, shortopts, longopts, longind, 0, data);
515 }
516
517 int
__getopt_long_only_r(int argc,char * const argv[],const char * shortopts,const struct option * longopts,int * longind,struct getopt_data * data)518 __getopt_long_only_r (int argc, char *const argv[], const char *shortopts,
519 const struct option *longopts, int *longind,
520 struct getopt_data *data)
521 {
522 return getopt_internal (argc, argv, shortopts, longopts, longind, 1, data);
523 }
524
525 #endif /* !HAVE_GETOPT */
526
527 /* end of file GETOPT.C */
528