1 /* $NetBSD: commandline.c,v 1.1.1.1 2018/08/12 12:08:23 christos Exp $ */ 2 3 /* 4 * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * See the COPYRIGHT file distributed with this work for additional 11 * information regarding copyright ownership. 12 */ 13 14 /* 15 * Copyright (c) 1987, 1993, 1994 16 * The Regents of the University of California. All rights reserved. 17 * 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions 20 * are met: 21 * 1. Redistributions of source code must retain the above copyright 22 * notice, this list of conditions and the following disclaimer. 23 * 2. Redistributions in binary form must reproduce the above copyright 24 * notice, this list of conditions and the following disclaimer in the 25 * documentation and/or other materials provided with the distribution. 26 * 3. Neither the name of the University nor the names of its contributors 27 * may be used to endorse or promote products derived from this software 28 * without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 */ 42 43 44 /*! \file 45 * This file was adapted from the NetBSD project's source tree, RCS ID: 46 * NetBSD: getopt.c,v 1.15 1999/09/20 04:39:37 lukem Exp 47 * 48 * The primary change has been to rename items to the ISC namespace 49 * and format in the ISC coding style. 50 */ 51 52 #include <config.h> 53 54 #include <stdio.h> 55 56 #include <isc/commandline.h> 57 #include <isc/mem.h> 58 #include <isc/msgs.h> 59 #include <isc/print.h> 60 #include <isc/string.h> 61 #include <isc/util.h> 62 63 /*% Index into parent argv vector. */ 64 LIBISC_EXTERNAL_DATA int isc_commandline_index = 1; 65 /*% Character checked for validity. */ 66 LIBISC_EXTERNAL_DATA int isc_commandline_option; 67 /*% Argument associated with option. */ 68 LIBISC_EXTERNAL_DATA char *isc_commandline_argument; 69 /*% For printing error messages. */ 70 LIBISC_EXTERNAL_DATA char *isc_commandline_progname; 71 /*% Print error messages. */ 72 LIBISC_EXTERNAL_DATA isc_boolean_t isc_commandline_errprint = ISC_TRUE; 73 /*% Reset processing. */ 74 LIBISC_EXTERNAL_DATA isc_boolean_t isc_commandline_reset = ISC_TRUE; 75 76 static char endopt = '\0'; 77 78 #define BADOPT '?' 79 #define BADARG ':' 80 #define ENDOPT &endopt 81 82 /*! 83 * getopt -- 84 * Parse argc/argv argument vector. 85 */ 86 int 87 isc_commandline_parse(int argc, char * const *argv, const char *options) { 88 static char *place = ENDOPT; 89 const char *option; /* Index into *options of option. */ 90 91 REQUIRE(argc >= 0 && argv != NULL && options != NULL); 92 93 /* 94 * Update scanning pointer, either because a reset was requested or 95 * the previous argv was finished. 96 */ 97 if (isc_commandline_reset || *place == '\0') { 98 if (isc_commandline_reset) { 99 isc_commandline_index = 1; 100 isc_commandline_reset = ISC_FALSE; 101 } 102 103 if (isc_commandline_progname == NULL) 104 isc_commandline_progname = argv[0]; 105 106 if (isc_commandline_index >= argc || 107 *(place = argv[isc_commandline_index]) != '-') { 108 /* 109 * Index out of range or points to non-option. 110 */ 111 place = ENDOPT; 112 return (-1); 113 } 114 115 if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { 116 /* 117 * Found '--' to signal end of options. Advance 118 * index to next argv, the first non-option. 119 */ 120 isc_commandline_index++; 121 place = ENDOPT; 122 return (-1); 123 } 124 } 125 126 isc_commandline_option = *place++; 127 option = strchr(options, isc_commandline_option); 128 129 /* 130 * Ensure valid option has been passed as specified by options string. 131 * '-:' is never a valid command line option because it could not 132 * distinguish ':' from the argument specifier in the options string. 133 */ 134 if (isc_commandline_option == ':' || option == NULL) { 135 if (*place == '\0') 136 isc_commandline_index++; 137 138 if (isc_commandline_errprint && *options != ':') 139 fprintf(stderr, "%s: %s -- %c\n", 140 isc_commandline_progname, 141 isc_msgcat_get(isc_msgcat, 142 ISC_MSGSET_COMMANDLINE, 143 ISC_MSG_ILLEGALOPT, 144 "illegal option"), 145 isc_commandline_option); 146 147 return (BADOPT); 148 } 149 150 if (*++option != ':') { 151 /* 152 * Option does not take an argument. 153 */ 154 isc_commandline_argument = NULL; 155 156 /* 157 * Skip to next argv if at the end of the current argv. 158 */ 159 if (*place == '\0') 160 ++isc_commandline_index; 161 162 } else { 163 /* 164 * Option needs an argument. 165 */ 166 if (*place != '\0') 167 /* 168 * Option is in this argv, -D1 style. 169 */ 170 isc_commandline_argument = place; 171 172 else if (argc > ++isc_commandline_index) 173 /* 174 * Option is next argv, -D 1 style. 175 */ 176 isc_commandline_argument = argv[isc_commandline_index]; 177 178 else { 179 /* 180 * Argument needed, but no more argv. 181 */ 182 place = ENDOPT; 183 184 /* 185 * Silent failure with "missing argument" return 186 * when ':' starts options string, per historical spec. 187 */ 188 if (*options == ':') 189 return (BADARG); 190 191 if (isc_commandline_errprint) 192 fprintf(stderr, "%s: %s -- %c\n", 193 isc_commandline_progname, 194 isc_msgcat_get(isc_msgcat, 195 ISC_MSGSET_COMMANDLINE, 196 ISC_MSG_OPTNEEDARG, 197 "option requires " 198 "an argument"), 199 isc_commandline_option); 200 201 return (BADOPT); 202 } 203 204 place = ENDOPT; 205 206 /* 207 * Point to argv that follows argument. 208 */ 209 isc_commandline_index++; 210 } 211 212 return (isc_commandline_option); 213 } 214 215 isc_result_t 216 isc_commandline_strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, 217 char ***argvp, unsigned int n) 218 { 219 isc_result_t result; 220 221 restart: 222 /* Discard leading whitespace. */ 223 while (*s == ' ' || *s == '\t') 224 s++; 225 226 if (*s == '\0') { 227 /* We have reached the end of the string. */ 228 *argcp = n; 229 *argvp = isc_mem_get(mctx, n * sizeof(char *)); 230 if (*argvp == NULL) 231 return (ISC_R_NOMEMORY); 232 } else { 233 char *p = s; 234 while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') { 235 if (*p == '\n') { 236 *p = ' '; 237 goto restart; 238 } 239 p++; 240 } 241 242 /* do "grouping", items between { and } are one arg */ 243 if (*p == '{') { 244 char *t = p; 245 /* 246 * shift all characters to left by 1 to get rid of '{' 247 */ 248 while (*t != '\0') { 249 t++; 250 *(t-1) = *t; 251 } 252 while (*p != '\0' && *p != '}') { 253 p++; 254 } 255 /* get rid of '}' character */ 256 if (*p == '}') { 257 *p = '\0'; 258 p++; 259 } 260 /* normal case, no "grouping" */ 261 } else if (*p != '\0') 262 *p++ = '\0'; 263 264 result = isc_commandline_strtoargv(mctx, p, 265 argcp, argvp, n + 1); 266 if (result != ISC_R_SUCCESS) 267 return (result); 268 (*argvp)[n] = s; 269 } 270 271 return (ISC_R_SUCCESS); 272 } 273