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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /*
27 * Copyright 2006-2019 J. Schilling
28 *
29 * @(#)getopt.c 1.22 19/12/14 J. Schilling
30 */
31 #if defined(sun)
32 #pragma ident "@(#)getopt.c 1.22 19/12/14 J. Schilling"
33 #endif
34
35 #if defined(sun)
36 #pragma ident "@(#)getopt.c 1.23 05/06/08 SMI"
37 #endif
38
39 /* Copyright (c) 1988 AT&T */
40 /* All Rights Reserved */
41
42
43 /*
44 * See getopt(3C) and SUS/XPG getopt() for function definition and
45 * requirements.
46 *
47 * This actual implementation is a bit looser than the specification
48 * as it allows any character other than ':' and '(' to be used as
49 * a short option character - The specification only guarantees the
50 * alnum characters ([a-z][A-Z][0-9]).
51 */
52
53 /*#pragma weak getopt = _getopt*/
54
55 /*#include "synonyms.h"*/
56 /*#include "_libc_gettext.h"*/
57
58 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
59 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
60 #endif
61 #define _libc_gettext(s) dgettext(TEXT_DOMAIN, s)
62
63 #include <schily/nlsdefs.h>
64
65 #include <schily/unistd.h>
66 #include <schily/string.h>
67 #include <schily/stdio.h>
68 #include <schily/getopt.h>
69 #ifndef HAVE_SNPRINTF
70 #include <schily/schily.h>
71 #define snprintf js_snprintf
72 #endif
73
74 #ifndef __CYGWIN__
75 /*
76 * Cygwin uses a nonstandard:
77 *
78 * __declspec(dllexport)
79 * or
80 * __declspec(dllimport)
81 *
82 * that is in conflict with our standard definition.
83 */
84 extern int optind, opterr, optopt;
85 extern int optflg;
86 extern char *optarg;
87 #endif
88
89 static char *parseshort __PR((const char *optstring, const int c));
90 #ifdef DO_GETOPT_LONGONLY
91 static int parseshortval __PR((const char *optstring, const char *cp));
92 #endif
93 static char *parselong __PR((const char *optstring, const char *opt,
94 char **longoptarg));
95
96 /*
97 * Generalized error processing macro. The parameter i is a pointer to
98 * the failed option string. If it is NULL, the character in c is converted
99 * to a string and displayed instead. s is the error text.
100 *
101 * This could be / should be a static function if it is used more, but
102 * that would require moving the 'optstring[0]' test outside of the
103 * function.
104 */
105 #define ERR(s, c, i) if (opterr && !colon) { \
106 char errbuf[256]; \
107 char cbuf[2]; \
108 cbuf[0] = c; \
109 cbuf[1] = '\0'; \
110 (void) snprintf(errbuf, sizeof (errbuf), s, argv[0], \
111 (i ? argv[i]+2 : cbuf)); \
112 (void) write(2, errbuf, strlen(errbuf)); }
113
114 /*
115 * _sp is required to keep state between successive calls to getopt() while
116 * extracting aggregated short-options (ie: -abcd). Hence, getopt() is not
117 * thread safe or reentrant, but it really doesn't matter.
118 *
119 * So, why isn't this "static" you ask? Because the historical Bourne
120 * shell has actually latched on to this little piece of private data.
121 */
122 #if defined(HAVE_PRAGMA_WEAK) && defined(HAVE_LINK_WEAK)
123 /*
124 * The name of the variable on SVr4 was _sp, but we enhanced the features
125 * of getopt() to support long options and since recent versions of the
126 * Bourne Shell rely on long option support, the easiest way to signal the
127 * enhanced features of this getopt() implementation is to change the name
128 * of the state variable to opt_sp.
129 */
130 #pragma weak _sp = opt_sp /* Backwards compatibility for old programs */
131 #endif
132 #define _sp opt_sp
133 int _sp = 1;
134
135 /*
136 * Determine if the specified character (c) is present in the string
137 * (optstring) as a regular, single character option. If the option is found,
138 * return a pointer into optstring pointing at the short-option character,
139 * otherwise return null. The characters ':' and '(' are not allowed.
140 */
141 static char *
parseshort(optstring,c)142 parseshort(optstring, c)
143 const char *optstring;
144 const char c;
145 {
146 char *cp = (char *)optstring;
147
148 if (c == ':' || c == '(')
149 return (NULL);
150 do {
151 #ifdef DO_GETOPT_LONGONLY
152 if (*cp == '?' && cp[1] >= '0' && cp[1] <= '9') {
153 const char *ocp = cp;
154
155 do {
156 cp++;
157 } while (*cp >= '0' && *cp <= '9');
158 if (*cp == '?')
159 cp++;
160 else
161 cp = (char *)ocp;
162 }
163 #endif
164 if (*cp == c)
165 return (cp);
166 while (*cp == '(')
167 while (*cp != '\0' && *cp != ')')
168 cp++;
169 } while (*cp++ != '\0');
170 return (NULL);
171 }
172
173 #ifdef DO_GETOPT_LONGONLY
174 /*
175 * Parse strings in the form "?ddd?", where ddd represents a
176 * decimal numeric value. We are called with cp pointing to the
177 * rightmost '?'.
178 */
179 static int
parseshortval(optstring,cp)180 parseshortval(optstring, cp)
181 const char *optstring;
182 const char *cp;
183 {
184 const char *p = cp;
185
186 while (p > optstring) {
187 p--;
188 if (*p < '0' || *p > '9')
189 break;
190 }
191 if ((p + 1) == cp) /* Did not find a number before '?' */
192 return ('?');
193 if (*p == '?') {
194 int i = 0;
195
196 while (*++p != '?') {
197 if (i > 2000000000) /* Avoid integer overflow */
198 return (-1);
199 i *= 10;
200 i += *p - '0';
201 }
202 return (i);
203 }
204 return ('?');
205 }
206 #endif
207
208 /*
209 * Determine if the specified string (opt) is present in the string
210 * (optstring) as a long-option contained within parenthesis. If the
211 * long-option specifies option-argument, return a pointer to it in
212 * longoptarg. Otherwise set longoptarg to null. If the option is found,
213 * return a pointer into optstring pointing at the short-option character
214 * associated with this long-option; otherwise return null.
215 *
216 * optstring The entire optstring passed to getopt() by the caller
217 *
218 * opt The long option read from the command line
219 *
220 * longoptarg The argument to the option is returned in this parameter,
221 * if an option exists. Possible return values in longoptarg
222 * are:
223 * NULL No argument was found
224 * empty string ("") Argument was explicitly left empty
225 * by the user (e.g., --option= )
226 * valid string Argument found on the command line
227 *
228 * returns Pointer to equivalent short-option in optstring, null
229 * if option not found in optstring.
230 *
231 * ASSUMES: No parameters are NULL
232 *
233 */
234 static char *
parselong(optstring,opt,longoptarg)235 parselong(optstring, opt, longoptarg)
236 const char *optstring;
237 const char *opt;
238 char **longoptarg;
239 {
240 char *cp; /* ptr into optstring, beginning of one option spec. */
241 char *ip; /* ptr into optstring, traverses every char */
242 char *op; /* pointer into opt */
243 int match; /* nonzero if opt is matching part of optstring */
244
245 cp = ip = (char *)optstring;
246 do {
247 if (*ip == '\0')
248 break;
249 if (*ip != '(' && *++ip == '\0')
250 break;
251 if (*ip == ':' && *++ip == '\0')
252 break;
253 while (*ip == '(') {
254 if (*++ip == '\0')
255 break;
256 op = (char *)opt;
257 match = 1;
258 while (*ip != ')' && *ip != '\0' && *op != '\0')
259 match = (*ip++ == *op++ && match);
260 if (match && *ip == ')' &&
261 (*op == '\0' || *op == '=')) {
262 if ((*op) == '=') {
263 /* may be an empty string - OK */
264 (*longoptarg) = op + 1;
265 } else {
266 (*longoptarg) = NULL;
267 }
268 return (cp);
269 }
270 if (*ip == ')' && *++ip == '\0')
271 break;
272 }
273 cp = ip;
274 /*
275 * Handle double-colon in optstring ("a::(longa)")
276 * The old getopt() accepts it and treats it as a
277 * required argument.
278 */
279 while ((cp > optstring) && ((*cp) == ':')) {
280 --cp;
281 }
282 } while (*cp != '\0');
283 return (NULL);
284 } /* parselong() */
285
286 /*
287 * External function entry point.
288 */
289 int
getopt(argc,argv,optstring)290 getopt(argc, argv, optstring)
291 int argc;
292 char *const *argv;
293 const char *optstring;
294 {
295 int c;
296 char *cp;
297 int longopt;
298 char *longoptarg = NULL;
299 int l = 2;
300 #ifdef DO_GETOPT_PLUS
301 int isplus; /* The current option starts with a '+' char */
302 int plus = 0; /* Found a '+' at the beginning of optstring */
303 #endif
304 int colon = 0; /* Found a ':' at the beginning of optstring */
305
306 while ((c = *optstring) != '\0') {
307 switch (c) {
308
309 #ifdef DO_GETOPT_PLUS
310 case '+':
311 plus = 1;
312 optstring++;
313 continue;
314 #endif
315 case ':':
316 colon = 1;
317 optstring++;
318 continue;
319 case '(':
320 default:
321 break;
322 }
323 break;
324 }
325 /*
326 * Has the end of the options been encountered? The following
327 * implements the SUS requirements:
328 *
329 * If, when getopt() is called:
330 * argv[optind] is a null pointer
331 * *argv[optind] is not the character '-'
332 * argv[optind] points to the string "-"
333 * getopt() returns -1 without changing optind. If
334 * argv[optind] points to the string "--"
335 * getopt() returns -1 after incrementing optind.
336 */
337 if (optind >= argc || argv[optind] == NULL ||
338 #ifdef DO_GETOPT_PLUS
339 ((!plus || argv[optind][0] != '+') && argv[optind][0] != '-') ||
340 #else
341 argv[optind][0] != '-' ||
342 #endif
343 argv[optind][1] == '\0') {
344 return (-1);
345 } else if (argv[optind][0] == '-' && argv[optind][1] == '-' &&
346 argv[optind][2] == '\0') { /* "--" */
347 optind++;
348 return (-1);
349 }
350
351 /*
352 * Getting this far indicates that an option has been encountered.
353 * Note that the syntax of optstring applies special meanings to
354 * the characters ':' and '(', so they are not permissible as
355 * option letters. A special meaning is also applied to the ')'
356 * character, but its meaning can be determined from context.
357 * Note that the specification only requires that the alnum
358 * characters be accepted.
359 *
360 * If the second character of the argument is a '-' this must be
361 * a long-option, otherwise it must be a short option. Scan for
362 * the option in optstring by the appropriate algorithm. Either
363 * scan will return a pointer to the short-option character in
364 * optstring if the option is found and NULL otherwise.
365 *
366 * For an unrecognized long-option, optopt will equal 0, but
367 * since long-options can't aggregate the failing option can
368 * be identified by argv[optind-1].
369 */
370 optopt = c = (unsigned char)argv[optind][_sp];
371 optarg = NULL;
372 longopt = (_sp == 1 && c == '-');
373 #ifdef DO_GETOPT_PLUS
374 isplus = plus && argv[optind][0] == '+'; /* actual option: +o */
375 optflg = isplus ? GETOPT_PLUS_FL : 0;
376 if (isplus)
377 longopt = (_sp == 1 && c == '+'); /* check for ++xxx */
378 #endif
379 #ifdef DO_GETOPT_SDASH_LONG
380 /*
381 * If optstring starts with "()", traditional UNIX "-long" options are
382 * allowed in addition to "--long".
383 */
384 if (optstring[0] == '(') {
385 if (!longopt && _sp == 1 &&
386 c != '\0' && argv[optind][2] != '\0') {
387 longopt = l = 1;
388 }
389 }
390 tryshort:
391 #endif
392 if (!(longopt ?
393 ((cp = parselong(optstring, argv[optind]+l, &longoptarg)) != NULL) :
394 ((cp = parseshort(optstring, c)) != NULL))) {
395 #ifdef DO_GETOPT_SDASH_LONG
396 #ifdef DO_GETOPT_PLUS
397 if (longopt && optopt != (isplus ? '+' : '-')) {
398 #else
399 if (longopt && optopt != '-') {
400 #endif
401 /*
402 * In case of "-long" retry as combined short options.
403 */
404 longopt = 0;
405 goto tryshort;
406 }
407 #endif
408 /* LINTED: variable format specifier */
409 ERR(_libc_gettext("%s: illegal option -- %s\n"),
410 c, (longopt ? optind : 0));
411 /*
412 * Note: When the long option is unrecognized, optopt
413 * will be '-' here, which matches the specification.
414 */
415 if (argv[optind][++_sp] == '\0' || longopt) {
416 optind++;
417 _sp = 1;
418 }
419 return ('?');
420 }
421 optopt = c = (unsigned char)*cp;
422 #ifdef DO_GETOPT_LONGONLY
423 if (*cp == '?') {
424 optopt = c = parseshortval(optstring, cp);
425 if (c < 0) {
426 optopt = (unsigned char)argv[optind++][_sp];
427 return ('?');
428 }
429 }
430 #endif
431
432 /*
433 * A valid option has been identified. If it should have an
434 * option-argument, process that now. SUS defines the setting
435 * of optarg as follows:
436 *
437 * 1. If the option was the last character in the string pointed to
438 * by an element of argv, then optarg contains the next element
439 * of argv, and optind is incremented by 2. If the resulting
440 * value of optind is not less than argc, this indicates a
441 * missing option-argument, and getopt() returns an error
442 * indication.
443 *
444 * 2. Otherwise, optarg points to the string following the option
445 * character in that element of argv, and optind is incremented
446 * by 1.
447 *
448 * The second clause allows -abcd (where b requires an option-argument)
449 * to be interpreted as "-a -b cd".
450 *
451 * Note that the option-argument can legally be an empty string,
452 * such as:
453 * command --option= operand
454 * which explicitly sets the value of --option to nil
455 */
456 if (*(cp + 1) == ':') {
457 /* The option takes an argument */
458 if (!longopt && argv[optind][_sp+1] != '\0') {
459 optarg = &argv[optind++][_sp+1];
460 } else if (longopt && longoptarg) {
461 /*
462 * The option argument was explicitly set to
463 * the empty string on the command line (--option=)
464 */
465 optind++;
466 optarg = longoptarg;
467 } else if (++optind >= argc) {
468 /* LINTED: variable format specifier */
469 ERR(_libc_gettext(
470 "%s: option requires an argument -- %s\n"),
471 c, (longopt ? optind - 1 : 0));
472 _sp = 1;
473 optarg = NULL;
474 return (colon ? ':' : '?');
475 } else
476 optarg = argv[optind++];
477 _sp = 1;
478 } else {
479 /* The option does NOT take an argument */
480 if (longopt && (longoptarg != NULL)) {
481 /* User supplied an arg to an option that takes none */
482 /* LINTED: variable format specifier */
483 ERR(_libc_gettext(
484 "%s: option doesn't take an argument -- %s\n"),
485 0, (longopt ? optind : 0));
486 optarg = longoptarg = NULL;
487 c = '?';
488 }
489
490 if (longopt || argv[optind][++_sp] == '\0') {
491 _sp = 1;
492 optind++;
493 }
494 optarg = NULL;
495 }
496 return (c);
497 } /* getopt() */
498