]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/apr/misc/unix/getopt.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / apr / misc / unix / getopt.c
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
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
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]. */
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  */
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 */
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 */
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
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 }