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  * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
28  *
29  * Sccsid @(#)getopt.c	1.7 (gritter) 6/22/05
30  */
31 /* from OpenSolaris "getopt.c	1.23	05/06/08 SMI" */
32 
33 /*	Copyright (c) 1988 AT&T	*/
34 /*	  All Rights Reserved  	*/
35 
36 
37 /*
38  * See getopt(3C) and SUS/XPG getopt() for function definition and
39  * requirements.
40  *
41  * This actual implementation is a bit looser than the specification
42  * as it allows any character other than ':' to be used as an option
43  * character - The specification only guarantees the alnum characters
44  * ([a-z][A-Z][0-9]).
45  */
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)	if (opterr && optstring[0] != ':') { \
61 	char errbuf[256]; \
62 	snprintf(errbuf, sizeof (errbuf), s, argv[0], c); \
63 	write(2, errbuf, strlen(errbuf)); }
64 
65 /*
66  * getopt_sp is required to keep state between successive calls to getopt()
67  * while extracting aggregated options (ie: -abcd). Hence, getopt() is not
68  * thread safe or reentrant, but it really doesn't matter.
69  *
70  * So, why isn't this "static" you ask?  Because the historical Bourne
71  * shell has actually latched on to this little piece of private data.
72  */
73 int getopt_sp = 1;
74 
75 /*
76  * Determine if the specified character (c) is present in the string
77  * (optstring) as a regular, single character option. If the option is found,
78  * return a pointer into optstring pointing at the option character,
79  * otherwise return null. The character ':' is not allowed.
80  */
81 static char *
parse(const char * optstring,const char c)82 parse(const char *optstring, const char c)
83 {
84 	char *cp = (char *)optstring;
85 
86 	if (c == ':')
87 		return (NULL);
88 	do {
89 		if (*cp == c)
90 			return (cp);
91 	} while (*cp++ != '\0');
92 	return (NULL);
93 }
94 
95 /*
96  * External function entry point.
97  */
98 int
getopt(int argc,char * const * argv,const char * optstring)99 getopt(int argc, char *const *argv, const char *optstring)
100 {
101 	char	c;
102 	char	*cp;
103 
104 	/*
105 	 * Has the end of the options been encountered?  The following
106 	 * implements the SUS requirements:
107 	 *
108 	 * If, when getopt() is called:
109 	 *	argv[optind]	is a null pointer
110 	 *	*argv[optind]	is not the character '-'
111 	 *	argv[optind]	points to the string "-"
112 	 * getopt() returns -1 without changing optind. If
113 	 *	argv[optind]	points to the string "--"
114 	 * getopt() returns -1 after incrementing optind.
115 	 */
116 	if (getopt_sp == 1) {
117 		if (optind >= argc || argv[optind][0] != '-' ||
118 		    argv[optind] == NULL || argv[optind][1] == '\0')
119 			return (EOF);
120 		else if (strcmp(argv[optind], "--") == 0) {
121 			optind++;
122 			return (EOF);
123 		}
124 	}
125 
126 	/*
127 	 * Getting this far indicates that an option has been encountered.
128 	 * Note that the syntax of optstring applies special meanings to
129 	 * the characters ':' and '(', so they are not permissible as
130 	 * option letters. A special meaning is also applied to the ')'
131 	 * character, but its meaning can be determined from context.
132 	 * Note that the specification only requires that the alnum
133 	 * characters be accepted.
134 	 */
135 	optopt = c = (unsigned char)argv[optind][getopt_sp];
136 	optarg = NULL;
137 	if ((cp = parse(optstring, c)) == NULL) {
138 		/* LINTED: variable format specifier */
139 		ERR("%s: illegal option -- %c\n", c);
140 		if (argv[optind][++getopt_sp] == '\0') {
141 			optind++;
142 			getopt_sp = 1;
143 		}
144 		return ('?');
145 	}
146 	optopt = c = *cp;
147 
148 	/*
149 	 * A valid option has been identified.  If it should have an
150 	 * option-argument, process that now.  SUS defines the setting
151 	 * of optarg as follows:
152 	 *
153 	 *   1.	If the option was the last character in the string pointed to
154 	 *	by an element of argv, then optarg contains the next element
155 	 *	of argv, and optind is incremented by 2. If the resulting
156 	 *	value of optind is not less than argc, this indicates a
157 	 *	missing option-argument, and getopt() returns an error
158 	 *	indication.
159 	 *
160 	 *   2.	Otherwise, optarg points to the string following the option
161 	 *	character in that element of argv, and optind is incremented
162 	 *	by 1.
163 	 *
164 	 * The second clause allows -abcd (where b requires an option-argument)
165 	 * to be interpreted as "-a -b cd".
166 	 */
167 	if (*(cp + 1) == ':') {
168 		/* The option takes an argument */
169 		if (argv[optind][getopt_sp+1] != '\0') {
170 			optarg = &argv[optind++][getopt_sp+1];
171 		} else if (++optind >= argc) {
172 			/* LINTED: variable format specifier */
173 			ERR("%s: option requires an argument -- %c\n", c);
174 			getopt_sp = 1;
175 			optarg = NULL;
176 			return (optstring[0] == ':' ? ':' : '?');
177 		} else
178 			optarg = argv[optind++];
179 		getopt_sp = 1;
180 	} else {
181 		/* The option does NOT take an argument */
182 		if (argv[optind][++getopt_sp] == '\0') {
183 			getopt_sp = 1;
184 			optind++;
185 		}
186 		optarg = NULL;
187 	}
188 	return (c);
189 } /* getopt() */
190