Improve handling variables, which can be either string or string array.
[build.git] / src / cli-option-parser.c
blobd0283628fe826891eed0607be802227e2f4d564c
1 /****************************************************************************
3 getopt.c - Read command line options
5 AUTHOR: Gregory Pietsch
6 CREATED Fri Jan 10 21:13:05 1997
8 DESCRIPTION:
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.
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.
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
26 --arg=value
30 --arg value
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.
36 The longind pointer points to the index of the current long option relative
37 to longopts if it is non-NULL.
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.
43 The getopt_long() function's return value is described in the header file.
45 The function getopt_long_only() is identical to getopt_long(), except that a
46 plus sign `+' can introduce long options as well as `--'.
48 The following describes how to deal with options that follow non-option
49 argv-elements.
51 If the caller did not specify anything, the default is REQUIRE_ORDER if the
52 environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
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.
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.
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.
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.
74 COPYRIGHT NOTICE AND DISCLAIMER:
76 Copyright (C) 1997 Gregory Pietsch
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.
82 Gregory Pietsch's current e-mail address:
83 gpietsch@comcast.net
84 ****************************************************************************/
86 /* Include files. */
87 #include <limits.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <string.h>
92 #include "cli-option-parser.h"
94 /* Types. */
95 enum ordering
97 PERMUTE,
98 RETURN_IN_ORDER,
99 REQUIRE_ORDER
102 /* Functions. */
104 /* Initialize CLI option parser state by default values. */
105 void
106 cli_option_parser_state_initialize (struct cli_option_parser_state *state)
108 state->argument = NULL;
109 state->index = 0;
110 state->where = 0;
111 state->error = true;
112 state->option = '?';
115 /* Reverses num elements starting at argv. */
116 static void
117 reverse_argv_elements (char **argv, int num)
119 int i;
120 char *tmp;
122 for (i = 0; i < (num >> 1); i++)
124 tmp = argv[i];
125 argv[i] = argv[num - i - 1];
126 argv[num - i - 1] = tmp;
130 /* Swap two blocks of argv-elements given their lengths. */
131 static void
132 permute (char **argv, int len1, int len2)
134 reverse_argv_elements (argv, len1);
135 reverse_argv_elements (argv, len1 + len2);
136 reverse_argv_elements (argv, len2);
139 /* Is this argv-element an option or the end of the option list? */
140 static int
141 is_option (char *argv_element)
143 return ((argv_element == NULL) || (argv_element[0] == '-'));
147 cli_option_parse (struct cli_option_parser_state *state,
148 int argc, char **argv,
149 const char *shortopts,
150 int optcount,
151 struct cli_option *longopts,
152 int *longind)
154 enum ordering ordering = PERMUTE;
155 int permute_from = 0;
156 int num_nonopts = 0;
157 int match_chars = 0;
158 char *possible_arg = NULL;
159 int longopt_match = -1;
160 int has_argument = -1;
161 char *cp = NULL;
162 int arg_next = 0;
164 if (state->index >= argc || argv[state->index] == NULL)
165 return EOF;
167 if (strcmp (argv[state->index], "--") == 0)
169 state->index++;
170 return EOF;
173 /* If this is our first time through. */
174 if (state->index == 0)
175 state->index = state->where = 1;
177 /* Define ordering. */
178 switch (*shortopts)
180 case '-':
181 ordering = RETURN_IN_ORDER;
182 shortopts++;
183 break;
184 case '+':
185 ordering = REQUIRE_ORDER;
186 shortopts++;
187 break;
188 default:
189 ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
190 break;
193 /* Based on ordering, find our next option, if we're at the beginning of one. */
194 if (state->where == 1)
196 switch (ordering)
198 case PERMUTE:
199 permute_from = state->index;
200 num_nonopts = 0;
202 while (!is_option (argv[state->index]))
204 state->index++;
205 num_nonopts++;
208 if (argv[state->index] == NULL)
210 /* No more options. */
211 state->index = permute_from;
212 return EOF;
214 else if (strcmp (argv[state->index], "--") == 0)
216 /* No more options, but have to get `--' out of the way. */
217 permute (argv + permute_from, num_nonopts, 1);
218 state->index = permute_from + 1;
219 return EOF;
222 break;
223 case RETURN_IN_ORDER:
224 if (!is_option (argv[state->index]))
226 state->argument = argv[state->index++];
227 state->option = 1;
228 return state->option;
231 break;
232 case REQUIRE_ORDER:
233 if (!is_option (argv[state->index]))
234 return EOF;
236 break;
240 /* We have got an option, so parse it. */
242 /* First, is it a long option? */
243 if (memcmp (argv[state->index], "--", 2) == 0 && state->where == 1)
245 /* Handle long options. */
246 state->where = 2;
247 possible_arg = strchr (argv[state->index] + state->where, '=');
249 if (possible_arg == NULL)
251 /* No =, so next argv might be argument. */
252 size_t len = strlen (argv[state->index]);
254 if (len > INT_MAX)
256 /* We have option which name is too long. */
257 if (state->error)
258 error (_("option `%s' is too long"), argv[state->index]);
260 return (state->option = '?');
263 match_chars = (int) len;
264 possible_arg = argv[state->index] + match_chars;
265 match_chars -= state->where;
267 else
268 match_chars = (int) (possible_arg - argv[state->index]) - state->where;
270 for (int optindex = 0; optindex < optcount; optindex++)
272 if (memcmp (argv[state->index] + state->where,
273 longopts[optindex].name,
274 (size_t) match_chars) == 0)
276 size_t len = strlen (longopts[optindex].name);
278 if (len > INT_MAX)
280 /* We have option which name is too long. */
281 if (state->error)
282 error (_("option `%s' is too long"), longopts[optindex].name);
284 return (state->option = '?');
287 /* Do we have an exact match? */
288 if (match_chars == (int) len)
290 longopt_match = optindex;
291 break;
293 /* Do any characters match? */
294 else
296 if (longopt_match < 0)
297 longopt_match = optindex;
298 else
300 /* We have ambiguous options. */
301 if (state->error)
302 error (_("option `%s' is ambiguous (could be `--%s' or `--%s')"),
303 argv[state->index],
304 longopts[longopt_match].name,
305 longopts[optindex].name);
307 return (state->option = '?');
313 if (longopt_match >= 0)
314 has_argument = longopts[longopt_match].has_argument;
317 /* If we did not find a long option, is it a short option? */
318 if (longopt_match < 0 && shortopts != NULL)
320 cp = strchr (shortopts, argv[state->index][state->where]);
322 if (cp == NULL)
324 /* Could not find option in shortopts. */
325 if (state->error)
326 error (_("invalid option -- `-%c'"),
327 argv[state->index][state->where]);
329 state->where++;
331 if (argv[state->index][state->where] == '\0')
333 state->index++;
334 state->where = 1;
337 return (state->option = '?');
340 has_argument = ((cp[1] == ':') ? ((cp[2] == ':') ? optional_argument : required_argument) : no_argument);
341 possible_arg = argv[state->index] + state->where + 1;
342 state->option = *cp;
345 /* Get argument and reset state->where. */
346 arg_next = 0;
348 switch (has_argument)
350 case optional_argument:
351 if (*possible_arg == '=')
352 possible_arg++;
354 if (*possible_arg != '\0')
356 state->argument = possible_arg;
357 state->where = 1;
359 else
360 state->argument = NULL;
362 break;
363 case required_argument:
364 if (*possible_arg == '=')
365 possible_arg++;
367 if (*possible_arg != '\0')
369 state->argument = possible_arg;
370 state->where = 1;
372 else if (state->index + 1 >= argc)
374 if (state->error)
376 if (longopt_match >= 0)
377 error (_("argument required for option `--%s'"), longopts[longopt_match].name);
378 else
379 error (_("argument required for option `-%c'"), *cp);
382 state->index++;
383 return (state->option = ':');
385 else
387 state->argument = argv[state->index + 1];
388 arg_next = 1;
389 state->where = 1;
392 break;
393 case no_argument:
394 if (longopt_match < 0)
396 state->where++;
398 if (argv[state->index][state->where] == '\0')
399 state->where = 1;
401 else
402 state->where = 1;
404 state->argument = NULL;
405 break;
408 /* Do we have to permute or otherwise modify state->index? */
409 if (ordering == PERMUTE && state->where == 1 && num_nonopts != 0)
411 permute (argv + permute_from, num_nonopts, 1 + arg_next);
412 state->index = permute_from + 1 + arg_next;
414 else if (state->where == 1)
415 state->index = state->index + 1 + arg_next;
417 /* Finally return. */
418 if (longopt_match >= 0)
420 if (longind != NULL)
421 *longind = longopt_match;
423 if (longopts[longopt_match].flag != NULL)
425 *(longopts[longopt_match].flag) = longopts[longopt_match].value;
426 return 0;
428 else
429 return longopts[longopt_match].value;
431 else
432 return state->option;