source: mainline/uspace/lib/c/generic/getopt.c@ 1d2f85e

Last change on this file since 1d2f85e was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • 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#define IGNORE_FIRST (*options == '-' || *options == '+')
56#define PRINT_ERROR ((opterr) && ((*options != ':') \
57 || (IGNORE_FIRST && options[1] != ':')))
58/*HelenOS Port - POSIXLY_CORRECT is always false */
59#define IS_POSIXLY_CORRECT 0
60#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
61/* XXX: GNU ignores PC if *options == '-' */
62#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
63
64/* return values */
65#define BADCH (int)'?'
66#define BADARG ((IGNORE_FIRST && options[1] == ':') \
67 || (*options == ':') ? (int)':' : (int)'?')
68#define INORDER (int)1
69
70static char EMSG[] = "";
71
72static int getopt_internal(int, char **, const char *);
73static int gcd(int, int);
74static void permute_args(int, int, int, char **);
75
76static char *place = EMSG; /* option letter processing */
77
78/* XXX: set optreset to 1 rather than these two */
79static int nonopt_start = -1; /* first non option argument (for permute) */
80static int nonopt_end = -1; /* first option after non options (for permute) */
81
82/* Error messages */
83
84/*
85 * HelenOS Port: Calls to warnx() were eliminated (as we have no stderr that
86 * may be redirected) and replaced with printf. As such, error messages now
87 * end in a newline
88 */
89
90static const char recargchar[] = "option requires an argument -- %c\n";
91static const char recargstring[] = "option requires an argument -- %s\n";
92static const char ambig[] = "ambiguous option -- %.*s\n";
93static const char noarg[] = "option doesn't take an argument -- %.*s\n";
94static const char illoptchar[] = "unknown option -- %c\n";
95static const char illoptstring[] = "unknown option -- %s\n";
96
97/*
98 * Compute the greatest common divisor of a and b.
99 */
100static int gcd(int a, int b)
101{
102 int c;
103
104 c = a % b;
105 while (c != 0) {
106 a = b;
107 b = c;
108 c = a % b;
109 }
110
111 return b;
112}
113
114/*
115 * Exchange the block from nonopt_start to nonopt_end with the block
116 * from nonopt_end to opt_end (keeping the same order of arguments
117 * in each block).
118 */
119static void permute_args(int panonopt_start, int panonopt_end, int opt_end,
120 char **nargv)
121{
122 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
123 char *swap;
124
125 assert(nargv != NULL);
126
127 /*
128 * compute lengths of blocks and number and size of cycles
129 */
130 nnonopts = panonopt_end - panonopt_start;
131 nopts = opt_end - panonopt_end;
132 ncycle = gcd(nnonopts, nopts);
133 cyclelen = (opt_end - panonopt_start) / ncycle;
134
135 for (i = 0; i < ncycle; i++) {
136 cstart = panonopt_end + i;
137 pos = cstart;
138 for (j = 0; j < cyclelen; j++) {
139 if (pos >= panonopt_end)
140 pos -= nnonopts;
141 else
142 pos += nopts;
143 swap = nargv[pos];
144 nargv[pos] = nargv[cstart];
145 nargv[cstart] = swap;
146 }
147 }
148}
149
150/*
151 * getopt_internal --
152 * Parse argc/argv argument vector. Called by user level routines.
153 * Returns -2 if -- is found (can be long option or end of options marker).
154 */
155static int getopt_internal(int nargc, char **nargv, const char *options)
156{
157 const char *oli; /* option letter list index */
158 int optchar;
159
160 assert(nargv != NULL);
161 assert(options != NULL);
162
163 optarg = NULL;
164
165 /*
166 * XXX Some programs (like rsyncd) expect to be able to
167 * XXX re-initialize optind to 0 and have getopt_long(3)
168 * XXX properly function again. Work around this braindamage.
169 */
170 if (optind == 0)
171 optind = 1;
172
173 if (optreset)
174 nonopt_start = nonopt_end = -1;
175start:
176 if (optreset || !*place) { /* update scanning pointer */
177 optreset = 0;
178 if (optind >= nargc) { /* end of argument vector */
179 place = EMSG;
180 if (nonopt_end != -1) {
181 /* do permutation, if we have to */
182 permute_args(nonopt_start, nonopt_end,
183 optind, nargv);
184 optind -= nonopt_end - nonopt_start;
185 } else if (nonopt_start != -1) {
186 /*
187 * If we skipped non-options, set optind
188 * to the first of them.
189 */
190 optind = nonopt_start;
191 }
192 nonopt_start = nonopt_end = -1;
193 return -1;
194 }
195 if ((*(place = nargv[optind]) != '-') ||
196 (place[1] == '\0')) { /* found non-option */
197 place = EMSG;
198 if (IN_ORDER) {
199 /*
200 * GNU extension:
201 * return non-option as argument to option 1
202 */
203 optarg = nargv[optind++];
204 return INORDER;
205 }
206 if (!PERMUTE) {
207 /*
208 * if no permutation wanted, stop parsing
209 * at first non-option
210 */
211 return -1;
212 }
213 /* do permutation */
214 if (nonopt_start == -1)
215 nonopt_start = optind;
216 else if (nonopt_end != -1) {
217 permute_args(nonopt_start, nonopt_end,
218 optind, nargv);
219 nonopt_start = optind -
220 (nonopt_end - nonopt_start);
221 nonopt_end = -1;
222 }
223 optind++;
224 /* process next argument */
225 goto start;
226 }
227 if (nonopt_start != -1 && nonopt_end == -1)
228 nonopt_end = optind;
229 if (place[1] && *++place == '-') { /* found "--" */
230 place++;
231 return -2;
232 }
233 }
234 if ((optchar = (int)*place++) == (int)':' ||
235 (oli = str_chr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
236 /* option letter unknown or ':' */
237 if (!*place)
238 ++optind;
239 if (PRINT_ERROR)
240 printf(illoptchar, optchar);
241 optopt = optchar;
242 return BADCH;
243 }
244 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
245 /* XXX: what if no long options provided (called by getopt)? */
246 if (*place)
247 return -2;
248
249 if (++optind >= nargc) { /* no arg */
250 place = EMSG;
251 if (PRINT_ERROR)
252 printf(recargchar, optchar);
253 optopt = optchar;
254 return BADARG;
255 } else /* white space */
256 place = nargv[optind];
257 /*
258 * Handle -W arg the same as --arg (which causes getopt to
259 * stop parsing).
260 */
261 return -2;
262 }
263 if (*++oli != ':') { /* doesn't take argument */
264 if (!*place)
265 ++optind;
266 } else { /* takes (optional) argument */
267 optarg = NULL;
268 if (*place) /* no white space */
269 optarg = place;
270 /* XXX: disable test for :: if PC? (GNU doesn't) */
271 else if (oli[1] != ':') { /* arg not optional */
272 if (++optind >= nargc) { /* no arg */
273 place = EMSG;
274 if (PRINT_ERROR)
275 printf(recargchar, optchar);
276 optopt = optchar;
277 return BADARG;
278 } else
279 optarg = nargv[optind];
280 }
281 place = EMSG;
282 ++optind;
283 }
284 /* dump back option letter */
285 return optchar;
286}
287
288/*
289 * getopt --
290 * Parse argc/argv argument vector.
291 */
292int getopt(int nargc, char *const *nargv, const char *options)
293{
294 int retval;
295
296 assert(nargv != NULL);
297 assert(options != NULL);
298
299 retval = getopt_internal(nargc, (char **)nargv, options);
300 if (retval == -2) {
301 ++optind;
302 /*
303 * We found an option (--), so if we skipped non-options,
304 * we have to permute.
305 */
306 if (nonopt_end != -1) {
307 permute_args(nonopt_start, nonopt_end, optind,
308 (char **)nargv);
309 optind -= nonopt_end - nonopt_start;
310 }
311 nonopt_start = nonopt_end = -1;
312 retval = -1;
313 }
314 return retval;
315}
316
317/*
318 * getopt_long --
319 * Parse argc/argv argument vector.
320 */
321int getopt_long(int nargc, char *const *nargv, const char *options,
322 const struct option *long_options, int *idx)
323{
324 int retval;
325
326#define IDENTICAL_INTERPRETATION(_x, _y) \
327 (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
328 long_options[(_x)].flag == long_options[(_y)].flag && \
329 long_options[(_x)].val == long_options[(_y)].val)
330
331 assert(nargv != NULL);
332 assert(options != NULL);
333 assert(long_options != NULL);
334 /* idx may be NULL */
335
336 retval = getopt_internal(nargc, (char **)nargv, options);
337 if (retval == -2) {
338 char *current_argv;
339 char *has_equal;
340 size_t current_argv_len;
341 int i, ambiguous, match;
342
343 current_argv = (char *)place;
344 match = -1;
345 ambiguous = 0;
346
347 optind++;
348 place = EMSG;
349
350 if (*current_argv == '\0') { /* found "--" */
351 /*
352 * We found an option (--), so if we skipped
353 * non-options, we have to permute.
354 */
355 if (nonopt_end != -1) {
356 permute_args(nonopt_start, nonopt_end,
357 optind, (char **)nargv);
358 optind -= nonopt_end - nonopt_start;
359 }
360 nonopt_start = nonopt_end = -1;
361 return -1;
362 }
363 if ((has_equal = str_chr(current_argv, '=')) != NULL) {
364 /* argument found (--option=arg) */
365 current_argv_len = has_equal - current_argv;
366 has_equal++;
367 } else
368 current_argv_len = str_bytes(current_argv);
369
370 for (i = 0; long_options[i].name; i++) {
371 /* find matching long option */
372 if (str_lcmp(current_argv, long_options[i].name,
373 str_ncode_points(current_argv, current_argv_len)))
374 continue;
375
376 if (str_bytes(long_options[i].name) ==
377 (unsigned)current_argv_len) {
378 /* exact match */
379 match = i;
380 ambiguous = 0;
381 break;
382 }
383 if (match == -1) /* partial match */
384 match = i;
385 else if (!IDENTICAL_INTERPRETATION(i, match))
386 ambiguous = 1;
387 }
388 if (ambiguous) {
389 /* ambiguous abbreviation */
390 if (PRINT_ERROR)
391 printf(ambig, (int)current_argv_len,
392 current_argv);
393 optopt = 0;
394 return BADCH;
395 }
396 if (match != -1) { /* option found */
397 if (long_options[match].has_arg == no_argument &&
398 has_equal) {
399 if (PRINT_ERROR)
400 printf(noarg, (int)current_argv_len,
401 current_argv);
402 /*
403 * XXX: GNU sets optopt to val regardless of
404 * flag
405 */
406 if (long_options[match].flag == NULL)
407 optopt = long_options[match].val;
408 else
409 optopt = 0;
410 return BADARG;
411 }
412 if (long_options[match].has_arg == required_argument ||
413 long_options[match].has_arg == optional_argument) {
414 if (has_equal)
415 optarg = has_equal;
416 else if (long_options[match].has_arg ==
417 required_argument) {
418 /*
419 * optional argument doesn't use
420 * next nargv
421 */
422 optarg = nargv[optind++];
423 }
424 }
425 if ((long_options[match].has_arg == required_argument) &&
426 (optarg == NULL)) {
427 /*
428 * Missing argument; leading ':'
429 * indicates no error should be generated
430 */
431 if (PRINT_ERROR)
432 printf(recargstring, current_argv);
433 /*
434 * XXX: GNU sets optopt to val regardless
435 * of flag
436 */
437 if (long_options[match].flag == NULL)
438 optopt = long_options[match].val;
439 else
440 optopt = 0;
441 --optind;
442 return BADARG;
443 }
444 } else { /* unknown option */
445 if (PRINT_ERROR)
446 printf(illoptstring, current_argv);
447 optopt = 0;
448 return BADCH;
449 }
450 if (long_options[match].flag) {
451 *long_options[match].flag = long_options[match].val;
452 retval = 0;
453 } else
454 retval = long_options[match].val;
455 if (idx)
456 *idx = match;
457 }
458 return retval;
459#undef IDENTICAL_INTERPRETATION
460}
Note: See TracBrowser for help on using the repository browser.