]>
Commit | Line | Data |
---|---|---|
791c9bda | 1 | /**************************************************************************** |
8a0efa53 | 2 | |
791c9bda JJ |
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 | COPYRIGHT NOTICE AND DISCLAIMER: | |
75 | ||
76 | Copyright (C) 1997 Gregory Pietsch | |
77 | ||
78 | This file and the accompanying getopt.h header file are hereby placed in the | |
79 | public domain without restrictions. Just give the author credit, don't | |
80 | claim you wrote it or prevent anyone else from using it. | |
81 | ||
82 | Gregory Pietsch's current e-mail address: | |
83 | gpietsch@comcast.net | |
84 | ****************************************************************************/ | |
85 | ||
c6228428 | 86 | #ifndef HAVE_GETOPT |
76ff710c | 87 | |
791c9bda | 88 | /* include files */ |
8a0efa53 CF |
89 | #include <stdio.h> |
90 | #include <stdlib.h> | |
91 | #include <string.h> | |
c6228428 | 92 | #define __need_getopt_newlib |
791c9bda | 93 | #include <getopt.h> |
8a0efa53 | 94 | |
791c9bda | 95 | /* macros */ |
8a0efa53 | 96 | |
791c9bda JJ |
97 | /* types */ |
98 | typedef enum GETOPT_ORDERING_T | |
99 | { | |
100 | PERMUTE, | |
101 | RETURN_IN_ORDER, | |
102 | REQUIRE_ORDER | |
103 | } GETOPT_ORDERING_T; | |
104 | ||
105 | /* globally-defined variables */ | |
76ff710c | 106 | char *optarg = 0; |
791c9bda JJ |
107 | int optind = 0; |
108 | int opterr = 1; | |
109 | int optopt = '?'; | |
110 | ||
76ff710c JJ |
111 | /* static variables */ |
112 | static int optwhere = 0; | |
113 | ||
791c9bda JJ |
114 | /* functions */ |
115 | ||
116 | /* reverse_argv_elements: reverses num elements starting at argv */ | |
117 | static void | |
76ff710c | 118 | reverse_argv_elements (char **argv, int num) |
791c9bda JJ |
119 | { |
120 | int i; | |
121 | char *tmp; | |
122 | ||
123 | for (i = 0; i < (num >> 1); i++) | |
124 | { | |
125 | tmp = argv[i]; | |
126 | argv[i] = argv[num - i - 1]; | |
127 | argv[num - i - 1] = tmp; | |
128 | } | |
129 | } | |
130 | ||
131 | /* permute: swap two blocks of argv-elements given their lengths */ | |
132 | static void | |
133 | permute (char *const argv[], int len1, int len2) | |
134 | { | |
76ff710c JJ |
135 | reverse_argv_elements ((char **) argv, len1); |
136 | reverse_argv_elements ((char **) argv, len1 + len2); | |
137 | reverse_argv_elements ((char **) argv, len2); | |
791c9bda JJ |
138 | } |
139 | ||
140 | /* is_option: is this argv-element an option or the end of the option list? */ | |
141 | static int | |
142 | is_option (char *argv_element, int only) | |
143 | { | |
76ff710c JJ |
144 | return ((argv_element == 0) |
145 | || (argv_element[0] == '-') || (only && argv_element[0] == '+')); | |
146 | } | |
147 | ||
148 | /* read_globals: read the values from the globals into a getopt_data | |
149 | structure */ | |
150 | static void | |
151 | read_globals (struct getopt_data *data) | |
152 | { | |
153 | data->optarg = optarg; | |
154 | data->optind = optind; | |
155 | data->opterr = opterr; | |
156 | data->optopt = optopt; | |
157 | data->optwhere = optwhere; | |
158 | } | |
159 | ||
160 | /* write_globals: write the values into the globals from a getopt_data | |
161 | structure */ | |
162 | static void | |
163 | write_globals (struct getopt_data *data) | |
164 | { | |
165 | optarg = data->optarg; | |
166 | optind = data->optind; | |
167 | opterr = data->opterr; | |
168 | optopt = data->optopt; | |
169 | optwhere = data->optwhere; | |
791c9bda JJ |
170 | } |
171 | ||
172 | /* getopt_internal: the function that does all the dirty work */ | |
173 | static int | |
174 | getopt_internal (int argc, char *const argv[], const char *shortopts, | |
76ff710c JJ |
175 | const struct option *longopts, int *longind, int only, |
176 | struct getopt_data *data) | |
791c9bda JJ |
177 | { |
178 | GETOPT_ORDERING_T ordering = PERMUTE; | |
791c9bda JJ |
179 | size_t permute_from = 0; |
180 | int num_nonopts = 0; | |
181 | int optindex = 0; | |
182 | size_t match_chars = 0; | |
76ff710c | 183 | char *possible_arg = 0; |
791c9bda JJ |
184 | int longopt_match = -1; |
185 | int has_arg = -1; | |
76ff710c | 186 | char *cp = 0; |
791c9bda JJ |
187 | int arg_next = 0; |
188 | ||
189 | /* first, deal with silly parameters and easy stuff */ | |
76ff710c JJ |
190 | if (argc == 0 || argv == 0 || (shortopts == 0 && longopts == 0) |
191 | || data->optind >= argc || argv[data->optind] == 0) | |
791c9bda | 192 | return EOF; |
76ff710c | 193 | if (strcmp (argv[data->optind], "--") == 0) |
791c9bda | 194 | { |
76ff710c | 195 | data->optind++; |
791c9bda JJ |
196 | return EOF; |
197 | } | |
76ff710c | 198 | |
791c9bda | 199 | /* if this is our first time through */ |
76ff710c JJ |
200 | if (data->optind == 0) |
201 | data->optind = data->optwhere = 1; | |
791c9bda JJ |
202 | |
203 | /* define ordering */ | |
76ff710c | 204 | if (shortopts != 0 && (*shortopts == '-' || *shortopts == '+')) |
791c9bda JJ |
205 | { |
206 | ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER; | |
207 | shortopts++; | |
208 | } | |
209 | else | |
76ff710c | 210 | ordering = (getenv ("POSIXLY_CORRECT") != 0) ? REQUIRE_ORDER : PERMUTE; |
791c9bda JJ |
211 | |
212 | /* | |
213 | * based on ordering, find our next option, if we're at the beginning of | |
214 | * one | |
215 | */ | |
76ff710c | 216 | if (data->optwhere == 1) |
791c9bda JJ |
217 | { |
218 | switch (ordering) | |
76ff710c JJ |
219 | { |
220 | default: /* shouldn't happen */ | |
221 | case PERMUTE: | |
222 | permute_from = data->optind; | |
223 | num_nonopts = 0; | |
224 | while (!is_option (argv[data->optind], only)) | |
225 | { | |
226 | data->optind++; | |
227 | num_nonopts++; | |
228 | } | |
229 | if (argv[data->optind] == 0) | |
230 | { | |
231 | /* no more options */ | |
232 | data->optind = permute_from; | |
233 | return EOF; | |
234 | } | |
235 | else if (strcmp (argv[data->optind], "--") == 0) | |
236 | { | |
237 | /* no more options, but have to get `--' out of the way */ | |
238 | permute (argv + permute_from, num_nonopts, 1); | |
239 | data->optind = permute_from + 1; | |
240 | return EOF; | |
241 | } | |
242 | break; | |
243 | case RETURN_IN_ORDER: | |
244 | if (!is_option (argv[data->optind], only)) | |
245 | { | |
246 | data->optarg = argv[data->optind++]; | |
247 | return (data->optopt = 1); | |
248 | } | |
249 | break; | |
250 | case REQUIRE_ORDER: | |
251 | if (!is_option (argv[data->optind], only)) | |
252 | return EOF; | |
253 | break; | |
254 | } | |
791c9bda JJ |
255 | } |
256 | /* we've got an option, so parse it */ | |
257 | ||
258 | /* first, is it a long option? */ | |
76ff710c JJ |
259 | if (longopts != 0 |
260 | && (memcmp (argv[data->optind], "--", 2) == 0 | |
261 | || (only && argv[data->optind][0] == '+')) && data->optwhere == 1) | |
791c9bda JJ |
262 | { |
263 | /* handle long options */ | |
76ff710c JJ |
264 | if (memcmp (argv[data->optind], "--", 2) == 0) |
265 | data->optwhere = 2; | |
791c9bda | 266 | longopt_match = -1; |
76ff710c JJ |
267 | possible_arg = strchr (argv[data->optind] + data->optwhere, '='); |
268 | if (possible_arg == 0) | |
269 | { | |
270 | /* no =, so next argv might be arg */ | |
271 | match_chars = strlen (argv[data->optind]); | |
272 | possible_arg = argv[data->optind] + match_chars; | |
273 | match_chars = match_chars - data->optwhere; | |
274 | } | |
791c9bda | 275 | else |
76ff710c JJ |
276 | match_chars = (possible_arg - argv[data->optind]) - data->optwhere; |
277 | for (optindex = 0; longopts[optindex].name != 0; ++optindex) | |
278 | { | |
279 | if (memcmp | |
280 | (argv[data->optind] + data->optwhere, longopts[optindex].name, | |
281 | match_chars) == 0) | |
282 | { | |
283 | /* do we have an exact match? */ | |
284 | if (match_chars == (int) (strlen (longopts[optindex].name))) | |
285 | { | |
286 | longopt_match = optindex; | |
287 | break; | |
288 | } | |
289 | /* do any characters match? */ | |
290 | else | |
291 | { | |
292 | if (longopt_match < 0) | |
293 | longopt_match = optindex; | |
294 | else | |
295 | { | |
296 | /* we have ambiguous options */ | |
297 | if (data->opterr) | |
298 | fprintf (stderr, "%s: option `%s' is ambiguous " | |
299 | "(could be `--%s' or `--%s')\n", | |
300 | argv[0], | |
301 | argv[data->optind], | |
302 | longopts[longopt_match].name, | |
303 | longopts[optindex].name); | |
304 | return (data->optopt = '?'); | |
305 | } | |
306 | } | |
307 | } | |
308 | } | |
791c9bda | 309 | if (longopt_match >= 0) |
76ff710c | 310 | has_arg = longopts[longopt_match].has_arg; |
791c9bda | 311 | } |
76ff710c | 312 | |
791c9bda | 313 | /* if we didn't find a long option, is it a short option? */ |
76ff710c | 314 | if (longopt_match < 0 && shortopts != 0) |
791c9bda | 315 | { |
76ff710c JJ |
316 | cp = strchr (shortopts, argv[data->optind][data->optwhere]); |
317 | if (cp == 0) | |
318 | { | |
319 | /* couldn't find option in shortopts */ | |
320 | if (data->opterr) | |
321 | fprintf (stderr, | |
322 | "%s: invalid option -- `-%c'\n", | |
323 | argv[0], argv[data->optind][data->optwhere]); | |
324 | data->optwhere++; | |
325 | if (argv[data->optind][data->optwhere] == '\0') | |
326 | { | |
327 | data->optind++; | |
328 | data->optwhere = 1; | |
329 | } | |
330 | return (data->optopt = '?'); | |
331 | } | |
791c9bda | 332 | has_arg = ((cp[1] == ':') |
76ff710c JJ |
333 | ? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG); |
334 | possible_arg = argv[data->optind] + data->optwhere + 1; | |
335 | data->optopt = *cp; | |
791c9bda | 336 | } |
76ff710c JJ |
337 | |
338 | /* get argument and reset data->optwhere */ | |
791c9bda JJ |
339 | arg_next = 0; |
340 | switch (has_arg) | |
341 | { | |
342 | case OPTIONAL_ARG: | |
343 | if (*possible_arg == '=') | |
76ff710c JJ |
344 | possible_arg++; |
345 | data->optarg = (*possible_arg != '\0') ? possible_arg : 0; | |
346 | data->optwhere = 1; | |
791c9bda JJ |
347 | break; |
348 | case REQUIRED_ARG: | |
349 | if (*possible_arg == '=') | |
76ff710c | 350 | possible_arg++; |
791c9bda | 351 | if (*possible_arg != '\0') |
76ff710c JJ |
352 | { |
353 | data->optarg = possible_arg; | |
354 | data->optwhere = 1; | |
355 | } | |
356 | else if (data->optind + 1 >= argc) | |
357 | { | |
358 | if (data->opterr) | |
359 | { | |
360 | fprintf (stderr, "%s: argument required for option `", argv[0]); | |
361 | if (longopt_match >= 0) | |
362 | fprintf (stderr, "--%s'\n", longopts[longopt_match].name); | |
363 | else | |
364 | fprintf (stderr, "-%c'\n", *cp); | |
365 | } | |
366 | data->optind++; | |
367 | return (data->optopt = ':'); | |
368 | } | |
791c9bda | 369 | else |
76ff710c JJ |
370 | { |
371 | data->optarg = argv[data->optind + 1]; | |
372 | arg_next = 1; | |
373 | data->optwhere = 1; | |
374 | } | |
791c9bda | 375 | break; |
76ff710c | 376 | default: /* shouldn't happen */ |
791c9bda JJ |
377 | case NO_ARG: |
378 | if (longopt_match < 0) | |
76ff710c JJ |
379 | { |
380 | data->optwhere++; | |
381 | if (argv[data->optind][data->optwhere] == '\0') | |
382 | data->optwhere = 1; | |
383 | } | |
791c9bda | 384 | else |
76ff710c JJ |
385 | data->optwhere = 1; |
386 | data->optarg = 0; | |
791c9bda JJ |
387 | break; |
388 | } | |
389 | ||
76ff710c JJ |
390 | /* do we have to permute or otherwise modify data->optind? */ |
391 | if (ordering == PERMUTE && data->optwhere == 1 && num_nonopts != 0) | |
791c9bda JJ |
392 | { |
393 | permute (argv + permute_from, num_nonopts, 1 + arg_next); | |
76ff710c | 394 | data->optind = permute_from + 1 + arg_next; |
791c9bda | 395 | } |
76ff710c JJ |
396 | else if (data->optwhere == 1) |
397 | data->optind = data->optind + 1 + arg_next; | |
791c9bda JJ |
398 | |
399 | /* finally return */ | |
400 | if (longopt_match >= 0) | |
401 | { | |
76ff710c JJ |
402 | if (longind != 0) |
403 | *longind = longopt_match; | |
404 | if (longopts[longopt_match].flag != 0) | |
405 | { | |
406 | *(longopts[longopt_match].flag) = longopts[longopt_match].val; | |
407 | return 0; | |
408 | } | |
791c9bda | 409 | else |
76ff710c | 410 | return longopts[longopt_match].val; |
791c9bda JJ |
411 | } |
412 | else | |
76ff710c | 413 | return data->optopt; |
791c9bda | 414 | } |
8a0efa53 | 415 | |
8a0efa53 | 416 | int |
791c9bda | 417 | getopt (int argc, char *const argv[], const char *optstring) |
8a0efa53 | 418 | { |
76ff710c JJ |
419 | struct getopt_data data; |
420 | int r; | |
421 | ||
422 | read_globals (&data); | |
423 | r = getopt_internal (argc, argv, optstring, 0, 0, 0, &data); | |
424 | write_globals (&data); | |
425 | return r; | |
8a0efa53 | 426 | } |
791c9bda JJ |
427 | |
428 | int | |
429 | getopt_long (int argc, char *const argv[], const char *shortopts, | |
76ff710c | 430 | const struct option *longopts, int *longind) |
791c9bda | 431 | { |
76ff710c JJ |
432 | struct getopt_data data; |
433 | int r; | |
434 | ||
435 | read_globals (&data); | |
436 | r = getopt_internal (argc, argv, shortopts, longopts, longind, 0, &data); | |
437 | write_globals (&data); | |
438 | return r; | |
791c9bda JJ |
439 | } |
440 | ||
441 | int | |
442 | getopt_long_only (int argc, char *const argv[], const char *shortopts, | |
76ff710c JJ |
443 | const struct option *longopts, int *longind) |
444 | { | |
445 | struct getopt_data data; | |
446 | int r; | |
447 | ||
448 | read_globals (&data); | |
449 | r = getopt_internal (argc, argv, shortopts, longopts, longind, 1, &data); | |
450 | write_globals (&data); | |
451 | return r; | |
452 | } | |
453 | ||
454 | int | |
455 | __getopt_r (int argc, char *const argv[], const char *optstring, | |
456 | struct getopt_data *data) | |
457 | { | |
458 | return getopt_internal (argc, argv, optstring, 0, 0, 0, data); | |
459 | } | |
460 | ||
461 | int | |
462 | __getopt_long_r (int argc, char *const argv[], const char *shortopts, | |
463 | const struct option *longopts, int *longind, | |
464 | struct getopt_data *data) | |
465 | { | |
466 | return getopt_internal (argc, argv, shortopts, longopts, longind, 0, data); | |
467 | } | |
468 | ||
469 | int | |
470 | __getopt_long_only_r (int argc, char *const argv[], const char *shortopts, | |
471 | const struct option *longopts, int *longind, | |
472 | struct getopt_data *data) | |
791c9bda | 473 | { |
76ff710c | 474 | return getopt_internal (argc, argv, shortopts, longopts, longind, 1, data); |
791c9bda JJ |
475 | } |
476 | ||
c6228428 JJ |
477 | #endif /* !HAVE_GETOPT */ |
478 | ||
791c9bda | 479 | /* end of file GETOPT.C */ |