/**************************************************************************** * 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 #endif #include "./vars_p.h" #include "aqhome/data/path.h" #include #include #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); 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:""); break; case AQH_Vars_DataType_Variable: GWEN_Buffer_AppendArgs(dbuf, "var....\"%s\"\n", (vt->data.dataString)?vt->data.dataString:""); break; case AQH_Vars_DataType_ValueString: GWEN_Buffer_AppendArgs(dbuf, "char...\"%s\"\n", (vt->data.dataString)?vt->data.dataString:""); 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"