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