/* * ConfigCore.cpp * * Created on: 5 Mar 2017 * Author: wentzelc */ // redA Libraries #include "ConfigCore.h" // Standard C/C++ Libraries #include #include #include #include #include //--------------------------------------------------------------------------- CConfigCore::CConfigCore() { // Parameter tree RootObject = CreateParam( NULL ); RootObject->Type = jtObject; // File Operation InputHandle = -1; OutputHandle = -1; Buffer = NULL; BufEnd = NULL; Level = 0; // Parsing operation BufPos = NULL; LineMark = NULL; LineNo = 0; // Printing operation Spacer[0] = 0; SpacerLen = 0; // Error reporting Error = false; ErrorText[0] = 0; } //--------------------------------------------------------------------------- CConfigCore::~CConfigCore() { // Destroy Params DeleteAll(); DestroyParam( &RootObject ); } //--------------------------------------------------------------------------- TConfigParam * CConfigCore::CreateParam( const char * Name ) { TConfigParam * Param; Param = (TConfigParam *)calloc( 1, sizeof(TConfigParam) ); if (Name && *Name) { Param->Name = (char *)malloc( strlen( Name )+1 ); strcpy( Param->Name, Name ); } return Param; } //--------------------------------------------------------------------------- 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 ); while ((*Param)->FirstObject) { DestroyParam( &((*Param)->FirstObject) ); } free( *Param ); // Restore list integrity *Param = NextParam; return true; } //--------------------------------------------------------------------------- bool CConfigCore::GetParent( const char * ParentPath, TConfigParam **Parent ) { TConfigParam * Param; char * ParentName; char * Pos; // Validate if (!Parent) { return false; } // Set first Parent *Parent = RootObject; // Split path Pos = (char*)ParentPath; while (*Pos) { // Set Name start ParentName = Pos; // Find delimeter while (*Pos && (*Pos != '/')) { Pos++; } // Zero terminate name if (*Pos) { *Pos = 0; Pos++; } // Find next parent Param = (*Parent)->FirstObject; while (Param && strcasecmp( Param->Name, ParentName )) { Param = Param->Next; } if (!Param) { return false; } // Set Next parent *Parent = Param; } return true; } //--------------------------------------------------------------------------- TConfigParam * CConfigCore::GetParam( TConfigParam * Parent, const char * Name ) { TConfigParam * Param; // Validate if (!Parent || !Name || !*Name) { return NULL; } // Get Param Param = Parent->FirstObject; while (Param && strcasecmp( Param->Name, Name )) Param = Param->Next; return Param; } //--------------------------------------------------------------------------- TConfigParam ** CConfigCore::GetParamPtr( TConfigParam * Parent, const char * Name ) { TConfigParam ** Param; // Validate if (!Parent || !Name || !*Name) { return NULL; } // Get Param Param = &(Parent->FirstObject); while (Param && strcasecmp( (*Param)->Name, Name )) Param = &((*Param)->Next); return Param; } //--------------------------------------------------------------------------- bool CConfigCore::DeleteParam( const char * ParentPath, const char * Name ) { TConfigParam * Parent; TConfigParam ** Param; // Check if exists if (!GetParent( ParentPath, &Parent ) || !(Param = GetParamPtr( Parent, Name ))) { return false; } // Destroy DestroyParam( Param ); Parent->Len--; return true; } //--------------------------------------------------------------------------- bool CConfigCore::DeleteAll() { while (RootObject->FirstObject) { DestroyParam( &(RootObject->FirstObject) ); } 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 switch (Type) { case jtNull: case jtObject: case jtArray: Param->Len = 0; Param->Value = NULL; break; case jtString: case jtFloat: case jtInt: case jtBool: Param->Len = (Len == -1)? strlen(Value) : Len; Param->Value = (char *)malloc( sizeof(Param->Len+1) ); if (Value) { memcpy( Param->Value, Value, Param->Len+1 ); } else { memset( Param->Value, 0, Param->Len+1 ); } Param->Value[Param->Len] = 0; break; } return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamObject( const char * ParentPath, const char * Name ) { TConfigParam * Parent; TConfigParam ** Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return false; } // Create new param if it doesn't exist if (!(Param = GetParamPtr( Parent, Name ))) { *Param = CreateParam( Name ); } SetParam( *Param, jtObject, NULL, -1 ); return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamStr( const char * ParentPath, const char * Name, const char * Value, const int Len ) { TConfigParam * Parent; TConfigParam ** Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return false; } // Create new param if it doesn't exist if (!(Param = GetParamPtr( Parent, Name ))) { *Param = CreateParam( Name ); } SetParam( *Param, jtString, Value, Len ); return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamInt( const char * ParentPath, const char * Name, const long Value ) { TConfigParam * Parent; TConfigParam ** Param; char ValueStr[50]; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return false; } // Create new param if it doesn't exist if (!(Param = GetParamPtr( Parent, Name ))) { *Param = CreateParam( Name ); } sprintf( ValueStr, "%ld", Value ); SetParam( *Param, jtInt, ValueStr, -1 ); return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamFloat( const char * ParentPath, const char * Name, const double Value ) { TConfigParam * Parent; TConfigParam ** Param; char ValueStr[50]; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return false; } // Create new param if it doesn't exist if (!*(Param = GetParamPtr( Parent, Name ))) { *Param = CreateParam( Name ); } sprintf( ValueStr, "%lf", Value ); SetParam( *Param, jtFloat, ValueStr, -1 ); return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamBool( const char * ParentPath, const char * Name, const bool Value ) { TConfigParam * Parent; TConfigParam ** Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return false; } // Create new param if it doesn't exist if (!*(Param = GetParamPtr( Parent, Name ))) { *Param = CreateParam( Name ); } SetParam( *Param, jtBool, ((Value == 0)? "false" : "true"), -1 ); return true; } //--------------------------------------------------------------------------- bool CConfigCore::SetParamNull( const char * ParentPath, const char * Name ) { TConfigParam * Parent; TConfigParam ** Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return false; } // Create new param if it doesn't exist if (!*(Param = GetParamPtr( Parent, Name ))) { *Param = CreateParam( Name ); } SetParam( *Param, jtNull, NULL, -1 ); return true; } //--------------------------------------------------------------------------- const EJSONtype CConfigCore::GetParamType( const char * ParentPath, const char * Name ) { TConfigParam * Parent; TConfigParam * Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return jtNull; } // Return type if ((Param = GetParam( Parent, Name ))) { return Param->Type; } else { return jtNull; } } //--------------------------------------------------------------------------- const char * CConfigCore::GetParamStr( const char * ParentPath, const char * Name, const char * Default ) { TConfigParam * Parent; TConfigParam * Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return Default; } // Return value if ((Param = GetParam( Parent, Name )) && (Param->Type == jtString)) { return Param->Value; } else { return Default; } } //--------------------------------------------------------------------------- const char * CConfigCore::GetParamStr( const char * ParentPath, const char * Name, int &Len, const char * Default ) { TConfigParam * Parent; TConfigParam * Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return Default; } // Return value if ((Param = GetParam( Parent, Name )) && (Param->Type == jtString)) { Len = Param->Len; return Param->Value; } else { Len = strlen( Default ); return Default; } } //--------------------------------------------------------------------------- const long CConfigCore::GetParamInt( const char * ParentPath, const char * Name, long Default ) { TConfigParam * Parent; TConfigParam * Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return Default; } // Return value if ((Param = GetParam( Parent, Name )) && (Param->Type == jtInt)) { return strtol( Param->Value, NULL, 10 ); } else { return Default; } } //--------------------------------------------------------------------------- const double CConfigCore::GetParamFloat( const char * ParentPath, const char * Name, double Default ) { TConfigParam * Parent; TConfigParam * Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return Default; } // Return value if ((Param = GetParam( Parent, Name )) && (Param->Type == jtFloat)) { return strtod( Param->Value, NULL ); } else { return Default; } } //--------------------------------------------------------------------------- const bool CConfigCore::GetParamBool( const char * ParentPath, const char * Name, bool Default ) { TConfigParam * Parent; TConfigParam * Param; // Validate if (!Name || !*Name || !GetParent( ParentPath, &Parent )) { return Default; } // Return value if ((Param = GetParam( Parent, 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]) { Error = true; sprintf( ErrorText, "No File path specified" ); return false; } // Open file if (!(InputHandle = open( FilePath, O_RDONLY ))) { Error = true; sprintf( ErrorText, "Could not open file" ); 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 = RootObject; Level = 0; // 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 close( InputHandle ); FreeBuffer(); return true; } //--------------------------------------------------------------------------- bool CConfigCore::CreateBuffer( int pBufLen ) { // Validate if (!pBufLen) return false; // Create buffer Buffer = new CShiftBuffer( pBufLen ); // Reset markers Buffer->PeekDirect( &BufPos, 0 ); BufEnd = BufPos; LineMark = BufPos; return true; } //--------------------------------------------------------------------------- bool CConfigCore::FillBuffer() { // Read from file Buffer->ReadFromFD( InputHandle ); // Update Markers Buffer->PeekDirect( &BufEnd, Buffer->Len() ); Buffer->PeekDirect( &BufPos, 0 ); LineMark = BufPos; return true; } //--------------------------------------------------------------------------- void CConfigCore::FreeBuffer() { // Destroy buffer if (Buffer) { delete Buffer; Buffer = NULL; // Update Markers BufEnd = NULL; BufPos = NULL; LineMark = NULL; } } //--------------------------------------------------------------------------- bool CConfigCore::ParseObject( TConfigParam * Object ) { TConfigParam ** ParamPtr = NULL; TConfigParam * Param = NULL; char * ParamName = NULL; int Len = 0; // Clear Values Error = false; // Check for start of Object if (*BufPos != '{') { return false; } BufPos++; // Set Type Level++; SetParam( Object, jtObject, NULL, -1 ); while (true) { // Look for Param Name SkipWhiteSpace(); if (*BufPos == '}') { break; } 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; } // Check if Param exists ParamPtr = &(Object->FirstObject); while (*ParamPtr && strcasecmp( (*ParamPtr)->Name, ParamName )) { ParamPtr = &((*ParamPtr)->Next); } // If not exist, add to end of list if (!*ParamPtr) { *ParamPtr = CreateParam( NULL ); (*ParamPtr)->Name = ParamName; } Param = *ParamPtr; // 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; } // One item added Object->Len++; // 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++; Level--; // 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 Indent ) { // Validate if (!FilePath || !FilePath[0]) return false; // Open file if (!(OutputHandle = open( FilePath, O_WRONLY ))) return false; // Save Root object Level = 0; SaveObject( RootObject, Indent ); // Close file close( OutputHandle ); return false; } //--------------------------------------------------------------------------- bool CConfigCore::SaveObject( TConfigParam * Object, const int Indent ) { TConfigParam * Param; // Opening brace dprintf( OutputHandle, "{\n" ); // Extend spacer Level++; memset( &Spacer[SpacerLen], ' ', 2 ); SpacerLen += 2; Spacer[SpacerLen] = 0; // Save parameters for (Param = Object->FirstObject; Param != NULL; (Param = Param->Next)) { // Write parameter name dprintf( OutputHandle, "%s\"%s\": ", Spacer, Param->Name ); switch (Param->Type) { case jtNull : dprintf( OutputHandle, "%s,\n", "null" ); break; case jtBool : case jtInt : case jtFloat : dprintf( OutputHandle, "%s,\n", Param->Value ); break; case jtString : dprintf( OutputHandle, "\"%s\",\n", Param->Value ); break; case jtArray : dprintf( OutputHandle, "[],\n" ); break; case jtObject : SaveObject( Param, Indent ); break; } } // Shorten spacer SpacerLen -= 2; Spacer[SpacerLen] = 0; Level--; // Closing brace if (Level == 0) { dprintf( OutputHandle, "%s}\n", Spacer ); } else { dprintf( OutputHandle, "%s},\n", Spacer ); } return false; } //---------------------------------------------------------------------------