/src/httpd/srclib/apr/misc/unix/getopt.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #define EMSG "" |
39 | | |
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 | 0 | { |
43 | 0 | void *argv_buff; |
44 | |
|
45 | 0 | *os = apr_palloc(cont, sizeof(apr_getopt_t)); |
46 | 0 | (*os)->cont = cont; |
47 | 0 | (*os)->reset = 0; |
48 | 0 | (*os)->errfn = (apr_getopt_err_fn_t*)(fprintf); |
49 | 0 | (*os)->errarg = (void*)(stderr); |
50 | |
|
51 | 0 | (*os)->place = EMSG; |
52 | 0 | (*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 | 0 | argv_buff = apr_palloc(cont, (argc + 1) * sizeof(const char *)); |
59 | 0 | memcpy(argv_buff, argv, argc * sizeof(const char *)); |
60 | 0 | (*os)->argv = argv_buff; |
61 | 0 | (*os)->argv[argc] = NULL; |
62 | |
|
63 | 0 | (*os)->interleave = 0; |
64 | 0 | (*os)->ind = 1; |
65 | 0 | (*os)->skip_start = 1; |
66 | 0 | (*os)->skip_end = 1; |
67 | |
|
68 | 0 | return APR_SUCCESS; |
69 | 0 | } |
70 | | |
71 | | APR_DECLARE(apr_status_t) apr_getopt(apr_getopt_t *os, const char *opts, |
72 | | char *optch, const char **optarg) |
73 | 0 | { |
74 | 0 | const char *oli; /* option letter list index */ |
75 | |
|
76 | 0 | if (os->reset || !*os->place) { /* update scanning pointer */ |
77 | 0 | os->reset = 0; |
78 | 0 | if (os->ind >= os->argc || *(os->place = os->argv[os->ind]) != '-') { |
79 | 0 | os->place = EMSG; |
80 | 0 | *optch = os->opt; |
81 | 0 | return (APR_EOF); |
82 | 0 | } |
83 | 0 | if (os->place[1] && *++os->place == '-') { /* found "--" */ |
84 | 0 | ++os->ind; |
85 | 0 | os->place = EMSG; |
86 | 0 | *optch = os->opt; |
87 | 0 | return (APR_EOF); |
88 | 0 | } |
89 | 0 | } /* option letter okay? */ |
90 | 0 | if ((os->opt = (int) *os->place++) == (int) ':' || |
91 | 0 | !(oli = strchr(opts, os->opt))) { |
92 | | /* |
93 | | * if the user didn't specify '-' as an option, |
94 | | * assume it means -1. |
95 | | */ |
96 | 0 | if (os->opt == (int) '-') { |
97 | 0 | *optch = os->opt; |
98 | 0 | return (APR_EOF); |
99 | 0 | } |
100 | 0 | if (!*os->place) |
101 | 0 | ++os->ind; |
102 | 0 | if (os->errfn && *opts != ':') { |
103 | 0 | (os->errfn)(os->errarg, "%s: illegal option -- %c\n", |
104 | 0 | apr_filepath_name_get(*os->argv), os->opt); |
105 | 0 | } |
106 | 0 | *optch = os->opt; |
107 | 0 | return (APR_BADCH); |
108 | 0 | } |
109 | 0 | if (*++oli != ':') { /* don't need argument */ |
110 | 0 | *optarg = NULL; |
111 | 0 | if (!*os->place) |
112 | 0 | ++os->ind; |
113 | 0 | } |
114 | 0 | else { /* need an argument */ |
115 | 0 | if (*os->place) /* no white space */ |
116 | 0 | *optarg = os->place; |
117 | 0 | else if (os->argc <= ++os->ind) { /* no arg */ |
118 | 0 | os->place = EMSG; |
119 | 0 | if (*opts == ':') { |
120 | 0 | *optch = os->opt; |
121 | 0 | return (APR_BADARG); |
122 | 0 | } |
123 | 0 | if (os->errfn) { |
124 | 0 | (os->errfn)(os->errarg, |
125 | 0 | "%s: option requires an argument -- %c\n", |
126 | 0 | apr_filepath_name_get(*os->argv), os->opt); |
127 | 0 | } |
128 | 0 | *optch = os->opt; |
129 | 0 | return (APR_BADCH); |
130 | 0 | } |
131 | 0 | else /* white space */ |
132 | 0 | *optarg = os->argv[os->ind]; |
133 | 0 | os->place = EMSG; |
134 | 0 | ++os->ind; |
135 | 0 | } |
136 | 0 | *optch = os->opt; |
137 | 0 | return APR_SUCCESS; |
138 | 0 | } |
139 | | |
140 | | /* Reverse the sequence argv[start..start+len-1]. */ |
141 | | static void reverse(const char **argv, int start, int len) |
142 | 0 | { |
143 | 0 | const char *temp; |
144 | |
|
145 | 0 | for (; len >= 2; start++, len -= 2) { |
146 | 0 | temp = argv[start]; |
147 | 0 | argv[start] = argv[start + len - 1]; |
148 | 0 | argv[start + len - 1] = temp; |
149 | 0 | } |
150 | 0 | } |
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 | | */ |
158 | | static void permute(apr_getopt_t *os) |
159 | 0 | { |
160 | 0 | int len1 = os->skip_end - os->skip_start; |
161 | 0 | int len2 = os->ind - os->skip_end; |
162 | |
|
163 | 0 | 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 | 0 | reverse(os->argv, os->skip_start, len1 + len2); |
171 | 0 | reverse(os->argv, os->skip_start, len2); |
172 | 0 | reverse(os->argv, os->skip_start + len2, len1); |
173 | 0 | } |
174 | | |
175 | | /* Reset skip range to the new location of the non-option sequence. */ |
176 | 0 | os->skip_start += len2; |
177 | 0 | os->skip_end += len2; |
178 | 0 | } |
179 | | |
180 | | /* Helper function to print out an error involving a long option */ |
181 | | static apr_status_t serr(apr_getopt_t *os, const char *err, const char *str, |
182 | | apr_status_t status) |
183 | 0 | { |
184 | 0 | if (os->errfn) |
185 | 0 | (os->errfn)(os->errarg, "%s: %s: %s\n", |
186 | 0 | apr_filepath_name_get(*os->argv), err, str); |
187 | 0 | return status; |
188 | 0 | } |
189 | | |
190 | | /* Helper function to print out an error involving a short option */ |
191 | | static apr_status_t cerr(apr_getopt_t *os, const char *err, int ch, |
192 | | apr_status_t status) |
193 | 0 | { |
194 | 0 | if (os->errfn) |
195 | 0 | (os->errfn)(os->errarg, "%s: %s: %c\n", |
196 | 0 | apr_filepath_name_get(*os->argv), err, ch); |
197 | 0 | return status; |
198 | 0 | } |
199 | | |
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 | 0 | { |
204 | 0 | const char *p; |
205 | 0 | int i; |
206 | | |
207 | | /* Let the calling program reset option processing. */ |
208 | 0 | if (os->reset) { |
209 | 0 | os->place = EMSG; |
210 | 0 | os->ind = 1; |
211 | 0 | os->reset = 0; |
212 | 0 | } |
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 | 0 | p = os->place; |
220 | 0 | if (*p == '\0') { |
221 | | /* If we are interleaving, skip non-option arguments. */ |
222 | 0 | if (os->interleave) { |
223 | 0 | while (os->ind < os->argc && *os->argv[os->ind] != '-') |
224 | 0 | os->ind++; |
225 | 0 | os->skip_end = os->ind; |
226 | 0 | } |
227 | 0 | if (os->ind >= os->argc || *os->argv[os->ind] != '-') { |
228 | 0 | os->ind = os->skip_start; |
229 | 0 | return APR_EOF; |
230 | 0 | } |
231 | | |
232 | 0 | p = os->argv[os->ind++] + 1; |
233 | 0 | if (*p == '-' && p[1] != '\0') { /* Long option */ |
234 | | /* Search for the long option name in the caller's table. */ |
235 | 0 | apr_size_t len = 0; |
236 | |
|
237 | 0 | p++; |
238 | 0 | for (i = 0; ; i++) { |
239 | 0 | if (opts[i].optch == 0) /* No match */ |
240 | 0 | return serr(os, "invalid option", p - 2, APR_BADCH); |
241 | | |
242 | 0 | if (opts[i].name) { |
243 | 0 | len = strlen(opts[i].name); |
244 | 0 | if (strncmp(p, opts[i].name, len) == 0 |
245 | 0 | && (p[len] == '\0' || p[len] == '=')) |
246 | 0 | break; |
247 | 0 | } |
248 | 0 | } |
249 | 0 | *optch = opts[i].optch; |
250 | |
|
251 | 0 | if (opts[i].has_arg) { |
252 | 0 | if (p[len] == '=') /* Argument inline */ |
253 | 0 | *optarg = p + len + 1; |
254 | 0 | else { |
255 | 0 | if (os->ind >= os->argc) /* Argument missing */ |
256 | 0 | return serr(os, "missing argument", p - 2, APR_BADARG); |
257 | 0 | else /* Argument in next arg */ |
258 | 0 | *optarg = os->argv[os->ind++]; |
259 | 0 | } |
260 | 0 | } else { |
261 | 0 | *optarg = NULL; |
262 | 0 | if (p[len] == '=') |
263 | 0 | return serr(os, "erroneous argument", p - 2, APR_BADARG); |
264 | 0 | } |
265 | 0 | permute(os); |
266 | 0 | return APR_SUCCESS; |
267 | 0 | } else { |
268 | 0 | if (*p == '-') { /* Bare "--"; we're done */ |
269 | 0 | permute(os); |
270 | 0 | os->ind = os->skip_start; |
271 | 0 | return APR_EOF; |
272 | 0 | } |
273 | 0 | else |
274 | 0 | if (*p == '\0') /* Bare "-" is illegal */ |
275 | 0 | return serr(os, "invalid option", p, APR_BADCH); |
276 | 0 | } |
277 | 0 | } |
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 | 0 | for (i = 0; ; i++) { |
284 | 0 | if (opts[i].optch == 0) /* No match */ |
285 | 0 | return cerr(os, "invalid option character", *p, APR_BADCH); |
286 | | |
287 | 0 | if (*p == opts[i].optch) |
288 | 0 | break; |
289 | 0 | } |
290 | 0 | *optch = *p++; |
291 | |
|
292 | 0 | if (opts[i].has_arg) { |
293 | 0 | if (*p != '\0') /* Argument inline */ |
294 | 0 | *optarg = p; |
295 | 0 | else { |
296 | 0 | if (os->ind >= os->argc) /* Argument missing */ |
297 | 0 | return cerr(os, "missing argument", *optch, APR_BADARG); |
298 | 0 | else /* Argument in next arg */ |
299 | 0 | *optarg = os->argv[os->ind++]; |
300 | 0 | } |
301 | 0 | os->place = EMSG; |
302 | 0 | } else { |
303 | 0 | *optarg = NULL; |
304 | 0 | os->place = p; |
305 | 0 | } |
306 | | |
307 | 0 | permute(os); |
308 | 0 | return APR_SUCCESS; |
309 | 0 | } |