Home | History | Annotate | Download | only in gen
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1988 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 /*
     33  * See getopt(3C) and SUS/XPG getopt() for function definition and
     34  * requirements.
     35  *
     36  * This actual implementation is a bit looser than the specification
     37  * as it allows any character other than ':' and '(' to be used as
     38  * a short option character - The specification only guarantees the
     39  * alnum characters ([a-z][A-Z][0-9]).
     40  */
     41 
     42 #pragma weak _getopt = getopt
     43 
     44 #include "lint.h"
     45 #include "_libc_gettext.h"
     46 
     47 #include <unistd.h>
     48 #include <string.h>
     49 #include <stdio.h>
     50 
     51 /*
     52  * Generalized error processing macro. The parameter i is a pointer to
     53  * the failed option string. If it is NULL, the character in c is converted
     54  * to a string and displayed instead. s is the error text.
     55  *
     56  * This could be / should be a static function if it is used more, but
     57  * that would require moving the 'optstring[0]' test outside of the
     58  * function.
     59  */
     60 #define	ERR(s, c, i)	if (opterr && optstring[0] != ':') { \
     61 	char errbuf[256]; \
     62 	char cbuf[2]; \
     63 	cbuf[0] = c; \
     64 	cbuf[1] = '\0'; \
     65 	(void) snprintf(errbuf, sizeof (errbuf), s, argv[0], \
     66 	    (i ? argv[i]+2 : cbuf)); \
     67 	(void) write(2, errbuf, strlen(errbuf)); }
     68 
     69 /*
     70  * _sp is required to keep state between successive calls to getopt() while
     71  * extracting aggregated short-options (ie: -abcd). Hence, getopt() is not
     72  * thread safe or reentrant, but it really doesn't matter.
     73  *
     74  * So, why isn't this "static" you ask?  Because the historical Bourne
     75  * shell has actually latched on to this little piece of private data.
     76  */
     77 int _sp = 1;
     78 
     79 /*
     80  * Determine if the specified character (c) is present in the string
     81  * (optstring) as a regular, single character option. If the option is found,
     82  * return a pointer into optstring pointing at the short-option character,
     83  * otherwise return null. The characters ':' and '(' are not allowed.
     84  */
     85 static char *
     86 parseshort(const char *optstring, const char c)
     87 {
     88 	char *cp = (char *)optstring;
     89 
     90 	if (c == ':' || c == '(')
     91 		return (NULL);
     92 	do {
     93 		if (*cp == c)
     94 			return (cp);
     95 		while (*cp == '(')
     96 			while (*cp != '\0' && *cp != ')')
     97 				cp++;
     98 	} while (*cp++ != '\0');
     99 	return (NULL);
    100 }
    101 
    102 /*
    103  * Determine if the specified string (opt) is present in the string
    104  * (optstring) as a long-option contained within parenthesis. If the
    105  * long-option specifies option-argument, return a pointer to it in
    106  * longoptarg.  Otherwise set longoptarg to null. If the option is found,
    107  * return a pointer into optstring pointing at the short-option character
    108  * associated with this long-option; otherwise return null.
    109  *
    110  * optstring 	The entire optstring passed to getopt() by the caller
    111  *
    112  * opt		The long option read from the command line
    113  *
    114  * longoptarg	The argument to the option is returned in this parameter,
    115  *              if an option exists. Possible return values in longoptarg
    116  *              are:
    117  *                  NULL		No argument was found
    118  *		    empty string ("")	Argument was explicitly left empty
    119  *					by the user (e.g., --option= )
    120  *		    valid string	Argument found on the command line
    121  *
    122  * returns	Pointer to equivalent short-option in optstring, null
    123  *              if option not found in optstring.
    124  *
    125  * ASSUMES: No parameters are NULL
    126  *
    127  */
    128 static char *
    129 parselong(const char *optstring, const char *opt, char **longoptarg)
    130 {
    131 	char	*cp;	/* ptr into optstring, beginning of one option spec. */
    132 	char	*ip;	/* ptr into optstring, traverses every char */
    133 	char	*op;	/* pointer into opt */
    134 	int	match;	/* nonzero if opt is matching part of optstring */
    135 
    136 	cp = ip = (char *)optstring;
    137 	do {
    138 		if (*ip != '(' && *++ip == '\0')
    139 			break;
    140 		if (*ip == ':' && *++ip == '\0')
    141 			break;
    142 		while (*ip == '(') {
    143 			if (*++ip == '\0')
    144 				break;
    145 			op = (char *)opt;
    146 			match = 1;
    147 			while (*ip != ')' && *ip != '\0' && *op != '\0')
    148 				match = (*ip++ == *op++ && match);
    149 			if (match && *ip == ')' &&
    150 			    (*op == '\0' || *op == '=')) {
    151 				if ((*op) == '=') {
    152 					/* may be an empty string - OK */
    153 					(*longoptarg) = op + 1;
    154 				} else {
    155 					(*longoptarg) = NULL;
    156 				}
    157 				return (cp);
    158 			}
    159 			if (*ip == ')' && *++ip == '\0')
    160 				break;
    161 		}
    162 		cp = ip;
    163 		/*
    164 		 * Handle double-colon in optstring ("a::(longa)")
    165 		 * The old getopt() accepts it and treats it as a
    166 		 * required argument.
    167 		 */
    168 		while ((cp > optstring) && ((*cp) == ':')) {
    169 			--cp;
    170 		}
    171 	} while (*cp != '\0');
    172 	return (NULL);
    173 } /* parselong() */
    174 
    175 /*
    176  * External function entry point.
    177  */
    178 int
    179 getopt(int argc, char *const *argv, const char *optstring)
    180 {
    181 	char	c;
    182 	char	*cp;
    183 	int	longopt;
    184 	char	*longoptarg;
    185 
    186 	/*
    187 	 * Has the end of the options been encountered?  The following
    188 	 * implements the SUS requirements:
    189 	 *
    190 	 * If, when getopt() is called:
    191 	 *	argv[optind]	is a null pointer
    192 	 *	*argv[optind]	is not the character '-'
    193 	 *	argv[optind]	points to the string "-"
    194 	 * getopt() returns -1 without changing optind. If
    195 	 *	argv[optind]	points to the string "--"
    196 	 * getopt() returns -1 after incrementing optind.
    197 	 */
    198 	if (_sp == 1) {
    199 		if (optind >= argc || argv[optind][0] != '-' ||
    200 		    argv[optind] == NULL || argv[optind][1] == '\0')
    201 			return (EOF);
    202 		else if (strcmp(argv[optind], "--") == NULL) {
    203 			optind++;
    204 			return (EOF);
    205 		}
    206 	}
    207 
    208 	/*
    209 	 * Getting this far indicates that an option has been encountered.
    210 	 * Note that the syntax of optstring applies special meanings to
    211 	 * the characters ':' and '(', so they are not permissible as
    212 	 * option letters. A special meaning is also applied to the ')'
    213 	 * character, but its meaning can be determined from context.
    214 	 * Note that the specification only requires that the alnum
    215 	 * characters be accepted.
    216 	 *
    217 	 * If the second character of the argument is a '-' this must be
    218 	 * a long-option, otherwise it must be a short option.  Scan for
    219 	 * the option in optstring by the appropriate algorithm. Either
    220 	 * scan will return a pointer to the short-option character in
    221 	 * optstring if the option is found and NULL otherwise.
    222 	 *
    223 	 * For an unrecognized long-option, optopt will equal 0, but
    224 	 * since long-options can't aggregate the failing option can
    225 	 * be identified by argv[optind-1].
    226 	 */
    227 	optopt = c = (unsigned char)argv[optind][_sp];
    228 	optarg = NULL;
    229 	longopt = (_sp == 1 && c == '-');
    230 	if (!(longopt ?
    231 	    ((cp = parselong(optstring, argv[optind]+2, &longoptarg)) != NULL) :
    232 	    ((cp = parseshort(optstring, c)) != NULL))) {
    233 		ERR(_libc_gettext("%s: illegal option -- %s\n"),
    234 		    c, (longopt ? optind : 0));
    235 		/*
    236 		 * Note: When the long option is unrecognized, optopt
    237 		 * will be '-' here, which matches the specification.
    238 		 */
    239 		if (argv[optind][++_sp] == '\0' || longopt) {
    240 			optind++;
    241 			_sp = 1;
    242 		}
    243 		return ('?');
    244 	}
    245 	optopt = c = *cp;
    246 
    247 	/*
    248 	 * A valid option has been identified.  If it should have an
    249 	 * option-argument, process that now.  SUS defines the setting
    250 	 * of optarg as follows:
    251 	 *
    252 	 *   1.	If the option was the last character in the string pointed to
    253 	 *	by an element of argv, then optarg contains the next element
    254 	 *	of argv, and optind is incremented by 2. If the resulting
    255 	 *	value of optind is not less than argc, this indicates a
    256 	 *	missing option-argument, and getopt() returns an error
    257 	 *	indication.
    258 	 *
    259 	 *   2.	Otherwise, optarg points to the string following the option
    260 	 *	character in that element of argv, and optind is incremented
    261 	 *	by 1.
    262 	 *
    263 	 * The second clause allows -abcd (where b requires an option-argument)
    264 	 * to be interpreted as "-a -b cd".
    265 	 *
    266 	 * Note that the option-argument can legally be an empty string,
    267 	 * such as:
    268 	 * 	command --option= operand
    269 	 * which explicitly sets the value of --option to nil
    270 	 */
    271 	if (*(cp + 1) == ':') {
    272 		/* The option takes an argument */
    273 		if (!longopt && argv[optind][_sp+1] != '\0') {
    274 			optarg = &argv[optind++][_sp+1];
    275 		} else if (longopt && longoptarg) {
    276 			/*
    277 			 * The option argument was explicitly set to
    278 			 * the empty string on the command line (--option=)
    279 			 */
    280 			optind++;
    281 			optarg = longoptarg;
    282 		} else if (++optind >= argc) {
    283 			ERR(_libc_gettext("%s: option requires an argument" \
    284 			    " -- %s\n"), c, (longopt ? optind - 1 : 0));
    285 			_sp = 1;
    286 			optarg = NULL;
    287 			return (optstring[0] == ':' ? ':' : '?');
    288 		} else
    289 			optarg = argv[optind++];
    290 		_sp = 1;
    291 	} else {
    292 		/* The option does NOT take an argument */
    293 		if (longopt && (longoptarg != NULL)) {
    294 			/* User supplied an arg to an option that takes none */
    295 			ERR(_libc_gettext(
    296 			    "%s: option doesn't take an argument -- %s\n"),
    297 			    0, (longopt ? optind : 0));
    298 			optarg = longoptarg = NULL;
    299 			c = '?';
    300 		}
    301 
    302 		if (longopt || argv[optind][++_sp] == '\0') {
    303 			_sp = 1;
    304 			optind++;
    305 		}
    306 		optarg = NULL;
    307 	}
    308 	return (c);
    309 } /* getopt() */
    310