2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * See getopt(3C) and SUS/XPG getopt() for function definition and
2N/A * requirements.
2N/A *
2N/A * This actual implementation is a bit looser than the specification
2N/A * as it allows any character other than ':' and '(' to be used as
2N/A * a short option character - The specification only guarantees the
2N/A * alnum characters ([a-z][A-Z][0-9]).
2N/A */
2N/A
2N/A#pragma weak _getopt = getopt
2N/A
2N/A#include "lint.h"
2N/A#include "_libc_gettext.h"
2N/A
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <stdio.h>
2N/A
2N/A/*
2N/A * Generalized error processing macro. The parameter i is a pointer to
2N/A * the failed option string. If it is NULL, the character in c is converted
2N/A * to a string and displayed instead. s is the error text.
2N/A *
2N/A * This could be / should be a static function if it is used more, but
2N/A * that would require moving the 'optstring[0]' test outside of the
2N/A * function.
2N/A */
2N/A#define ERR(s, c, i) if (opterr && optstring[0] != ':') { \
2N/A char errbuf[256]; \
2N/A char cbuf[2]; \
2N/A cbuf[0] = c; \
2N/A cbuf[1] = '\0'; \
2N/A (void) snprintf(errbuf, sizeof (errbuf), s, argv[0], \
2N/A (i ? argv[i]+2 : cbuf)); \
2N/A (void) write(2, errbuf, strlen(errbuf)); }
2N/A
2N/A/*
2N/A * _sp is required to keep state between successive calls to getopt() while
2N/A * extracting aggregated short-options (ie: -abcd). Hence, getopt() is not
2N/A * thread safe or reentrant, but it really doesn't matter.
2N/A *
2N/A * So, why isn't this "static" you ask? Because the historical Bourne
2N/A * shell has actually latched on to this little piece of private data.
2N/A */
2N/Aint _sp = 1;
2N/A
2N/A/*
2N/A * Determine if the specified character (c) is present in the string
2N/A * (optstring) as a regular, single character option. If the option is found,
2N/A * return a pointer into optstring pointing at the short-option character,
2N/A * otherwise return null. The characters ':' and '(' are not allowed.
2N/A */
2N/Astatic char *
2N/Aparseshort(const char *optstring, const char c)
2N/A{
2N/A char *cp = (char *)optstring;
2N/A
2N/A if (c == ':' || c == '(')
2N/A return (NULL);
2N/A do {
2N/A if (*cp == c)
2N/A return (cp);
2N/A while (*cp == '(')
2N/A while (*cp != '\0' && *cp != ')')
2N/A cp++;
2N/A } while (*cp++ != '\0');
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Determine if the specified string (opt) is present in the string
2N/A * (optstring) as a long-option contained within parenthesis. If the
2N/A * long-option specifies option-argument, return a pointer to it in
2N/A * longoptarg. Otherwise set longoptarg to null. If the option is found,
2N/A * return a pointer into optstring pointing at the short-option character
2N/A * associated with this long-option; otherwise return null.
2N/A *
2N/A * optstring The entire optstring passed to getopt() by the caller
2N/A *
2N/A * opt The long option read from the command line
2N/A *
2N/A * longoptarg The argument to the option is returned in this parameter,
2N/A * if an option exists. Possible return values in longoptarg
2N/A * are:
2N/A * NULL No argument was found
2N/A * empty string ("") Argument was explicitly left empty
2N/A * by the user (e.g., --option= )
2N/A * valid string Argument found on the command line
2N/A *
2N/A * returns Pointer to equivalent short-option in optstring, null
2N/A * if option not found in optstring.
2N/A *
2N/A * ASSUMES: No parameters are NULL
2N/A *
2N/A */
2N/Astatic char *
2N/Aparselong(const char *optstring, const char *opt, char **longoptarg)
2N/A{
2N/A char *cp; /* ptr into optstring, beginning of one option spec. */
2N/A char *ip; /* ptr into optstring, traverses every char */
2N/A char *op; /* pointer into opt */
2N/A int match; /* nonzero if opt is matching part of optstring */
2N/A
2N/A cp = ip = (char *)optstring;
2N/A do {
2N/A if (*ip != '(' && *++ip == '\0')
2N/A break;
2N/A if (*ip == ':' && *++ip == '\0')
2N/A break;
2N/A while (*ip == '(') {
2N/A if (*++ip == '\0')
2N/A break;
2N/A op = (char *)opt;
2N/A match = 1;
2N/A while (*ip != ')' && *ip != '\0' && *op != '\0')
2N/A match = (*ip++ == *op++ && match);
2N/A if (match && *ip == ')' &&
2N/A (*op == '\0' || *op == '=')) {
2N/A if ((*op) == '=') {
2N/A /* may be an empty string - OK */
2N/A (*longoptarg) = op + 1;
2N/A } else {
2N/A (*longoptarg) = NULL;
2N/A }
2N/A return (cp);
2N/A }
2N/A if (*ip == ')' && *++ip == '\0')
2N/A break;
2N/A }
2N/A cp = ip;
2N/A /*
2N/A * Handle double-colon in optstring ("a::(longa)")
2N/A * The old getopt() accepts it and treats it as a
2N/A * required argument.
2N/A */
2N/A while ((cp > optstring) && ((*cp) == ':')) {
2N/A --cp;
2N/A }
2N/A } while (*cp != '\0');
2N/A return (NULL);
2N/A} /* parselong() */
2N/A
2N/A/*
2N/A * External function entry point.
2N/A */
2N/Aint
2N/Agetopt(int argc, char *const *argv, const char *optstring)
2N/A{
2N/A char c;
2N/A char *cp;
2N/A int longopt;
2N/A char *longoptarg;
2N/A
2N/A /*
2N/A * Has the end of the options been encountered? The following
2N/A * implements the SUS requirements:
2N/A *
2N/A * If, when getopt() is called:
2N/A * argv[optind] is a null pointer
2N/A * *argv[optind] is not the character '-'
2N/A * argv[optind] points to the string "-"
2N/A * getopt() returns -1 without changing optind. If
2N/A * argv[optind] points to the string "--"
2N/A * getopt() returns -1 after incrementing optind.
2N/A */
2N/A if (_sp == 1) {
2N/A if (optind >= argc || argv[optind][0] != '-' ||
2N/A argv[optind] == NULL || argv[optind][1] == '\0')
2N/A return (EOF);
2N/A else if (strcmp(argv[optind], "--") == NULL) {
2N/A optind++;
2N/A return (EOF);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Getting this far indicates that an option has been encountered.
2N/A * Note that the syntax of optstring applies special meanings to
2N/A * the characters ':' and '(', so they are not permissible as
2N/A * option letters. A special meaning is also applied to the ')'
2N/A * character, but its meaning can be determined from context.
2N/A * Note that the specification only requires that the alnum
2N/A * characters be accepted.
2N/A *
2N/A * If the second character of the argument is a '-' this must be
2N/A * a long-option, otherwise it must be a short option. Scan for
2N/A * the option in optstring by the appropriate algorithm. Either
2N/A * scan will return a pointer to the short-option character in
2N/A * optstring if the option is found and NULL otherwise.
2N/A *
2N/A * For an unrecognized long-option, optopt will equal 0, but
2N/A * since long-options can't aggregate the failing option can
2N/A * be identified by argv[optind-1].
2N/A */
2N/A optopt = c = (unsigned char)argv[optind][_sp];
2N/A optarg = NULL;
2N/A longopt = (_sp == 1 && c == '-');
2N/A if (!(longopt ?
2N/A ((cp = parselong(optstring, argv[optind]+2, &longoptarg)) != NULL) :
2N/A ((cp = parseshort(optstring, c)) != NULL))) {
2N/A ERR(_libc_gettext("%s: illegal option -- %s\n"),
2N/A c, (longopt ? optind : 0));
2N/A /*
2N/A * Note: When the long option is unrecognized, optopt
2N/A * will be '-' here, which matches the specification.
2N/A */
2N/A if (argv[optind][++_sp] == '\0' || longopt) {
2N/A optind++;
2N/A _sp = 1;
2N/A }
2N/A return ('?');
2N/A }
2N/A optopt = c = *cp;
2N/A
2N/A /*
2N/A * A valid option has been identified. If it should have an
2N/A * option-argument, process that now. SUS defines the setting
2N/A * of optarg as follows:
2N/A *
2N/A * 1. If the option was the last character in the string pointed to
2N/A * by an element of argv, then optarg contains the next element
2N/A * of argv, and optind is incremented by 2. If the resulting
2N/A * value of optind is not less than argc, this indicates a
2N/A * missing option-argument, and getopt() returns an error
2N/A * indication.
2N/A *
2N/A * 2. Otherwise, optarg points to the string following the option
2N/A * character in that element of argv, and optind is incremented
2N/A * by 1.
2N/A *
2N/A * The second clause allows -abcd (where b requires an option-argument)
2N/A * to be interpreted as "-a -b cd".
2N/A *
2N/A * Note that the option-argument can legally be an empty string,
2N/A * such as:
2N/A * command --option= operand
2N/A * which explicitly sets the value of --option to nil
2N/A */
2N/A if (*(cp + 1) == ':') {
2N/A /* The option takes an argument */
2N/A if (!longopt && argv[optind][_sp+1] != '\0') {
2N/A optarg = &argv[optind++][_sp+1];
2N/A } else if (longopt && longoptarg) {
2N/A /*
2N/A * The option argument was explicitly set to
2N/A * the empty string on the command line (--option=)
2N/A */
2N/A optind++;
2N/A optarg = longoptarg;
2N/A } else if (++optind >= argc) {
2N/A ERR(_libc_gettext("%s: option requires an argument" \
2N/A " -- %s\n"), c, (longopt ? optind - 1 : 0));
2N/A _sp = 1;
2N/A optarg = NULL;
2N/A return (optstring[0] == ':' ? ':' : '?');
2N/A } else
2N/A optarg = argv[optind++];
2N/A _sp = 1;
2N/A } else {
2N/A /* The option does NOT take an argument */
2N/A if (longopt && (longoptarg != NULL)) {
2N/A /* User supplied an arg to an option that takes none */
2N/A ERR(_libc_gettext(
2N/A "%s: option doesn't take an argument -- %s\n"),
2N/A 0, (longopt ? optind : 0));
2N/A optarg = longoptarg = NULL;
2N/A c = '?';
2N/A }
2N/A
2N/A if (longopt || argv[optind][++_sp] == '\0') {
2N/A _sp = 1;
2N/A optind++;
2N/A }
2N/A optarg = NULL;
2N/A }
2N/A return (c);
2N/A} /* getopt() */