180529Sdd/* 280529Sdd * ***************************************************************************** 380529Sdd * 480529Sdd * SPDX-License-Identifier: BSD-2-Clause 580529Sdd * 680529Sdd * Copyright (c) 2018-2023 Gavin D. Howard and contributors. 780529Sdd * 880529Sdd * Redistribution and use in source and binary forms, with or without 980529Sdd * modification, are permitted provided that the following conditions are met: 1080529Sdd * 1180529Sdd * * Redistributions of source code must retain the above copyright notice, this 1280529Sdd * list of conditions and the following disclaimer. 1380529Sdd * 1480529Sdd * * Redistributions in binary form must reproduce the above copyright notice, 1580529Sdd * this list of conditions and the following disclaimer in the documentation 1680529Sdd * and/or other materials provided with the distribution. 1780529Sdd * 1880529Sdd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1980529Sdd * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2080529Sdd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2180529Sdd * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 2280529Sdd * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2380529Sdd * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2480529Sdd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2580529Sdd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2680529Sdd * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2780529Sdd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2880529Sdd * POSSIBILITY OF SUCH DAMAGE. 2980529Sdd * 3080529Sdd * ***************************************************************************** 3180529Sdd * 3280529Sdd * Adapted from https://github.com/skeeto/optparse 3380529Sdd * 3480529Sdd * ***************************************************************************** 3580529Sdd * 3680529Sdd * Code for getopt_long() replacement. It turns out that getopt_long() has 3780529Sdd * different behavior on different platforms. 3880529Sdd * 3980529Sdd */ 4080529Sdd 4180529Sdd#include <assert.h> 4280529Sdd#include <stdbool.h> 4380529Sdd#include <stdlib.h> 4480529Sdd#include <string.h> 4580529Sdd 4680529Sdd#include <status.h> 4780529Sdd#include <opt.h> 4880529Sdd#include <vm.h> 4980529Sdd 5080529Sdd/** 5180529Sdd * Returns true if index @a i is the end of the longopts array. 5280529Sdd * @param longopts The long options array. 5380529Sdd * @param i The index to test. 5480529Sdd * @return True if @a i is the last index, false otherwise. 5580529Sdd */ 5680529Sddstatic inline bool 5780529Sddbc_opt_longoptsEnd(const BcOptLong* longopts, size_t i) 5880529Sdd{ 5980529Sdd return !longopts[i].name && !longopts[i].val; 6080529Sdd} 6180529Sdd 6280529Sdd/** 6380529Sdd * Returns the name of the long option that matches the character @a c. 6480529Sdd * @param longopts The long options array. 6580529Sdd * @param c The character to match against. 6680529Sdd * @return The name of the long option that matches @a c, or "NULL". 6780529Sdd */ 6880529Sddstatic const char* 6980529Sddbc_opt_longopt(const BcOptLong* longopts, int c) 7080529Sdd{ 7180529Sdd size_t i; 7284212Sdillon 7384212Sdillon for (i = 0; !bc_opt_longoptsEnd(longopts, i); ++i) 7480529Sdd { 7580529Sdd if (longopts[i].val == c) return longopts[i].name; 7680529Sdd } 7780529Sdd 7880529Sdd BC_UNREACHABLE 7980529Sdd 8080529Sdd#if !BC_CLANG 8180529Sdd return "NULL"; 8280529Sdd#endif // !BC_CLANG 8380529Sdd} 8480529Sdd 8580529Sdd/** 8680529Sdd * Issues a fatal error for an option parsing failure. 8780529Sdd * @param err The error. 8880529Sdd * @param c The character for the failing option. 8980529Sdd * @param str Either the string for the failing option, or the invalid 9080529Sdd * option. 9180529Sdd * @param use_short True if the short option should be used for error printing, 9280529Sdd * false otherwise. 9380529Sdd */ 9480529Sddstatic void 9580529Sddbc_opt_error(BcErr err, int c, const char* str, bool use_short) 9680529Sdd{ 9780529Sdd if (err == BC_ERR_FATAL_OPTION) 98160840Ssimon { 99160840Ssimon if (use_short) 10080529Sdd { 10180529Sdd char short_str[2]; 10280529Sdd 10380529Sdd short_str[0] = (char) c; 10480529Sdd short_str[1] = '\0'; 10580529Sdd 106160840Ssimon bc_error(err, 0, short_str); 107160840Ssimon } 10880529Sdd else bc_error(err, 0, str); 10980529Sdd } 11080529Sdd else bc_error(err, 0, (int) c, str); 11180529Sdd} 11280529Sdd 11380529Sdd/** 11480529Sdd * Returns the type of the long option that matches @a c. 11580529Sdd * @param longopts The long options array. 11680529Sdd * @param c The character to match against. 11780529Sdd * @return The type of the long option as an integer, or -1 if none. 11880529Sdd */ 11980529Sddstatic int 12080529Sddbc_opt_type(const BcOptLong* longopts, char c) 12180529Sdd{ 12280529Sdd size_t i; 12380529Sdd 12480529Sdd if (c == ':') return -1; 12580529Sdd 12680529Sdd for (i = 0; !bc_opt_longoptsEnd(longopts, i) && longopts[i].val != c; ++i) 12780529Sdd { 12880529Sdd continue; 12980529Sdd } 13080529Sdd 13180529Sdd if (bc_opt_longoptsEnd(longopts, i)) return -1; 13280529Sdd 13380529Sdd return (int) longopts[i].type; 13480529Sdd} 13580529Sdd 13680529Sdd/** 13780529Sdd * Parses a short option. 13880529Sdd * @param o The option parser. 13980529Sdd * @param longopts The long options array. 14080529Sdd * @return The character for the short option, or -1 if none left. 14180529Sdd */ 14280529Sddstatic int 14380529Sddbc_opt_parseShort(BcOpt* o, const BcOptLong* longopts) 14480529Sdd{ 14580529Sdd int type; 146160805Ssimon char* next; 14780529Sdd char* option = o->argv[o->optind]; 148160805Ssimon int ret = -1; 149160805Ssimon 150160805Ssimon // Make sure to clear these. 15180529Sdd o->optopt = 0; 152160805Ssimon o->optarg = NULL; 15380529Sdd 15480529Sdd // Get the next option. 155160805Ssimon option += o->subopt + 1; 15680529Sdd o->optopt = option[0]; 15780529Sdd 15880529Sdd // Get the type and the next data. 15980529Sdd type = bc_opt_type(longopts, option[0]); 16080529Sdd next = o->argv[o->optind + 1]; 16180529Sdd 16280529Sdd switch (type) 16380529Sdd { 16480529Sdd case -1: 16580529Sdd case BC_OPT_BC_ONLY: 16680529Sdd case BC_OPT_DC_ONLY: 16780529Sdd { 16880529Sdd // Check for invalid option and barf if so. 16980529Sdd if (type == -1 || (type == BC_OPT_BC_ONLY && BC_IS_DC) || 17080529Sdd (type == BC_OPT_DC_ONLY && BC_IS_BC)) 17180529Sdd { 17280529Sdd char str[2] = { 0, 0 }; 17380529Sdd 17480529Sdd str[0] = option[0]; 17580529Sdd o->optind += 1; 17680529Sdd 17780529Sdd bc_opt_error(BC_ERR_FATAL_OPTION, option[0], str, true); 17880529Sdd } 17980529Sdd 18080529Sdd // Fallthrough. 18180529Sdd BC_FALLTHROUGH 18280529Sdd } 18380529Sdd 18480529Sdd case BC_OPT_NONE: 18580529Sdd { 18680529Sdd // If there is something else, update the suboption. 18780529Sdd if (option[1]) o->subopt += 1; 18880529Sdd else 18980529Sdd { 19080529Sdd // Go to the next argument. 19180529Sdd o->subopt = 0; 19280529Sdd o->optind += 1; 19380529Sdd } 19480529Sdd 19580529Sdd ret = (int) option[0]; 19680529Sdd 19780529Sdd break; 19880529Sdd } 19980529Sdd 20080529Sdd case BC_OPT_REQUIRED_BC_ONLY: 20180529Sdd { 20280529Sdd#if DC_ENABLED 20380529Sdd if (BC_IS_DC) 20480529Sdd { 20580529Sdd bc_opt_error(BC_ERR_FATAL_OPTION, option[0], 20680529Sdd bc_opt_longopt(longopts, option[0]), true); 20780529Sdd } 20880529Sdd#endif // DC_ENABLED 20980529Sdd 21080529Sdd // Fallthrough 21180529Sdd BC_FALLTHROUGH 21280529Sdd } 21380529Sdd 21480529Sdd case BC_OPT_REQUIRED: 21580529Sdd { 21680529Sdd // Always go to the next argument. 21780529Sdd o->subopt = 0; 21880529Sdd o->optind += 1; 21980529Sdd 22080529Sdd // Use the next characters, if they exist. 22180529Sdd if (option[1]) o->optarg = option + 1; 22280529Sdd else if (next != NULL) 22380529Sdd { 22480529Sdd // USe the next. 22580529Sdd o->optarg = next; 22680529Sdd o->optind += 1; 227160840Ssimon } 228160840Ssimon // No argument, barf. 22980529Sdd else 23080529Sdd { 23180529Sdd bc_opt_error(BC_ERR_FATAL_OPTION_NO_ARG, option[0], 23280529Sdd bc_opt_longopt(longopts, option[0]), true); 23380529Sdd } 234160805Ssimon 23580529Sdd ret = (int) option[0]; 23680529Sdd 23780529Sdd break; 23880529Sdd } 23980529Sdd } 24080529Sdd 24180529Sdd return ret; 24280529Sdd} 24380529Sdd 244160840Ssimon/** 24580529Sdd * Ensures that a long option argument matches a long option name, regardless of 246160840Ssimon * "=<data>" at the end. 247160840Ssimon * @param name The name to match. 248160840Ssimon * @param option The command-line argument. 249160840Ssimon * @return True if @a option matches @a name, false otherwise. 250160840Ssimon */ 25180529Sddstatic bool 25280529Sddbc_opt_longoptsMatch(const char* name, const char* option) 25380529Sdd{ 25480529Sdd const char* a = option; 25580529Sdd const char* n = name; 25680529Sdd 25780529Sdd // Can never match a NULL name. 25880529Sdd if (name == NULL) return false; 25980529Sdd 26080529Sdd // Loop through. 26180529Sdd for (; *a && *n && *a != '='; ++a, ++n) 26280529Sdd { 26380529Sdd if (*a != *n) return false; 26480529Sdd } 26580529Sdd 26680529Sdd // Ensure they both end at the same place. 26780529Sdd return (*n == '\0' && (*a == '\0' || *a == '=')); 26880529Sdd} 26980529Sdd 27080529Sdd/** 27180529Sdd * Returns a pointer to the argument of a long option, or NULL if it not in the 27280529Sdd * same argument. 27380529Sdd * @param option The option to find the argument of. 27480529Sdd * @return A pointer to the argument of the option, or NULL if none. 27580529Sdd */ 27680529Sddstatic char* 27780529Sddbc_opt_longoptsArg(char* option) 27880529Sdd{ 27980529Sdd // Find the end or equals sign. 28080529Sdd for (; *option && *option != '='; ++option) 28180529Sdd { 28280529Sdd continue; 28380529Sdd } 28480529Sdd 28580529Sdd if (*option == '=') return option + 1; 28680529Sdd else return NULL; 28780529Sdd} 28880529Sdd 28980529Sddint 29080529Sddbc_opt_parse(BcOpt* o, const BcOptLong* longopts) 29180529Sdd{ 29280529Sdd size_t i; 29380529Sdd char* option; 29480529Sdd bool empty; 29580529Sdd 29680529Sdd // This just eats empty options. 29780529Sdd do 29880529Sdd { 29980529Sdd option = o->argv[o->optind]; 30080529Sdd if (option == NULL) return -1; 30180529Sdd 30280529Sdd empty = !strcmp(option, ""); 30380529Sdd o->optind += empty; 30480529Sdd } 30580529Sdd while (empty); 30680529Sdd 30780529Sdd // If the option is just a "--". 30880529Sdd if (BC_OPT_ISDASHDASH(option)) 30980529Sdd { 31080529Sdd // Consume "--". 31180529Sdd o->optind += 1; 31280529Sdd return -1; 31380529Sdd } 31480529Sdd // Parse a short option. 31580529Sdd else if (BC_OPT_ISSHORTOPT(option)) return bc_opt_parseShort(o, longopts); 31680529Sdd // If the option is not long at this point, we are done. 31780529Sdd else if (!BC_OPT_ISLONGOPT(option)) return -1; 31880529Sdd 31980529Sdd // Clear these. 32080529Sdd o->optopt = 0; 32180529Sdd o->optarg = NULL; 32280529Sdd 32380529Sdd // Skip "--" at beginning of the option. 32480529Sdd option += 2; 32580529Sdd o->optind += 1; 32680529Sdd 32780529Sdd // Loop through the valid long options. 32880529Sdd for (i = 0; !bc_opt_longoptsEnd(longopts, i); i++) 32980529Sdd { 33080529Sdd const char* name = longopts[i].name; 33180529Sdd 33280529Sdd // If we have a match... 33380529Sdd if (bc_opt_longoptsMatch(name, option)) 33480529Sdd { 33580529Sdd char* arg; 33680529Sdd 33780529Sdd // Get the option char and the argument. 33880529Sdd o->optopt = longopts[i].val; 33980529Sdd arg = bc_opt_longoptsArg(option); 34080529Sdd 34180529Sdd // Error if the option is invalid.. 34280529Sdd if ((longopts[i].type == BC_OPT_BC_ONLY && BC_IS_DC) || 34380529Sdd (longopts[i].type == BC_OPT_REQUIRED_BC_ONLY && BC_IS_DC) || 34480529Sdd (longopts[i].type == BC_OPT_DC_ONLY && BC_IS_BC)) 34580529Sdd { 34680529Sdd bc_opt_error(BC_ERR_FATAL_OPTION, o->optopt, name, false); 34780529Sdd } 34880529Sdd 34980529Sdd // Error if we have an argument and should not. 35080529Sdd if (longopts[i].type == BC_OPT_NONE && arg != NULL) 35180529Sdd { 35280529Sdd bc_opt_error(BC_ERR_FATAL_OPTION_ARG, o->optopt, name, false); 35380529Sdd } 35480529Sdd 35580529Sdd // Set the argument, or check the next argument if we don't have 35680529Sdd // one. 35780529Sdd if (arg != NULL) o->optarg = arg; 35880529Sdd else if (longopts[i].type == BC_OPT_REQUIRED || 35980529Sdd longopts[i].type == BC_OPT_REQUIRED_BC_ONLY) 36080529Sdd { 36180529Sdd // Get the next argument. 362160840Ssimon o->optarg = o->argv[o->optind]; 36380529Sdd 36480529Sdd // All's good if it exists; otherwise, barf. 36580529Sdd if (o->optarg != NULL) o->optind += 1; 36680529Sdd else 36780529Sdd { 368160840Ssimon bc_opt_error(BC_ERR_FATAL_OPTION_NO_ARG, o->optopt, name, 369160840Ssimon false); 370160840Ssimon } 37180529Sdd } 37280529Sdd 37380529Sdd return o->optopt; 37480529Sdd } 37580529Sdd } 37680529Sdd 37780529Sdd // If we reach this point, the option is invalid. 37880529Sdd bc_opt_error(BC_ERR_FATAL_OPTION, 0, option, false); 379160840Ssimon 38080529Sdd BC_UNREACHABLE 381160840Ssimon 38280529Sdd#if !BC_CLANG 38380529Sdd return -1; 38480529Sdd#endif // !BC_CLANG 385160840Ssimon} 38680529Sdd 38780529Sddvoid 38880529Sddbc_opt_init(BcOpt* o, char* argv[]) 38980529Sdd{ 39080529Sdd o->argv = argv; 39180529Sdd o->optind = 1; 39280529Sdd o->subopt = 0; 39380529Sdd o->optarg = NULL; 394160840Ssimon} 39580529Sdd