1251875Speter/*
2251875Speter * Copyright (c) 1987, 1993, 1994
3251875Speter *      The Regents of the University of California.  All rights reserved.
4251875Speter *
5251875Speter * Redistribution and use in source and binary forms, with or without
6251875Speter * modification, are permitted provided that the following conditions
7251875Speter * are met:
8251875Speter * 1. Redistributions of source code must retain the above copyright
9251875Speter *    notice, this list of conditions and the following disclaimer.
10251875Speter * 2. Redistributions in binary form must reproduce the above copyright
11251875Speter *    notice, this list of conditions and the following disclaimer in the
12251875Speter *    documentation and/or other materials provided with the distribution.
13251875Speter * 3. All advertising materials mentioning features or use of this software
14251875Speter *    must display the following acknowledgement:
15251875Speter *      This product includes software developed by the University of
16251875Speter *      California, Berkeley and its contributors.
17251875Speter * 4. Neither the name of the University nor the names of its contributors
18251875Speter *    may be used to endorse or promote products derived from this software
19251875Speter *    without specific prior written permission.
20251875Speter *
21251875Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22251875Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23251875Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24251875Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25251875Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26251875Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27251875Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28251875Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29251875Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30251875Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31251875Speter * SUCH DAMAGE.
32251875Speter */
33251875Speter
34251875Speter#include "apr_arch_misc.h"
35251875Speter#include "apr_strings.h"
36251875Speter#include "apr_lib.h"
37251875Speter
38251875Speter#define EMSG    ""
39251875Speter
40251875SpeterAPR_DECLARE(apr_status_t) apr_getopt_init(apr_getopt_t **os, apr_pool_t *cont,
41251875Speter                                      int argc, const char *const *argv)
42251875Speter{
43251875Speter    void *argv_buff;
44251875Speter
45251875Speter    *os = apr_palloc(cont, sizeof(apr_getopt_t));
46251875Speter    (*os)->cont = cont;
47251875Speter    (*os)->reset = 0;
48251875Speter    (*os)->errfn = (apr_getopt_err_fn_t*)(fprintf);
49251875Speter    (*os)->errarg = (void*)(stderr);
50251875Speter
51251875Speter    (*os)->place = EMSG;
52251875Speter    (*os)->argc = argc;
53251875Speter
54251875Speter    /* The argv parameter must be compatible with main()'s argv, since
55251875Speter       that's the primary purpose of this function.  But people might
56251875Speter       want to use this function with arrays other than the main argv,
57251875Speter       and we shouldn't touch the caller's data.  So we copy. */
58251875Speter    argv_buff = apr_palloc(cont, (argc + 1) * sizeof(const char *));
59251875Speter    memcpy(argv_buff, argv, argc * sizeof(const char *));
60251875Speter    (*os)->argv = argv_buff;
61251875Speter    (*os)->argv[argc] = NULL;
62251875Speter
63251875Speter    (*os)->interleave = 0;
64251875Speter    (*os)->ind = 1;
65251875Speter    (*os)->skip_start = 1;
66251875Speter    (*os)->skip_end = 1;
67251875Speter
68251875Speter    return APR_SUCCESS;
69251875Speter}
70251875Speter
71251875SpeterAPR_DECLARE(apr_status_t) apr_getopt(apr_getopt_t *os, const char *opts,
72251875Speter                                     char *optch, const char **optarg)
73251875Speter{
74251875Speter    const char *oli;  /* option letter list index */
75251875Speter
76251875Speter    if (os->reset || !*os->place) {   /* update scanning pointer */
77251875Speter        os->reset = 0;
78251875Speter        if (os->ind >= os->argc || *(os->place = os->argv[os->ind]) != '-') {
79251875Speter            os->place = EMSG;
80251875Speter            *optch = os->opt;
81251875Speter            return (APR_EOF);
82251875Speter        }
83251875Speter        if (os->place[1] && *++os->place == '-') {        /* found "--" */
84251875Speter            ++os->ind;
85251875Speter            os->place = EMSG;
86251875Speter            *optch = os->opt;
87251875Speter            return (APR_EOF);
88251875Speter        }
89251875Speter    }                                /* option letter okay? */
90251875Speter    if ((os->opt = (int) *os->place++) == (int) ':' ||
91251875Speter        !(oli = strchr(opts, os->opt))) {
92251875Speter        /*
93251875Speter         * if the user didn't specify '-' as an option,
94251875Speter         * assume it means -1.
95251875Speter         */
96251875Speter        if (os->opt == (int) '-') {
97251875Speter            *optch = os->opt;
98251875Speter            return (APR_EOF);
99251875Speter        }
100251875Speter        if (!*os->place)
101251875Speter            ++os->ind;
102251875Speter        if (os->errfn && *opts != ':') {
103251875Speter            (os->errfn)(os->errarg, "%s: illegal option -- %c\n",
104251875Speter                        apr_filepath_name_get(*os->argv), os->opt);
105251875Speter        }
106251875Speter        *optch = os->opt;
107251875Speter        return (APR_BADCH);
108251875Speter    }
109251875Speter    if (*++oli != ':') {        /* don't need argument */
110251875Speter        *optarg = NULL;
111251875Speter        if (!*os->place)
112251875Speter            ++os->ind;
113251875Speter    }
114251875Speter    else {                        /* need an argument */
115251875Speter        if (*os->place)                /* no white space */
116251875Speter            *optarg = os->place;
117251875Speter        else if (os->argc <= ++os->ind) {        /* no arg */
118251875Speter            os->place = EMSG;
119251875Speter            if (*opts == ':') {
120251875Speter                *optch = os->opt;
121251875Speter                return (APR_BADARG);
122251875Speter            }
123251875Speter            if (os->errfn) {
124251875Speter                (os->errfn)(os->errarg,
125251875Speter                            "%s: option requires an argument -- %c\n",
126251875Speter                            apr_filepath_name_get(*os->argv), os->opt);
127251875Speter            }
128251875Speter            *optch = os->opt;
129251875Speter            return (APR_BADCH);
130251875Speter        }
131251875Speter        else                        /* white space */
132251875Speter            *optarg = os->argv[os->ind];
133251875Speter        os->place = EMSG;
134251875Speter        ++os->ind;
135251875Speter    }
136251875Speter    *optch = os->opt;
137251875Speter    return APR_SUCCESS;
138251875Speter}
139251875Speter
140251875Speter/* Reverse the sequence argv[start..start+len-1]. */
141251875Speterstatic void reverse(const char **argv, int start, int len)
142251875Speter{
143251875Speter    const char *temp;
144251875Speter
145251875Speter    for (; len >= 2; start++, len -= 2) {
146251875Speter        temp = argv[start];
147251875Speter        argv[start] = argv[start + len - 1];
148251875Speter        argv[start + len - 1] = temp;
149251875Speter    }
150251875Speter}
151251875Speter
152251875Speter/*
153251875Speter * Permute os->argv with the goal that non-option arguments will all
154251875Speter * appear at the end.  os->skip_start is where we started skipping
155251875Speter * non-option arguments, os->skip_end is where we stopped, and os->ind
156251875Speter * is where we are now.
157251875Speter */
158251875Speterstatic void permute(apr_getopt_t *os)
159251875Speter{
160251875Speter    int len1 = os->skip_end - os->skip_start;
161251875Speter    int len2 = os->ind - os->skip_end;
162251875Speter
163251875Speter    if (os->interleave) {
164251875Speter        /*
165251875Speter         * Exchange the sequences argv[os->skip_start..os->skip_end-1] and
166251875Speter         * argv[os->skip_end..os->ind-1].  The easiest way to do that is
167251875Speter         * to reverse the entire range and then reverse the two
168251875Speter         * sub-ranges.
169251875Speter         */
170251875Speter        reverse(os->argv, os->skip_start, len1 + len2);
171251875Speter        reverse(os->argv, os->skip_start, len2);
172251875Speter        reverse(os->argv, os->skip_start + len2, len1);
173251875Speter    }
174251875Speter
175251875Speter    /* Reset skip range to the new location of the non-option sequence. */
176251875Speter    os->skip_start += len2;
177251875Speter    os->skip_end += len2;
178251875Speter}
179251875Speter
180251875Speter/* Helper function to print out an error involving a long option */
181251875Speterstatic apr_status_t serr(apr_getopt_t *os, const char *err, const char *str,
182251875Speter                         apr_status_t status)
183251875Speter{
184251875Speter    if (os->errfn)
185251875Speter        (os->errfn)(os->errarg, "%s: %s: %s\n",
186251875Speter                    apr_filepath_name_get(*os->argv), err, str);
187251875Speter    return status;
188251875Speter}
189251875Speter
190251875Speter/* Helper function to print out an error involving a short option */
191251875Speterstatic apr_status_t cerr(apr_getopt_t *os, const char *err, int ch,
192251875Speter                         apr_status_t status)
193251875Speter{
194251875Speter    if (os->errfn)
195251875Speter        (os->errfn)(os->errarg, "%s: %s: %c\n",
196251875Speter                    apr_filepath_name_get(*os->argv), err, ch);
197251875Speter    return status;
198251875Speter}
199251875Speter
200251875SpeterAPR_DECLARE(apr_status_t) apr_getopt_long(apr_getopt_t *os,
201251875Speter                                          const apr_getopt_option_t *opts,
202251875Speter                                          int *optch, const char **optarg)
203251875Speter{
204251875Speter    const char *p;
205251875Speter    int i;
206251875Speter
207251875Speter    /* Let the calling program reset option processing. */
208251875Speter    if (os->reset) {
209251875Speter        os->place = EMSG;
210251875Speter        os->ind = 1;
211251875Speter        os->reset = 0;
212251875Speter    }
213251875Speter
214251875Speter    /*
215251875Speter     * We can be in one of two states: in the middle of processing a
216251875Speter     * run of short options, or about to process a new argument.
217251875Speter     * Since the second case can lead to the first one, handle that
218251875Speter     * one first.  */
219251875Speter    p = os->place;
220251875Speter    if (*p == '\0') {
221251875Speter        /* If we are interleaving, skip non-option arguments. */
222251875Speter        if (os->interleave) {
223251875Speter            while (os->ind < os->argc && *os->argv[os->ind] != '-')
224251875Speter                os->ind++;
225251875Speter            os->skip_end = os->ind;
226251875Speter        }
227251875Speter        if (os->ind >= os->argc || *os->argv[os->ind] != '-') {
228251875Speter            os->ind = os->skip_start;
229251875Speter            return APR_EOF;
230251875Speter        }
231251875Speter
232251875Speter        p = os->argv[os->ind++] + 1;
233251875Speter        if (*p == '-' && p[1] != '\0') {        /* Long option */
234251875Speter            /* Search for the long option name in the caller's table. */
235251875Speter            apr_size_t len = 0;
236251875Speter
237251875Speter            p++;
238251875Speter            for (i = 0; ; i++) {
239251875Speter                if (opts[i].optch == 0)             /* No match */
240251875Speter                    return serr(os, "invalid option", p - 2, APR_BADCH);
241251875Speter
242251875Speter                if (opts[i].name) {
243251875Speter                    len = strlen(opts[i].name);
244251875Speter                    if (strncmp(p, opts[i].name, len) == 0
245251875Speter                        && (p[len] == '\0' || p[len] == '='))
246251875Speter                        break;
247251875Speter                }
248251875Speter            }
249251875Speter            *optch = opts[i].optch;
250251875Speter
251251875Speter            if (opts[i].has_arg) {
252251875Speter                if (p[len] == '=')             /* Argument inline */
253251875Speter                    *optarg = p + len + 1;
254251875Speter                else {
255251875Speter                    if (os->ind >= os->argc)   /* Argument missing */
256251875Speter                        return serr(os, "missing argument", p - 2, APR_BADARG);
257251875Speter                    else                       /* Argument in next arg */
258251875Speter                        *optarg = os->argv[os->ind++];
259251875Speter                }
260251875Speter            } else {
261251875Speter                *optarg = NULL;
262251875Speter                if (p[len] == '=')
263251875Speter                    return serr(os, "erroneous argument", p - 2, APR_BADARG);
264251875Speter            }
265251875Speter            permute(os);
266251875Speter            return APR_SUCCESS;
267251875Speter        } else {
268251875Speter            if (*p == '-') {                 /* Bare "--"; we're done */
269251875Speter                permute(os);
270251875Speter                os->ind = os->skip_start;
271251875Speter                return APR_EOF;
272251875Speter            }
273251875Speter            else
274251875Speter                if (*p == '\0')                    /* Bare "-" is illegal */
275251875Speter                    return serr(os, "invalid option", p, APR_BADCH);
276251875Speter        }
277251875Speter    }
278251875Speter
279251875Speter    /*
280251875Speter     * Now we're in a run of short options, and *p is the next one.
281251875Speter     * Look for it in the caller's table.
282251875Speter     */
283251875Speter    for (i = 0; ; i++) {
284251875Speter        if (opts[i].optch == 0)                     /* No match */
285251875Speter            return cerr(os, "invalid option character", *p, APR_BADCH);
286251875Speter
287251875Speter        if (*p == opts[i].optch)
288251875Speter            break;
289251875Speter    }
290251875Speter    *optch = *p++;
291251875Speter
292251875Speter    if (opts[i].has_arg) {
293251875Speter        if (*p != '\0')                         /* Argument inline */
294251875Speter            *optarg = p;
295251875Speter        else {
296251875Speter            if (os->ind >= os->argc)           /* Argument missing */
297251875Speter                return cerr(os, "missing argument", *optch, APR_BADARG);
298251875Speter            else                               /* Argument in next arg */
299251875Speter                *optarg = os->argv[os->ind++];
300251875Speter        }
301251875Speter        os->place = EMSG;
302251875Speter    } else {
303251875Speter        *optarg = NULL;
304251875Speter        os->place = p;
305251875Speter    }
306251875Speter
307251875Speter    permute(os);
308251875Speter    return APR_SUCCESS;
309251875Speter}
310