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 *
parseshort(const char * optstring,const char c)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 *
parselong(const char * optstring,const char * opt,char ** longoptarg)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
getopt(int argc,char * const * argv,const char * optstring)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