diff --git a/CMakeLists.txt b/CMakeLists.txt index 52a6f78..8b8a71e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,12 +41,14 @@ endif () add_library(${PROJECT_NAME} STATIC src/lib/DeepState.c src/lib/Log.c + src/lib/Option.c src/lib/Stream.c ) add_library(${PROJECT_NAME}32 STATIC src/lib/DeepState.c src/lib/Log.c + src/lib/Option.c src/lib/Stream.c ) diff --git a/bin/deepstate/common.py b/bin/deepstate/common.py index 8fd0430..393d58b 100644 --- a/bin/deepstate/common.py +++ b/bin/deepstate/common.py @@ -118,7 +118,8 @@ class DeepState(object): help="Number of workers to spawn for testing and test generation.") parser.add_argument( - "--output_test_dir", type=str, required=False) + "--output_test_dir", type=str, required=False, + "Directory where tests will be saved.") parser.add_argument( "binary", type=str, help="Path to the test binary to run.") diff --git a/examples/Euler.cpp b/examples/Euler.cpp index 8a341f7..20f9790 100644 --- a/examples/Euler.cpp +++ b/examples/Euler.cpp @@ -39,6 +39,7 @@ TEST(Euler, SumsOfLikePowers) { << "^5 + " << d << "^5 = " << e << "^5"; } -int main(void) { +int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); return DeepState_Run(); } diff --git a/examples/Fixture.cpp b/examples/Fixture.cpp index 72a4e67..a4af0e1 100644 --- a/examples/Fixture.cpp +++ b/examples/Fixture.cpp @@ -36,7 +36,8 @@ TEST_F(MyTest, Something) { ASSUME_NE(x, 0); } -int main(void) { +int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); return DeepState_Run(); } diff --git a/examples/IntegerArithmetic.cpp b/examples/IntegerArithmetic.cpp index 6bd4f74..37b2ac0 100644 --- a/examples/IntegerArithmetic.cpp +++ b/examples/IntegerArithmetic.cpp @@ -45,6 +45,7 @@ TEST(Arithmetic, InvertibleMultiplication_CanFail) { }); } -int main(void) { +int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); return DeepState_Run(); } diff --git a/examples/IntegerOverflow.cpp b/examples/IntegerOverflow.cpp index 362b639..09fd32f 100644 --- a/examples/IntegerOverflow.cpp +++ b/examples/IntegerOverflow.cpp @@ -41,5 +41,6 @@ TEST(SignedInteger, MultiplicationOverflow) { } int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); return DeepState_Run(); } diff --git a/examples/Lists.cpp b/examples/Lists.cpp index 1723b63..1e4ad12 100644 --- a/examples/Lists.cpp +++ b/examples/Lists.cpp @@ -31,6 +31,7 @@ TEST(Vector, DoubleReversal) { }); } -int main(void) { +int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); DeepState_Run(); } diff --git a/examples/OneOf.cpp b/examples/OneOf.cpp index 14a5c9a..1a9bddf 100644 --- a/examples/OneOf.cpp +++ b/examples/OneOf.cpp @@ -65,5 +65,6 @@ TEST(OneOfExample, ProduceSixtyOrHigher) { } int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); return DeepState_Run(); } diff --git a/examples/Primes.cpp b/examples/Primes.cpp index 127e203..de94102 100644 --- a/examples/Primes.cpp +++ b/examples/Primes.cpp @@ -42,5 +42,6 @@ TEST(PrimePolynomial, OnlyGeneratesPrimes) { } int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); return DeepState_Run(); } diff --git a/examples/Squares.c b/examples/Squares.c index 1b4ac54..3d1b45c 100644 --- a/examples/Squares.c +++ b/examples/Squares.c @@ -23,7 +23,8 @@ int main(int argc, const char *argv[]) { #endif -int main(int argc, const char *argv[]) { +int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); if(argc != 2) { printf("Usage: %s \n", argv[0]); diff --git a/examples/StreamingAndFormatting.cpp b/examples/StreamingAndFormatting.cpp index da5487c..f2fb956 100644 --- a/examples/StreamingAndFormatting.cpp +++ b/examples/StreamingAndFormatting.cpp @@ -15,7 +15,7 @@ */ #include -/* + TEST(Streaming, BasicLevels) { LOG(DEBUG) << "This is a debug message"; LOG(INFO) << "This is an info message"; @@ -32,7 +32,7 @@ TEST(Streaming, BasicTypes) { LOG(INFO) << "string"; LOG(INFO) << nullptr; } -*/ + TEST(Formatting, OverridePrintf) { printf("hello string=%s hex_lower=%x hex_upper=%X octal=%o char=%c dec=%d" "double=%f sci=%e SCI=%E pointer=%p", @@ -40,6 +40,7 @@ TEST(Formatting, OverridePrintf) { printf("hello again!"); } -int main(void) { +int main(int argc, char *argv[]) { + DeepState_InitOptions(argc, argv); return DeepState_Run(); } diff --git a/src/include/deepstate/Compiler.h b/src/include/deepstate/Compiler.h index efa2073..0955266 100644 --- a/src/include/deepstate/Compiler.h +++ b/src/include/deepstate/Compiler.h @@ -20,6 +20,11 @@ #include #include +/* Concatenation macros. */ +#define DEEPSTATE_CAT__(x, y) x ## y +#define DEEPSTATE_CAT_(x, y) DEEPSTATE_CAT__(x, y) +#define DEEPSTATE_CAT(x, y) DEEPSTATE_CAT_(x, y) + /* Stringify a macro parameter. */ #define DEEPSTATE_TO_STR(a) _DEEPSTATE_TO_STR(a) #define _DEEPSTATE_TO_STR(a) __DEEPSTATE_TO_STR(a) diff --git a/src/include/deepstate/DeepState.h b/src/include/deepstate/DeepState.h index 66ebfa3..33b3372 100644 --- a/src/include/deepstate/DeepState.h +++ b/src/include/deepstate/DeepState.h @@ -30,6 +30,7 @@ #include #include +#include #include #ifdef assert @@ -47,6 +48,9 @@ DEEPSTATE_BEGIN_EXTERN_C +DECLARE_string(output_test_dir); +DECLARE_string(input_test_dir); + /* Return a symbolic value of a given type. */ extern int DeepState_Bool(void); extern size_t DeepState_Size(void); @@ -319,11 +323,20 @@ extern int DeepState_CatchFail(void); /* Returns 1 if this test case was abandoned. */ extern int DeepState_CatchAbandoned(void); +/* Save a passing test to the output test directory. */ +extern void DeepState_SavePassingTest(void); + +/* Save a failing test to the output test directory. */ +extern void DeepState_SaveFailingTest(void); + /* Jump buffer for returning to `DeepState_Run`. */ extern jmp_buf DeepState_ReturnToRun; /* Start DeepState and run the tests. Returns the number of failed tests. */ static int DeepState_Run(void) { + if (!DeepState_OptionsAreInitialized) { + DeepState_Abandon("Please call DeepState_InitOptions(argc, argv) in main."); + } int num_failed_tests = 0; int use_drfuzz = getenv("DYNAMORIO_EXE_PATH") != NULL; struct DeepState_TestInfo *test = NULL; @@ -362,6 +375,9 @@ static int DeepState_Run(void) { } else if (DeepState_CatchFail()) { ++num_failed_tests; DeepState_LogFormat(DeepState_LogError, "Failed: %s", test->test_name); + if (HAS_FLAG_output_test_dir) { + DeepState_SaveFailingTest(); + } /* The test was abandoned. We may have gotten soft failures before * abandoning, so we prefer to catch those first. */ @@ -371,6 +387,9 @@ static int DeepState_Run(void) { /* The test passed. */ } else { DeepState_LogFormat(DeepState_LogInfo, "Passed: %s", test->test_name); + if (HAS_FLAG_output_test_dir) { + DeepState_SavePassingTest(); + } } } diff --git a/src/include/deepstate/Option.h b/src/include/deepstate/Option.h new file mode 100644 index 0000000..044730a --- /dev/null +++ b/src/include/deepstate/Option.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_INCLUDE_DEEPSTATE_OPTION_H_ +#define SRC_INCLUDE_DEEPSTATE_OPTION_H_ + +#include "deepstate/Compiler.h" + +#include + +#define DEEPSTATE_FLAG_NAME(name) FLAGS_ ## name +#define DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name) HAS_FLAG_ ## name + +#define DEEPSTATE_REGISTER_OPTION(name, parser, docstring) \ + static struct DeepState_Option DeepState_Option_ ## name = { \ + NULL, \ + DEEPSTATE_TO_STR(name), \ + DEEPSTATE_TO_STR(no_ ## name), \ + &parser, \ + (void *) &DEEPSTATE_FLAG_NAME(name), \ + &DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name), \ + docstring, \ + }; \ + DEEPSTATE_INITIALIZER(DeepState_AddOption_ ## name) { \ + DeepState_AddOption(&(DeepState_Option_ ## name)); \ + } + +#define DEFINE_string(name, default_value, docstring) \ + DECLARE_string(name); \ + DEEPSTATE_REGISTER_OPTION(name, DeepState_ParseStringOption, docstring) \ + int DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name) = 0; \ + const char *DEEPSTATE_FLAG_NAME(name) = default_value + +#define DECLARE_string(name) \ + extern int DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name); \ + extern const char *DEEPSTATE_FLAG_NAME(name) + +#define DEFINE_bool(name, default_value, docstring) \ + DECLARE_bool(name); \ + DEEPSTATE_REGISTER_OPTION(name, DeepState_ParseBoolOption, docstring) \ + int DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name) = 0; \ + int DEEPSTATE_FLAG_NAME(name) = default_value + +#define DECLARE_bool(name) \ + extern int DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name); \ + extern int DEEPSTATE_FLAG_NAME(name) + +#define DEFINE_int(name, default_value, docstring) \ + DECLARE_int(name); \ + DEEPSTATE_REGISTER_OPTION(name, DeepState_ParseIntOption, docstring) \ + int DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name) = 0; \ + int DEEPSTATE_FLAG_NAME(name) = default_value + +#define DECLARE_int(name) \ + extern int DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name); \ + extern int DEEPSTATE_FLAG_NAME(name) + +#define DECLARE_uint(name) \ + extern int DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name); \ + extern unsigned DEEPSTATE_FLAG_NAME(name) + +#define DEFINE_uint(name, default_value, docstring) \ + DECLARE_uint(name); \ + DEEPSTATE_REGISTER_OPTION(name, DeepState_ParseUIntOption, docstring) \ + int DEEPSTATE_HAS_DEEPSTATE_FLAG_NAME(name) = 0; \ + unsigned DEEPSTATE_FLAG_NAME(name) = default_value + +DEEPSTATE_BEGIN_EXTERN_C + +/* Backing structure for describing command-line options to DeepState. */ +struct DeepState_Option { + struct DeepState_Option *next; + const char * const name; + const char * const alt_name; /* Only used for booleans. */ + void (* const parse)(struct DeepState_Option *); + void * const value; + int * const has_value; + const char * const docstring; +}; + +extern int DeepState_OptionsAreInitialized; + +/* Initialize the options from the command-line arguments. */ +void DeepState_InitOptions(int argc, ... /* const char **argv */); + +/* Works for `--help` option: print out each options along with + * their documentation. */ +void DeepState_PrintAllOptions(const char *prog_name); + +/* Initialize an option. */ +void DeepState_AddOption(struct DeepState_Option *option); + +/* Parse an option that is a string. */ +void DeepState_ParseStringOption(struct DeepState_Option *option); + +/* Parse an option that will be interpreted as a boolean value. */ +void DeepState_ParseBoolOption(struct DeepState_Option *option); + +/* Parse an option that will be interpreted as an integer. */ +void DeepState_ParseIntOption(struct DeepState_Option *option); + +/* Parse an option that will be interpreted as an unsigned integer. */ +void DeepState_ParseUIntOption(struct DeepState_Option *option); + +DEEPSTATE_END_EXTERN_C + +#endif /* SRC_INCLUDE_DEEPSTATE_OPTION_H_ */ diff --git a/src/lib/DeepState.c b/src/lib/DeepState.c index 866a574..160493c 100644 --- a/src/lib/DeepState.c +++ b/src/lib/DeepState.c @@ -15,6 +15,7 @@ */ #include "deepstate/DeepState.h" +#include "deepstate/Option.h" #include "deepstate/Log.h" #include @@ -24,6 +25,12 @@ DEEPSTATE_BEGIN_EXTERN_C +DEFINE_uint(num_workers, 1, + "Number of workers to spawn for testing and test generation."); + +DEFINE_string(input_test_dir, "", "Directory where tests will be saved."); +DEFINE_string(output_test_dir, "", "Directory where tests will be saved."); + /* Pointer to the last registers DeepState_TestInfo data structure */ struct DeepState_TestInfo *DeepState_LastTestInfo = NULL; @@ -202,7 +209,6 @@ int8_t DeepState_Char(void) { #undef MAKE_SYMBOL_FUNC - /* Returns the minimum satisfiable value for a given symbolic value, given * the constraints present on that value. */ uint32_t DeepState_MinUInt(uint32_t v) { @@ -314,6 +320,8 @@ void DeepState_Begin(struct DeepState_TestInfo *info) { info->test_name, info->file_name, info->line_number); } +/* Save a failing test. */ + /* Runs in a child process, under the control of Dr. Memory */ void DrMemFuzzFunc(volatile uint8_t *buff, size_t size) { struct DeepState_TestInfo *test = DeepState_DrFuzzTest; @@ -340,6 +348,9 @@ void DrMemFuzzFunc(volatile uint8_t *buff, size_t size) { /* We caught a failure when running the test. */ } else if (DeepState_CatchFail()) { DeepState_LogFormat(DeepState_LogError, "Failed: %s", test->test_name); + if (HAS_FLAG_output_test_dir) { + DeepState_SaveFailingTest(); + } /* The test was abandoned. We may have gotten soft failures before * abandoning, so we prefer to catch those first. */ @@ -349,6 +360,9 @@ void DrMemFuzzFunc(volatile uint8_t *buff, size_t size) { /* The test passed. */ } else { DeepState_LogFormat(DeepState_LogInfo, "Passed: %s", test->test_name); + if (HAS_FLAG_output_test_dir) { + DeepState_SavePassingTest(); + } } } @@ -358,6 +372,16 @@ void DeepState_BeginDrFuzz(struct DeepState_TestInfo *test) { DrMemFuzzFunc(DeepState_Input, DeepState_InputSize); } +/* Save a passing test to the output test directory. */ +void DeepState_SavePassingTest(void) { + +} + +/* Save a failing test to the output test directory. */ +void DeepState_SaveFailingTest(void) { + printf("Saving to %s\n", FLAGS_output_test_dir); +} + /* Return the first test case to run. */ struct DeepState_TestInfo *DeepState_FirstTest(void) { return DeepState_LastTestInfo; diff --git a/src/lib/Option.c b/src/lib/Option.c new file mode 100644 index 0000000..4c833d6 --- /dev/null +++ b/src/lib/Option.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2018 Trail of Bits, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "deepstate/DeepState.h" +#include "deepstate/Option.h" + +#include +#include +#include + +enum { + kMaxNumOptions = 32, + kMaxOptionLength = 1024 - 1 +}; + +/* Linked list of registered options. */ +static struct DeepState_Option *DeepState_Options = NULL; +int DeepState_OptionsAreInitialized = 0; + +static const char DeepState_FakeSpace = ' ' | 0x80; + +/* Copy of the option string. */ +static int DeepState_OptionStringLength = 0; +static char DeepState_OptionString[kMaxOptionLength + 1] = {'\0'}; +static const char *DeepState_OptionNames[kMaxNumOptions] = {NULL}; +static const char *DeepState_OptionValues[kMaxNumOptions] = {NULL}; + +/* Copy a substring into the main options string. */ +static int CopyStringIntoOptions(int offset, const char *string, + int replace_spaces) { + for (; offset < kMaxOptionLength && *string; ++string) { + char ch = *string; + if (' ' == ch && replace_spaces) { + ch = DeepState_FakeSpace; + } + DeepState_OptionString[offset++] = ch; + } + return offset; +} + +/* Finalize the option string. */ +static void TerminateOptionString(int length) { + if (kMaxOptionLength <= length) { + DeepState_Abandon("Option string is too long."); + } + DeepState_OptionString[length] = '\0'; + DeepState_OptionStringLength = length; +} + +/* Check that a character is a valid option character. */ +static int IsValidOptionChar(char ch) { + return ('a' <= ch && ch <= 'z') || + ('A' <= ch && ch <= 'Z') || + ('_' == ch); +} + +/* Check that a character is a valid option character. */ +static int IsValidValueChar(char ch) { + return (' ' < ch && ch <= '~') || ch == DeepState_FakeSpace; +} + +/* Format an option string into a more amenable internal format. This is a sort + * of pre-processing step to distinguish options from values. */ +static void ProcessOptionString(void) { + char *ch = &DeepState_OptionString[0]; + char * const max_ch = &DeepState_OptionString[DeepState_OptionStringLength]; + unsigned num_options = 0; + + enum OptionLexState { + kInOption, + kInValue, + kSeenEqual, + kSeenSpace, + kSeenDash, + kElsewhere + } state = kElsewhere; + + for (; ch < max_ch; ++ch) { + switch (state) { + case kInOption: { + const char ch_val = *ch; + + /* Terminate the option name. */ + if (!IsValidOptionChar(ch_val)) { + *ch = '\0'; + + /* We've seen an equal, which mean's we're moving into the + * beginning of a value. */ + if ('=' == ch_val) { + state = kSeenEqual; + } else if (' ' == ch_val || DeepState_FakeSpace == ch_val) { + state = kSeenSpace; + } else { + state = kElsewhere; + } + } + break; + } + + case kInValue: + if (!IsValidValueChar(*ch)) { + state = kElsewhere; + *ch = '\0'; + } else if (DeepState_FakeSpace == *ch) { + *ch = ' '; /* Convert back to a space. */ + } + break; + + case kSeenSpace: + if (' ' == *ch || DeepState_FakeSpace == *ch) { + *ch = '\0'; + state = kSeenSpace; + + } else if (IsValidValueChar(*ch)) { /* E.g. `--tools bbcount`. */ + state = kInValue; + DeepState_OptionValues[num_options - 1] = ch; + + } else { + state = kElsewhere; + } + break; + + case kSeenEqual: + if (IsValidValueChar(*ch)) { /* E.g. `--tools=bbcount`. */ + state = kInValue; + DeepState_OptionValues[num_options - 1] = ch; + } else { /* E.g. `--tools=`. */ + state = kElsewhere; + } + break; + + case kSeenDash: + if ('-' == *ch) { + state = kInOption; /* Default to positional. */ + if (kMaxNumOptions <= num_options) { + DeepState_Abandon("Parsed too many options!"); + } + DeepState_OptionValues[num_options] = ""; + DeepState_OptionNames[num_options++] = ch + 1; + } else { + state = kElsewhere; + } + *ch = '\0'; + break; + + case kElsewhere: + if ('-' == *ch) { + state = kSeenDash; + } + *ch = '\0'; + break; + } + } +} + +/* Returns a pointer to the value for an option name, or a NULL if the option + * name was not found (or if it was specified but had no value). */ +static const char *FindValueForName(const char *name) { + for (int i = 0; i < kMaxNumOptions && DeepState_OptionNames[i]; ++i) { + if (!strcmp(DeepState_OptionNames[i], name)) { + return DeepState_OptionValues[i]; + } + } + return NULL; +} + +/* Process the pending options.. */ +static void ProcessPendingOptions(void) { + struct DeepState_Option *option = DeepState_Options; + struct DeepState_Option *next_option = NULL; + for (; option != NULL; option = next_option) { + next_option = option->next; + option->parse(option); + } +} + +/* Initialize the options from the command-line arguments. */ +void DeepState_InitOptions(int argc, ...) { + va_list args; + va_start(args, argc); + const char **argv = va_arg(args, const char **); + va_end(args); + + int offset = 0; + int arg = 1; + for (const char *sep = ""; arg < argc; ++arg, sep = " ") { + offset = CopyStringIntoOptions(offset, sep, 0); + offset = CopyStringIntoOptions(offset, argv[arg], 1); + } + TerminateOptionString(offset); + ProcessOptionString(); + DeepState_OptionsAreInitialized = 1; + ProcessPendingOptions(); +} + +enum { + kLineLength = 80, + kTabLength = 8, + kBufferMaxLength = kLineLength - kTabLength +}; + +/* Perform line buffering of the document string. */ +static const char *BufferDocString(char *buff, const char *docstring) { + char *last_stop = buff; + const char *docstring_last_stop = docstring; + const char *docstring_stop = docstring + kBufferMaxLength; + for (; docstring < docstring_stop && *docstring; ) { + if (' ' == *docstring) { + last_stop = buff; + docstring_last_stop = docstring + 1; + } else if ('\n' == *docstring) { + last_stop = buff; + docstring_last_stop = docstring + 1; + break; + } + *buff++ = *docstring++; + } + if (docstring < docstring_stop && !*docstring) { + *buff = '\0'; + return docstring; + } else { + *last_stop = '\0'; + return docstring_last_stop; + } +} + +/* Works for --help option: print out each options along with their document. */ +void DeepState_PrintAllOptions(const char *prog_name) { + fprintf(stderr, "Usage: %s \n\n", prog_name); + + char line_buff[kLineLength]; + struct DeepState_Option *option = DeepState_Options; + struct DeepState_Option *next_option = NULL; + for (; option != NULL; option = next_option) { + next_option = option->next; + + fprintf(stderr, "--%s", option->name); + const char *docstring = option->docstring; + do { + docstring = BufferDocString(line_buff, docstring); + fprintf(stderr, "\n %s", line_buff); + } while (*docstring); + fprintf(stderr, "\n\n"); + } +} + + +/* Initialize an option. */ +void DeepState_AddOption(struct DeepState_Option *option) { + if (DeepState_OptionsAreInitialized) { + option->parse(option); /* Added late? */ + } + if (!option->next) { + option->next = DeepState_Options; + DeepState_Options = option; + } +} + +/* Parse an option that is a string. */ +void DeepState_ParseStringOption(struct DeepState_Option *option) { + const char *value = FindValueForName(option->name); + if (value != NULL) { + *(option->has_value) = 1; + *((const char **) (option->value)) = value; + } +} + +/* Parse an option that will be interpreted as a boolean value. */ +void DeepState_ParseBoolOption(struct DeepState_Option *option) { + const char *value = FindValueForName(option->name); + if (value != NULL) { + switch (*value) { + case '1': case 'y': case 'Y': case 't': case 'T': + case '\0': /* Treat the presence of the option as truth. */ + *(option->has_value) = 1; + *((int *) (option->value)) = 1; + break; + case '0': case 'n': case 'N': case 'f': case 'F': + *(option->has_value) = 1; + *((int *) (option->value)) = 0; + break; + default: + break; + } + + /* Alternative name, e.g. `--foo` vs. `--no_foo`. */ + } else { + const char *alt_value = FindValueForName(option->alt_name); + if (alt_value != NULL) { + if ('\0' != alt_value[0]) { + DeepState_Abandon("Got an option value for a negated boolean option."); + } + *(option->has_value) = 1; + *((int *) (option->value)) = 0; + } + } +} + + +/* Parse an option that will be interpreted as an unsigned integer. */ +void DeepState_ParseIntOption(struct DeepState_Option *option) { + const char *value = FindValueForName(option->name); + if (value != NULL) { + int int_value = 0; + if (sscanf(value, "%d", &int_value)) { + *(option->has_value) = 1; + *((int *) (option->value)) = int_value; + } + } +} + +/* Parse an option that will be interpreted as an unsigned integer. */ +void DeepState_ParseUIntOption(struct DeepState_Option *option) { + const char *value = FindValueForName(option->name); + if (value != NULL) { + unsigned uint_value = 0; + if (sscanf(value, "%u", &uint_value)) { + *(option->has_value) = 1; + *((unsigned *) (option->value)) = uint_value; + } + } +}