#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#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;
}
#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
// Unit Test Framework Includes
#include "UnitTest++.h"
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
// 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" ) );
}
}