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], "--") == 0) {
 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() */