diff --git a/apps/aqhome-react/main.c b/apps/aqhome-react/main.c index 1f8576c..b325cce 100644 --- a/apps/aqhome-react/main.c +++ b/apps/aqhome-react/main.c @@ -21,6 +21,7 @@ #include "aqhome/data/path-t.h" #include "aqhome/data/vars-t.h" #include "aqhome/data/vars_dbread-t.h" +#include "aqhome/data/vars_dbwrite-t.h" #include #include @@ -329,6 +330,12 @@ int _testModules(int argc, char **argv) return 2; } + rv=AQH_Vars_DbWrite_AddTests(TestFramework_GetModulesRoot(tf)); + if (rv<0) { + fprintf(stderr, "Adding module \"vars_dbwrite\" failed.\n"); + return 2; + } + argc--; argv++; rv=TestFramework_Run(tf, argc, argv); diff --git a/aqhome/data/0BUILD b/aqhome/data/0BUILD index c59f67a..9769501 100644 --- a/aqhome/data/0BUILD +++ b/aqhome/data/0BUILD @@ -69,6 +69,7 @@ vars.h vars_p.h vars_dbread.h + vars_dbwrite.h path.h @@ -82,6 +83,7 @@ datafile_direct.c vars.c vars_dbread.c + vars_dbwrite.c path.c @@ -93,6 +95,8 @@ vars-t.c vars_dbread-t.h vars_dbread-t.c + vars_dbwrite-t.h + vars_dbwrite-t.c diff --git a/aqhome/data/vars_dbwrite-t.c b/aqhome/data/vars_dbwrite-t.c new file mode 100644 index 0000000..1750386 --- /dev/null +++ b/aqhome/data/vars_dbwrite-t.c @@ -0,0 +1,134 @@ +/**************************************************************************** + * 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 "vars_dbread.c" */ + + +#include +#include "vars_dbwrite-t.h" + + +#ifdef AQHOME_ENABLE_TESTCODE + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int GWENHYWFAR_CB test1(GWEN_TEST_MODULE *mod); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AQH_Vars_DbWrite_AddTests(GWEN_TEST_MODULE *mod) +{ + GWEN_TEST_MODULE *newMod; + + newMod=GWEN_Test_Module_AddModule(mod, "AQH_Vars_DbWrite", NULL); + + GWEN_Test_Module_AddTest(newMod, "test 1: read, write and read groups and vars", test1, NULL); + + return 0; +} + + + +int GWENHYWFAR_CB test1(GWEN_UNUSED GWEN_TEST_MODULE *mod) +{ + AQH_VARS *vtRoot; + const char *testData= + "testGroup1 {\n" + " char testVar1=\"value1\"\n" + " secondGroup { # here starts the 2nd group\n" + "# this is the second comment\n" + " int firstVarOfSecondGroup=\"1234\";\n" + " secondVarOfSecondGroup=\"s%65condValue\"\n" + " }\n" + "}"; + const char *s; + GWEN_BUFFER *obuf; + int rv; + int i; + + vtRoot=AQH_Vars_ReadDbFormat(testData); + if (vtRoot==NULL) { + DBG_ERROR(AQH_LOGDOMAIN, "Error reading dbformat"); + return GWEN_ERROR_GENERIC; + } + + obuf=GWEN_Buffer_new(0, 256, 0, 1); + rv=AQH_Vars_WriteDbFormat(vtRoot, obuf); + if (rv<0) { + GWEN_Buffer_free(obuf); + AQH_Vars_free(vtRoot); + return rv; + } + + vtRoot=AQH_Vars_ReadDbFormat(GWEN_Buffer_GetStart(obuf)); + if (vtRoot==NULL) { + DBG_ERROR(AQH_LOGDOMAIN, "Error reading dbformat"); + GWEN_Buffer_free(obuf); + return GWEN_ERROR_GENERIC; + } + GWEN_Buffer_free(obuf); + + if (NULL==AQH_Vars_GetGroup(vtRoot, "testGroup1", AQH_PATH_FLAGS_PATHMUSTEXIST)) { + DBG_ERROR(AQH_LOGDOMAIN, "Freshly read group \"testGroup1\" not found"); + AQH_Vars_free(vtRoot); + return GWEN_ERROR_GENERIC; + } + + s=AQH_Vars_GetCharValue(vtRoot, "testGroup1/testVar1", 0, NULL); + if (!(s && strcasecmp(s, "value1")==0)) { + DBG_ERROR(AQH_LOGDOMAIN, "Freshly read var \"testVar1\" with bad value (%s)", s?s:""); + AQH_Vars_free(vtRoot); + return GWEN_ERROR_GENERIC; + } + + i=AQH_Vars_GetIntValue(vtRoot, "testGroup1/secondGroup/firstVarOfSecondGroup", 0, -1); + if (i!=1234) { + DBG_ERROR(AQH_LOGDOMAIN, "Freshly read var \"testGroup1/secondGroup/firstVarOfSecondGroup\" with bad value (%i)", i); + AQH_Vars_free(vtRoot); + return GWEN_ERROR_GENERIC; + } + + s=AQH_Vars_GetCharValue(vtRoot, "testGroup1/secondGroup/secondVarOfSecondGroup", 0, NULL); + if (!(s && strcasecmp(s, "secondValue")==0)) { + DBG_ERROR(AQH_LOGDOMAIN, "Freshly read var \"testGroup1/secondGroup/secondVarOfSecondGroup\" with bad value (%s)", s?s:""); + AQH_Vars_free(vtRoot); + return GWEN_ERROR_GENERIC; + } + + AQH_Vars_free(vtRoot); + + return 0; +} + + +#else + +int AQH_Vars_DbRead_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/vars_dbwrite-t.h b/aqhome/data/vars_dbwrite-t.h new file mode 100644 index 0000000..5e919ca --- /dev/null +++ b/aqhome/data/vars_dbwrite-t.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * 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_DBWRITE_T_H +#define AQH_VARS_DBWRITE_T_H + +#include + +#include +#include + + + +/** + * Tests for "Vars" reading and writing GWEN_DB data. + */ +AQHOME_API int AQH_Vars_DbWrite_AddTests(GWEN_TEST_MODULE *mod); + + + + +#endif + + diff --git a/aqhome/data/vars_dbwrite.c b/aqhome/data/vars_dbwrite.c new file mode 100644 index 0000000..1551657 --- /dev/null +++ b/aqhome/data/vars_dbwrite.c @@ -0,0 +1,207 @@ +/**************************************************************************** + * 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 "./vars_p.h" +#include "./vars_dbread.h" +#include "aqhome/data/path.h" + +#include +#include +#include + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * definitions + * ------------------------------------------------------------------------------------------------ + */ + +const char *_acceptableChars=",.:;-+/&"; + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _groupToBuffer(const AQH_VARS *vt, GWEN_BUFFER *dbuf, int indent); +static int _groupChildrenToBuffer(const AQH_VARS *vt, GWEN_BUFFER *dbuf, int indent); +static int _varToBuffer(const AQH_VARS *vt, GWEN_BUFFER *dbuf, int indent); +static void _appendEscapedString(const char *s, GWEN_BUFFER *dbuf); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + + + +int AQH_Vars_WriteDbFormat(const AQH_VARS *vt, GWEN_BUFFER *dbuf) +{ + if (vt && dbuf) { + int rv; + + rv=_groupChildrenToBuffer(vt, dbuf, 0); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + } + return 0; +} + + + +int _groupToBuffer(const AQH_VARS *vt, GWEN_BUFFER *dbuf, int indent) +{ + int rv; + + if (indent) + GWEN_Buffer_FillWithBytes(dbuf, ' ', indent); + + if (vt->data.dataString && *(vt->data.dataString)) { + _appendEscapedString(vt->data.dataString, dbuf); + GWEN_Buffer_AppendString(dbuf, " {\n"); + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "Group has no name"); + return GWEN_ERROR_GENERIC; + } + + rv=_groupChildrenToBuffer(vt, dbuf, indent+2); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + if (indent) + GWEN_Buffer_FillWithBytes(dbuf, ' ', indent); + GWEN_Buffer_AppendString(dbuf, "}\n"); + return 0; +} + + + +int _groupChildrenToBuffer(const AQH_VARS *vt, GWEN_BUFFER *dbuf, int indent) +{ + const AQH_VARS *vtChild; + + vtChild=AQH_Vars_Tree2_GetFirstChild(vt); + while(vtChild) { + int rv; + + switch(vtChild->dataType) { + case AQH_Vars_DataType_Group: rv=_groupToBuffer(vtChild, dbuf, indent+2); break; + case AQH_Vars_DataType_Variable: rv=_varToBuffer(vtChild, dbuf, indent+2); break; + default: + DBG_ERROR(AQH_LOGDOMAIN, + "Unexpected data type \"%d\" (%s)", + vtChild->dataType, + AQH_Vars_DataTypeToString(vtChild->dataType)); + return GWEN_ERROR_INVALID; + } + + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + return rv; + } + + vtChild=AQH_Vars_Tree2_GetNext(vtChild); + } + + return 0; +} + + + +int _varToBuffer(const AQH_VARS *vt, GWEN_BUFFER *dbuf, int indent) +{ + const AQH_VARS *vtChild; + int isFirst=1; + + vtChild=AQH_Vars_Tree2_GetFirstChild(vt); + if (vtChild) { + if (vt->data.dataString && *(vt->data.dataString)) { + if (indent) + GWEN_Buffer_FillWithBytes(dbuf, ' ', indent); + GWEN_Buffer_AppendArgs(dbuf, "%s ", AQH_Vars_DataTypeToString(vtChild->dataType)); + _appendEscapedString(vt->data.dataString, dbuf); + GWEN_Buffer_AppendString(dbuf, "="); + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "Variable has no name"); + return GWEN_ERROR_GENERIC; + } + + while(vtChild) { + if (!isFirst) { + GWEN_Buffer_AppendString(dbuf, ", "); + isFirst=0; + } + + switch(vtChild->dataType) { + case AQH_Vars_DataType_ValueString: + GWEN_Buffer_AppendByte(dbuf, '\"'); + _appendEscapedString(vtChild->data.dataString, dbuf); + GWEN_Buffer_AppendByte(dbuf, '\"'); + break; + case AQH_Vars_DataType_ValueInt: + GWEN_Buffer_AppendArgs(dbuf, "\"%d\"", vtChild->data.dataInt); + break; + case AQH_Vars_DataType_ValueDouble: + GWEN_Buffer_AppendArgs(dbuf, "\"%f\"", vtChild->data.dataDouble); + break; + + default: + DBG_ERROR(AQH_LOGDOMAIN, + "Unexpected data type \"%d\" (%s)", + vtChild->dataType, + AQH_Vars_DataTypeToString(vtChild->dataType)); + return GWEN_ERROR_INVALID; + } + + vtChild=AQH_Vars_Tree2_GetNext(vtChild); + } + GWEN_Buffer_AppendString(dbuf, "\n"); + } /* if vtCild */ + return 0; +} + + + +void _appendEscapedString(const char *s, GWEN_BUFFER *dbuf) +{ + while(*s) { + unsigned char x; + + x=*s; + if ((x>='A' && x<='Z') || (x>='a' && x<='z') || (x>='0' && x<='9') || strchr(_acceptableChars, x)) + GWEN_Buffer_AppendByte(dbuf, x); + else + GWEN_Buffer_AppendArgs(dbuf, "%%%02x", x); + + s++; + } +} + + + + + +#include "./vars_dbwrite-t.c" + diff --git a/aqhome/data/vars_dbwrite.h b/aqhome/data/vars_dbwrite.h new file mode 100644 index 0000000..107e352 --- /dev/null +++ b/aqhome/data/vars_dbwrite.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * 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_DBWRITE_H +#define AQH_VARS_DBWRITE_H + +#include + +#include + + +AQHOME_API int AQH_Vars_WriteDbFormat(const AQH_VARS *vt, GWEN_BUFFER *dbuf); + + + +#endif + +