1 /*
2  * getopt_long() -- long options parser
3  *
4  * Portions Copyright (c) 1987, 1993, 1994
5  * The Regents of the University of California.  All rights reserved.
6  *
7  * Portions Copyright (c) 2003
8  * PostgreSQL Global Development Group
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *	  notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *	  notice, this list of conditions and the following disclaimer in the
17  *	  documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *	  may be used to endorse or promote products derived from this software
20  *	  without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * src/port/getopt_long.c
35  */
36 
37 #include "c.h"
38 
39 #include "getopt_long.h"
40 
41 #define BADCH	'?'
42 #define BADARG	':'
43 #define EMSG	""
44 
45 
46 /*
47  * getopt_long
48  *	Parse argc/argv argument vector, with long options.
49  *
50  * This implementation does not use optreset.  Instead, we guarantee that
51  * it can be restarted on a new argv array after a previous call returned -1,
52  * if the caller resets optind to 1 before the first call of the new series.
53  * (Internally, this means we must be sure to reset "place" to EMSG before
54  * returning -1.)
55  */
56 int
getopt_long(int argc,char * const argv[],const char * optstring,const struct option * longopts,int * longindex)57 getopt_long(int argc, char *const argv[],
58 			const char *optstring,
59 			const struct option *longopts, int *longindex)
60 {
61 	static char *place = EMSG;	/* option letter processing */
62 	char	   *oli;			/* option letter list index */
63 
64 	if (!*place)
65 	{							/* update scanning pointer */
66 		if (optind >= argc)
67 		{
68 			place = EMSG;
69 			return -1;
70 		}
71 
72 		place = argv[optind];
73 
74 		if (place[0] != '-')
75 		{
76 			place = EMSG;
77 			return -1;
78 		}
79 
80 		place++;
81 
82 		if (!*place)
83 		{
84 			/* treat "-" as not being an option */
85 			place = EMSG;
86 			return -1;
87 		}
88 
89 		if (place[0] == '-' && place[1] == '\0')
90 		{
91 			/* found "--", treat it as end of options */
92 			++optind;
93 			place = EMSG;
94 			return -1;
95 		}
96 
97 		if (place[0] == '-' && place[1])
98 		{
99 			/* long option */
100 			size_t		namelen;
101 			int			i;
102 
103 			place++;
104 
105 			namelen = strcspn(place, "=");
106 			for (i = 0; longopts[i].name != NULL; i++)
107 			{
108 				if (strlen(longopts[i].name) == namelen
109 					&& strncmp(place, longopts[i].name, namelen) == 0)
110 				{
111 					int			has_arg = longopts[i].has_arg;
112 
113 					if (has_arg != no_argument)
114 					{
115 						if (place[namelen] == '=')
116 							optarg = place + namelen + 1;
117 						else if (optind < argc - 1 &&
118 								 has_arg == required_argument)
119 						{
120 							optind++;
121 							optarg = argv[optind];
122 						}
123 						else
124 						{
125 							if (optstring[0] == ':')
126 								return BADARG;
127 
128 							if (opterr && has_arg == required_argument)
129 								fprintf(stderr,
130 										"%s: option requires an argument -- %s\n",
131 										argv[0], place);
132 
133 							place = EMSG;
134 							optind++;
135 
136 							if (has_arg == required_argument)
137 								return BADCH;
138 							optarg = NULL;
139 						}
140 					}
141 					else
142 					{
143 						optarg = NULL;
144 						if (place[namelen] != 0)
145 						{
146 							/* XXX error? */
147 						}
148 					}
149 
150 					optind++;
151 
152 					if (longindex)
153 						*longindex = i;
154 
155 					place = EMSG;
156 
157 					if (longopts[i].flag == NULL)
158 						return longopts[i].val;
159 					else
160 					{
161 						*longopts[i].flag = longopts[i].val;
162 						return 0;
163 					}
164 				}
165 			}
166 
167 			if (opterr && optstring[0] != ':')
168 				fprintf(stderr,
169 						"%s: illegal option -- %s\n", argv[0], place);
170 			place = EMSG;
171 			optind++;
172 			return BADCH;
173 		}
174 	}
175 
176 	/* short option */
177 	optopt = (int) *place++;
178 
179 	oli = strchr(optstring, optopt);
180 	if (!oli)
181 	{
182 		if (!*place)
183 			++optind;
184 		if (opterr && *optstring != ':')
185 			fprintf(stderr,
186 					"%s: illegal option -- %c\n", argv[0], optopt);
187 		return BADCH;
188 	}
189 
190 	if (oli[1] != ':')
191 	{							/* don't need argument */
192 		optarg = NULL;
193 		if (!*place)
194 			++optind;
195 	}
196 	else
197 	{							/* need an argument */
198 		if (*place)				/* no white space */
199 			optarg = place;
200 		else if (argc <= ++optind)
201 		{						/* no arg */
202 			place = EMSG;
203 			if (*optstring == ':')
204 				return BADARG;
205 			if (opterr)
206 				fprintf(stderr,
207 						"%s: option requires an argument -- %c\n",
208 						argv[0], optopt);
209 			return BADCH;
210 		}
211 		else
212 			/* white space */
213 			optarg = argv[optind];
214 		place = EMSG;
215 		++optind;
216 	}
217 	return optopt;
218 }
219