]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/libc/getopt.c
* Makefile.in (DLL_OFILES): Add getopt.o and iruserok.o.
[newlib-cygwin.git] / winsup / cygwin / libc / getopt.c
1 /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include "winsup.h"
40 #include <assert.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <getopt.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47
48 #define REPLACE_GETOPT
49
50 #define _DIAGASSERT(x) do {} while (0)
51
52 #ifdef REPLACE_GETOPT
53 #ifdef __weak_alias
54 __weak_alias(getopt,_getopt)
55 #endif
56 int __declspec(dllexport) opterr; /* if error message should be printed */
57 int __declspec(dllexport) optind; /* index into parent argv vector */
58 int __declspec(dllexport) optopt; /* character checked for validity */
59 int __declspec(dllexport) optreset; /* reset getopt */
60 char __declspec(dllexport) *optarg; /* argument associated with option */
61 #endif
62
63 #ifdef __weak_alias
64 __weak_alias(getopt_long,_getopt_long)
65 #endif
66
67 #ifndef __CYGWIN__
68 #define __progname __argv[0]
69 #else
70 extern char *__progname;
71 #endif
72
73 #define IGNORE_FIRST (*options == '-' || *options == '+')
74 #define PRINT_ERROR ((opterr) && ((*options != ':') \
75 || (IGNORE_FIRST && options[1] != ':')))
76
77 #define IS_POSIXLY_CORRECT (getenv("POSIXLY_INCORRECT_GETOPT") == NULL)
78
79 #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
80 /* XXX: GNU ignores PC if *options == '-' */
81 #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
82
83 /* return values */
84 #define BADCH (int)'?'
85 #define BADARG ((IGNORE_FIRST && options[1] == ':') \
86 || (*options == ':') ? (int)':' : (int)'?')
87 #define INORDER (int)1
88
89 static char EMSG[1];
90
91 static int getopt_internal (int, char * const *, const char *);
92 static int gcd (int, int);
93 static void permute_args (int, int, int, char * const *);
94
95 static char *place = EMSG; /* option letter processing */
96
97 /* XXX: set optreset to 1 rather than these two */
98 static int nonopt_start = -1; /* first non option argument (for permute) */
99 static int nonopt_end = -1; /* first option after non options (for permute) */
100
101 /* Error messages */
102 static const char recargchar[] = "option requires an argument -- %c";
103 static const char recargstring[] = "option requires an argument -- %s";
104 static const char ambig[] = "ambiguous option -- %.*s";
105 static const char noarg[] = "option doesn't take an argument -- %.*s";
106 static const char illoptchar[] = "unknown option -- %c";
107 static const char illoptstring[] = "unknown option -- %s";
108
109 static void
110 _vwarnx(const char *fmt, va_list ap)
111 {
112 (void)fprintf(stderr, "%s: ", __progname);
113 if (fmt != NULL)
114 (void)vfprintf(stderr, fmt, ap);
115 (void)fprintf(stderr, "\n");
116 }
117
118 static void
119 warnx(const char *fmt, ...)
120 {
121 va_list ap;
122 va_start(ap, fmt);
123 _vwarnx(fmt, ap);
124 va_end(ap);
125 }
126
127 /*
128 * Compute the greatest common divisor of a and b.
129 */
130 static int
131 gcd(a, b)
132 int a;
133 int b;
134 {
135 int c;
136
137 c = a % b;
138 while (c != 0) {
139 a = b;
140 b = c;
141 c = a % b;
142 }
143
144 return b;
145 }
146
147 /*
148 * Exchange the block from nonopt_start to nonopt_end with the block
149 * from nonopt_end to opt_end (keeping the same order of arguments
150 * in each block).
151 */
152 static void
153 permute_args(panonopt_start, panonopt_end, opt_end, nargv)
154 int panonopt_start;
155 int panonopt_end;
156 int opt_end;
157 char * const *nargv;
158 {
159 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
160 char *swap;
161
162 _DIAGASSERT(nargv != NULL);
163
164 /*
165 * compute lengths of blocks and number and size of cycles
166 */
167 nnonopts = panonopt_end - panonopt_start;
168 nopts = opt_end - panonopt_end;
169 ncycle = gcd(nnonopts, nopts);
170 cyclelen = (opt_end - panonopt_start) / ncycle;
171
172 for (i = 0; i < ncycle; i++) {
173 cstart = panonopt_end+i;
174 pos = cstart;
175 for (j = 0; j < cyclelen; j++) {
176 if (pos >= panonopt_end)
177 pos -= nnonopts;
178 else
179 pos += nopts;
180 swap = nargv[pos];
181 /* LINTED const cast */
182 ((char **) nargv)[pos] = nargv[cstart];
183 /* LINTED const cast */
184 ((char **)nargv)[cstart] = swap;
185 }
186 }
187 }
188
189 /*
190 * getopt_internal --
191 * Parse argc/argv argument vector. Called by user level routines.
192 * Returns -2 if -- is found (can be long option or end of options marker).
193 */
194 static int
195 getopt_internal(nargc, nargv, options)
196 int nargc;
197 char * const *nargv;
198 const char *options;
199 {
200 char *oli; /* option letter list index */
201 int optchar;
202
203 _DIAGASSERT(nargv != NULL);
204 _DIAGASSERT(options != NULL);
205
206 optarg = NULL;
207
208 /*
209 * XXX Some programs (like rsyncd) expect to be able to
210 * XXX re-initialize optind to 0 and have getopt_long(3)
211 * XXX properly function again. Work around this braindamage.
212 */
213 if (optind == 0)
214 optind = 1;
215
216 if (optreset)
217 nonopt_start = nonopt_end = -1;
218 start:
219 if (optreset || !*place) { /* update scanning pointer */
220 optreset = 0;
221 if (optind >= nargc) { /* end of argument vector */
222 place = EMSG;
223 if (nonopt_end != -1) {
224 /* do permutation, if we have to */
225 permute_args(nonopt_start, nonopt_end,
226 optind, nargv);
227 optind -= nonopt_end - nonopt_start;
228 }
229 else if (nonopt_start != -1) {
230 /*
231 * If we skipped non-options, set optind
232 * to the first of them.
233 */
234 optind = nonopt_start;
235 }
236 nonopt_start = nonopt_end = -1;
237 return -1;
238 }
239 if ((*(place = nargv[optind]) != '-')
240 || (place[1] == '\0')) { /* found non-option */
241 place = EMSG;
242 if (IN_ORDER) {
243 /*
244 * GNU extension:
245 * return non-option as argument to option 1
246 */
247 optarg = nargv[optind++];
248 return INORDER;
249 }
250 if (!PERMUTE) {
251 /*
252 * if no permutation wanted, stop parsing
253 * at first non-option
254 */
255 return -1;
256 }
257 /* do permutation */
258 if (nonopt_start == -1)
259 nonopt_start = optind;
260 else if (nonopt_end != -1) {
261 permute_args(nonopt_start, nonopt_end,
262 optind, nargv);
263 nonopt_start = optind -
264 (nonopt_end - nonopt_start);
265 nonopt_end = -1;
266 }
267 optind++;
268 /* process next argument */
269 goto start;
270 }
271 if (nonopt_start != -1 && nonopt_end == -1)
272 nonopt_end = optind;
273 if (place[1] && *++place == '-') { /* found "--" */
274 place++;
275 return -2;
276 }
277 }
278 if ((optchar = (int)*place++) == (int)':' ||
279 (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
280 /* option letter unknown or ':' */
281 if (!*place)
282 ++optind;
283 if (PRINT_ERROR)
284 warnx(illoptchar, optchar);
285 optopt = optchar;
286 return BADCH;
287 }
288 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
289 /* XXX: what if no long options provided (called by getopt)? */
290 if (*place)
291 return -2;
292
293 if (++optind >= nargc) { /* no arg */
294 place = EMSG;
295 if (PRINT_ERROR)
296 warnx(recargchar, optchar);
297 optopt = optchar;
298 return BADARG;
299 } else /* white space */
300 place = nargv[optind];
301 /*
302 * Handle -W arg the same as --arg (which causes getopt to
303 * stop parsing).
304 */
305 return -2;
306 }
307 if (*++oli != ':') { /* doesn't take argument */
308 if (!*place)
309 ++optind;
310 } else { /* takes (optional) argument */
311 optarg = NULL;
312 if (*place) /* no white space */
313 optarg = place;
314 /* XXX: disable test for :: if PC? (GNU doesn't) */
315 else if (oli[1] != ':') { /* arg not optional */
316 if (++optind >= nargc) { /* no arg */
317 place = EMSG;
318 if (PRINT_ERROR)
319 warnx(recargchar, optchar);
320 optopt = optchar;
321 return BADARG;
322 } else
323 optarg = nargv[optind];
324 }
325 place = EMSG;
326 ++optind;
327 }
328 /* dump back option letter */
329 return optchar;
330 }
331
332 #ifdef REPLACE_GETOPT
333 /*
334 * getopt --
335 * Parse argc/argv argument vector.
336 *
337 * [eventually this will replace the real getopt]
338 */
339 int
340 getopt(nargc, nargv, options)
341 int nargc;
342 char * const *nargv;
343 const char *options;
344 {
345 int retval;
346
347 _DIAGASSERT(nargv != NULL);
348 _DIAGASSERT(options != NULL);
349
350 if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
351 ++optind;
352 /*
353 * We found an option (--), so if we skipped non-options,
354 * we have to permute.
355 */
356 if (nonopt_end != -1) {
357 permute_args(nonopt_start, nonopt_end, optind,
358 nargv);
359 optind -= nonopt_end - nonopt_start;
360 }
361 nonopt_start = nonopt_end = -1;
362 retval = -1;
363 }
364 return retval;
365 }
366 #endif
367
368 /*
369 * getopt_long --
370 * Parse argc/argv argument vector.
371 */
372 int
373 getopt_long(nargc, nargv, options, long_options, idx)
374 int nargc;
375 char * const *nargv;
376 const char *options;
377 const struct option *long_options;
378 int *idx;
379 {
380 int retval;
381
382 _DIAGASSERT(nargv != NULL);
383 _DIAGASSERT(options != NULL);
384 _DIAGASSERT(long_options != NULL);
385 /* idx may be NULL */
386
387 if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
388 char *current_argv, *has_equal;
389 size_t current_argv_len;
390 int i, match;
391
392 current_argv = place;
393 match = -1;
394
395 optind++;
396 place = EMSG;
397
398 if (*current_argv == '\0') { /* found "--" */
399 /*
400 * We found an option (--), so if we skipped
401 * non-options, we have to permute.
402 */
403 if (nonopt_end != -1) {
404 permute_args(nonopt_start, nonopt_end,
405 optind, nargv);
406 optind -= nonopt_end - nonopt_start;
407 }
408 nonopt_start = nonopt_end = -1;
409 return -1;
410 }
411 if ((has_equal = strchr(current_argv, '=')) != NULL) {
412 /* argument found (--option=arg) */
413 current_argv_len = has_equal - current_argv;
414 has_equal++;
415 } else
416 current_argv_len = strlen(current_argv);
417
418 for (i = 0; long_options[i].name; i++) {
419 /* find matching long option */
420 if (strncmp(current_argv, long_options[i].name,
421 current_argv_len))
422 continue;
423
424 if (strlen(long_options[i].name) ==
425 (unsigned)current_argv_len) {
426 /* exact match */
427 match = i;
428 break;
429 }
430 if (match == -1) /* partial match */
431 match = i;
432 else {
433 /* ambiguous abbreviation */
434 if (PRINT_ERROR)
435 warnx(ambig, (int)current_argv_len,
436 current_argv);
437 optopt = 0;
438 return BADCH;
439 }
440 }
441 if (match != -1) { /* option found */
442 if (long_options[match].has_arg == no_argument
443 && has_equal) {
444 if (PRINT_ERROR)
445 warnx(noarg, (int)current_argv_len,
446 current_argv);
447 /*
448 * XXX: GNU sets optopt to val regardless of
449 * flag
450 */
451 if (long_options[match].flag == NULL)
452 optopt = long_options[match].val;
453 else
454 optopt = 0;
455 return BADARG;
456 }
457 if (long_options[match].has_arg == required_argument ||
458 long_options[match].has_arg == optional_argument) {
459 if (has_equal)
460 optarg = has_equal;
461 else if (long_options[match].has_arg ==
462 required_argument) {
463 /*
464 * optional argument doesn't use
465 * next nargv
466 */
467 optarg = nargv[optind++];
468 }
469 }
470 if ((long_options[match].has_arg == required_argument)
471 && (optarg == NULL)) {
472 /*
473 * Missing argument; leading ':'
474 * indicates no error should be generated
475 */
476 if (PRINT_ERROR)
477 warnx(recargstring, current_argv);
478 /*
479 * XXX: GNU sets optopt to val regardless
480 * of flag
481 */
482 if (long_options[match].flag == NULL)
483 optopt = long_options[match].val;
484 else
485 optopt = 0;
486 --optind;
487 return BADARG;
488 }
489 } else { /* unknown option */
490 if (PRINT_ERROR)
491 warnx(illoptstring, current_argv);
492 optopt = 0;
493 return BADCH;
494 }
495 if (long_options[match].flag) {
496 *long_options[match].flag = long_options[match].val;
497 retval = 0;
498 } else
499 retval = long_options[match].val;
500 if (idx)
501 *idx = match;
502 }
503 return retval;
504 }
This page took 0.06106 seconds and 5 git commands to generate.