521 lines
15 KiB
C
521 lines
15 KiB
C
|
/** dropt_handlers.c
|
||
|
*
|
||
|
* Default type handlers for dropt.
|
||
|
*
|
||
|
* Copyright (C) 2006-2018 James D. Lin <jamesdlin@berkeley.edu>
|
||
|
*
|
||
|
* The latest version of this file can be downloaded from:
|
||
|
* <http://www.taenarum.com/software/dropt/>
|
||
|
*
|
||
|
* 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 <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <limits.h>
|
||
|
#include <float.h>
|
||
|
#include <errno.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#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;
|
||
|
}
|