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