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