source: mainline/uspace/lib/c/generic/getopt.c@ 6ff23ff

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6ff23ff was 7c3fb9b, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix block comment formatting (ccheck).

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/* $NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $ */
2
3/*
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Dieter Baron and Thomas Klausner.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/* Ported to HelenOS August 2008 by Tim Post <echo@echoreply.us> */
33
34#include <assert.h>
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <stddef.h>
39#include <errno.h>
40#include <getopt.h>
41#include <str.h>
42
43/*
44 * HelenOS Port : We're incorporating only the modern getopt_long with wrappers
45 * to keep legacy getopt() usage from breaking. All references to REPLACE_GETOPT
46 * are dropped, we just include the code
47 */
48
49int opterr = 1; /* if error message should be printed */
50int optind = 1; /* index into parent argv vector */
51int optopt = '?'; /* character checked for validity */
52int optreset; /* reset getopt */
53char *optarg; /* argument associated with option */
54
55
56#define IGNORE_FIRST (*options == '-' || *options == '+')
57#define PRINT_ERROR ((opterr) && ((*options != ':') \
58 || (IGNORE_FIRST && options[1] != ':')))
59/*HelenOS Port - POSIXLY_CORRECT is always false */
60#define IS_POSIXLY_CORRECT 0
61#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
62/* XXX: GNU ignores PC if *options == '-' */
63#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
64
65/* return values */
66#define BADCH (int)'?'
67#define BADARG ((IGNORE_FIRST && options[1] == ':') \
68 || (*options == ':') ? (int)':' : (int)'?')
69#define INORDER (int)1
70
71static char EMSG[] = "";
72
73static int getopt_internal(int, char **, const char *);
74static int gcd(int, int);
75static void permute_args(int, int, int, char **);
76
77static char *place = EMSG; /* option letter processing */
78
79/* XXX: set optreset to 1 rather than these two */
80static int nonopt_start = -1; /* first non option argument (for permute) */
81static int nonopt_end = -1; /* first option after non options (for permute) */
82
83/* Error messages */
84
85/*
86 * HelenOS Port: Calls to warnx() were eliminated (as we have no stderr that
87 * may be redirected) and replaced with printf. As such, error messages now
88 * end in a newline
89 */
90
91static const char recargchar[] = "option requires an argument -- %c\n";
92static const char recargstring[] = "option requires an argument -- %s\n";
93static const char ambig[] = "ambiguous option -- %.*s\n";
94static const char noarg[] = "option doesn't take an argument -- %.*s\n";
95static const char illoptchar[] = "unknown option -- %c\n";
96static const char illoptstring[] = "unknown option -- %s\n";
97
98
99/*
100 * Compute the greatest common divisor of a and b.
101 */
102static int gcd(int a, int b)
103{
104 int c;
105
106 c = a % b;
107 while (c != 0) {
108 a = b;
109 b = c;
110 c = a % b;
111 }
112
113 return b;
114}
115
116/*
117 * Exchange the block from nonopt_start to nonopt_end with the block
118 * from nonopt_end to opt_end (keeping the same order of arguments
119 * in each block).
120 */
121static void permute_args(int panonopt_start, int panonopt_end, int opt_end,
122 char **nargv)
123{
124 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
125 char *swap;
126
127 assert(nargv != NULL);
128
129 /*
130 * compute lengths of blocks and number and size of cycles
131 */
132 nnonopts = panonopt_end - panonopt_start;
133 nopts = opt_end - panonopt_end;
134 ncycle = gcd(nnonopts, nopts);
135 cyclelen = (opt_end - panonopt_start) / ncycle;
136
137 for (i = 0; i < ncycle; i++) {
138 cstart = panonopt_end + i;
139 pos = cstart;
140 for (j = 0; j < cyclelen; j++) {
141 if (pos >= panonopt_end)
142 pos -= nnonopts;
143 else
144 pos += nopts;
145 swap = nargv[pos];
146 nargv[pos] = nargv[cstart];
147 nargv[cstart] = swap;
148 }
149 }
150}
151
152/*
153 * getopt_internal --
154 * Parse argc/argv argument vector. Called by user level routines.
155 * Returns -2 if -- is found (can be long option or end of options marker).
156 */
157static int getopt_internal(int nargc, char **nargv, const char *options)
158{
159 const char *oli; /* option letter list index */
160 int optchar;
161
162 assert(nargv != NULL);
163 assert(options != NULL);
164
165 optarg = NULL;
166
167 /*
168 * XXX Some programs (like rsyncd) expect to be able to
169 * XXX re-initialize optind to 0 and have getopt_long(3)
170 * XXX properly function again. Work around this braindamage.
171 */
172 if (optind == 0)
173 optind = 1;
174
175 if (optreset)
176 nonopt_start = nonopt_end = -1;
177start:
178 if (optreset || !*place) { /* update scanning pointer */
179 optreset = 0;
180 if (optind >= nargc) { /* end of argument vector */
181 place = EMSG;
182 if (nonopt_end != -1) {
183 /* do permutation, if we have to */
184 permute_args(nonopt_start, nonopt_end,
185 optind, nargv);
186 optind -= nonopt_end - nonopt_start;
187 } else if (nonopt_start != -1) {
188 /*
189 * If we skipped non-options, set optind
190 * to the first of them.
191 */
192 optind = nonopt_start;
193 }
194 nonopt_start = nonopt_end = -1;
195 return -1;
196 }
197 if ((*(place = nargv[optind]) != '-') ||
198 (place[1] == '\0')) { /* found non-option */
199 place = EMSG;
200 if (IN_ORDER) {
201 /*
202 * GNU extension:
203 * return non-option as argument to option 1
204 */
205 optarg = nargv[optind++];
206 return INORDER;
207 }
208 if (!PERMUTE) {
209 /*
210 * if no permutation wanted, stop parsing
211 * at first non-option
212 */
213 return -1;
214 }
215 /* do permutation */
216 if (nonopt_start == -1)
217 nonopt_start = optind;
218 else if (nonopt_end != -1) {
219 permute_args(nonopt_start, nonopt_end,
220 optind, nargv);
221 nonopt_start = optind -
222 (nonopt_end - nonopt_start);
223 nonopt_end = -1;
224 }
225 optind++;
226 /* process next argument */
227 goto start;
228 }
229 if (nonopt_start != -1 && nonopt_end == -1)
230 nonopt_end = optind;
231 if (place[1] && *++place == '-') { /* found "--" */
232 place++;
233 return -2;
234 }
235 }
236 if ((optchar = (int)*place++) == (int)':' ||
237 (oli = str_chr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
238 /* option letter unknown or ':' */
239 if (!*place)
240 ++optind;
241 if (PRINT_ERROR)
242 printf(illoptchar, optchar);
243 optopt = optchar;
244 return BADCH;
245 }
246 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
247 /* XXX: what if no long options provided (called by getopt)? */
248 if (*place)
249 return -2;
250
251 if (++optind >= nargc) { /* no arg */
252 place = EMSG;
253 if (PRINT_ERROR)
254 printf(recargchar, optchar);
255 optopt = optchar;
256 return BADARG;
257 } else /* white space */
258 place = nargv[optind];
259 /*
260 * Handle -W arg the same as --arg (which causes getopt to
261 * stop parsing).
262 */
263 return -2;
264 }
265 if (*++oli != ':') { /* doesn't take argument */
266 if (!*place)
267 ++optind;
268 } else { /* takes (optional) argument */
269 optarg = NULL;
270 if (*place) /* no white space */
271 optarg = place;
272 /* XXX: disable test for :: if PC? (GNU doesn't) */
273 else if (oli[1] != ':') { /* arg not optional */
274 if (++optind >= nargc) { /* no arg */
275 place = EMSG;
276 if (PRINT_ERROR)
277 printf(recargchar, optchar);
278 optopt = optchar;
279 return BADARG;
280 } else
281 optarg = nargv[optind];
282 }
283 place = EMSG;
284 ++optind;
285 }
286 /* dump back option letter */
287 return optchar;
288}
289
290/*
291 * getopt --
292 * Parse argc/argv argument vector.
293 */
294int getopt(int nargc, char *const *nargv, const char *options)
295{
296 int retval;
297
298 assert(nargv != NULL);
299 assert(options != NULL);
300
301 retval = getopt_internal(nargc, (char **)nargv, options);
302 if (retval == -2) {
303 ++optind;
304 /*
305 * We found an option (--), so if we skipped non-options,
306 * we have to permute.
307 */
308 if (nonopt_end != -1) {
309 permute_args(nonopt_start, nonopt_end, optind,
310 (char **)nargv);
311 optind -= nonopt_end - nonopt_start;
312 }
313 nonopt_start = nonopt_end = -1;
314 retval = -1;
315 }
316 return retval;
317}
318
319/*
320 * getopt_long --
321 * Parse argc/argv argument vector.
322 */
323int getopt_long(int nargc, char *const *nargv, const char *options,
324 const struct option *long_options, int *idx)
325{
326 int retval;
327
328#define IDENTICAL_INTERPRETATION(_x, _y) \
329 (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
330 long_options[(_x)].flag == long_options[(_y)].flag && \
331 long_options[(_x)].val == long_options[(_y)].val)
332
333 assert(nargv != NULL);
334 assert(options != NULL);
335 assert(long_options != NULL);
336 /* idx may be NULL */
337
338 retval = getopt_internal(nargc, (char **)nargv, options);
339 if (retval == -2) {
340 char *current_argv;
341 char *has_equal;
342 size_t current_argv_len;
343 int i, ambiguous, match;
344
345 current_argv = (char *)place;
346 match = -1;
347 ambiguous = 0;
348
349 optind++;
350 place = EMSG;
351
352 if (*current_argv == '\0') { /* found "--" */
353 /*
354 * We found an option (--), so if we skipped
355 * non-options, we have to permute.
356 */
357 if (nonopt_end != -1) {
358 permute_args(nonopt_start, nonopt_end,
359 optind, (char **)nargv);
360 optind -= nonopt_end - nonopt_start;
361 }
362 nonopt_start = nonopt_end = -1;
363 return -1;
364 }
365 if ((has_equal = str_chr(current_argv, '=')) != NULL) {
366 /* argument found (--option=arg) */
367 current_argv_len = has_equal - current_argv;
368 has_equal++;
369 } else
370 current_argv_len = str_size(current_argv);
371
372 for (i = 0; long_options[i].name; i++) {
373 /* find matching long option */
374 if (str_lcmp(current_argv, long_options[i].name,
375 str_nlength(current_argv, current_argv_len)))
376 continue;
377
378 if (str_size(long_options[i].name) ==
379 (unsigned)current_argv_len) {
380 /* exact match */
381 match = i;
382 ambiguous = 0;
383 break;
384 }
385 if (match == -1) /* partial match */
386 match = i;
387 else if (!IDENTICAL_INTERPRETATION(i, match))
388 ambiguous = 1;
389 }
390 if (ambiguous) {
391 /* ambiguous abbreviation */
392 if (PRINT_ERROR)
393 printf(ambig, (int)current_argv_len,
394 current_argv);
395 optopt = 0;
396 return BADCH;
397 }
398 if (match != -1) { /* option found */
399 if (long_options[match].has_arg == no_argument &&
400 has_equal) {
401 if (PRINT_ERROR)
402 printf(noarg, (int)current_argv_len,
403 current_argv);
404 /*
405 * XXX: GNU sets optopt to val regardless of
406 * flag
407 */
408 if (long_options[match].flag == NULL)
409 optopt = long_options[match].val;
410 else
411 optopt = 0;
412 return BADARG;
413 }
414 if (long_options[match].has_arg == required_argument ||
415 long_options[match].has_arg == optional_argument) {
416 if (has_equal)
417 optarg = has_equal;
418 else if (long_options[match].has_arg ==
419 required_argument) {
420 /*
421 * optional argument doesn't use
422 * next nargv
423 */
424 optarg = nargv[optind++];
425 }
426 }
427 if ((long_options[match].has_arg == required_argument) &&
428 (optarg == NULL)) {
429 /*
430 * Missing argument; leading ':'
431 * indicates no error should be generated
432 */
433 if (PRINT_ERROR)
434 printf(recargstring, current_argv);
435 /*
436 * XXX: GNU sets optopt to val regardless
437 * of flag
438 */
439 if (long_options[match].flag == NULL)
440 optopt = long_options[match].val;
441 else
442 optopt = 0;
443 --optind;
444 return BADARG;
445 }
446 } else { /* unknown option */
447 if (PRINT_ERROR)
448 printf(illoptstring, current_argv);
449 optopt = 0;
450 return BADCH;
451 }
452 if (long_options[match].flag) {
453 *long_options[match].flag = long_options[match].val;
454 retval = 0;
455 } else
456 retval = long_options[match].val;
457 if (idx)
458 *idx = match;
459 }
460 return retval;
461#undef IDENTICAL_INTERPRETATION
462}
463
Note: See TracBrowser for help on using the repository browser.