Files
aqhomecontrol/aqhome/data/vars.c
2024-05-17 20:34:31 +02:00

684 lines
16 KiB
C

/****************************************************************************
* This file is part of the project AqHome.
* 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 <config.h>
#endif
#include "./vars_p.h"
#include "aqhome/data/path.h"
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/text.h>
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);
static AQH_VARS *_getValueNodeByIdx(const AQH_VARS *vt, int idx);
static AQH_VARS *_getPath(AQH_VARS *vt, const char *s, uint32_t flags);
static void *_pathHandlerFn(const char *entry, void *data, int idx, uint32_t flags);
static AQH_VARS *_getNodeByTypeAndName(const AQH_VARS *vt, AQH_VARS_DATATYPE dt, const char *name, int idx);
static void _dumpElement(const AQH_VARS *vt, int indent, GWEN_BUFFER *dbuf);
static int GWENHYWFAR_CB _replaceVarsCb(void *cbPtr, const char *name, int index, int maxLen, GWEN_BUFFER *dstBuf);
/* ------------------------------------------------------------------------------------------------
* 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;
}
}
AQH_VARS *_dupData(const AQH_VARS *vt)
{
if (vt) {
AQH_VARS *vtCopy;
vtCopy=_newData();
vtCopy->dataType=vt->dataType;
switch(vt->dataType) {
case AQH_Vars_DataType_Unknown:
break;
case AQH_Vars_DataType_Group:
case AQH_Vars_DataType_Variable:
case AQH_Vars_DataType_ValueString:
vtCopy->data.dataString=(vt->data.dataString)?strdup(vt->data.dataString):NULL;
break;
case AQH_Vars_DataType_ValueInt:
vtCopy->data.dataInt=vt->data.dataInt;
break;
case AQH_Vars_DataType_ValueDouble:
vtCopy->data.dataDouble=vt->data.dataDouble;
break;
}
return vtCopy;
}
return NULL;
}
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_DATATYPE AQH_Vars_DataTypeFromString(const char *s)
{
if (s && *s) {
if (strcasecmp(s, "group")==0)
return AQH_Vars_DataType_Group;
else if (strcasecmp(s, "variable")==0)
return AQH_Vars_DataType_Variable;
else if (strcasecmp(s, "char")==0)
return AQH_Vars_DataType_ValueString;
else if (strcasecmp(s, "int")==0)
return AQH_Vars_DataType_ValueInt;
else if (strcasecmp(s, "double")==0)
return AQH_Vars_DataType_ValueDouble;
}
return AQH_Vars_DataType_Unknown;
}
const char *AQH_Vars_DataTypeToString(AQH_VARS_DATATYPE dt)
{
switch(dt) {
case AQH_Vars_DataType_Unknown: break;
case AQH_Vars_DataType_Group: return "group";
case AQH_Vars_DataType_Variable: return "variable";
case AQH_Vars_DataType_ValueString: return "char";
case AQH_Vars_DataType_ValueInt: return "int";
case AQH_Vars_DataType_ValueDouble: return "double";
}
return "unknown";
}
AQH_VARS *AQH_Vars_dup(const AQH_VARS *vt)
{
AQH_VARS *vtCopy;
AQH_VARS *vtChild;
vtCopy=_dupData(vt);
vtChild=AQH_Vars_Tree2_GetFirstChild(vt);
while(vtChild) {
AQH_Vars_Tree2_AddChild(vtCopy, _dupData(vtChild));
vtChild=AQH_Vars_Tree2_GetNext(vtChild);
}
return vtCopy;
}
AQH_VARS *AQH_Vars_CreateGroup(const char *s)
{
return _newStringElement(s?strdup(s):NULL, AQH_Vars_DataType_Group);
}
AQH_VARS *AQH_Vars_CreateVariable(const char *s)
{
return _newStringElement(s?strdup(s):NULL, 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;
case AQH_Vars_DataType_ValueInt:
DBG_INFO(AQH_LOGDOMAIN, "Unexpected type INT: %d", vt->data.dataInt);
break;
case AQH_Vars_DataType_ValueDouble:
DBG_INFO(AQH_LOGDOMAIN, "Unexpected type DOUBLE: %f", vt->data.dataDouble);
break;
default:
DBG_INFO(AQH_LOGDOMAIN, "Unexpected type %d", vt->dataType);
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;
}
AQH_VARS *AQH_Vars_GetGroup(AQH_VARS *vt, const char *path, uint32_t flags)
{
return vt?_getPath(vt, path, (flags & ~AQH_PATH_FLAGS_VARIABLE)):NULL;
}
int AQH_Vars_SetCharValue(AQH_VARS *vt, uint32_t flags, const char *path, const char *value)
{
if (vt) {
AQH_VARS *vtChild;
AQH_VARS *vtValue;
vtChild=_getPath(vt, path, flags | AQH_PATH_FLAGS_VARIABLE);
if (vtChild==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Error getting path for var \"%s\"", path);
return GWEN_ERROR_GENERIC;
}
vtValue=AQH_Vars_CreateStringValue(value?strdup(value):NULL);
AQH_Vars_Tree2_AddChild(vtChild, vtValue);
return 0;
}
return GWEN_ERROR_INVALID;
}
const char *AQH_Vars_GetCharValue(AQH_VARS *vt, const char *path, int idx, const char *defaultValue)
{
if (vt && idx>=0) {
AQH_VARS *vtChild;
AQH_VARS *vtValue;
vtChild=_getPath(vt, path, AQH_PATH_FLAGS_PATHMUSTEXIST | AQH_PATH_FLAGS_VARIABLE);
if (vtChild==NULL)
return defaultValue;
vtValue=_getValueNodeByIdx(vtChild, idx);
if (vtValue)
return AQH_Vars_GetStringData(vtValue, defaultValue);
}
return defaultValue;
}
int AQH_Vars_SetIntValue(AQH_VARS *vt, uint32_t flags, const char *path, int value)
{
if (vt) {
AQH_VARS *vtChild;
AQH_VARS *vtValue;
vtChild=_getPath(vt, path, flags | AQH_PATH_FLAGS_VARIABLE);
if (vtChild==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Error getting path for var \"%s\"", path);
return GWEN_ERROR_GENERIC;
}
vtValue=AQH_Vars_CreateIntValue(value);
AQH_Vars_Tree2_AddChild(vtChild, vtValue);
return 0;
}
return GWEN_ERROR_INVALID;
}
int AQH_Vars_GetIntValue(AQH_VARS *vt, const char *path, int idx, int defaultValue)
{
if (vt && idx>=0) {
AQH_VARS *vtChild;
AQH_VARS *vtValue;
vtChild=_getPath(vt, path, AQH_PATH_FLAGS_PATHMUSTEXIST | AQH_PATH_FLAGS_VARIABLE);
if (vtChild==NULL)
return defaultValue;
vtValue=_getValueNodeByIdx(vtChild, idx);
if (vtValue)
return AQH_Vars_GetIntData(vtValue, defaultValue);
}
return defaultValue;
}
int AQH_Vars_SetDoubleValue(AQH_VARS *vt, uint32_t flags, const char *path, double value)
{
if (vt) {
AQH_VARS *vtChild;
AQH_VARS *vtValue;
vtChild=_getPath(vt, path, flags | AQH_PATH_FLAGS_VARIABLE);
if (vtChild==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Error getting path for var \"%s\"", path);
return GWEN_ERROR_GENERIC;
}
vtValue=AQH_Vars_CreateDoubleValue(value);
AQH_Vars_Tree2_AddChild(vtChild, vtValue);
return 0;
}
return GWEN_ERROR_INVALID;
}
double AQH_Vars_GetDoubleValue(AQH_VARS *vt, const char *path, int idx, double defaultValue)
{
if (vt && idx>=0) {
AQH_VARS *vtChild;
AQH_VARS *vtValue;
vtChild=_getPath(vt, path, AQH_PATH_FLAGS_PATHMUSTEXIST | AQH_PATH_FLAGS_VARIABLE);
if (vtChild==NULL)
return defaultValue;
vtValue=_getValueNodeByIdx(vtChild, idx);
if (vtValue)
return AQH_Vars_GetDoubleData(vtValue, defaultValue);
}
return defaultValue;
}
AQH_VARS *_getValueNodeByIdx(const AQH_VARS *vt, int idx)
{
if (vt && idx>=0) {
AQH_VARS *vtValue;
vtValue=AQH_Vars_Tree2_GetFirstChild(vt);
while(vtValue && idx) {
idx--;
vtValue=AQH_Vars_Tree2_GetNext(vtValue);
}
return vtValue;
}
return NULL;
}
AQH_VARS *_getPath(AQH_VARS *vt, const char *s, uint32_t flags)
{
return (AQH_VARS *)AQH_Path_Handle(s, (void*) vt, flags, "/", _pathHandlerFn);
}
void *_pathHandlerFn(const char *entry, void *data, int idx, uint32_t flags)
{
AQH_VARS *vt;
AQH_VARS *vtChild;
AQH_VARS_DATATYPE dt;
vt=(AQH_VARS*) data;
dt=((flags & AQH_PATH_FLAGS_LAST) && (flags & AQH_PATH_FLAGS_VARIABLE))?AQH_Vars_DataType_Variable:AQH_Vars_DataType_Group;
if (
!(
(flags & AQH_PATH_FLAGS_PATHCREATE) ||
((flags & AQH_PATH_FLAGS_LAST) && (flags & AQH_PATH_FLAGS_LASTCREATE))
)
) {
/* look it up */
vtChild=_getNodeByTypeAndName(vt, dt, entry, idx);
if (vtChild) {
/* already exists, should it? */
if (
(flags & AQH_PATH_FLAGS_PATHMUSTNOTEXIST) ||
((flags & AQH_PATH_FLAGS_LAST) && (flags & AQH_PATH_FLAGS_LASTMUSTNOTEXIST))
) {
DBG_INFO(AQH_LOGDOMAIN, "Element \"%s[%d]\" exists when it should not", entry, idx);
return NULL;
}
/* already exists, should we overwrite it (i.e. remove children)? */
if (
(flags & AQH_PATH_FLAGS_LAST) &&
(
((dt==AQH_Vars_DataType_Group) && (flags & AQH_VARS_PATHFLAGS_OVERWRITE_GROUPS)) ||
((dt==AQH_Vars_DataType_Variable) && (flags & AQH_VARS_PATHFLAGS_OVERWRITE_VARS))
)
)
AQH_Vars_Tree2_ClearChildren(vtChild);
return vtChild;
}
else {
/* does not exist, should it? */
if (flags & AQH_PATH_FLAGS_PATHMUSTEXIST) {
DBG_INFO(AQH_LOGDOMAIN, "Element \"%s[%d]\" does not exist when it should", entry, idx);
return NULL;
}
}
}
/* create new string element (either group or var) */
vtChild=_newStringElement(strdup(entry), dt);
AQH_Vars_Tree2_AddChild(vt, vtChild);
return vtChild;
}
AQH_VARS *_getNodeByTypeAndName(const AQH_VARS *vt, AQH_VARS_DATATYPE dt, const char *name, int idx)
{
AQH_VARS *vtChild=NULL;
if (idx>=0) {
/* look it up */
vtChild=AQH_Vars_GetFirstChildByType(vt, dt);
while(vtChild) {
if (vtChild->data.dataString && strcasecmp(vtChild->data.dataString, name)==0) {
if (idx==0)
break;
else
idx--;
}
vtChild=AQH_Vars_GetNextByType(vtChild, dt);
}
}
return vtChild;
}
void AQH_Vars_Dump(const AQH_VARS *vt, int indent)
{
GWEN_BUFFER *dbuf;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
_dumpElement(vt, indent, dbuf);
fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
}
void _dumpElement(const AQH_VARS *vt, int indent, GWEN_BUFFER *dbuf)
{
const AQH_VARS *vtChild;
if (indent)
GWEN_Buffer_FillWithBytes(dbuf, ' ', indent);
switch(vt->dataType) {
case AQH_Vars_DataType_Unknown:
GWEN_Buffer_AppendString(dbuf, "unknown\n");
break;
case AQH_Vars_DataType_Group:
GWEN_Buffer_AppendArgs(dbuf, "group..\"%s\"\n", (vt->data.dataString)?vt->data.dataString:"<no name>");
break;
case AQH_Vars_DataType_Variable:
GWEN_Buffer_AppendArgs(dbuf, "var....\"%s\"\n", (vt->data.dataString)?vt->data.dataString:"<no name>");
break;
case AQH_Vars_DataType_ValueString:
GWEN_Buffer_AppendArgs(dbuf, "char...\"%s\"\n", (vt->data.dataString)?vt->data.dataString:"<no data>");
break;
case AQH_Vars_DataType_ValueInt:
GWEN_Buffer_AppendArgs(dbuf, "int....\"%d\"\n", vt->data.dataInt);
break;
case AQH_Vars_DataType_ValueDouble:
GWEN_Buffer_AppendArgs(dbuf, "double.\"%f\"\n", vt->data.dataDouble);
break;
}
vtChild=AQH_Vars_Tree2_GetFirstChild(vt);
while(vtChild) {
_dumpElement(vtChild, indent+2, dbuf);
vtChild=AQH_Vars_Tree2_GetNext(vtChild);
}
}
int AQH_Vars_ReplaceVars(AQH_VARS *vars, const char *s, GWEN_BUFFER *dbuf)
{
int rv;
rv=GWEN_Text_ReplaceVars(s, dbuf, _replaceVarsCb, vars);
if (rv<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
return rv;
}
return 0;
}
int GWENHYWFAR_CB _replaceVarsCb(void *cbPtr, const char *name, int index, GWEN_UNUSED int maxLen, GWEN_BUFFER *dstBuf)
{
AQH_VARS *vars;
AQH_VARS *vtVariable;
vars=(AQH_VARS*) cbPtr;
vtVariable=_getPath(vars, name, AQH_PATH_FLAGS_PATHMUSTEXIST | AQH_PATH_FLAGS_VARIABLE);
if (vtVariable) {
AQH_VARS *vtValue;
vtValue=_getValueNodeByIdx(vtVariable, index);
if (vtValue) {
switch(vtValue->dataType) {
case AQH_Vars_DataType_ValueString: GWEN_Buffer_AppendArgs(dstBuf, "%s", vtValue->data.dataString); return 0;
case AQH_Vars_DataType_ValueInt: GWEN_Buffer_AppendArgs(dstBuf, "%d", vtValue->data.dataInt); return 0;
case AQH_Vars_DataType_ValueDouble: GWEN_Buffer_AppendArgs(dstBuf, "%f", vtValue->data.dataDouble); return 0;
case AQH_Vars_DataType_Unknown:
case AQH_Vars_DataType_Group:
case AQH_Vars_DataType_Variable:
break;
}
DBG_ERROR(AQH_LOGDOMAIN, "Unexpected data type \"%d\" (%s)", vtValue->dataType, AQH_Vars_DataTypeToString(vtValue->dataType));
return GWEN_ERROR_INVALID;
}
}
return 0;
}
#include "./vars-t.c"