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