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
40APR_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
71APR_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]. */
141static 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 */
158static 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 */
181static 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 */
191static 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
200APR_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