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