1 /* 2 * Copyright (c) 2017 - 2020, Arm Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 9 #include "win_posix.h" 10 11 /* 12 * This variable is set by getopt to the index of the next element of the 13 * argv array to be processed. Once getopt has found all of the option 14 * arguments, you can use this variable to determine where the remaining 15 * non-option arguments begin. The initial value of this variable is 1. 16 */ 17 int optind = 1; 18 19 /* 20 * If the value of this variable is nonzero, then getopt prints an error 21 * message to the standard error stream if it encounters an unknown option 22 * default character or an option with a missing required argument. 23 * If you set this variable to zero, getopt does not print any messages, 24 * but it still returns the character ? to indicate an error. 25 */ 26 const int opterr; /* = 0; */ 27 /* const because we do not implement error printing.*/ 28 /* Not initialised to conform with the coding standard. */ 29 30 /* 31 * When getopt encounters an unknown option character or an option with a 32 * missing required argument, it stores that option character in this 33 * variable. 34 */ 35 int optopt; /* = 0; */ 36 37 /* 38 * This variable is set by getopt to point at the value of the option 39 * argument, for those options that accept arguments. 40 */ 41 char *optarg; /* = 0; */ 42 43 enum return_flags { 44 RET_ERROR = -1, 45 RET_END_OPT_LIST = -1, 46 RET_NO_PARAM = '?', 47 RET_NO_PARAM2 = ':', 48 RET_UNKNOWN_OPT = '?' 49 }; 50 51 /* 52 * Common initialisation on entry. 53 */ 54 static 55 void getopt_init(void) 56 { 57 optarg = (char *)0; 58 optopt = 0; 59 /* optind may be zero with some POSIX uses. 60 * For our purposes we just change it to 1. 61 */ 62 if (optind == 0) 63 optind = 1; 64 } 65 66 /* 67 * Common handling for a single letter option. 68 */ 69 static 70 int getopt_1char(int argc, 71 char *const argv[], 72 const char *const opstring, 73 const int optchar) 74 { 75 size_t nlen = (opstring == 0) ? 0 : strlen(opstring); 76 size_t loptn; 77 78 for (loptn = 0; loptn < nlen; loptn++) { 79 if (optchar == opstring[loptn]) { 80 if (opstring[loptn + 1] == ':') { 81 /* Option has argument */ 82 if (optind < argc) { 83 /* Found argument. */ 84 assert(argv != 0); 85 optind++; 86 optarg = argv[optind++]; 87 return optchar; 88 } 89 /* Missing argument. */ 90 if (opstring[loptn + 2] == ':') { 91 /* OK if optional "x::". */ 92 optind++; 93 return optchar; 94 } 95 /* Actual missing value. */ 96 optopt = optchar; 97 return ((opstring[0] == ':') 98 ? RET_NO_PARAM2 99 : RET_NO_PARAM); 100 } 101 /* No argument, just return option char */ 102 optind++; 103 return optchar; 104 } 105 } 106 /* 107 * If getopt finds an option character in argv that was not included in 108 * options, ... it returns '?' and sets the external variable optopt to 109 * the actual option character. 110 */ 111 optopt = optchar; 112 return RET_UNKNOWN_OPT; 113 } 114 115 int getopt(int argc, 116 char *argv[], 117 char *opstring) 118 { 119 int result = RET_END_OPT_LIST; 120 size_t argn = 0; 121 size_t nlen = strlen(opstring); 122 123 getopt_init(); 124 /* If we have an argument left to play with */ 125 if ((argc > optind) && (argv != 0)) { 126 const char *arg = (const char *)argv[optind]; 127 128 if ((arg != 0) && (arg[0] == '-')) 129 result = getopt_1char(argc, argv, opstring, arg[1]); 130 } 131 132 return result; 133 } 134 135 /* 136 * Match an argument value against an option name. 137 * Note that we only match over the shorter length of the pair, to allow 138 * for abbreviation or say --match=value 139 * Long option names may be abbreviated if the abbreviation is unique or an 140 * exact match for some defined option. This function does not check that the 141 * abbreviations are unique and should be handled by the caller. 142 * A long option may take a parameter, of the form --opt=param or --opt param. 143 */ 144 static 145 int optmatch(const char *argval, const char *optname) 146 { 147 int result = 0; 148 149 while ((result == 0) && (*optname != 0) && (*argval != 0)) 150 result = (*argval++) - (*optname++); 151 return result; 152 } 153 154 /* Handling for a single long option. */ 155 static 156 int getopt_1long(const int argc, 157 char *const argv[], 158 const struct option *const longopts, 159 const char *const optname, 160 int *const indexptr) 161 { 162 int result = RET_UNKNOWN_OPT; 163 size_t loptn = 0; 164 bool match_found = false; 165 166 /* 167 * Long option names may be abbreviated if the abbreviation 168 * is unique or an exact match for some defined option. 169 * To handle this: 170 * - First search for an exact match. 171 * - If exact match was not found search for a abbreviated match. 172 * By doing this an incorrect option selection can be avoided. 173 */ 174 175 /* 1. Search for an exact match. */ 176 while (longopts[loptn].name != NULL) { 177 if (strcmp(optname, longopts[loptn].name) == 0) { 178 match_found = true; 179 break; 180 } 181 ++loptn; 182 } 183 184 /* 2. If exact match was not found search for a abbreviated match. */ 185 if (!match_found) { 186 loptn = 0; 187 while (longopts[loptn].name != NULL) { 188 if (optmatch(optname, longopts[loptn].name) == 0) { 189 match_found = true; 190 break; 191 } 192 ++loptn; 193 } 194 } 195 196 if (match_found) { 197 /* We found a match. */ 198 result = longopts[loptn].val; 199 if (indexptr != 0) { 200 *indexptr = loptn; 201 } 202 switch (longopts[loptn].has_arg) { 203 case required_argument: 204 if ((optind + 1) >= argc) { 205 /* Missing argument. */ 206 optopt = result; 207 return RET_NO_PARAM; 208 } 209 /* Fallthrough to get option value. */ 210 211 case optional_argument: 212 if ((argc - optind) > 0) { 213 /* Found argument. */ 214 optarg = argv[++optind]; 215 } 216 /* Fallthrough to handle flag. */ 217 218 case no_argument: 219 optind++; 220 if (longopts[loptn].flag != 0) { 221 *longopts[loptn].flag = result; 222 result = 0; 223 } 224 break; 225 226 } 227 return result; 228 } 229 230 /* 231 * If getopt finds an option character in argv that was not included 232 * in options, ... it returns '?' and sets the external variable 233 * optopt to the actual option character. 234 */ 235 return RET_UNKNOWN_OPT; 236 } 237 238 /* 239 * getopt_long gets the next option argument from the argument list 240 * specified by the argv and argc arguments. Options may be either short 241 * (single letter) as for getopt, or longer names (preceded by --). 242 */ 243 int getopt_long(int argc, 244 char *argv[], 245 const char *shortopts, 246 const struct option *longopts, 247 int *indexptr) 248 { 249 int result = RET_END_OPT_LIST; 250 251 getopt_init(); 252 /* If we have an argument left to play with */ 253 if ((argc > optind) && (argv != 0)) { 254 const char *arg = argv[optind]; 255 256 if ((arg != 0) && (arg[0] == '-')) { 257 if (arg[1] == '-') { 258 /* Looks like a long option. */ 259 result = getopt_1long(argc, 260 argv, 261 longopts, 262 &arg[2], 263 indexptr); 264 } else { 265 result = getopt_1char(argc, 266 argv, 267 shortopts, 268 arg[1]); 269 } 270 } 271 } 272 return result; 273 } 274 275 /* 276 * getopt_long_only gets the next option argument from the argument list 277 * specified by the argv and argc arguments. Options may be either short 278 * or long as for getopt_long, but the long names may have a single '-' 279 * prefix too. 280 */ 281 int getopt_long_only(int argc, 282 char *argv[], 283 const char *shortopts, 284 const struct option *longopts, 285 int *indexptr) 286 { 287 int result = RET_END_OPT_LIST; 288 289 getopt_init(); 290 /* If we have an argument left to play with */ 291 if ((argc > optind) && (argv != 0)) { 292 const char *arg = argv[optind]; 293 294 if ((arg != 0) && (arg[0] == '-')) { 295 if (arg[1] == '-') { 296 /* Looks like a long option. */ 297 result = getopt_1long(argc, 298 argv, 299 longopts, 300 &arg[2], 301 indexptr); 302 } else { 303 result = getopt_1long(argc, 304 argv, 305 longopts, 306 &arg[1], 307 indexptr); 308 if (result == RET_UNKNOWN_OPT) { 309 result = getopt_1char(argc, 310 argv, 311 shortopts, 312 arg[1]); 313 } 314 } 315 } 316 } 317 return result; 318 } 319