From 2a4e83368d98ff82aa1fe90855da287f5ba11757 Mon Sep 17 00:00:00 2001 From: "Mike D. Lowis" Date: Fri, 1 Jun 2012 16:50:49 -0400 Subject: [PATCH] Implemented tokenization and skeleton for option parsing. Also added typedefs for required structs --- source/opts.c | 121 +++++++++++++++++++++++----- source/opts.h | 73 ++++++++++++----- tests/test_opts.cpp | 187 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 340 insertions(+), 41 deletions(-) diff --git a/source/opts.c b/source/opts.c index dfc901c..f3f3fd9 100644 --- a/source/opts.c +++ b/source/opts.c @@ -1,55 +1,134 @@ #include #include +#include #include "opts.h" -OptionList_T* OPTS_ParseOptions( int argc, char** argv ) +Result_T* OPTS_ParseOptions( OptionConfig_T* opts, int argc, char** argv ) { - ParserContext_T ctx; - ctx.options = 0; + // Setup the stream + StreamContext_T ctx; + ctx.options = opts; + OPTS_InitContext( &ctx, argc, argv ); - //while( ctx.tok_stream.stream.cur_char != EOF ) - //{ - // OPTS_ParseOption( &(ctx) ); - //} + // Until we run out of characters + while( ctx.current != EOF ) + { + // Get rid of any whitespace + while ( ' ' == ctx.current ) + { + (void)OPTS_NextCharacter( &ctx ); + } + + // If we have an option + if ( '-' == ctx.current ) + { + // And its a long one + if( '-' == OPTS_NextCharacter( &ctx ) ) + { + (void)OPTS_ParseLongOption( &ctx ); + } + // or a short one + else + { + (void)OPTS_ParseShortOption( &ctx ); + } + } + // Or we have a floating argument + else + { + (void)OPTS_ParseArgument( &ctx ); + } + } + + return ctx.results; +} + +void OPTS_InitContext( StreamContext_T* ctx, int argc, char** argv ) +{ + // Setup the char stream + ctx->line_idx = 0; + ctx->col_idx = -1; + ctx->arg_count = argc; + ctx->arg_vect = argv; + (void)OPTS_NextCharacter( ctx ); // Loads up the first char + + // Setup the results object + ctx->results = (Result_T*)malloc( sizeof( Result_T ) ); + ctx->results->options = (OptionList_T*)malloc( sizeof( OptionList_T ) ); + ctx->results->options->head = NULL; + ctx->results->options->tail = NULL; + ctx->results->arguments = (ArgumentList_T*)malloc( sizeof( ArgumentList_T ) ); + ctx->results->arguments->head = NULL; + ctx->results->arguments->tail = NULL; +} - return ctx.options; +void OPTS_ParseShortOption( StreamContext_T* ctx ) +{ } -OptionList_T* OPTS_ParseOption( ParserContext_T* ctx ) +void OPTS_ParseLongOption( StreamContext_T* ctx ) { - return NULL; } -char* OPTS_NextToken( TokenContext_T* ctx ) +void OPTS_ParseArgument( StreamContext_T* ctx ) { - return NULL; +} + +char* OPTS_NextToken( StreamContext_T* ctx ) +{ + char* tok = NULL; + if ( EOF != ctx->current ) + { + // Setup the string + tok = (char*)malloc(2); + tok[0] = ctx->current; + tok[1] = '\0'; + + (void)OPTS_NextCharacter( ctx ); + while( (EOF != ctx->current) && (' ' != ctx->current) && ('=' != ctx->current) ) + { + OPTS_AppendCharacter( tok, ctx->current ); + (void)OPTS_NextCharacter( ctx ); + } + } + return tok; } char OPTS_NextCharacter( StreamContext_T* ctx ) { char current = EOF; + ctx->current = EOF; if( ctx->line_idx < ctx->arg_count ) { ctx->col_idx++; - if(ctx->arg_vect[ ctx->line_idx ][ ctx->col_idx ] == '\0') - { - ctx->col_idx = 0; - ctx->line_idx++; - } - if( ctx->line_idx < ctx->arg_count ) { - current = ctx->arg_vect[ ctx->line_idx ][ ctx->col_idx ]; + if(ctx->arg_vect[ ctx->line_idx ][ ctx->col_idx ] == '\0') + { + ctx->col_idx = -1; + ctx->line_idx++; + current = ' '; + ctx->current = current; + } + else + { + current = ctx->arg_vect[ ctx->line_idx ][ ctx->col_idx ]; + ctx->current = current; + } } } return current; } -void OPTS_FreeOptionList( OptionList_T* opts ) +char* OPTS_AppendCharacter( char* str, char ch ) { - + unsigned int new_size = strlen( str ) + 2; + str = (char*)realloc( str, new_size ); + str[ new_size - 2 ] = ch; + str[ new_size - 1 ] = '\0'; + return str; } diff --git a/source/opts.h b/source/opts.h index f8dbf27..6329c20 100644 --- a/source/opts.h +++ b/source/opts.h @@ -1,37 +1,70 @@ #ifndef OPTS_H #define OPTS_H +typedef struct Option_T { + char* key; + char* val; + struct Option_T* next; +} Option_T; + +typedef struct OptionList { + Option_T* head; + Option_T* tail; +} OptionList_T; + +typedef struct Argument_T { + char* val; + struct Argument_T* next; +} Argument_T; + +typedef struct ArgumentList { + Argument_T* head; + Argument_T* tail; +} ArgumentList_T; + +typedef struct Result_T { + ArgumentList_T* arguments; + OptionList_T* options; +} Result_T; + +typedef enum { + LONG, + SHORT, + END +} OptionType_T; + +typedef struct OptionConfig_T { + int type; + char* name; + char* dest; + int has_arg; + char* desc; +} OptionConfig_T; + typedef struct { - int line_idx; - int col_idx; - int arg_count; + unsigned int line_idx; + unsigned int col_idx; + unsigned int arg_count; char** arg_vect; + char current; + OptionConfig_T* options; + Result_T* results; } StreamContext_T; -typedef struct { - StreamContext_T stream; - char* cur_token; -} TokenContext_T; +Result_T* OPTS_ParseOptions( OptionConfig_T* opts, int argc, char** argv ); -typedef struct OptionList { - char** key; - char** val; - struct OptionList* next; -} OptionList_T; +void OPTS_InitContext( StreamContext_T* ctx, int argc, char** argv ); -typedef struct { - TokenContext_T tok_stream; - OptionList_T* options; -} ParserContext_T; +void OPTS_ParseShortOption( StreamContext_T* ctx ); -OptionList_T* OPTS_ParseOptions( int argc, char** argv ); +void OPTS_ParseLongOption( StreamContext_T* ctx ); -OptionList_T* OPTS_ParseOption( ParserContext_T* ctx ); +void OPTS_ParseArgument( StreamContext_T* ctx ); -char* OPTS_NextToken( TokenContext_T* ctx ); +char* OPTS_NextToken( StreamContext_T* ctx ); char OPTS_NextCharacter( StreamContext_T* ctx ); -void OPTS_FreeOptionList( OptionList_T* options ); +char* OPTS_AppendCharacter( char* str, char ch ); #endif diff --git a/tests/test_opts.cpp b/tests/test_opts.cpp index 13e99a8..163cb87 100644 --- a/tests/test_opts.cpp +++ b/tests/test_opts.cpp @@ -1,20 +1,207 @@ // Unit Test Framework Includes #include "UnitTest++.h" +#include +#include +#include // File To Test #include "opts.h" using namespace UnitTest; +//----------------------------------------------------------------------------- +// Sample Option Configuration +//----------------------------------------------------------------------------- +OptionConfig_T Options_Config[] = { + { SHORT, "a", "test_a", 0, "A simple test option" }, + { SHORT, "b", "test_b", 1, "A simple test option" }, + { LONG, "foo", "test_d", 0, "A simple test option" }, + { LONG, "bar", "test_e", 1, "A simple test option" }, + { END, NULL, NULL, 0, NULL } +}; + //----------------------------------------------------------------------------- // Begin Unit Tests //----------------------------------------------------------------------------- namespace { + //------------------------------------------------------------------------- + // Test InitContext Function + //------------------------------------------------------------------------- + TEST(Verify_InitContext_Initializes_The_StreamContext) + { + StreamContext_T ctx; + char* args[] = { "-a", "b", "--foo", "--foo=bar" }; + OPTS_InitContext( &ctx, 4, args ); + + CHECK( 0 == ctx.line_idx ); + CHECK( 0 == ctx.col_idx ); + CHECK( 4 == ctx.arg_count ); + CHECK( args == ctx.arg_vect ); + CHECK( '-' == ctx.current ); + CHECK( NULL != ctx.results ); + CHECK( NULL != ctx.results->options ); + CHECK( NULL == ctx.results->options->head ); + CHECK( NULL == ctx.results->options->tail ); + CHECK( NULL != ctx.results->arguments ); + CHECK( NULL == ctx.results->arguments->head ); + CHECK( NULL == ctx.results->arguments->tail ); + } + + //------------------------------------------------------------------------- + // Test ParseOptions Function + //------------------------------------------------------------------------- + //TEST(Verify_ParseOptions_Parses_A_Short_Option_With_No_Param) + //{ + // Result_T* results = NULL; + // char* args[] = { "-a" }; + // results = OPTS_ParseOptions( Options_Config, 1, args ); + // CHECK( 0 == 1 ); + // free(results); + //} + + //TEST(Verify_ParseOptions_Parses_A_Short_Option_With_A_Param_And_A_Space) + //{ + // Result_T* results = NULL; + // char* args[] = { "-b5" }; + // results = OPTS_ParseOptions( Options_Config, 1, args ); + // CHECK( 0 == 1 ); + // free(results); + //} + + //TEST(Verify_ParseOptions_Parses_A_Short_Option_With_A_Param_And_No_Space) + //{ + // Result_T* results = NULL; + // char* args[] = { "-b", "5" }; + // results = OPTS_ParseOptions( Options_Config, 1, args ); + // CHECK( 0 == 1 ); + // free(results); + //} + + //TEST(Verify_ParseOptions_Parses_A_Long_Option_With_No_Param) + //{ + // Result_T* results = NULL; + // char* args[] = { "--foo" }; + // results = OPTS_ParseOptions( Options_Config, 1, args ); + // CHECK( 0 == 1 ); + // free(results); + //} + + //TEST(Verify_ParseOptions_Parses_A_Long_Option_With_A_Param_And_A_Space) + //{ + // Result_T* results = NULL; + // char* args[] = { "--bar=5" }; + // results = OPTS_ParseOptions( Options_Config, 1, args ); + // CHECK( 0 == 1 ); + // free(results); + //} + + //TEST(Verify_ParseOptions_Parses_A_Long_Option_With_A_Param_And_An_Equals_Sign) + //{ + // Result_T* results = NULL; + // char* args[] = { "--bar", "5" }; + // results = OPTS_ParseOptions( Options_Config, 1, args ); + // CHECK( 0 == 1 ); + // free(results); + //} + + //------------------------------------------------------------------------- + // Test NextToken Function + //------------------------------------------------------------------------- + TEST(Verify_NextToken_Properly_Tokenizes_Stream) + { + char* token = NULL; + char* args[] = { "-a", "b", "--foo", "--foo=bar" }; + StreamContext_T ctx; + ctx.line_idx = 0; + ctx.col_idx = -1; + ctx.arg_count = 4; + ctx.arg_vect = args; + + (void)OPTS_NextCharacter( &ctx ); + token = OPTS_NextToken( &ctx ); + CHECK( 0 == strcmp( token, "-a" ) ); + free( token ); + + (void)OPTS_NextCharacter( &ctx ); + token = OPTS_NextToken( &ctx ); + CHECK( 0 == strcmp( token, "b" ) ); + free( token ); + + (void)OPTS_NextCharacter( &ctx ); + token = OPTS_NextToken( &ctx ); + CHECK( 0 == strcmp( token, "--foo" ) ); + free( token ); + + (void)OPTS_NextCharacter( &ctx ); + token = OPTS_NextToken( &ctx ); + CHECK( 0 == strcmp( token, "--foo" ) ); + free( token ); + + (void)OPTS_NextCharacter( &ctx ); + token = OPTS_NextToken( &ctx ); + CHECK( 0 == strcmp( token, "bar" ) ); + free( token ); + } + //------------------------------------------------------------------------- // Test NextCharacter Function //------------------------------------------------------------------------- TEST(Verify_NextCharacter_Can_Loop_Through_Characters) { + char* args[] = { "abc", "123" }; + StreamContext_T ctx; + ctx.line_idx = 0; + ctx.col_idx = -1; + ctx.arg_count = 2; + ctx.arg_vect = args; + + CHECK( 'a' == OPTS_NextCharacter( &ctx ) ); + CHECK( 'a' == ctx.current ); + CHECK( 'b' == OPTS_NextCharacter( &ctx ) ); + CHECK( 'b' == ctx.current ); + CHECK( 'c' == OPTS_NextCharacter( &ctx ) ); + CHECK( 'c' == ctx.current ); + CHECK( ' ' == OPTS_NextCharacter( &ctx ) ); + CHECK( ' ' == ctx.current ); + CHECK( '1' == OPTS_NextCharacter( &ctx ) ); + CHECK( '1' == ctx.current ); + CHECK( '2' == OPTS_NextCharacter( &ctx ) ); + CHECK( '2' == ctx.current ); + CHECK( '3' == OPTS_NextCharacter( &ctx ) ); + CHECK( '3' == ctx.current ); + CHECK( ' ' == OPTS_NextCharacter( &ctx ) ); + CHECK( ' ' == ctx.current ); + CHECK( EOF == OPTS_NextCharacter( &ctx ) ); + CHECK( EOF == ctx.current ); + CHECK( EOF == OPTS_NextCharacter( &ctx ) ); + CHECK( EOF == ctx.current ); + } + + TEST(Verify_NextCharacter_Can_Handle_Empty_Input) + { + char* args[] = {}; StreamContext_T ctx; + ctx.line_idx = 0; + ctx.col_idx = -1; + ctx.arg_count = 0; + ctx.arg_vect = args; + + CHECK( EOF == OPTS_NextCharacter( &ctx ) ); + CHECK( EOF == ctx.current ); + CHECK( EOF == OPTS_NextCharacter( &ctx ) ); + CHECK( EOF == ctx.current ); + } + + //------------------------------------------------------------------------- + // Test AppendCharacter Function + //------------------------------------------------------------------------- + TEST(Verify_AppendCharacter_Dynamically_Resizes_and_Appends_Char) + { + char* str = (char*)malloc(2); + str[0] = 'a'; + str[1] = '\0'; + str = OPTS_AppendCharacter( str, 'b' ); + + CHECK( 0 == strcmp( str, "ab" ) ); } } -- 2.49.0