diff options
Diffstat (limited to 'usr/src/lib/libc/port/gen/getopt.c')
-rw-r--r-- | usr/src/lib/libc/port/gen/getopt.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/usr/src/lib/libc/port/gen/getopt.c b/usr/src/lib/libc/port/gen/getopt.c new file mode 100644 index 0000000000..e2cb47c5d2 --- /dev/null +++ b/usr/src/lib/libc/port/gen/getopt.c @@ -0,0 +1,312 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* 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 "synonyms.h" +#include "_libc_gettext.h" + +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +/* + * 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], "--") == NULL) { + 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))) { + /* LINTED: variable format specifier */ + 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) { + /* LINTED: variable format specifier */ + 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 */ + /* LINTED: variable format specifier */ + 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() */ |