/* * ConfigCore.cpp * * Created on: 5 Mar 2017 * Author: wentzelc */ // redA Libraries #include "ConfigCore.h" // Standard C/C++ Libraries #include #include #include #include //--------------------------------------------------------------------------- CConfigCore::CConfigCore() { // Parameter tree FirstParam = NULL; // File Operation InputFile = NULL; BufLen = 500; Buffer = NULL; BufEnd = NULL; // Parsing operation BufPos = NULL; LineMark = NULL; LineNo = 0; Error = false; ErrorText[0] = 0; } //--------------------------------------------------------------------------- CConfigCore::~CConfigCore() { // Destroy Params while (FirstParam) DestroyParam( &FirstParam ); } //--------------------------------------------------------------------------- bool CConfigCore::DestroyParam( TConfigParam ** Param ) { TConfigParam * NextParam; // Valdate if (!Param || !*Param) return false; // Get next param in list NextParam = (*Param)->Next; // Destroy if ((*Param)->Name) free( (*Param)->Name ); if ((*Param)->Value) free( (*Param)->Value ); free( *Param ); // Restore list integrity *Param = NextParam; return true; } //--------------------------------------------------------------------------- bool CConfigCore::DeleteParam( const char * Name ) { TConfigParam ** Param; // Check if exists if (!(Param = GetParamPtr( Name ))) return false; // Destory DestroyParam( Param ); return true; } //--------------------------------------------------------------------------- bool CConfigCore::DeleteAll() { while (FirstParam) DestroyParam( &FirstParam ); return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParam( TConfigParam * Param, EJSONtype Type, const char * Value, const int Len ) { // Clear previous value if (Param->Value != NULL) { free( Param->Value ); } // Set type Param->Type = Type; // Set new value if (Type == jtNull) { Param->Len = 0; Param->Value = NULL; } else { Param->Len = (Len == -1)? strlen(Value) : Len; Param->Value = (char *)malloc( sizeof(Param->Len+1) ); memcpy( Param->Value, Value, Param->Len+1 ); Param->Value[Param->Len] = 0; } return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamStr( const char * Name, const char * Value, const int Len ) { TConfigParam * Param; // Create new param if it doesn't exist Param = FindCreateParam( Name ); SetParam( Param, jtString, Value, Len ); return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamInt( const char * Name, const long Value ) { TConfigParam * Param = NULL; char ValueStr[50]; // Create new param if it doesn't exist Param = FindCreateParam( Name ); sprintf( ValueStr, "%ld", Value ); SetParam( Param, jtInt, ValueStr, -1 ); return false; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamFloat( const char * Name, const double Value ) { TConfigParam * Param = NULL; char ValueStr[50]; // Create new param if it doesn't exist Param = FindCreateParam( Name ); sprintf( ValueStr, "%lf", Value ); SetParam( Param, jtFloat, ValueStr, -1 ); return false; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamBool( const char * Name, const bool Value ) { TConfigParam * Param = NULL; // Create new param if it doesn't exist Param = FindCreateParam( Name ); SetParam( Param, jtBool, ((Value == 0)? "false" : "true"), -1 ); return false; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamNull( const char * Name ) { TConfigParam * Param = NULL; // Create new param if it doesn't exist Param = FindCreateParam( Name ); SetParam( Param, jtNull, NULL, -1 ); return false; } //--------------------------------------------------------------------------- const EJSONtype CConfigCore::GetParamType( const char * Name ) { TConfigParam * Param; if ((Param = GetParam( Name ))) { return Param->Type; } else { return jtNull; } } //--------------------------------------------------------------------------- const char * CConfigCore::GetParamStr( const char * Name, const char * Default ) { TConfigParam * Param; if ((Param = GetParam( Name )) && (Param->Type == jtString)) { return Param->Value; } else { return Default; } } //--------------------------------------------------------------------------- const char * CConfigCore::GetParamStr( const char * Name, int &Len, const char * Default ) { TConfigParam * Param; if ((Param = GetParam( Name )) && (Param->Type == jtString)) { Len = Param->Len; return Param->Value; } else { Len = strlen( Default ); return Default; } } //--------------------------------------------------------------------------- const long CConfigCore::GetParamInt( const char * Name, long Default ) { TConfigParam * Param; if ((Param = GetParam( Name )) && (Param->Type == jtInt)) { return strtol( Param->Value, NULL, 10 ); } else { return Default; } } //--------------------------------------------------------------------------- const double CConfigCore::GetParamFloat( const char * Name, double Default ) { TConfigParam * Param; if ((Param = GetParam( Name )) && (Param->Type == jtFloat)) { return strtod( Param->Value, NULL ); } else { return Default; } } //--------------------------------------------------------------------------- const bool CConfigCore::GetParamBool( const char * Name, bool Default ) { TConfigParam * Param; if ((Param = GetParam( Name )) && (Param->Type == jtBool)) { return ((!strcasecmp( Param->Value, "true" ))? true : false ); } else { return Default; } } //--------------------------------------------------------------------------- bool CConfigCore::LoadFile( const char * FilePath, int pBufLen ) { TConfigParam * Object = NULL; // Validate if (!FilePath || !FilePath[0]) return false; // Open file if (!(InputFile = fopen( FilePath, "r" ))) return false; // Load Buffer CreateBuffer( pBufLen ); if (!FillBuffer()) { Error = true; sprintf( ErrorText, "Could not read from file" ); FreeBuffer(); return false; } // Reset values LineNo = 1; Error = false; // Create Root object DeleteAll(); Object = FindCreateParam( "root" ); // Parse Root Object SkipWhiteSpace(); if (!ParseObject( Object )) { if (!Error) { Error = true; sprintf( ErrorText, "First entry in file must be an Object on line %d:%ld", LineNo, BufPos-LineMark ); } FreeBuffer(); return false; } // Ensure remainder of file is empty SkipWhiteSpace(); if (*BufPos != 0) { Error = true; sprintf( ErrorText, "No content expected after Root object on line %d:%ld", LineNo, BufPos-LineMark ); FreeBuffer(); return false; } // Success FreeBuffer(); return true; } //--------------------------------------------------------------------------- bool CConfigCore::CreateBuffer( int pBufLen ) { // Validate if (!pBufLen) return false; // Create buffer BufLen = pBufLen; Buffer = (char*)malloc( BufLen+1 ); // Reset markers BufEnd = Buffer; BufPos = Buffer; LineMark = Buffer; return true; } //--------------------------------------------------------------------------- bool CConfigCore::FillBuffer() { int BufCount = 0; // Read from file BufCount = fread( Buffer, 1, 500, InputFile ); if (BufCount < 1) { return false; } // Update Markers BufEnd += BufCount; BufPos = Buffer; LineMark = Buffer; return true; } //--------------------------------------------------------------------------- void CConfigCore::FreeBuffer() { // Destroy buffer if (Buffer) { free( Buffer ); Buffer = NULL; } BufLen = 0; } //--------------------------------------------------------------------------- bool CConfigCore::ParseObject( TConfigParam * Object ) { TConfigParam * Param = NULL; char * ParamName = NULL; int Len = 0; // Clear Values Error = false; // Check for start of Object if (*BufPos != '{') { return false; } BufPos++; while (true) { // Look for Param Name SkipWhiteSpace(); if (*BufPos == 0) { Error = true; sprintf( ErrorText, "Expect quoted key name on line %d:%ld", LineNo, BufPos-LineMark ); } else if (!ParseString( &ParamName, &Len )) { if (!Error) { Error = true; sprintf( ErrorText, "Expect quoted key name on line %d:%ld", LineNo, BufPos-LineMark ); } return false; } else if (!Len) { Error = true; sprintf( ErrorText, "Empty parameter name on line %d:%ld", LineNo, BufPos-LineMark ); return false; } // Create parameter Param = FindCreateParam( ParamName ); // Check for delimiter SkipWhiteSpace(); if (*BufPos != ':') { Error = true; sprintf( ErrorText, "Expected ':' delimiter on line %d:%ld", LineNo, BufPos-LineMark ); return false; } BufPos++; // Get Value SkipWhiteSpace(); if (!ParseObject( Param ) && !Error && !ParseString( &Param->Value, &Param->Len, &Param->Type ) && !Error && !ParsePrimitive( &Param->Value, &Param->Len, &Param->Type )) { return false; } // Check if more parameters to follow SkipWhiteSpace(); if (*BufPos != ',') { // No more parameters break; } BufPos++; } // Expect end of object if (*BufPos != '}') { Error = true; sprintf( ErrorText, "Closing brace for object '}' expected on line %d:%ld", LineNo, BufPos-LineMark ); return false; } BufPos++; // success return true; } //--------------------------------------------------------------------------- bool CConfigCore::ParseString( char ** Value, int *pLen, EJSONtype *pType ) { char * Mark; char * EndMark; int Len; // Clear value Error = false; *Value = NULL; Len = 0; // Check for opening quote if (*BufPos != '"') { return false; } // Mark start of quote Mark = BufPos; BufPos++; // Check for closing quote while ((BufPos = strpbrk( BufPos, "\"/\\\n\t\b\f\n\r" ))) { if (*BufPos == '"') { // End of string found BufPos++; break; } else if (!*BufPos) { Error = true; sprintf( ErrorText, "Expect closing '\"' for string on line %d:%ld", LineNo, Mark-LineMark ); return false; } else if (*BufPos == '\\') { if (*(BufPos+1) == 'u') { for (EndMark = BufPos+2; EndMark < BufPos+6; EndMark++) { if (!isxdigit( *EndMark )) { Error = true; sprintf( ErrorText, "Expect 4-digit hex value for escape value on line %d:%ld", LineNo, Mark-LineMark ); return false; } } BufPos += 6; } else if (strchr( "bfnrt/\\\"", *(BufPos+1) )) { BufPos += 2; } else { Error = true; sprintf( ErrorText, "Invalid escape sequence on line %d:%ld", LineNo, Mark-LineMark ); return false; } } else { Error = true; sprintf( ErrorText, "Un-escaped special character in string on line %d:%ld", LineNo, BufPos-LineMark ); return false; } } // Validate size of Param name Len = BufPos-Mark-2; // Create Return value pointer *Value = (char*)malloc( Len+1 ); memcpy( *Value, Mark+1, Len ); (*Value)[Len] = 0; // Set other parameters if (pType) *pType = jtString; if (pLen) *pLen = Len; // Success return true; } //--------------------------------------------------------------------------- bool CConfigCore::ParsePrimitive( char ** Value, int * pLen, EJSONtype * pType ) { char * Mark; char * EndMark; int Len; // Clear value Error = false; *Value = NULL; Len = 0; // Mark start of value Mark = BufPos; // Get end of value while ((*BufPos != 0) && !isspace(*BufPos) && (*BufPos != ',')) { BufPos++; } // Check length of value Len = BufPos - Mark; if (!Len) { Error = true; sprintf( ErrorText, "Missing param value on line %d:%ld", LineNo, Mark-LineMark ); return false; } // Check for primitive values if ((Len == 4) && !strncasecmp( Mark, "null", 4 )) { if (pType) *pType = jtNull; if (pLen) *pLen = Len; *Value = (char*)malloc( 5 ); strcpy( *Value, "null" ); } else if ((Len == 4) && !strncasecmp( Mark, "true", 4 )) { if (pType) *pType = jtBool; if (pLen) *pLen = Len; *Value = (char*)malloc( 5 ); strcpy( *Value, "true" ); } else if ((Len == 5) && !strncasecmp( Mark, "false", 5 )) { if (pType) *pType = jtBool; if (pLen) *pLen = Len; *Value = (char*)malloc( 6 ); strcpy( *Value, "false" ); } else { // Try conversion to int strtol( Mark, &EndMark, 10 ); if (EndMark == BufPos) { if (pType) *pType = jtInt; if (pLen) *pLen = Len; *Value = (char*)malloc( Len+1 ); memcpy( *Value, Mark, Len ); *Value[Len] = 0; } else { // Try conversion to float strtod( Mark, &EndMark ); if (EndMark == BufPos) { if (pType) *pType = jtFloat; if (pLen) *pLen = Len; *Value = (char*)malloc( Len+1 ); memcpy( *Value, Mark, Len ); *Value[Len] = 0; } else { Error = true; sprintf( ErrorText, "Invalid primitive param value on line %d:%ld", LineNo, Mark-LineMark ); return false; } } } // Success return true; } //--------------------------------------------------------------------------- bool CConfigCore::SaveFile( const char * FilePath, const int ValueTab ) { FILE * OutputFile = NULL; TConfigParam * Param; int SpacerLen; char Spacer[50]; // Validate if (!FilePath || !FilePath[0]) return false; // Open file if (!(OutputFile = fopen( FilePath, "w" ))) return false; // Save parameters for (Param = FirstParam; Param != NULL; (Param = Param->Next)) { // Write parameter name fprintf( OutputFile, "\"%s\":", Param->Name ); // Create fixed offset for values SpacerLen = ValueTab - (strlen(Param->Name) + 3); if (SpacerLen > 0) { memset( Spacer, ' ', SpacerLen ); Spacer[SpacerLen] = 0; fwrite( Spacer, 1, SpacerLen, OutputFile ); } switch (Param->Type) { case jtNull : fprintf( OutputFile, "%s,\n", "null" ); break; case jtBool : case jtInt : case jtFloat : fprintf( OutputFile, "%s,\n", Param->Value ); break; case jtString : fprintf( OutputFile, "\"%s\",\n", Param->Value ); break; case jtArray : fprintf( OutputFile, "[],\n" ); break; case jtObject : fprintf( OutputFile, "{},\n" ); break; } } // Close file fclose( OutputFile ); return false; } //---------------------------------------------------------------------------