]> git.mdlowis.com Git - projs/opts.git/commitdiff
Implemented tokenization and skeleton for option parsing. Also added typedefs for...
authorMike D. Lowis <mike@mdlowis.com>
Fri, 1 Jun 2012 20:50:49 +0000 (16:50 -0400)
committerMike D. Lowis <mike@mdlowis.com>
Fri, 1 Jun 2012 20:50:49 +0000 (16:50 -0400)
source/opts.c
source/opts.h
tests/test_opts.cpp

index dfc901cef4f13c38a1252ff4940bd39156986ec2..f3f3fd9ad246f2fdfacbb603d7cfc375921fb0f5 100644 (file)
 #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;
 }
 
index f8dbf27aa4631864fb97986cedc9de2070786bd6..6329c208fa5d9df24383ee793a4e82e5c4091eac 100644 (file)
@@ -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
index 13e99a8d02bf8cb0db0d747a176b82d1aee99429..163cb87835b0c1143fa52619b7ae85f109f3ba50 100644 (file)
 // 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" ) );
     }
 }