Files
redAcore/ConfigCore.cpp
Charl Wentzel 508cf0fb0d Important Update: (incomplete)
- Implemented JSON type NULL
- Implement dynamic buffer
- Split parsing int separate re-usable methods
- Implement basic parsing of an object
- Add GetError() method
- Add SkipWhiteSpace as inline method
- Make parsing variable private attributes
- Improve checking when getting parameters
2017-03-15 04:29:01 +02:00

675 lines
17 KiB
C++

/*
* ConfigCore.cpp
*
* Created on: 5 Mar 2017
* Author: wentzelc
*/
// redA Libraries
#include "ConfigCore.h"
// Standard C/C++ Libraries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
//---------------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------------