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