diff --git a/apps/aqhome-react/main.c b/apps/aqhome-react/main.c index 1e535e2..24b9321 100644 --- a/apps/aqhome-react/main.c +++ b/apps/aqhome-react/main.c @@ -18,6 +18,7 @@ #include "aqhome-react/types/prgrule-t.h" #include "aqhome/aqhome.h" +#include "aqhome/data/path-t.h" #include #include @@ -304,7 +305,13 @@ int _testModules(int argc, char **argv) rv=AQHREACT_PrgRule_AddTests(TestFramework_GetModulesRoot(tf)); if (rv<0) { - fprintf(stderr, "Adding module failed.\n"); + fprintf(stderr, "Adding module \"PrgRule\" failed.\n"); + return 2; + } + + rv=AQH_Path_AddTests(TestFramework_GetModulesRoot(tf)); + if (rv<0) { + fprintf(stderr, "Adding module \"path\" failed.\n"); return 2; } diff --git a/aqhome/data/0BUILD b/aqhome/data/0BUILD index 73cf137..c0fdb7a 100644 --- a/aqhome/data/0BUILD +++ b/aqhome/data/0BUILD @@ -66,6 +66,9 @@ storage_readxml.h storage_writexml.h datafile_direct_p.h + vars.h + vars_p.h + path.h @@ -76,10 +79,14 @@ storage_readxml.c storage_writexml.c datafile_direct.c + vars.c + path.c + path-t.h + path-t.c diff --git a/aqhome/data/path-t.c b/aqhome/data/path-t.c new file mode 100644 index 0000000..9335caf --- /dev/null +++ b/aqhome/data/path-t.c @@ -0,0 +1,168 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +/* This file is included by "path.c" */ + + +#include +#include "path-t.h" + + +#ifdef AQHOME_ENABLE_TESTCODE + + +typedef struct PATH_TEST_ENTRY PATH_TEST_ENTRY; +struct PATH_TEST_ENTRY { + const char *element; + int index; + uint32_t flags; +}; + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int GWENHYWFAR_CB test1(GWEN_TEST_MODULE *mod); +static int GWENHYWFAR_CB test2(GWEN_TEST_MODULE *mod); +static int GWENHYWFAR_CB test3(GWEN_TEST_MODULE *mod); + + +static void *_cbTestElement(const char *element, void *data, int idx, uint32_t flags); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AQH_Path_AddTests(GWEN_TEST_MODULE *mod) +{ + GWEN_TEST_MODULE *newMod; + + newMod=GWEN_Test_Module_AddModule(mod, "AQH_Path", NULL); + + GWEN_Test_Module_AddTest(newMod, "test 1: path with index", test1, NULL); + GWEN_Test_Module_AddTest(newMod, "test 2: path without index", test2, NULL); + GWEN_Test_Module_AddTest(newMod, "test 3: path with backspace escape", test3, NULL); + + return 0; +} + + + +int test1(GWEN_UNUSED GWEN_TEST_MODULE *mod) +{ + static char path[]="/test1/test2[3]/test3[2]"; + static PATH_TEST_ENTRY expected[]={ + {"test1", 0, AQH_PATH_FLAGS_PARSEIDX | AQH_PATH_FLAGS_ROOT}, + {"test2", 3, AQH_PATH_FLAGS_PARSEIDX}, + {"test3", 2, AQH_PATH_FLAGS_PARSEIDX | AQH_PATH_FLAGS_LAST}, + {NULL, 0, 0} + }; + + if (AQH_Path_Handle(path, expected, AQH_PATH_FLAGS_PARSEIDX, _cbTestElement)==NULL) { + DBG_ERROR(NULL, "Error handling patch \"%s\"", path); + return GWEN_ERROR_GENERIC; + } + + return 0; +} + + + +int test2(GWEN_UNUSED GWEN_TEST_MODULE *mod) +{ + static char path[]="/test1/test2[3]/test3[2]"; + static PATH_TEST_ENTRY expected[]={ + {"test1", 0, AQH_PATH_FLAGS_ROOT}, + {"test2[3]", 0, 0}, + {"test3[2]", 0, AQH_PATH_FLAGS_LAST}, + {NULL, 0, 0} + }; + + if (AQH_Path_Handle(path, expected, 0, _cbTestElement)==NULL) { + DBG_ERROR(NULL, "Error handling patch \"%s\"", path); + return GWEN_ERROR_GENERIC; + } + + return 0; +} + + + +int test3(GWEN_UNUSED GWEN_TEST_MODULE *mod) +{ + static char path[]="/test1\\/test2[3]/test3[2]/test4[1]"; + static PATH_TEST_ENTRY expected[]={ + {"test1/test2", 3, AQH_PATH_FLAGS_PARSEIDX | AQH_PATH_FLAGS_ROOT}, + {"test3", 2, AQH_PATH_FLAGS_PARSEIDX}, + {"test4", 1, AQH_PATH_FLAGS_PARSEIDX | AQH_PATH_FLAGS_LAST}, + {NULL, 0, 0} + }; + + if (AQH_Path_Handle(path, expected, AQH_PATH_FLAGS_PARSEIDX, _cbTestElement)==NULL) { + DBG_ERROR(NULL, "Error handling patch \"%s\"", path); + return GWEN_ERROR_GENERIC; + } + + return 0; +} + + + + + + +void *_cbTestElement(const char *element, void *data, int idx, uint32_t flags) +{ + PATH_TEST_ENTRY *currentEntry; + + DBG_INFO(NULL, "Testing element \"%s\":%d (%08x)", element, idx, flags); + currentEntry=(PATH_TEST_ENTRY*) data; + if (currentEntry->element==NULL) { + DBG_ERROR(NULL, "current element is NULL when handling path element \"%s\"", element); + return NULL; + } + + if (idx!=currentEntry->index) { + DBG_ERROR(NULL, "Current index \"%d\" doesn't match expected index \"%d\"", idx, currentEntry->index); + return NULL; + } + + if (flags!=currentEntry->flags) { + DBG_ERROR(NULL, "Current flags \"%08x\" don't match expected flags \"%08x\"", flags, currentEntry->flags); + return NULL; + } + + if (strcmp(currentEntry->element, element)!=0) { + DBG_ERROR(NULL, "Current element \"%s\" doesn't match expected element \"%s\"", element, currentEntry->element); + return NULL; + } + + currentEntry++; + return currentEntry; +} + + + + + +#else + +int AQH_Path_AddTests(GWEN_TEST_MODULE *mod) +{ + DBG_ERROR(GWEN_LOGDOMAIN, "AqHome was compiled without test code enabled."); + return GWEN_ERROR_GENERIC; +} + + +#endif diff --git a/aqhome/data/path-t.h b/aqhome/data/path-t.h new file mode 100644 index 0000000..cd0fb7b --- /dev/null +++ b/aqhome/data/path-t.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQH_PATH_T_H +#define AQH_PATH_T_H + + +#include +#include + + + +/** + * Tests for "Path" module. + */ +AQHOME_API int AQH_Path_AddTests(GWEN_TEST_MODULE *mod); + + + +#endif diff --git a/aqhome/data/path.c b/aqhome/data/path.c new file mode 100644 index 0000000..00a1db1 --- /dev/null +++ b/aqhome/data/path.c @@ -0,0 +1,135 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + + + +#include "./path.h" + +#include +#include +#include + +#include +#include +#include + + + +static void *_handleElement(const char *element, void *data, uint32_t flags, AQH_PATHHANDLERFN fn); + + + + +void *AQH_Path_Handle(const char *path, void *data, uint32_t flags, AQH_PATHHANDLERFN fn) +{ + if (path && *path) { + GWEN_STRINGLIST *elementList; + + /* clear internal flags */ + flags&=~(AQH_PATH_FLAGS_ROOT | AQH_PATH_FLAGS_LAST); + + if (*path=='/') { + flags|=AQH_PATH_FLAGS_ROOT; + path++; + } + elementList=GWEN_StringList_fromString2(path, "/", 0, GWEN_TEXT_FLAGS_CHECK_BACKSLASH | GWEN_TEXT_FLAGS_DEL_QUOTES); + if (elementList) { + GWEN_STRINGLISTENTRY *se; + + se=GWEN_StringList_FirstEntry(elementList); + while(se) { + const char *s; + GWEN_STRINGLISTENTRY *seNext; + + seNext=GWEN_StringListEntry_Next(se); + if (seNext) + flags&=~AQH_PATH_FLAGS_LAST; + else + flags|=AQH_PATH_FLAGS_LAST; + s=GWEN_StringListEntry_Data(se); + if (s && *s) { + void *result; + + result=_handleElement(s, data, flags, fn); + if (result==NULL) { + DBG_INFO(NULL, "Error handling element \"%s\" (%08x)", s, flags); + GWEN_StringList_free(elementList); + return NULL; + } + data=result; + } + flags&=~AQH_PATH_FLAGS_ROOT; + se=GWEN_StringListEntry_Next(se); + } + GWEN_StringList_free(elementList); + return data; + } + else { + DBG_INFO(NULL, "Error reading path \"%s\" into stringlist", path); + } + } + else { + DBG_ERROR(NULL, "Empty path given"); + } + return NULL; +} + + + +void *_handleElement(const char *element, void *data, uint32_t flags, AQH_PATHHANDLERFN fn) +{ + if (flags & AQH_PATH_FLAGS_PARSEIDX) { + const char *ptrOpenBracket; + + ptrOpenBracket=strchr(element, '['); + if (ptrOpenBracket) { + if (ptrOpenBracket!=element) { + const char *s; + int idx=0; + + s=ptrOpenBracket+1; + while(*s && isdigit(*s)) { + idx*=10; + idx+=*s-'0'; + s++; + } + if (*s!=']') { + DBG_ERROR(NULL, "Closed bracket expected (got \"%c\")", *s); + return NULL; + } + else { + char *rawElement; + void *result; + + s++; + rawElement=GWEN_Text_strndup(element, ptrOpenBracket-element); + result=fn?fn(rawElement, data, idx, flags):data; + free(rawElement); + return result; + } + } + else { + DBG_ERROR(NULL, "Empty element with index"); + return NULL; + } + } + } + return fn(element, data, 0, flags); +} + + + +#include "./path-t.c" + + + + diff --git a/aqhome/data/path.h b/aqhome/data/path.h new file mode 100644 index 0000000..17dd0f8 --- /dev/null +++ b/aqhome/data/path.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQH_PATH_H +#define AQH_PATH_H + +#include + +#include + + +#define AQH_PATH_FLAGS_PATHMUSTEXIST 0x00000001 +#define AQH_PATH_FLAGS_PATHMUSTNOTEXIST 0x00000002 +#define AQH_PATH_FLAGS_PATHCREATE 0x00000004 +#define AQH_PATH_FLAGS_NAMEMUSTEXIST 0x00000008 +#define AQH_PATH_FLAGS_NAMEMUSTNOTEXIST 0x00000010 +#define AQH_PATH_FLAGS_CREATE_GROUP 0x00000020 +#define AQH_PATH_FLAGS_CREATE_VAR 0x00000040 +#define AQH_PATH_FLAGS_VARIABLE 0x00000080 +#define AQH_PATH_FLAGS_PARSEIDX 0x00000100 + +#define AQH_PATH_FLAGS_ROOT 0x10000000 +#define AQH_PATH_FLAGS_LAST 0x20000000 + + + +typedef void *(*AQH_PATHHANDLERFN)(const char *entry, void *data, int idx, uint32_t flags); + + + + +AQHOME_API void *AQH_Path_Handle(const char *path, void *data, uint32_t flags, AQH_PATHHANDLERFN fn); + + + + + +#endif diff --git a/aqhome/data/vars.c b/aqhome/data/vars.c new file mode 100644 index 0000000..8980252 --- /dev/null +++ b/aqhome/data/vars.c @@ -0,0 +1,273 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * Gwenhywfar (c) by 2023 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + + + +#include "./vars_p.h" + +#include +#include + + + +GWEN_TREE2_FUNCTIONS(AQH_VARS, AQH_Vars); + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static AQH_VARS *_newData(void); +static void _releaseData(AQH_VARS *vt); +static AQH_VARS *_newStringElement(char *s, AQH_VARS_DATATYPE dt); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +AQH_VARS *_newData(void) +{ + AQH_VARS *vt; + + GWEN_NEW_OBJECT(AQH_VARS, vt); + GWEN_TREE2_INIT(AQH_VARS, vt, AQH_Vars); + return vt; +} + + + +AQH_VARS *_newStringElement(char *s, AQH_VARS_DATATYPE dt) +{ + AQH_VARS *vt; + + vt=_newData(); + vt->data.dataString=s; + vt->dataType=dt; + + return vt; +} + + + + +void _releaseData(AQH_VARS *vt) +{ + if (vt) { + switch(vt->dataType) { + case AQH_Vars_DataType_Group: + case AQH_Vars_DataType_Variable: + case AQH_Vars_DataType_ValueString: + free(vt->data.dataString); + vt->data.dataString=NULL; + break; + default: + break; + } + vt->dataType=AQH_Vars_DataType_Unknown; + } +} + + + +void AQH_Vars_free(AQH_VARS *vt) +{ + if (vt) { + GWEN_TREE2_FINI(AQH_VARS, vt, AQH_Vars); + _releaseData(vt); + GWEN_FREE_OBJECT(vt); + } +} + + + +AQH_VARS *AQH_Vars_CreateGroup(char *s) +{ + return _newStringElement(s, AQH_Vars_DataType_Group); +} + + + +AQH_VARS *AQH_Vars_CreateVariable(char *s) +{ + return _newStringElement(s, AQH_Vars_DataType_Variable); +} + + + +AQH_VARS *AQH_Vars_CreateStringValue(char *s) +{ + return _newStringElement(s, AQH_Vars_DataType_ValueString); +} + + + +AQH_VARS *AQH_Vars_CreateIntValue(int d) +{ + AQH_VARS *vt; + + vt=_newData(); + vt->data.dataInt=d; + vt->dataType=AQH_Vars_DataType_ValueInt; + + return vt; +} + + + +AQH_VARS *AQH_Vars_CreateDoubleValue(double d) +{ + AQH_VARS *vt; + + vt=_newData(); + vt->data.dataDouble=d; + vt->dataType=AQH_Vars_DataType_ValueDouble; + + return vt; +} + + + +AQH_VARS_DATATYPE AQH_Vars_GetDataType(const AQH_VARS *vt) +{ + return vt?vt->dataType:AQH_Vars_DataType_Unknown; +} + + + +uint32_t AQH_Vars_GetFlags(const AQH_VARS *vt) +{ + return vt?vt->flags:0; +} + + + +const char *AQH_Vars_GetStringData(const AQH_VARS *vt, const char *defValue) +{ + if (vt) { + switch(vt->dataType) { + case AQH_Vars_DataType_Group: + case AQH_Vars_DataType_Variable: + case AQH_Vars_DataType_ValueString: + return vt->data.dataString; + default: + break; + } + } + + return defValue; +} + + + +void AQH_Vars_SetStringData(AQH_VARS *vt, char *s) +{ + if (vt) { + _releaseData(vt); + vt->data.dataString=s; + vt->dataType=AQH_Vars_DataType_ValueString; + } +} + + + +int AQH_Vars_GetIntData(const AQH_VARS *vt, int defValue) +{ + if (vt && vt->dataType==AQH_Vars_DataType_ValueInt) + return vt->data.dataInt; + + return defValue; +} + + + +void AQH_Vars_SetIntData(AQH_VARS *vt, int d) +{ + if (vt) { + _releaseData(vt); + vt->data.dataInt=d; + vt->dataType=AQH_Vars_DataType_ValueInt; + } +} + + + +double AQH_Vars_GetDoubleData(const AQH_VARS *vt, double defValue) +{ + if (vt && vt->dataType==AQH_Vars_DataType_ValueDouble) + return vt->data.dataDouble; + + return defValue; +} + + + +void AQH_Vars_SetDoubleData(AQH_VARS *vt, double d) +{ + if (vt) { + _releaseData(vt); + vt->data.dataDouble=d; + vt->dataType=AQH_Vars_DataType_ValueDouble; + } +} + + + +AQH_VARS *AQH_Vars_GetFirstChildByType(const AQH_VARS *vt, AQH_VARS_DATATYPE dt) +{ + if (vt) { + AQH_VARS *vtChild; + + vtChild=AQH_Vars_Tree2_GetFirstChild(vt); + while(vtChild) { + if (vtChild->dataType==dt) + return vtChild; + vtChild=AQH_Vars_Tree2_GetNext(vtChild); + } + } + + return NULL; +} + + + +AQH_VARS *AQH_Vars_GetNextByType(const AQH_VARS *vt, AQH_VARS_DATATYPE dt) +{ + if (vt) { + AQH_VARS *vtNext; + + vtNext=AQH_Vars_Tree2_GetNext(vt); + while(vtNext) { + if (vtNext->dataType==dt) + return vtNext; + vtNext=AQH_Vars_Tree2_GetNext(vtNext); + } + } + + return NULL; +} + + + + + + + + + + + + + diff --git a/aqhome/data/vars.h b/aqhome/data/vars.h new file mode 100644 index 0000000..2ed37ed --- /dev/null +++ b/aqhome/data/vars.h @@ -0,0 +1,63 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * Gwenhywfar (c) by 2023 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQH_VARS_H +#define AQH_VARS_H + + +#include + + + +typedef enum { + AQH_Vars_DataType_Unknown=0, + AQH_Vars_DataType_Group, + AQH_Vars_DataType_Variable, + AQH_Vars_DataType_ValueString=100, + AQH_Vars_DataType_ValueInt, + AQH_Vars_DataType_ValueDouble +} AQH_VARS_DATATYPE; + + + +typedef struct AQH_VARS AQH_VARS; +GWEN_TREE2_FUNCTION_DEFS(AQH_VARS, AQH_Vars); + + +AQH_VARS *AQH_Vars_CreateGroup(char *s); +AQH_VARS *AQH_Vars_CreateVariable(char *s); +AQH_VARS *AQH_Vars_CreateStringValue(char *s); +AQH_VARS *AQH_Vars_CreateIntValue(int d); +AQH_VARS *AQH_Vars_CreateDoubleValue(double d); + +void AQH_Vars_free(AQH_VARS *vt); + + +AQH_VARS_DATATYPE AQH_Vars_GetDataType(const AQH_VARS *vt); +uint32_t AQH_Vars_GetFlags(const AQH_VARS *vt); + +const char *AQH_Vars_GetStringData(const AQH_VARS *vt, const char *defValue); +void AQH_Vars_SetStringData(AQH_VARS *vt, char *s); + +int AQH_Vars_GetIntData(const AQH_VARS *vt, int defValue); +void AQH_Vars_SetIntData(AQH_VARS *vt, int d); + +double AQH_Vars_GetDoubleData(const AQH_VARS *vt, double defValue); +void AQH_Vars_SetDoubleData(AQH_VARS *vt, double d); + + +AQH_VARS *AQH_Vars_GetFirstChildByType(const AQH_VARS *vt, AQH_VARS_DATATYPE dt); +AQH_VARS *AQH_Vars_GetNextByType(const AQH_VARS *vt, AQH_VARS_DATATYPE dt); + + + + + +#endif + + diff --git a/aqhome/data/vars_p.h b/aqhome/data/vars_p.h new file mode 100644 index 0000000..1b7af96 --- /dev/null +++ b/aqhome/data/vars_p.h @@ -0,0 +1,35 @@ +/**************************************************************************** + * This file is part of the project Gwenhywfar. + * Gwenhywfar (c) by 2023 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQH_VARS_P_H +#define AQH_VARS_P_H + + +#include "./vars.h" + + + +struct AQH_VARS { + GWEN_TREE2_ELEMENT(AQH_VARS); + + AQH_VARS_DATATYPE dataType; + uint32_t flags; + union { + char *dataString; + int dataInt; + double dataDouble; + } data; +}; + + + + + +#endif + +