xref: /illumos-gate/usr/src/lib/libc/port/gen/getopt_long.c (revision e067707b65c63939ade45e268f2b0dff6f65f75d)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
26  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 /*
35  * Copyright (c) 2000 The NetBSD Foundation, Inc.
36  * All rights reserved.
37  *
38  * This code is derived from software contributed to The NetBSD Foundation
39  * by Dieter Baron and Thomas Klausner.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *	This product includes software developed by the NetBSD
52  *	Foundation, Inc. and its contributors.
53  * 4. Neither the name of The NetBSD Foundation nor the names of its
54  *	contributors may be used to endorse or promote products derived
55  *	from this software without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67  * POSSIBILITY OF SUCH DAMAGE.
68  */
69 
70 #pragma weak getopt_clip = _getopt_clip
71 #pragma weak getopt_long = _getopt_long
72 #pragma weak getopt_long_only = _getopt_long_only
73 
74 #include "synonyms.h"
75 #include <getopt.h>
76 #include <stdio.h>
77 #include <errno.h>
78 #include <unistd.h>
79 #include <stdlib.h>
80 #include <string.h>
81 
82 #include "_libc_gettext.h"
83 
84 static int optreset = 0;	/* keep track of first entry to getopt() */
85 #define	PRINT_ERROR	((opterr) && (*options != ':'))
86 #define	FLAG_IS_SET(flag) 	((flags & flag) != 0)	/* is flag turned on? */
87 
88 /* Determine if an argument is required for this long option */
89 #define	LONGOPT_REQUIRES_ARG(longopt) \
90 		((((longopt).has_arg == optional_argument) && \
91 			(!FLAG_IS_SET(FLAG_OPTIONAL_ARGS))) || \
92 			((longopt).has_arg == required_argument))
93 
94 #define	FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
95 #define	FLAG_ALLARGS	0x02	/* treat non-options as args to option "1" */
96 #define	FLAG_LONGONLY	0x04	/* operate as getopt_long_only() */
97 #define	FLAG_OPTIONAL_ARGS 0x08	/* allow optional arguments to options */
98 #define	FLAG_REQUIRE_EQUIVALENTS 0x10 /* require short<->long equivalents */
99 #define	FLAG_ABBREV	0x20 	/* long option abbreviation allowed. */
100 #define	FLAG_W_SEMICOLON 0x40 	/* Support for W; in optstring */
101 #define	FLAG_PLUS_DASH_START 0x80	/* leading '+' or '-' in optstring */
102 
103 /* return values */
104 #define	BADCH		(int)'?'
105 #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
106 #define	INORDER 	(int)1
107 
108 #define	EMSG		""
109 
110 static int getopt_internal(int, char * const *, const char *,
111 				const struct option *, int *, uint_t);
112 static int parse_long_options(int nargc, char * const *nargv, const char *,
113 				const struct option *, int *, int,
114 				uint_t flags);
115 static int gcd(int, int);
116 static void permute_args(int, int, int, char * const *);
117 
118 static char *place = EMSG; /* option letter processing */
119 
120 /* XXX: set optreset to 1 rather than these two */
121 static int nonopt_start = -1; /* first non option argument (for permute) */
122 static int nonopt_end = -1;   /* first option after non options (for permute) */
123 
124 /*
125  * Generalized error message output.
126  *
127  * NOTE ON ERROR MESSAGES: All the error messages in this file
128  * use %s (not %c) because they are all routed through warnx(),
129  * which takes a string argument. Character arguments passed
130  * to warnxchar() are converted to strings automatically before
131  * being passed to warnx().
132  */
133 static void
134 warnx(const char *argv0, const char *msg, const char *arg) {
135 	char errbuf[256];
136 	(void) snprintf(errbuf, sizeof (errbuf), msg, argv0, arg);
137 	(void) write(2, errbuf, strlen(errbuf));
138 	(void) write(2, "\n", 1);
139 }
140 
141 /*
142  * Generalized error message output.
143  */
144 static void
145 warnxchar(const char *argv0, const char *msg, const char c) {
146 	char charbuf[2];
147 	charbuf[0] = c;
148 	charbuf[1] = '\0';
149 	warnx(argv0, msg, charbuf);
150 }
151 
152 /*
153  * Generalized error message output.
154  */
155 static void
156 warnxlen(const char *argv0, const char *msg, int argLen, const char *arg) {
157 	char argbuf[256];
158 	(void) strncpy(argbuf, arg, argLen);
159 	argbuf[argLen < (sizeof (argbuf)-1)? argLen:(sizeof (argbuf)-1)] = '\0';
160 	warnx(argv0, msg, argbuf);
161 }
162 
163 /*
164  * Compute the greatest common divisor of a and b.
165  */
166 static int
167 gcd(int a, int b)
168 {
169 	int c;
170 
171 	c = a % b;
172 	while (c != 0) {
173 		a = b;
174 		b = c;
175 		c = a % b;
176 	}
177 
178 	return (b);
179 }
180 
181 /*
182  * Exchange the block from nonopt_start to nonopt_end with the block
183  * from nonopt_end to opt_end (keeping the same order of arguments
184  * in each block).
185  */
186 static void
187 permute_args(int panonopt_start, int panonopt_end, int opt_end,
188 	char * const *nargv)
189 {
190 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
191 	char *swap;
192 
193 	/*
194 	 * compute lengths of blocks and number and size of cycles
195 	 */
196 	nnonopts = panonopt_end - panonopt_start;
197 	nopts = opt_end - panonopt_end;
198 	ncycle = gcd(nnonopts, nopts);
199 	cyclelen = (opt_end - panonopt_start) / ncycle;
200 
201 	for (i = 0; i < ncycle; i++) {
202 		cstart = panonopt_end+i;
203 		pos = cstart;
204 		for (j = 0; j < cyclelen; j++) {
205 			if (pos >= panonopt_end)
206 				pos -= nnonopts;
207 			else
208 				pos += nopts;
209 			swap = nargv[pos];
210 			((char **)nargv)[pos] = nargv[cstart];
211 			((char **)nargv)[cstart] = swap;
212 		}
213 	}
214 } /* permute_args() */
215 
216 /*
217  * Verify that each short option (character flag) has a long equivalent,
218  * and that each long option has a short option equivalent. Note that
219  * multiple long options can map to the same character.
220  *
221  * This behavior is defined by Sun's CLIP specification (11/12/02),
222  * and currently getopt_clip() is the only getopt variant that
223  * requires it.
224  *
225  * If error output is enabled and an error is found, this function
226  * prints ONE error message (the first error found) and returns an
227  * error value.
228  *
229  * ASSUMES: options != NULL
230  * ASSUMES: long_options may be NULL
231  *
232  * Returns < 0 if an error is found
233  * Returns >= 0 on success
234  */
235 static int
236 /* LINTED: argument unused in function: nargc */
237 verify_short_long_equivalents(int nargc,
238 				char *const *nargv,
239 				const char *options,
240 				const struct option *long_options,
241 				uint_t flags) {
242 	int short_i = 0;
243 	int long_i = 0;
244 	int equivFound = 0;
245 	int ch = 0;
246 
247 	/*
248 	 * Find a long option for each short option
249 	 */
250 	equivFound = 1;
251 	for (short_i = 0; equivFound && (options[short_i] != 0); ++short_i) {
252 		ch = options[short_i];
253 
254 		if (ch == ':') {
255 			continue;
256 		}
257 		if (FLAG_IS_SET(FLAG_W_SEMICOLON) &&
258 			(ch == 'W') && (options[short_i+1] == ';')) {
259 			/* W; is a special case */
260 			++short_i;
261 			continue;
262 		}
263 
264 		equivFound = 0;
265 		if (long_options != NULL) {
266 			for (long_i = 0; ((!equivFound) &&
267 					(long_options[long_i].name != NULL));
268 								++long_i) {
269 				equivFound = (ch == long_options[long_i].val);
270 			}
271 		}
272 		if ((!equivFound) && (PRINT_ERROR)) {
273 			warnxchar(nargv[0],
274 				_libc_gettext(
275 				"%s: equivalent long option required -- %s"),
276 				ch);
277 		}
278 	} /* short_i */
279 
280 	/*
281 	 * Find a short option for each long option. Note that if we came
282 	 * out of the above loop with equivFound==0, we are already done.
283 	 */
284 	if (equivFound && (long_options != NULL)) {
285 		for (long_i = 0; (equivFound &&
286 				(long_options[long_i].name != NULL));
287 								++long_i) {
288 			equivFound = ((long_options[long_i].val != 0) &&
289 				(strchr(options, long_options[long_i].val)
290 								!= NULL));
291 
292 			if ((!equivFound) && (PRINT_ERROR)) {
293 				warnx(nargv[0],
294 					_libc_gettext(
295 				"%s: equivalent short option required -- %s"),
296 					long_options[long_i].name);
297 			}
298 		} /* for long_i */
299 	}
300 
301 	return (equivFound? 0:-1);
302 } /* verify_short_long_equivalents() */
303 
304 /*
305  * parse_long_options --
306  *	Parse long options in argc/argv argument vector.
307  * Returns -1 if short_too is set and the option does not match long_options.
308  */
309 static int
310 parse_long_options(int nargc, char * const *nargv, const char *options,
311 	const struct option *long_options, int *idx, int short_too,
312 	uint_t flags)
313 {
314 	char *current_argv = NULL;
315 	char *argv_equal_ptr = NULL;
316 	size_t current_argv_len = 0;
317 	size_t long_option_len = 0;
318 	int i = 0;
319 	int match = 0;
320 
321 	current_argv = place;
322 	match = -1;
323 
324 	optind++;
325 
326 	if ((argv_equal_ptr = strchr(current_argv, '=')) != NULL) {
327 		/* argument found (--option=arg) */
328 		current_argv_len = (argv_equal_ptr - current_argv);
329 		argv_equal_ptr++;
330 	} else {
331 		current_argv_len = strlen(current_argv);
332 	}
333 
334 	for (i = 0; (long_options[i].name != NULL); i++) {
335 
336 		/* find matching long option */
337 		if (strncmp(current_argv, long_options[i].name,
338 						current_argv_len) != 0) {
339 			continue;	/* no match  */
340 		}
341 		long_option_len = strlen(long_options[i].name);
342 		if ((!FLAG_IS_SET(FLAG_ABBREV)) &&
343 		    (long_option_len > current_argv_len)) {
344 			continue;	/* Abbreviations are disabled */
345 		}
346 
347 		if (long_option_len == current_argv_len) {
348 			/* exact match */
349 			match = i;
350 			break;
351 		}
352 		/*
353 		 * If this is a known short option, don't allow
354 		 * a partial match of a single character.
355 		 */
356 		if (short_too && current_argv_len == 1)
357 			continue;
358 
359 		if (match == -1)	/* partial match */
360 			match = i;
361 		else {
362 			/* ambiguous abbreviation */
363 			if (PRINT_ERROR) {
364 				warnxlen(nargv[0],
365 				    _libc_gettext(
366 					"%s: ambiguous option -- %s"),
367 				    (int)current_argv_len,
368 				    current_argv);
369 			}
370 			optopt = 0;
371 			return (BADCH);
372 		}
373 	} /* for i */
374 	if (match != -1) {		/* option found */
375 		if ((long_options[match].has_arg == no_argument) &&
376 		    (argv_equal_ptr != NULL)) {
377 			if (PRINT_ERROR) {
378 				warnxlen(nargv[0],
379 					_libc_gettext(
380 				"%s: option doesn't take an argument -- %s"),
381 					(int)current_argv_len,
382 					current_argv);
383 			}
384 			/*
385 			 * XXX: GNU sets optopt to val regardless of flag
386 			 */
387 			if (long_options[match].flag == NULL)
388 				optopt = long_options[match].val;
389 			else
390 				optopt = 0;
391 			return (BADARG);
392 		}
393 		if (long_options[match].has_arg == required_argument ||
394 		    long_options[match].has_arg == optional_argument) {
395 			if (argv_equal_ptr != NULL) {
396 				optarg = argv_equal_ptr;
397 			} else if (LONGOPT_REQUIRES_ARG(long_options[match])) {
398 				/* The next argv must be the option argument */
399 				if (optind < nargc) {
400 				    optarg = nargv[optind];
401 				}
402 				++optind; /* code below depends on this */
403 			}
404 		}
405 		if (LONGOPT_REQUIRES_ARG(long_options[match]) &&
406 		    (optarg == NULL)) {
407 			/*
408 			 * Missing argument; leading ':' indicates no error
409 			 * should be generated.
410 			 */
411 			if (PRINT_ERROR) {
412 				warnx(nargv[0],
413 					_libc_gettext(
414 				"%s: option requires an argument -- %s"),
415 					current_argv);
416 			}
417 			/*
418 			 * XXX: GNU sets optopt to val regardless of flag
419 			 */
420 			if (long_options[match].flag == NULL)
421 				optopt = long_options[match].val;
422 			else
423 				optopt = 0;
424 			--optind;
425 			return (BADARG);
426 		}
427 	} else {			/* unknown option */
428 		if (short_too) {
429 			--optind;
430 			return (-1);
431 		}
432 		if (PRINT_ERROR) {
433 			warnx(nargv[0],
434 				_libc_gettext("%s: illegal option -- %s"),
435 				current_argv);
436 		}
437 		optopt = 0;
438 		return (BADCH);
439 	}
440 	if (idx)
441 		*idx = match;
442 	if (long_options[match].flag != NULL) {
443 		*long_options[match].flag = long_options[match].val;
444 		return (0);
445 	} else {
446 		optopt = long_options[match].val;
447 		return (optopt);
448 	}
449 } /* parse_long_options() */
450 
451 /*
452  * getopt_internal() --
453  *	Parse argc/argv argument vector.  Called by user level routines.
454  *
455  * This implements all of the getopt_long(), getopt_long_only(),
456  * getopt_clip() variants.
457  */
458 static int
459 getopt_internal(int nargc, char * const *nargv, const char *options,
460 	const struct option *long_options, int *idx, uint_t flags)
461 {
462 	char *oli;				/* option letter list index */
463 	int optchar, short_too;
464 	static int posixly_correct = -1;
465 
466 	if (options == NULL)
467 		return (-1);
468 
469 	/*
470 	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
471 	 * string begins with a '+'.
472 	 */
473 	if (posixly_correct == -1) {
474 		posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
475 	}
476 	if (FLAG_IS_SET(FLAG_PLUS_DASH_START)) {
477 		/*
478 		 * + or - at start of optstring takes precedence
479 		 * over POSIXLY_CORRECT.
480 		 */
481 		if (*options == '+') {
482 			/*
483 			 * leading + means POSIX-compliant; first non-option
484 			 * ends option list. Therefore, don't permute args.
485 			 */
486 			posixly_correct = 1;
487 		} else if (*options == '-') {
488 			posixly_correct = 0;
489 			flags |= FLAG_ALLARGS;
490 		}
491 		if ((*options == '+') || (*options == '-')) {
492 			options++;
493 		}
494 	} /* if FLAG_PLUS_DASH_START */
495 
496 	if (posixly_correct) {
497 	    flags &= ~FLAG_PERMUTE;
498 	    flags &= ~FLAG_ALLARGS;
499 	    flags &= ~FLAG_OPTIONAL_ARGS;
500 	}
501 
502 	/*
503 	 * Some programs (like GNU cvs) set optind to 0 to restart
504 	 * option processing. Work around this braindamage.
505 	 *
506 	 * The above problem comes from using global variables. We
507 	 * should avoid their use in the future.
508 	 */
509 	if (optind == 0) {
510 		optind = optreset = 1;
511 	}
512 
513 	optarg = NULL;
514 	optopt = 0;
515 
516 	if (optreset) {
517 		nonopt_start = nonopt_end = -1;
518 	}
519 
520 	/*
521 	 * On the first call, make sure that there is a short equivalent
522 	 * for each long option, and vice versa. This is required by
523 	 * Sun's CLIP specification (11/12/02).
524 	 */
525 	if ((optind == 1) && FLAG_IS_SET(FLAG_REQUIRE_EQUIVALENTS)) {
526 	    if (verify_short_long_equivalents(
527 			nargc, nargv, options, long_options, flags) < 0) {
528 		/* function printed any necessary messages */
529 		errno = EINVAL;		/* invalid argument */
530 		return (-1);
531 	    }
532 	}
533 
534 start:
535 	if (optreset || !*place) {		/* update scanning pointer */
536 		optreset = 0;
537 		if (optind >= nargc) {		/* end of argument vector */
538 			place = EMSG;
539 			if (nonopt_end != -1) {
540 				/* do permutation, if we have to */
541 				permute_args(nonopt_start, nonopt_end,
542 				    optind, nargv);
543 				optind -= nonopt_end - nonopt_start;
544 
545 			} else if (nonopt_start != -1) {
546 				/*
547 				 * If we skipped non-options, set optind
548 				 * to the first of them.
549 				 */
550 				optind = nonopt_start;
551 			}
552 			nonopt_start = nonopt_end = -1;
553 			return (-1);
554 		}
555 		if ((*(place = nargv[optind]) != '-') || (place[1] == '\0')) {
556 			place = EMSG;		/* found non-option */
557 			if (flags & FLAG_ALLARGS) {
558 				/*
559 				 * GNU extension:
560 				 * return non-option as argument to option '\1'
561 				 */
562 				optarg = nargv[optind++];
563 				return (INORDER);
564 			}
565 			if (!(flags & FLAG_PERMUTE)) {
566 				/*
567 				 * If no permutation wanted, stop parsing
568 				 * at first non-option.
569 				 */
570 				return (-1);
571 			}
572 			/* do permutation */
573 			if (nonopt_start == -1)
574 				nonopt_start = optind;
575 			else if (nonopt_end != -1) {
576 				permute_args(nonopt_start, nonopt_end,
577 				    optind, nargv);
578 				nonopt_start = optind -
579 				    (nonopt_end - nonopt_start);
580 				nonopt_end = -1;
581 			}
582 			optind++;
583 			/* process next argument */
584 			goto start;
585 		}
586 		if (nonopt_start != -1 && nonopt_end == -1)
587 			nonopt_end = optind;
588 
589 		/*
590 		 * Check for "--" or "--foo" with no long options
591 		 * but if place is simply "-" leave it unmolested.
592 		 */
593 		if (place[1] != '\0' && *++place == '-' &&
594 		    (place[1] == '\0' || long_options == NULL)) {
595 			optind++;
596 			place = EMSG;
597 			/*
598 			 * We found an option (--), so if we skipped
599 			 * non-options, we have to permute.
600 			 */
601 			if (nonopt_end != -1) {
602 				permute_args(nonopt_start, nonopt_end,
603 				    optind, nargv);
604 				optind -= nonopt_end - nonopt_start;
605 			}
606 			nonopt_start = nonopt_end = -1;
607 			return (-1);
608 		}
609 	}
610 
611 	/*
612 	 * Check long options if:
613 	 *  1) we were passed some
614 	 *  2) the arg is not just "-"
615 	 *  3) either the arg starts with -- or we are getopt_long_only()
616 	 */
617 	if (long_options != NULL && place != nargv[optind] &&
618 	    (*place == '-' || (FLAG_IS_SET(FLAG_LONGONLY)))) {
619 		short_too = 0;
620 		if (*place == '-')
621 			place++;		/* --foo long option */
622 		else if (*place != ':' && strchr(options, *place) != NULL)
623 			short_too = 1;		/* could be short option too */
624 
625 		optchar = parse_long_options(nargc, nargv, options,
626 				long_options, idx, short_too, flags);
627 		if (optchar != -1) {
628 			place = EMSG;
629 			return (optchar);
630 		}
631 	}
632 
633 	if ((optchar = (int)*place++) == (int)':' ||
634 	    (oli = strchr(options, optchar)) == NULL) {
635 		/*
636 		 * If the user didn't specify '-' as an option,
637 		 * assume it means -1 as POSIX specifies.
638 		 */
639 		if (optchar == (int)'-')
640 			return (-1);
641 		/* option letter unknown or ':' */
642 		if (!*place)
643 			++optind;
644 		if (PRINT_ERROR)
645 			warnxchar(nargv[0],
646 				_libc_gettext("%s: illegal option -- %s"),
647 				optchar);
648 		optopt = optchar;
649 		return (BADCH);
650 	}
651 	if (FLAG_IS_SET(FLAG_W_SEMICOLON) &&
652 	    (long_options != NULL) && (optchar == 'W') && (oli[1] == ';')) {
653 		/* -W long-option */
654 		/* LINTED: statement has no consequent: if */
655 		if (*place) {			/* no space */
656 			/* NOTHING */;
657 		} else if (++optind >= nargc) {	/* no long-option after -W */
658 			place = EMSG;
659 			if (PRINT_ERROR)
660 				warnxchar(nargv[0],
661 					_libc_gettext(
662 				"%s: option requires an argument -- %s"),
663 					optchar);
664 			optopt = optchar;
665 			return (BADARG);
666 		} else {			/* white space */
667 			place = nargv[optind];
668 		}
669 		optchar = parse_long_options(
670 				nargc, nargv, options, long_options,
671 		    idx, 0, flags);
672 
673 		/*
674 		 * PSARC 2003/645 - Match GNU behavior, set optarg to
675 		 * the long-option.
676 		 */
677 		if (optarg == NULL) {
678 			optarg = nargv[optind-1];
679 		}
680 		place = EMSG;
681 		return (optchar);
682 	}
683 	if (*++oli != ':') {			/* doesn't take argument */
684 		if (!*place)
685 			++optind;
686 	} else {				/* takes (optional) argument */
687 		optarg = NULL;
688 		if (*place) {			/* no white space */
689 			optarg = place;
690 		/* XXX: disable test for :: if PC? (GNU doesn't) */
691 		} else if (!(FLAG_IS_SET(FLAG_OPTIONAL_ARGS) &&
692 			    (oli[1] == ':'))) {
693 			/* arg is required (not optional) */
694 
695 			if (++optind >= nargc) {	/* no arg */
696 				place = EMSG;
697 				if (PRINT_ERROR) {
698 					warnxchar(nargv[0],
699 						_libc_gettext(
700 				"%s: option requires an argument -- %s"),
701 						optchar);
702 				}
703 				optopt = optchar;
704 				return (BADARG);
705 			} else
706 				optarg = nargv[optind];
707 		}
708 		place = EMSG;
709 		++optind;
710 	}
711 	/* return valid option letter */
712 	optopt = optchar;		/* preserve getopt() behavior */
713 	return (optchar);
714 } /* getopt_internal() */
715 
716 /*
717  * getopt_long() --
718  *	Parse argc/argv argument vector.
719  *
720  * Requires that long options be preceded with a two dashes
721  * (e.g., --longoption).
722  */
723 int
724 getopt_long(int nargc, char *const *nargv,
725 		const char *optstring,
726 		const struct option *long_options, int *long_index)
727 {
728 
729 	return (getopt_internal(
730 		nargc, nargv, optstring, long_options, long_index,
731 		FLAG_PERMUTE
732 		| FLAG_OPTIONAL_ARGS
733 		| FLAG_ABBREV
734 		| FLAG_W_SEMICOLON
735 		| FLAG_PLUS_DASH_START));
736 } /* getopt_long() */
737 
738 /*
739  * getopt_long_only() --
740  *	Parse argc/argv argument vector.
741  *
742  * Long options may be preceded with a single dash (e.g., -longoption)
743  */
744 int
745 getopt_long_only(int nargc, char *const *nargv,
746 		const char *optstring,
747 		const struct option *long_options, int *long_index)
748 {
749 
750 	return (getopt_internal(
751 		nargc, nargv, optstring, long_options, long_index,
752 		FLAG_PERMUTE
753 		| FLAG_OPTIONAL_ARGS
754 		| FLAG_ABBREV
755 		| FLAG_W_SEMICOLON
756 		| FLAG_PLUS_DASH_START
757 		| FLAG_LONGONLY));
758 } /* getopt_long_only() */
759 
760 /*
761  * getopt_clip() --
762  *	Parse argc/argv argument vector, requiring compliance with
763  * 	Sun's CLIP specification (11/12/02)
764  *
765  * o Does not allow arguments to be optional (optional_argument is
766  *   treated as required_argument).
767  *
768  * o Does not allow long options to be abbreviated on the command line
769  *
770  * o Does not allow long argument to be preceded by a single dash
771  *   (Double-dash '--' is required)
772  *
773  * o Stops option processing at the first non-option
774  *
775  * o Requires that every long option have a short-option (single
776  *   character) equivalent and vice-versa. If a short option or
777  *   long option without an equivalent is found, an error message
778  *   is printed and -1 is returned on the first call, and errno
779  *   is set to EINVAL.
780  *
781  * o Leading + or - in optstring is ignored, and opstring is
782  *   treated as if it began after the + or - .
783  */
784 int
785 getopt_clip(int nargc, char *const *nargv,
786 		const char *optstring,
787 		const struct option *long_options, int *long_index)
788 {
789 	return getopt_internal(
790 		nargc, nargv, optstring, long_options, long_index,
791 		/*
792 		 * no permutation,
793 		 * no optional args,
794 		 * no long-only,
795 		 * no abbreviations
796 		 * no support for +- at start of optstring
797 		 * yes support for "W;" in optstring
798 		 */
799 		FLAG_W_SEMICOLON
800 		| FLAG_REQUIRE_EQUIVALENTS);
801 } /* getopt_clip() */
802