/** dropt_handlers.c * * Default type handlers for dropt. * * Copyright (C) 2006-2018 James D. Lin * * The latest version of this file can be downloaded from: * * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. */ #include #include #include #include #include #include #include "dropt.h" #include "dropt_string.h" #define CONCAT(s, t) s ## t #define XCONCAT(s, t) CONCAT(s, t) #define STATIC_ASSERT(cond) \ enum { XCONCAT(static_assert_line_, __LINE__) = 1 / ((cond) != 0) } #define ABS(x) (((x) < 0) ? -(x) : (x)) typedef enum { false, true } bool; /** dropt_handle_bool * * Stores a boolean value parsed from the given string if possible. * * PARAMETERS: * IN/OUT context : The options context. * IN option : The matched option. For more information, see * `dropt_option_handler_decl`. * IN optionArgument : A string representing a boolean value (0 or 1). * If `NULL`, the boolean value is assumed to be true. * OUT dest : A `dropt_bool*`. * On success, set to the interpreted boolean value. * On error, left untouched. * * RETURNS: * dropt_error_none * dropt_error_unknown * dropt_error_bad_configuration * dropt_error_mismatch */ dropt_error dropt_handle_bool(dropt_context* context, const dropt_option* option, const dropt_char* optionArgument, void* dest) { dropt_error err = dropt_error_none; bool val = false; dropt_bool* out = dest; if (out == NULL) { DROPT_MISUSE("No handler destination specified."); err = dropt_error_bad_configuration; } else if (optionArgument == NULL) { /* No explicit argument implies that the option is being turned on. */ val = true; } else if (optionArgument[0] == DROPT_TEXT_LITERAL('\0')) { err = dropt_error_mismatch; } else { unsigned int i = 0; err = dropt_handle_uint(context, option, optionArgument, &i); if (err == dropt_error_none) { switch (i) { case 0: val = false; break; case 1: val = true; break; default: err = dropt_error_mismatch; break; } } else if (err == dropt_error_overflow) { err = dropt_error_mismatch; } } if (err == dropt_error_none) { *out = val; } return err; } /** dropt_handle_verbose_bool * * Like `dropt_handle_bool` but accepts "true" and "false" string values. * * PARAMETERS: * IN/OUT context : The options context. * IN option : The matched option. For more information, see * `dropt_option_handler_decl`. * IN optionArgument : A string representing a boolean value. * If `NULL`, the boolean value is assumed to be true. * OUT dest : A `dropt_bool*`. * On success, set to the interpreted boolean value. * On error, left untouched. * * RETURNS: * See dropt_handle_bool. */ dropt_error dropt_handle_verbose_bool(dropt_context* context, const dropt_option* option, const dropt_char* optionArgument, void* dest) { dropt_error err = dropt_handle_bool(context, option, optionArgument, dest); if (err == dropt_error_mismatch) { bool val = false; dropt_bool* out = dest; /* `dropt_handle_bool` already checks for this. */ assert(out != NULL); if (dropt_stricmp(optionArgument, DROPT_TEXT_LITERAL("false")) == 0) { val = false; err = dropt_error_none; } else if (dropt_stricmp(optionArgument, DROPT_TEXT_LITERAL("true")) == 0) { val = true; err = dropt_error_none; } if (err == dropt_error_none) { *out = val; } } return err; } /** dropt_handle_int * * Stores an integer parsed from the given string. * * PARAMETERS: * IN/OUT context : The options context. * IN option : The matched option. For more information, see * `dropt_option_handler_decl`. * IN optionArgument : A string representing a base-10 integer. * If `NULL`, returns * `dropt_error_insufficient_arguments`. * OUT dest : An `int*`. * On success, set to the interpreted integer. * On error, left untouched. * * RETURNS: * dropt_error_none * dropt_error_unknown * dropt_error_bad_configuration * dropt_error_insufficient_arguments * dropt_error_mismatch * dropt_error_overflow */ dropt_error dropt_handle_int(dropt_context* context, const dropt_option* option, const dropt_char* optionArgument, void* dest) { dropt_error err = dropt_error_none; int val = 0; int* out = dest; if (out == NULL) { DROPT_MISUSE("No handler destination specified."); err = dropt_error_bad_configuration; } else if ( optionArgument == NULL || optionArgument[0] == DROPT_TEXT_LITERAL('\0')) { err = dropt_error_insufficient_arguments; } else { dropt_char* end; long n; errno = 0; n = dropt_strtol(optionArgument, &end, 10); /* Check that we matched at least one digit. * (`strtol`/`strtoul` will return 0 if fed a string with no digits.) */ if (*end == DROPT_TEXT_LITERAL('\0') && end > optionArgument) { if (errno == ERANGE || n < INT_MIN || n > INT_MAX) { err = dropt_error_overflow; val = (n < 0) ? INT_MIN : INT_MAX; } else if (errno == 0) { val = (int) n; } else { err = dropt_error_unknown; } } else { err = dropt_error_mismatch; } } if (err == dropt_error_none) { *out = val; } return err; } /** dropt_handle_uint * * Stores an unsigned integer parsed from the given string. * * PARAMETERS: * IN/OUT context : The options context. * IN option : The matched option. For more information, see * `dropt_option_handler_decl`. * IN optionArgument : A string representing an unsigned base-10 integer. * If `NULL`, returns * `dropt_error_insufficient_arguments`. * IN option : The matched option. For more information, see * dropt_option_handler_decl. * OUT dest : An `unsigned int*`. * On success, set to the interpreted integer. * On error, left untouched. * * RETURNS: * dropt_error_none * dropt_error_unknown * dropt_error_bad_configuration * dropt_error_insufficient_arguments * dropt_error_mismatch * dropt_error_overflow */ dropt_error dropt_handle_uint(dropt_context* context, const dropt_option* option, const dropt_char* optionArgument, void* dest) { dropt_error err = dropt_error_none; int val = 0; unsigned int* out = dest; if (out == NULL) { DROPT_MISUSE("No handler destination specified."); err = dropt_error_bad_configuration; } else if ( optionArgument == NULL || optionArgument[0] == DROPT_TEXT_LITERAL('\0')) { err = dropt_error_insufficient_arguments; } else if (optionArgument[0] == DROPT_TEXT_LITERAL('-')) { err = dropt_error_mismatch; } else { dropt_char* end; unsigned long n; errno = 0; n = dropt_strtoul(optionArgument, &end, 10); /* Check that we matched at least one digit. * (`strtol`/`strtoul` will return 0 if fed a string with no digits.) */ if (*end == DROPT_TEXT_LITERAL('\0') && end > optionArgument) { if (errno == ERANGE || n > UINT_MAX) { err = dropt_error_overflow; val = UINT_MAX; } else if (errno == 0) { val = (unsigned int) n; } else { err = dropt_error_unknown; } } else { err = dropt_error_mismatch; } } if (err == dropt_error_none) { *out = val; } return err; } /** dropt_handle_double * * Stores a `double` parsed from the given string. * * PARAMETERS: * IN/OUT context : The options context. * IN option : The matched option. For more information, see * `dropt_option_handler_decl`. * IN optionArgument : A string representing a base-10 floating-point * number. * If `NULL`, returns * `dropt_error_insufficient_arguments`. * OUT dest : A `double*`. * On success, set to the interpreted `double`. * On error, left untouched. * * RETURNS: * dropt_error_none * dropt_error_unknown * dropt_error_bad_configuration * dropt_error_insufficient_arguments * dropt_error_mismatch * dropt_error_overflow * dropt_error_underflow */ dropt_error dropt_handle_double(dropt_context* context, const dropt_option* option, const dropt_char* optionArgument, void* dest) { dropt_error err = dropt_error_none; double val = 0.0; double* out = dest; if (out == NULL) { DROPT_MISUSE("No handler destination specified."); err = dropt_error_bad_configuration; } else if ( optionArgument == NULL || optionArgument[0] == DROPT_TEXT_LITERAL('\0')) { err = dropt_error_insufficient_arguments; } else { dropt_char* end; errno = 0; val = dropt_strtod(optionArgument, &end); /* Check that we matched at least one digit. * (`strtod` will return 0 if fed a string with no digits.) */ if (*end == DROPT_TEXT_LITERAL('\0') && end > optionArgument) { if (errno == ERANGE) { /* Note that setting `errno` to `ERANGE` for underflow errors * is implementation-defined behavior, but glibc, BSD's * libc, and Microsoft's CRT all have implementations of * `strtod` documented to return 0 and to set `errno` to * `ERANGE` for such cases. */ err = (ABS(val) <= DBL_MIN) ? dropt_error_underflow : dropt_error_overflow; } else if (errno != 0) { err = dropt_error_unknown; } } else { err = dropt_error_mismatch; } } if (err == dropt_error_none) { *out = val; } return err; } /** dropt_handle_string * * Stores a string. * * PARAMETERS: * IN/OUT context : The options context. * IN option : The matched option. For more information, see * `dropt_option_handler_decl`. * IN optionArgument : A string. * If `NULL`, returns * `dropt_error_insufficient_arguments`. * OUT dest : A `dropt_char**`. * On success, set to the input string. The string is * NOT copied from the original `argv` array, so do * not free it. * On error, left untouched. * * RETURNS: * dropt_error_none * dropt_error_bad_configuration * dropt_error_insufficient_arguments */ dropt_error dropt_handle_string(dropt_context* context, const dropt_option* option, const dropt_char* optionArgument, void* dest) { dropt_error err = dropt_error_none; const dropt_char** out = dest; if (out == NULL) { DROPT_MISUSE("No handler destination specified."); err = dropt_error_bad_configuration; } else if (optionArgument == NULL) { err = dropt_error_insufficient_arguments; } if (err == dropt_error_none) { *out = optionArgument; } return err; } /** dropt_handle_const * * Stores a predefined value. This can be used to set a single variable * to different values in response to different boolean-like command-line * options. * * PARAMETERS: * IN/OUT context : The options context. * IN option : The matched option. For more information, see * `dropt_option_handler_decl`. * IN optionArgument : Must be `NULL`. * OUT dest : A `dropt_uintptr*`. * On success, set to the constant value specified by * `option->extra_data`. * On error, left untouched. * * RETURNS: * dropt_error_none * dropt_error_bad_configuration * dropt_error_mismatch */ dropt_error dropt_handle_const(dropt_context* context, const dropt_option* option, const dropt_char* optionArgument, void* dest) { dropt_error err = dropt_error_none; dropt_uintptr* out = dest; STATIC_ASSERT(sizeof (dropt_uintptr) >= sizeof (void*)); if (out == NULL) { DROPT_MISUSE("No handler destination specified."); err = dropt_error_bad_configuration; } else if (option == NULL) { DROPT_MISUSE("No option entry given."); err = dropt_error_bad_configuration; } else if (optionArgument != NULL) { err = dropt_error_mismatch; } if (err == dropt_error_none) { *out = option->extra_data; } return err; }