1 /*
2 * Copyright (c) 1987, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include "apr_arch_misc.h"
35 #include "apr_strings.h"
36 #include "apr_lib.h"
37
38 #define EMSG ""
39
apr_getopt_init(apr_getopt_t ** os,apr_pool_t * cont,int argc,const char * const * argv)40 APR_DECLARE(apr_status_t) apr_getopt_init(apr_getopt_t **os, apr_pool_t *cont,
41 int argc, const char *const *argv)
42 {
43 void *argv_buff;
44
45 *os = apr_palloc(cont, sizeof(apr_getopt_t));
46 (*os)->cont = cont;
47 (*os)->reset = 0;
48 (*os)->errfn = (apr_getopt_err_fn_t*)(fprintf);
49 (*os)->errarg = (void*)(stderr);
50
51 (*os)->place = EMSG;
52 (*os)->argc = argc;
53
54 /* The argv parameter must be compatible with main()'s argv, since
55 that's the primary purpose of this function. But people might
56 want to use this function with arrays other than the main argv,
57 and we shouldn't touch the caller's data. So we copy. */
58 argv_buff = apr_palloc(cont, (argc + 1) * sizeof(const char *));
59 memcpy(argv_buff, argv, argc * sizeof(const char *));
60 (*os)->argv = argv_buff;
61 (*os)->argv[argc] = NULL;
62
63 (*os)->interleave = 0;
64 (*os)->ind = 1;
65 (*os)->skip_start = 1;
66 (*os)->skip_end = 1;
67
68 return APR_SUCCESS;
69 }
70
apr_getopt(apr_getopt_t * os,const char * opts,char * optch,const char ** optarg)71 APR_DECLARE(apr_status_t) apr_getopt(apr_getopt_t *os, const char *opts,
72 char *optch, const char **optarg)
73 {
74 const char *oli; /* option letter list index */
75
76 if (os->reset || !*os->place) { /* update scanning pointer */
77 os->reset = 0;
78 if (os->ind >= os->argc || *(os->place = os->argv[os->ind]) != '-') {
79 os->place = EMSG;
80 *optch = os->opt;
81 return (APR_EOF);
82 }
83 if (os->place[1] && *++os->place == '-') { /* found "--" */
84 ++os->ind;
85 os->place = EMSG;
86 *optch = os->opt;
87 return (APR_EOF);
88 }
89 } /* option letter okay? */
90 if ((os->opt = (int) *os->place++) == (int) ':' ||
91 !(oli = strchr(opts, os->opt))) {
92 /*
93 * if the user didn't specify '-' as an option,
94 * assume it means -1.
95 */
96 if (os->opt == (int) '-') {
97 *optch = os->opt;
98 return (APR_EOF);
99 }
100 if (!*os->place)
101 ++os->ind;
102 if (os->errfn && *opts != ':') {
103 (os->errfn)(os->errarg, "%s: illegal option -- %c\n",
104 apr_filepath_name_get(*os->argv), os->opt);
105 }
106 *optch = os->opt;
107 return (APR_BADCH);
108 }
109 if (*++oli != ':') { /* don't need argument */
110 *optarg = NULL;
111 if (!*os->place)
112 ++os->ind;
113 }
114 else { /* need an argument */
115 if (*os->place) /* no white space */
116 *optarg = os->place;
117 else if (os->argc <= ++os->ind) { /* no arg */
118 os->place = EMSG;
119 if (*opts == ':') {
120 *optch = os->opt;
121 return (APR_BADARG);
122 }
123 if (os->errfn) {
124 (os->errfn)(os->errarg,
125 "%s: option requires an argument -- %c\n",
126 apr_filepath_name_get(*os->argv), os->opt);
127 }
128 *optch = os->opt;
129 return (APR_BADCH);
130 }
131 else /* white space */
132 *optarg = os->argv[os->ind];
133 os->place = EMSG;
134 ++os->ind;
135 }
136 *optch = os->opt;
137 return APR_SUCCESS;
138 }
139
140 /* Reverse the sequence argv[start..start+len-1]. */
reverse(const char ** argv,int start,int len)141 static void reverse(const char **argv, int start, int len)
142 {
143 const char *temp;
144
145 for (; len >= 2; start++, len -= 2) {
146 temp = argv[start];
147 argv[start] = argv[start + len - 1];
148 argv[start + len - 1] = temp;
149 }
150 }
151
152 /*
153 * Permute os->argv with the goal that non-option arguments will all
154 * appear at the end. os->skip_start is where we started skipping
155 * non-option arguments, os->skip_end is where we stopped, and os->ind
156 * is where we are now.
157 */
permute(apr_getopt_t * os)158 static void permute(apr_getopt_t *os)
159 {
160 int len1 = os->skip_end - os->skip_start;
161 int len2 = os->ind - os->skip_end;
162
163 if (os->interleave) {
164 /*
165 * Exchange the sequences argv[os->skip_start..os->skip_end-1] and
166 * argv[os->skip_end..os->ind-1]. The easiest way to do that is
167 * to reverse the entire range and then reverse the two
168 * sub-ranges.
169 */
170 reverse(os->argv, os->skip_start, len1 + len2);
171 reverse(os->argv, os->skip_start, len2);
172 reverse(os->argv, os->skip_start + len2, len1);
173 }
174
175 /* Reset skip range to the new location of the non-option sequence. */
176 os->skip_start += len2;
177 os->skip_end += len2;
178 }
179
180 /* Helper function to print out an error involving a long option */
serr(apr_getopt_t * os,const char * err,const char * str,apr_status_t status)181 static apr_status_t serr(apr_getopt_t *os, const char *err, const char *str,
182 apr_status_t status)
183 {
184 if (os->errfn)
185 (os->errfn)(os->errarg, "%s: %s: %s\n",
186 apr_filepath_name_get(*os->argv), err, str);
187 return status;
188 }
189
190 /* Helper function to print out an error involving a short option */
cerr(apr_getopt_t * os,const char * err,int ch,apr_status_t status)191 static apr_status_t cerr(apr_getopt_t *os, const char *err, int ch,
192 apr_status_t status)
193 {
194 if (os->errfn)
195 (os->errfn)(os->errarg, "%s: %s: %c\n",
196 apr_filepath_name_get(*os->argv), err, ch);
197 return status;
198 }
199
apr_getopt_long(apr_getopt_t * os,const apr_getopt_option_t * opts,int * optch,const char ** optarg)200 APR_DECLARE(apr_status_t) apr_getopt_long(apr_getopt_t *os,
201 const apr_getopt_option_t *opts,
202 int *optch, const char **optarg)
203 {
204 const char *p;
205 int i;
206
207 /* Let the calling program reset option processing. */
208 if (os->reset) {
209 os->place = EMSG;
210 os->ind = 1;
211 os->reset = 0;
212 }
213
214 /*
215 * We can be in one of two states: in the middle of processing a
216 * run of short options, or about to process a new argument.
217 * Since the second case can lead to the first one, handle that
218 * one first. */
219 p = os->place;
220 if (*p == '\0') {
221 /* If we are interleaving, skip non-option arguments. */
222 if (os->interleave) {
223 while (os->ind < os->argc && *os->argv[os->ind] != '-')
224 os->ind++;
225 os->skip_end = os->ind;
226 }
227 if (os->ind >= os->argc || *os->argv[os->ind] != '-') {
228 os->ind = os->skip_start;
229 return APR_EOF;
230 }
231
232 p = os->argv[os->ind++] + 1;
233 if (*p == '-' && p[1] != '\0') { /* Long option */
234 /* Search for the long option name in the caller's table. */
235 apr_size_t len = 0;
236
237 p++;
238 for (i = 0; ; i++) {
239 if (opts[i].optch == 0) /* No match */
240 return serr(os, "invalid option", p - 2, APR_BADCH);
241
242 if (opts[i].name) {
243 len = strlen(opts[i].name);
244 if (strncmp(p, opts[i].name, len) == 0
245 && (p[len] == '\0' || p[len] == '='))
246 break;
247 }
248 }
249 *optch = opts[i].optch;
250
251 if (opts[i].has_arg) {
252 if (p[len] == '=') /* Argument inline */
253 *optarg = p + len + 1;
254 else {
255 if (os->ind >= os->argc) /* Argument missing */
256 return serr(os, "missing argument", p - 2, APR_BADARG);
257 else /* Argument in next arg */
258 *optarg = os->argv[os->ind++];
259 }
260 } else {
261 *optarg = NULL;
262 if (p[len] == '=')
263 return serr(os, "erroneous argument", p - 2, APR_BADARG);
264 }
265 permute(os);
266 return APR_SUCCESS;
267 } else {
268 if (*p == '-') { /* Bare "--"; we're done */
269 permute(os);
270 os->ind = os->skip_start;
271 return APR_EOF;
272 }
273 else
274 if (*p == '\0') /* Bare "-" is illegal */
275 return serr(os, "invalid option", p, APR_BADCH);
276 }
277 }
278
279 /*
280 * Now we're in a run of short options, and *p is the next one.
281 * Look for it in the caller's table.
282 */
283 for (i = 0; ; i++) {
284 if (opts[i].optch == 0) /* No match */
285 return cerr(os, "invalid option character", *p, APR_BADCH);
286
287 if (*p == opts[i].optch)
288 break;
289 }
290 *optch = *p++;
291
292 if (opts[i].has_arg) {
293 if (*p != '\0') /* Argument inline */
294 *optarg = p;
295 else {
296 if (os->ind >= os->argc) /* Argument missing */
297 return cerr(os, "missing argument", *optch, APR_BADARG);
298 else /* Argument in next arg */
299 *optarg = os->argv[os->ind++];
300 }
301 os->place = EMSG;
302 } else {
303 *optarg = NULL;
304 os->place = p;
305 }
306
307 permute(os);
308 return APR_SUCCESS;
309 }
310