xref: /titanic_50/usr/src/lib/libc/port/gen/getopt.c (revision 7257d1b4d25bfac0c802847390e98a464fd787ac)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7257d1b4Sraf  * Common Development and Distribution License (the "License").
6*7257d1b4Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21*7257d1b4Sraf 
227c478bd9Sstevel@tonic-gate /*
23*7257d1b4Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
287c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
297c478bd9Sstevel@tonic-gate 
30*7257d1b4Sraf #pragma ident	"%Z%%M%	%I%	%E% SMI"
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  * See getopt(3C) and SUS/XPG getopt() for function definition and
347c478bd9Sstevel@tonic-gate  * requirements.
357c478bd9Sstevel@tonic-gate  *
367c478bd9Sstevel@tonic-gate  * This actual implementation is a bit looser than the specification
377c478bd9Sstevel@tonic-gate  * as it allows any character other than ':' and '(' to be used as
387c478bd9Sstevel@tonic-gate  * a short option character - The specification only guarantees the
397c478bd9Sstevel@tonic-gate  * alnum characters ([a-z][A-Z][0-9]).
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
42*7257d1b4Sraf #pragma weak _getopt = getopt
437c478bd9Sstevel@tonic-gate 
44*7257d1b4Sraf #include "lint.h"
457c478bd9Sstevel@tonic-gate #include "_libc_gettext.h"
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #include <unistd.h>
487c478bd9Sstevel@tonic-gate #include <string.h>
497c478bd9Sstevel@tonic-gate #include <stdio.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate /*
527c478bd9Sstevel@tonic-gate  * Generalized error processing macro. The parameter i is a pointer to
537c478bd9Sstevel@tonic-gate  * the failed option string. If it is NULL, the character in c is converted
547c478bd9Sstevel@tonic-gate  * to a string and displayed instead. s is the error text.
557c478bd9Sstevel@tonic-gate  *
567c478bd9Sstevel@tonic-gate  * This could be / should be a static function if it is used more, but
577c478bd9Sstevel@tonic-gate  * that would require moving the 'optstring[0]' test outside of the
587c478bd9Sstevel@tonic-gate  * function.
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate #define	ERR(s, c, i)	if (opterr && optstring[0] != ':') { \
617c478bd9Sstevel@tonic-gate 	char errbuf[256]; \
627c478bd9Sstevel@tonic-gate 	char cbuf[2]; \
637c478bd9Sstevel@tonic-gate 	cbuf[0] = c; \
647c478bd9Sstevel@tonic-gate 	cbuf[1] = '\0'; \
657c478bd9Sstevel@tonic-gate 	(void) snprintf(errbuf, sizeof (errbuf), s, argv[0], \
667c478bd9Sstevel@tonic-gate 	    (i ? argv[i]+2 : cbuf)); \
677c478bd9Sstevel@tonic-gate 	(void) write(2, errbuf, strlen(errbuf)); }
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
707c478bd9Sstevel@tonic-gate  * _sp is required to keep state between successive calls to getopt() while
717c478bd9Sstevel@tonic-gate  * extracting aggregated short-options (ie: -abcd). Hence, getopt() is not
727c478bd9Sstevel@tonic-gate  * thread safe or reentrant, but it really doesn't matter.
737c478bd9Sstevel@tonic-gate  *
747c478bd9Sstevel@tonic-gate  * So, why isn't this "static" you ask?  Because the historical Bourne
757c478bd9Sstevel@tonic-gate  * shell has actually latched on to this little piece of private data.
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate int _sp = 1;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /*
807c478bd9Sstevel@tonic-gate  * Determine if the specified character (c) is present in the string
817c478bd9Sstevel@tonic-gate  * (optstring) as a regular, single character option. If the option is found,
827c478bd9Sstevel@tonic-gate  * return a pointer into optstring pointing at the short-option character,
837c478bd9Sstevel@tonic-gate  * otherwise return null. The characters ':' and '(' are not allowed.
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate static char *
parseshort(const char * optstring,const char c)867c478bd9Sstevel@tonic-gate parseshort(const char *optstring, const char c)
877c478bd9Sstevel@tonic-gate {
887c478bd9Sstevel@tonic-gate 	char *cp = (char *)optstring;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	if (c == ':' || c == '(')
917c478bd9Sstevel@tonic-gate 		return (NULL);
927c478bd9Sstevel@tonic-gate 	do {
937c478bd9Sstevel@tonic-gate 		if (*cp == c)
947c478bd9Sstevel@tonic-gate 			return (cp);
957c478bd9Sstevel@tonic-gate 		while (*cp == '(')
967c478bd9Sstevel@tonic-gate 			while (*cp != '\0' && *cp != ')')
977c478bd9Sstevel@tonic-gate 				cp++;
987c478bd9Sstevel@tonic-gate 	} while (*cp++ != '\0');
997c478bd9Sstevel@tonic-gate 	return (NULL);
1007c478bd9Sstevel@tonic-gate }
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate /*
1037c478bd9Sstevel@tonic-gate  * Determine if the specified string (opt) is present in the string
1047c478bd9Sstevel@tonic-gate  * (optstring) as a long-option contained within parenthesis. If the
1057c478bd9Sstevel@tonic-gate  * long-option specifies option-argument, return a pointer to it in
1067c478bd9Sstevel@tonic-gate  * longoptarg.  Otherwise set longoptarg to null. If the option is found,
1077c478bd9Sstevel@tonic-gate  * return a pointer into optstring pointing at the short-option character
1087c478bd9Sstevel@tonic-gate  * associated with this long-option; otherwise return null.
1097c478bd9Sstevel@tonic-gate  *
1107c478bd9Sstevel@tonic-gate  * optstring 	The entire optstring passed to getopt() by the caller
1117c478bd9Sstevel@tonic-gate  *
1127c478bd9Sstevel@tonic-gate  * opt		The long option read from the command line
1137c478bd9Sstevel@tonic-gate  *
1147c478bd9Sstevel@tonic-gate  * longoptarg	The argument to the option is returned in this parameter,
1157c478bd9Sstevel@tonic-gate  *              if an option exists. Possible return values in longoptarg
1167c478bd9Sstevel@tonic-gate  *              are:
1177c478bd9Sstevel@tonic-gate  *                  NULL		No argument was found
1187c478bd9Sstevel@tonic-gate  *		    empty string ("")	Argument was explicitly left empty
1197c478bd9Sstevel@tonic-gate  *					by the user (e.g., --option= )
1207c478bd9Sstevel@tonic-gate  *		    valid string	Argument found on the command line
1217c478bd9Sstevel@tonic-gate  *
1227c478bd9Sstevel@tonic-gate  * returns	Pointer to equivalent short-option in optstring, null
1237c478bd9Sstevel@tonic-gate  *              if option not found in optstring.
1247c478bd9Sstevel@tonic-gate  *
1257c478bd9Sstevel@tonic-gate  * ASSUMES: No parameters are NULL
1267c478bd9Sstevel@tonic-gate  *
1277c478bd9Sstevel@tonic-gate  */
1287c478bd9Sstevel@tonic-gate static char *
parselong(const char * optstring,const char * opt,char ** longoptarg)1297c478bd9Sstevel@tonic-gate parselong(const char *optstring, const char *opt, char **longoptarg)
1307c478bd9Sstevel@tonic-gate {
1317c478bd9Sstevel@tonic-gate 	char	*cp;	/* ptr into optstring, beginning of one option spec. */
1327c478bd9Sstevel@tonic-gate 	char	*ip;	/* ptr into optstring, traverses every char */
1337c478bd9Sstevel@tonic-gate 	char	*op;	/* pointer into opt */
1347c478bd9Sstevel@tonic-gate 	int	match;	/* nonzero if opt is matching part of optstring */
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	cp = ip = (char *)optstring;
1377c478bd9Sstevel@tonic-gate 	do {
1387c478bd9Sstevel@tonic-gate 		if (*ip != '(' && *++ip == '\0')
1397c478bd9Sstevel@tonic-gate 			break;
1407c478bd9Sstevel@tonic-gate 		if (*ip == ':' && *++ip == '\0')
1417c478bd9Sstevel@tonic-gate 			break;
1427c478bd9Sstevel@tonic-gate 		while (*ip == '(') {
1437c478bd9Sstevel@tonic-gate 			if (*++ip == '\0')
1447c478bd9Sstevel@tonic-gate 				break;
1457c478bd9Sstevel@tonic-gate 			op = (char *)opt;
1467c478bd9Sstevel@tonic-gate 			match = 1;
1477c478bd9Sstevel@tonic-gate 			while (*ip != ')' && *ip != '\0' && *op != '\0')
1487c478bd9Sstevel@tonic-gate 				match = (*ip++ == *op++ && match);
1497c478bd9Sstevel@tonic-gate 			if (match && *ip == ')' &&
1507c478bd9Sstevel@tonic-gate 			    (*op == '\0' || *op == '=')) {
1517c478bd9Sstevel@tonic-gate 				if ((*op) == '=') {
1527c478bd9Sstevel@tonic-gate 					/* may be an empty string - OK */
1537c478bd9Sstevel@tonic-gate 					(*longoptarg) = op + 1;
1547c478bd9Sstevel@tonic-gate 				} else {
1557c478bd9Sstevel@tonic-gate 					(*longoptarg) = NULL;
1567c478bd9Sstevel@tonic-gate 				}
1577c478bd9Sstevel@tonic-gate 				return (cp);
1587c478bd9Sstevel@tonic-gate 			}
1597c478bd9Sstevel@tonic-gate 			if (*ip == ')' && *++ip == '\0')
1607c478bd9Sstevel@tonic-gate 				break;
1617c478bd9Sstevel@tonic-gate 		}
1627c478bd9Sstevel@tonic-gate 		cp = ip;
1637c478bd9Sstevel@tonic-gate 		/*
1647c478bd9Sstevel@tonic-gate 		 * Handle double-colon in optstring ("a::(longa)")
1657c478bd9Sstevel@tonic-gate 		 * The old getopt() accepts it and treats it as a
1667c478bd9Sstevel@tonic-gate 		 * required argument.
1677c478bd9Sstevel@tonic-gate 		 */
1687c478bd9Sstevel@tonic-gate 		while ((cp > optstring) && ((*cp) == ':')) {
1697c478bd9Sstevel@tonic-gate 			--cp;
1707c478bd9Sstevel@tonic-gate 		}
1717c478bd9Sstevel@tonic-gate 	} while (*cp != '\0');
1727c478bd9Sstevel@tonic-gate 	return (NULL);
1737c478bd9Sstevel@tonic-gate } /* parselong() */
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate /*
1767c478bd9Sstevel@tonic-gate  * External function entry point.
1777c478bd9Sstevel@tonic-gate  */
1787c478bd9Sstevel@tonic-gate int
getopt(int argc,char * const * argv,const char * optstring)1797c478bd9Sstevel@tonic-gate getopt(int argc, char *const *argv, const char *optstring)
1807c478bd9Sstevel@tonic-gate {
1817c478bd9Sstevel@tonic-gate 	char	c;
1827c478bd9Sstevel@tonic-gate 	char	*cp;
1837c478bd9Sstevel@tonic-gate 	int	longopt;
1847c478bd9Sstevel@tonic-gate 	char	*longoptarg;
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	/*
1877c478bd9Sstevel@tonic-gate 	 * Has the end of the options been encountered?  The following
1887c478bd9Sstevel@tonic-gate 	 * implements the SUS requirements:
1897c478bd9Sstevel@tonic-gate 	 *
1907c478bd9Sstevel@tonic-gate 	 * If, when getopt() is called:
1917c478bd9Sstevel@tonic-gate 	 *	argv[optind]	is a null pointer
1927c478bd9Sstevel@tonic-gate 	 *	*argv[optind]	is not the character '-'
1937c478bd9Sstevel@tonic-gate 	 *	argv[optind]	points to the string "-"
1947c478bd9Sstevel@tonic-gate 	 * getopt() returns -1 without changing optind. If
1957c478bd9Sstevel@tonic-gate 	 *	argv[optind]	points to the string "--"
1967c478bd9Sstevel@tonic-gate 	 * getopt() returns -1 after incrementing optind.
1977c478bd9Sstevel@tonic-gate 	 */
1987c478bd9Sstevel@tonic-gate 	if (_sp == 1) {
1997c478bd9Sstevel@tonic-gate 		if (optind >= argc || argv[optind][0] != '-' ||
2007c478bd9Sstevel@tonic-gate 		    argv[optind] == NULL || argv[optind][1] == '\0')
2017c478bd9Sstevel@tonic-gate 			return (EOF);
2027c478bd9Sstevel@tonic-gate 		else if (strcmp(argv[optind], "--") == NULL) {
2037c478bd9Sstevel@tonic-gate 			optind++;
2047c478bd9Sstevel@tonic-gate 			return (EOF);
2057c478bd9Sstevel@tonic-gate 		}
2067c478bd9Sstevel@tonic-gate 	}
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	/*
2097c478bd9Sstevel@tonic-gate 	 * Getting this far indicates that an option has been encountered.
2107c478bd9Sstevel@tonic-gate 	 * Note that the syntax of optstring applies special meanings to
2117c478bd9Sstevel@tonic-gate 	 * the characters ':' and '(', so they are not permissible as
2127c478bd9Sstevel@tonic-gate 	 * option letters. A special meaning is also applied to the ')'
2137c478bd9Sstevel@tonic-gate 	 * character, but its meaning can be determined from context.
2147c478bd9Sstevel@tonic-gate 	 * Note that the specification only requires that the alnum
2157c478bd9Sstevel@tonic-gate 	 * characters be accepted.
2167c478bd9Sstevel@tonic-gate 	 *
2177c478bd9Sstevel@tonic-gate 	 * If the second character of the argument is a '-' this must be
2187c478bd9Sstevel@tonic-gate 	 * a long-option, otherwise it must be a short option.  Scan for
2197c478bd9Sstevel@tonic-gate 	 * the option in optstring by the appropriate algorithm. Either
2207c478bd9Sstevel@tonic-gate 	 * scan will return a pointer to the short-option character in
2217c478bd9Sstevel@tonic-gate 	 * optstring if the option is found and NULL otherwise.
2227c478bd9Sstevel@tonic-gate 	 *
2237c478bd9Sstevel@tonic-gate 	 * For an unrecognized long-option, optopt will equal 0, but
2247c478bd9Sstevel@tonic-gate 	 * since long-options can't aggregate the failing option can
2257c478bd9Sstevel@tonic-gate 	 * be identified by argv[optind-1].
2267c478bd9Sstevel@tonic-gate 	 */
2277c478bd9Sstevel@tonic-gate 	optopt = c = (unsigned char)argv[optind][_sp];
2287c478bd9Sstevel@tonic-gate 	optarg = NULL;
2297c478bd9Sstevel@tonic-gate 	longopt = (_sp == 1 && c == '-');
2307c478bd9Sstevel@tonic-gate 	if (!(longopt ?
2317c478bd9Sstevel@tonic-gate 	    ((cp = parselong(optstring, argv[optind]+2, &longoptarg)) != NULL) :
2327c478bd9Sstevel@tonic-gate 	    ((cp = parseshort(optstring, c)) != NULL))) {
2337c478bd9Sstevel@tonic-gate 		ERR(_libc_gettext("%s: illegal option -- %s\n"),
2347c478bd9Sstevel@tonic-gate 		    c, (longopt ? optind : 0));
2357c478bd9Sstevel@tonic-gate 		/*
2367c478bd9Sstevel@tonic-gate 		 * Note: When the long option is unrecognized, optopt
2377c478bd9Sstevel@tonic-gate 		 * will be '-' here, which matches the specification.
2387c478bd9Sstevel@tonic-gate 		 */
2397c478bd9Sstevel@tonic-gate 		if (argv[optind][++_sp] == '\0' || longopt) {
2407c478bd9Sstevel@tonic-gate 			optind++;
2417c478bd9Sstevel@tonic-gate 			_sp = 1;
2427c478bd9Sstevel@tonic-gate 		}
2437c478bd9Sstevel@tonic-gate 		return ('?');
2447c478bd9Sstevel@tonic-gate 	}
2457c478bd9Sstevel@tonic-gate 	optopt = c = *cp;
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	/*
2487c478bd9Sstevel@tonic-gate 	 * A valid option has been identified.  If it should have an
2497c478bd9Sstevel@tonic-gate 	 * option-argument, process that now.  SUS defines the setting
2507c478bd9Sstevel@tonic-gate 	 * of optarg as follows:
2517c478bd9Sstevel@tonic-gate 	 *
2527c478bd9Sstevel@tonic-gate 	 *   1.	If the option was the last character in the string pointed to
2537c478bd9Sstevel@tonic-gate 	 *	by an element of argv, then optarg contains the next element
2547c478bd9Sstevel@tonic-gate 	 *	of argv, and optind is incremented by 2. If the resulting
2557c478bd9Sstevel@tonic-gate 	 *	value of optind is not less than argc, this indicates a
2567c478bd9Sstevel@tonic-gate 	 *	missing option-argument, and getopt() returns an error
2577c478bd9Sstevel@tonic-gate 	 *	indication.
2587c478bd9Sstevel@tonic-gate 	 *
2597c478bd9Sstevel@tonic-gate 	 *   2.	Otherwise, optarg points to the string following the option
2607c478bd9Sstevel@tonic-gate 	 *	character in that element of argv, and optind is incremented
2617c478bd9Sstevel@tonic-gate 	 *	by 1.
2627c478bd9Sstevel@tonic-gate 	 *
2637c478bd9Sstevel@tonic-gate 	 * The second clause allows -abcd (where b requires an option-argument)
2647c478bd9Sstevel@tonic-gate 	 * to be interpreted as "-a -b cd".
2657c478bd9Sstevel@tonic-gate 	 *
2667c478bd9Sstevel@tonic-gate 	 * Note that the option-argument can legally be an empty string,
2677c478bd9Sstevel@tonic-gate 	 * such as:
2687c478bd9Sstevel@tonic-gate 	 * 	command --option= operand
2697c478bd9Sstevel@tonic-gate 	 * which explicitly sets the value of --option to nil
2707c478bd9Sstevel@tonic-gate 	 */
2717c478bd9Sstevel@tonic-gate 	if (*(cp + 1) == ':') {
2727c478bd9Sstevel@tonic-gate 		/* The option takes an argument */
2737c478bd9Sstevel@tonic-gate 		if (!longopt && argv[optind][_sp+1] != '\0') {
2747c478bd9Sstevel@tonic-gate 			optarg = &argv[optind++][_sp+1];
2757c478bd9Sstevel@tonic-gate 		} else if (longopt && longoptarg) {
2767c478bd9Sstevel@tonic-gate 			/*
2777c478bd9Sstevel@tonic-gate 			 * The option argument was explicitly set to
2787c478bd9Sstevel@tonic-gate 			 * the empty string on the command line (--option=)
2797c478bd9Sstevel@tonic-gate 			 */
2807c478bd9Sstevel@tonic-gate 			optind++;
2817c478bd9Sstevel@tonic-gate 			optarg = longoptarg;
2827c478bd9Sstevel@tonic-gate 		} else if (++optind >= argc) {
2837c478bd9Sstevel@tonic-gate 			ERR(_libc_gettext("%s: option requires an argument" \
2847c478bd9Sstevel@tonic-gate 			    " -- %s\n"), c, (longopt ? optind - 1 : 0));
2857c478bd9Sstevel@tonic-gate 			_sp = 1;
2867c478bd9Sstevel@tonic-gate 			optarg = NULL;
2877c478bd9Sstevel@tonic-gate 			return (optstring[0] == ':' ? ':' : '?');
2887c478bd9Sstevel@tonic-gate 		} else
2897c478bd9Sstevel@tonic-gate 			optarg = argv[optind++];
2907c478bd9Sstevel@tonic-gate 		_sp = 1;
2917c478bd9Sstevel@tonic-gate 	} else {
2927c478bd9Sstevel@tonic-gate 		/* The option does NOT take an argument */
2937c478bd9Sstevel@tonic-gate 		if (longopt && (longoptarg != NULL)) {
2947c478bd9Sstevel@tonic-gate 			/* User supplied an arg to an option that takes none */
2957c478bd9Sstevel@tonic-gate 			ERR(_libc_gettext(
2967c478bd9Sstevel@tonic-gate 			    "%s: option doesn't take an argument -- %s\n"),
2977c478bd9Sstevel@tonic-gate 			    0, (longopt ? optind : 0));
2987c478bd9Sstevel@tonic-gate 			optarg = longoptarg = NULL;
2997c478bd9Sstevel@tonic-gate 			c = '?';
3007c478bd9Sstevel@tonic-gate 		}
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 		if (longopt || argv[optind][++_sp] == '\0') {
3037c478bd9Sstevel@tonic-gate 			_sp = 1;
3047c478bd9Sstevel@tonic-gate 			optind++;
3057c478bd9Sstevel@tonic-gate 		}
3067c478bd9Sstevel@tonic-gate 		optarg = NULL;
3077c478bd9Sstevel@tonic-gate 	}
3087c478bd9Sstevel@tonic-gate 	return (c);
3097c478bd9Sstevel@tonic-gate } /* getopt() */
310