/*------------------------------------------------------------------------- The hierarchical parameter database code. For more information, see hipadaba.h copyright: GPL Mark Koennecke, June 2006 ---------------------------------------------------------------------------*/ #include #include #include #include #include "hipadaba.h" #define ABS(x) (x < 0 ? -(x) : (x)) #define HDBMAGICK 77119900 /*================== internal functions ===================================*/ void DeleteCallbackChain(pHdbCallback root){ pHdbCallback current = NULL, thisEntry; current = root; while(current != NULL){ if(current->killFunc != NULL){ current->killFunc(current->userData); } thisEntry = current; current = (pHdbCallback)current->next; free(thisEntry); } } /*-----------------------------------------------------------------------*/ void DeleteNodeData(pHdb node){ pHdb tmp = NULL; if(node == NULL){ return; } DeleteCallbackChain(node->writeCallbacks); DeleteCallbackChain(node->updateCallbacks); DeleteCallbackChain(node->readCallbacks); DeleteCallbackChain(node->treeChangeCallbacks); if(node->properties != NULL){ DeleteStringDict(node->properties); } if(node->name != NULL){ free(node->name); } ReleaseHdbValue(&node->value); node->magic = 000000; while(node->child != NULL){ tmp = node->child; node->child = node->child->next; DeleteNodeData(tmp); } free(node); } /*-------------------------------------------------------------------------*/ int InvokeCallbackChain(pHdbCallback root, pHdb node, void *callData, hdbValue v){ pHdbCallback current = root; int status; while(current != NULL){ status = current->userCallback(current->userData,callData, node,v); if(status != 1){ return status; } current = current->next; } return 1; } /*------------------------------------------------------------------------*/ void RemoveHdbNodeFromParent(pHdb node, void *callData){ pHdb parent = NULL; pHdb current = NULL; parent = node->mama; if(parent != NULL){ if(parent->child == node){ parent->child = node->next; return; } current = parent->child; while(current->next != node){ current = current->next; } current->next = current->next->next; InvokeCallbackChain(parent->treeChangeCallbacks, parent,callData,parent->value); } } /*----------------------------------------------------------------------*/ int CountHdbChildren(pHdb node){ int count = 0; pHdb current = NULL; current = node->child; while(current != NULL){ current = current->next; count++; } return count; } /*-----------------------------------------------------------------------*/ static void RemoveCallbackNode(pHdbCallback victim){ if(victim->previous != NULL) { victim->previous->next = victim->next; } if(victim->next != NULL){ victim->next->previous = victim->previous; } if(victim->killFunc != NULL){ victim->killFunc(victim->userData); } free(victim); } /*----------------------------------------------------------------------- * This code is ugly: the problem is fixing up the start of the chain. * Think about it and improve * ----------------------------------------------------------------------*/ static pHdbCallback DeleteForID(pHdbCallback root, int id){ pHdbCallback current = root; pHdbCallback tmp = NULL; pHdbCallback result = NULL; if(root == NULL){ return NULL; } /* * delete at the start of the chain */ result = root; while(result->id == id){ tmp = result; result = result->next; RemoveCallbackNode(tmp); if(result == NULL){ return NULL; } } /* * delete nodes in the middle of the chain */ current = result; while(current != NULL){ if(current->id == id){ tmp = current; current = (pHdbCallback)current->next; RemoveCallbackNode(tmp); } else { current = (pHdbCallback)current->next; } } return result; } /*-----------------------------------------------------------------------*/ static pHdbCallback DeleteForInternalID(pHdbCallback root, int id){ pHdbCallback current = root; pHdbCallback tmp = NULL; pHdbCallback result = NULL; if(root == NULL){ return NULL; } /* * delete at the start of the chain */ result = root; while(result->internalID == id){ tmp = result; result = result->next; if(tmp->killFunc != NULL){ tmp->killFunc(tmp->userData); } free(tmp); if(result == NULL){ return NULL; } } /* * delete nodes in the middle of the chain */ current = result; while(current != NULL){ if(current->internalID == id){ if(current->next != NULL){ current->next->previous = current->previous; } if(current->previous != NULL){ current->previous->next = current->next; } tmp = current; current = (pHdbCallback)current->next; if(tmp->killFunc != NULL){ tmp->killFunc(tmp->userData); } free(tmp); } else { current = (pHdbCallback)current->next; } } return result; } /*----------------------------------------------------------------------*/ char *hdbTrim(char *str) { char *ibuf = str, *obuf = str; int i = 0, cnt = 0; /* ** Trap NULL */ if (str) { /* ** Remove leading spaces (from RMLEAD.C) */ for (ibuf = str; *ibuf && isspace(*ibuf); ++ibuf) ; if (str != ibuf) memmove(str, ibuf, ibuf - str); /* ** Collapse embedded spaces (from LV1WS.C) */ while (*ibuf) { if (isspace(*ibuf) && cnt) ibuf++; else { if (!isspace(*ibuf)) cnt = 0; else { *ibuf = ' '; cnt = 1; } obuf[i++] = *ibuf++; } } obuf[i] = '\0'; /* ** Remove trailing spaces (from RMTRAIL.C) */ while (--i >= 0) { if (!isspace(obuf[i])) break; } obuf[++i] = '\0'; } return str; } /*------------------------------------------------------------------------*/ static pHdb locateChild(pHdb root, char *name){ pHdb current = NULL; current = root->child; while(current != NULL){ if(strcmp(current->name,name) == 0){ return current; } current = current->next; } return NULL; } /*================= data functions ========================================*/ hdbValue makeHdbValue(int datatype, int length){ hdbValue val; memset(&val,0,sizeof(hdbValue)); val.dataType = datatype; switch(datatype){ case HIPINTAR: case HIPINTVARAR: val.arrayLength = length; val.v.intArray = malloc(length*sizeof(int)); if(val.v.intArray != NULL){ memset(val.v.intArray,0,length*sizeof(int)); } break; case HIPFLOATAR: case HIPFLOATVARAR: val.arrayLength = length; val.v.floatArray = malloc(length*sizeof(double)); if(val.v.floatArray != NULL){ memset(val.v.floatArray,0,length*sizeof(double)); } break; case HIPTEXT: val.v.text = strdup("UNKNOWN"); val.arrayLength = length; break; } return val; } /*------------------------------------------------------------------------*/ hdbValue makeHdbData(int datatype, int length, void *data){ hdbValue val; memset(&val,0,sizeof(hdbValue)); val.dataType = datatype; switch(datatype){ case HIPINT: if(data != NULL){ memcpy(&val.v.intValue,data,sizeof(int)); } break; case HIPFLOAT: if(data != NULL){ memcpy(&val.v.doubleValue,data,sizeof(double)); } break; case HIPINTAR: case HIPINTVARAR: val.arrayLength = length; val.v.intArray = malloc(length*sizeof(int)); if(val.v.intArray != NULL){ memset(val.v.intArray,0,length*sizeof(int)); } if(data != NULL){ memcpy(val.v.intArray,data,length*sizeof(int)); } break; case HIPFLOATAR: case HIPFLOATVARAR: val.arrayLength = length; val.v.floatArray = malloc(length*sizeof(double)); if(val.v.floatArray != NULL){ memset(val.v.floatArray,0,length*sizeof(double)); } if(data != NULL){ memcpy(val.v.floatArray,data,length*sizeof(double)); } break; case HIPTEXT: if(data != NULL){ val.v.text = strdup((char *)data); } else { val.v.text = strdup("UNKNOWN"); } val.arrayLength = strlen(val.v.text); break; case HIPOBJ: val.v.obj = data; break; case HIPFUNC: val.v.obj = data; break; } return val; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbInt(int initValue){ hdbValue result; result.dataType = HIPINT; result.arrayLength = 1; result.v.intValue = initValue; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbFloat(double initValue){ hdbValue result; result.dataType = HIPFLOAT; result.arrayLength = 1; result.v.doubleValue = initValue; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbText(char *initText){ hdbValue result; result.dataType = HIPTEXT; result.v.text = initText; result.arrayLength = strlen(initText); return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbIntArray(int length, int *data){ hdbValue result; result.dataType = HIPINTAR; result.arrayLength = length; result.v.intArray = data; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbFloatArrray(int length, double *data){ hdbValue result; result.dataType = HIPFLOATAR; result.arrayLength = length; result.v.floatArray = data; return result; } /*-------------------------------------------------------------------------*/ void ReleaseHdbValue(hdbValue *v){ switch(v->dataType){ case HIPTEXT: if(v->v.text != NULL){ free(v->v.text); } break; case HIPINTAR: case HIPINTVARAR: if(v->v.intArray != NULL){ free(v->v.intArray); } break; case HIPFLOATAR: case HIPFLOATVARAR: if(v->v.floatArray != NULL){ free(v->v.floatArray); } break; } } /*------------------------------------------------------------------------*/ int compareHdbValue(hdbValue v1, hdbValue v2){ int i; if(v1.dataType != v2.dataType){ return 0; } switch(v1.dataType){ case HIPNONE: return 0; break; case HIPINT: if(v1.v.intValue == v2.v.intValue){ return 1; } else { return 0; } break; case HIPFLOAT: if(ABS(v1.v.doubleValue - v2.v.doubleValue) < .01){ return 1; } else { return 0; } break; case HIPTEXT: if(v1.v.text == NULL || v2.v.text == NULL){ return 0; } if(strcmp(v1.v.text,v2.v.text) == 0){ return 1; } else { return 0; } break; case HIPINTAR: case HIPINTVARAR: if(v1.arrayLength != v2.arrayLength){ return 0; } if(v1.v.intArray == NULL || v2.v.intArray == NULL){ return 0; } for(i = 0; i < v1.arrayLength; i++){ if(v1.v.intArray[i] != v2.v.intArray[i]){ return 0; } } return 1; break; case HIPFLOATAR: case HIPFLOATVARAR: if(v1.arrayLength != v2.arrayLength){ return 0; } if(v1.v.floatArray == NULL || v2.v.floatArray == NULL){ return 0; } for(i = 0; i < v1.arrayLength; i++){ if(ABS(v1.v.floatArray[i] - v2.v.floatArray[i]) > .01){ return 0; } } return 1; break; case HIPOBJ: case HIPFUNC: if(v2.v.obj == v1.v.obj) { return 1; } else { return 0; } break; default: assert(0); break; } return 0; } /*-------------------------------------------------------------------------*/ int cloneHdbValue(hdbValue *source, hdbValue *clone){ memset(clone,0,sizeof(hdbValue)); clone->dataType = source->dataType; return copyHdbValue(source, clone); } /*-------------------------------------------------------------------------*/ int getHdbValueLength(hdbValue v){ int length = 0; switch(v.dataType){ case HIPNONE: break; case HIPINT: length = sizeof(int); break; case HIPFLOAT: length = sizeof(double); break; case HIPINTAR: case HIPINTVARAR: length = v.arrayLength * sizeof(int); break; case HIPFLOATAR: case HIPFLOATVARAR: length = v.arrayLength * sizeof(double); break; case HIPTEXT: length = strlen(v.v.text); break; case HIPOBJ: case HIPFUNC: length = sizeof(void *); break; } return length; } /*================= node functions ========================================*/ pHdb MakeHipadabaNode(char *name, int datatype, int length){ pHdb pNew = NULL; pNew = malloc(sizeof(Hdb)); if(pNew == NULL){ return NULL; } memset(pNew,0,sizeof(Hdb)); pNew->magic = HDBMAGICK; pNew->name = strdup(name); pNew->value.dataType = datatype; pNew->properties = CreateStringDict(); if(pNew->properties == NULL || pNew->name == NULL){ return NULL; } switch(datatype){ case HIPINTAR: case HIPINTVARAR: pNew->value.arrayLength = length; pNew->value.v.intArray = malloc(length*sizeof(int)); if(pNew->value.v.intArray == NULL){ return NULL; } memset(pNew->value.v.intArray,0,length*sizeof(int)); break; case HIPFLOATAR: case HIPFLOATVARAR: pNew->value.arrayLength = length; pNew->value.v.floatArray = malloc(length*sizeof(double)); if(pNew->value.v.floatArray == NULL){ return NULL; } memset(pNew->value.v.floatArray,0,length*sizeof(double)); break; case HIPTEXT: pNew->value.arrayLength = length; pNew->value.v.text = strdup("UNKNOWN"); break; } return pNew; } /*-------------------------------------------------------------------------*/ void AddHipadabaChild(pHdb parent, pHdb child, void *callData){ pHdb current = NULL, prev = NULL; assert(parent != NULL); if(child == NULL){ return; } current = parent->child; child->mama = parent; if(current == NULL){ parent->child = child; child->next = NULL; } else { /* * step to end of child chain */ while(current != NULL){ prev = current; current = current->next; } child->next = NULL; prev->next = child; } InvokeCallbackChain(parent->treeChangeCallbacks, parent,callData,parent->value); } /*--------------------------------------------------------------------------*/ void DeleteHipadabaNode(pHdb node, void *callData){ pHdb current = NULL, tmp = NULL; if(node == NULL){ return; } RemoveHdbNodeFromParent(node, callData); DeleteNodeData(node); } /*--------------------------------------------------------------------------*/ int isHdbNodeValid(pHdb node){ if(node == NULL){ return 0; } if(node->magic == HDBMAGICK){ return 1; } else { return 0; } } /*--------------------------------------------------------------------------*/ pHdb GetHipadabaNode(pHdb root, char *puth){ pHdb resultNode = NULL; char *separator = NULL; char *path = NULL, *pathData; /* * I need to make a copy in order to get the path in writable memory. * Otherwise we SEGFAULT in hdbTrim when this function is called with * a string constant in puth */ pathData = strdup(puth); path = pathData; if(path == NULL){ return NULL; } path = hdbTrim(path); if(strcmp(path,"/") == 0 || strlen(path) == 0){ free(pathData); return root; } if(path[0] == '/'){ path++; } separator = strchr(path,'/'); if(separator == NULL){ resultNode = locateChild(root,path); free(pathData); return resultNode; } else { *separator = '\0'; resultNode = locateChild(root, path); if(resultNode == NULL){ free(pathData); return NULL; } else { separator++; resultNode = GetHipadabaNode(resultNode,separator); free(pathData); return resultNode; } } } /*--------------------------------------------------------------------------*/ char *GetHipadabaPath(pHdb node){ pHdb nodeStack[64]; int depth = 0, length = 0, i; pHdb current = NULL; char *pPtr = NULL; /** * build a nodestack and find out required string length for path */ current = node; while(current != NULL){ length += strlen(current->name) + 1; nodeStack[depth] = current; depth++; assert(depth < 64); current = current->mama; } pPtr = malloc(length*sizeof(char)); if(pPtr == NULL){ return NULL; } memset(pPtr,0,length*sizeof(char)); /* * we wish to decremement by one because above loop * increments one to many and we wish to ignore the * root node */ for(i = depth - 2; i >= 0; i--){ strcat(pPtr,"/"); strcat(pPtr,nodeStack[i]->name); } return pPtr; } /*==================== Callback Functions ==================================*/ pHdbCallback MakeHipadabaCallback(hdbCallbackFunction func, void *userData, killUserData killFunc, int id, int internalID){ pHdbCallback pNew = NULL; assert(func != NULL); pNew = malloc(sizeof(hdbCallback)); if(pNew == NULL){ return NULL; } memset(pNew,0,sizeof(hdbCallback)); pNew->userCallback = func; pNew->userData = userData; pNew->killFunc = killFunc; pNew->id = id; pNew->internalID = internalID; return pNew; } /*-------------------------------------------------------------------*/ void AppendHipadabaCallback(pHdb node, int type, pHdbCallback newCB){ pHdbCallback current = NULL; switch(type){ case HCBSET: if(node->writeCallbacks == NULL){ node->writeCallbacks = newCB; return; } else { current = node->writeCallbacks; } break; case HCBUPDATE: if(node->updateCallbacks == NULL){ node->updateCallbacks = newCB; return; } else { current = node->updateCallbacks; } break; case HCBREAD: if(node->readCallbacks == NULL){ node->readCallbacks = newCB; return; } else { current = node->readCallbacks; } break; case HCBTREE: if(node->treeChangeCallbacks == NULL){ node->treeChangeCallbacks = newCB; return; } else { current = node->treeChangeCallbacks; } break; default: assert(0); break; } if(current != NULL){ while(current->next != NULL){ current = (pHdbCallback)current->next; } current->next= newCB; newCB->previous = current; } } /*---------------------------------------------------------------------------*/ void PrependHipadabaCallback(pHdb node, int type, pHdbCallback newCB){ switch(type){ case HCBSET: if(node->writeCallbacks == NULL){ node->writeCallbacks = newCB; return; } else { newCB->next = node->writeCallbacks; node->writeCallbacks->previous = newCB; node->writeCallbacks = newCB; } break; case HCBUPDATE: if(node->updateCallbacks == NULL){ node->updateCallbacks = newCB; return; } else { newCB->next = node->updateCallbacks; node->updateCallbacks->previous = newCB; node->updateCallbacks = newCB; } break; case HCBREAD: if(node->readCallbacks == NULL){ node->readCallbacks = newCB; return; } else { newCB->next = node->readCallbacks; node->readCallbacks->previous = newCB; node->readCallbacks = newCB; } break; case HCBTREE: if(node->treeChangeCallbacks == NULL){ node->treeChangeCallbacks = newCB; return; } else { newCB->next = node->treeChangeCallbacks; node->treeChangeCallbacks->previous = newCB; node->treeChangeCallbacks = newCB; } break; default: assert(0); break; } } /*----------------------------------------------------------------------------*/ void RemoveHipadabaCallback(pHdb root, int id){ pHdb current = NULL; root->writeCallbacks = DeleteForID(root->writeCallbacks,id); root->updateCallbacks = DeleteForID(root->updateCallbacks,id); root->readCallbacks = DeleteForID(root->readCallbacks,id); root->treeChangeCallbacks = DeleteForID(root->treeChangeCallbacks,id); current = root->child; while(current != NULL){ RemoveHipadabaCallback(current,id); current = current->next; } } /*----------------------------------------------------------------------------*/ void InternalRemoveHipadabaCallback(pHdb root, int internalID){ pHdb current = NULL; root->writeCallbacks = DeleteForInternalID(root->writeCallbacks,internalID); root->updateCallbacks = DeleteForInternalID(root->updateCallbacks,internalID); root->readCallbacks = DeleteForInternalID(root->readCallbacks,internalID); root->treeChangeCallbacks = DeleteForInternalID(root->treeChangeCallbacks,internalID); current = root->child; while(current != NULL){ InternalRemoveHipadabaCallback(current,internalID); current = current->next; } } /*=================== parameter interface ====================================*/ int copyHdbValue(hdbValue *source, hdbValue *target){ int i; if(source->dataType != target->dataType){ return 0; } switch(source->dataType){ case HIPNONE: break; case HIPINT: target->v.intValue = source->v.intValue; break; case HIPFLOAT: target->v.doubleValue = source->v.doubleValue; break; case HIPTEXT: if(target->v.text != NULL){ free(target->v.text); } target->v.text = strdup(source->v.text); break; case HIPINTAR: case HIPINTVARAR: if(target->arrayLength != source->arrayLength || target->v.intArray == NULL){ if(target->v.intArray != NULL){ free(target->v.intArray); } target->v.intArray = malloc(source->arrayLength * sizeof(int)); if(target->v.intArray == NULL){ return 0; } memset(target->v.intArray,0,source->arrayLength * sizeof(int)); target->arrayLength = source->arrayLength; } if(source->v.intArray != NULL){ for(i = 0; i < source->arrayLength; i++){ target->v.intArray[i] = source->v.intArray[i]; } } break; case HIPFLOATAR: case HIPFLOATVARAR: if(target->arrayLength != source->arrayLength || target->v.floatArray == NULL){ if(target->v.floatArray != NULL){ free(target->v.floatArray); } target->v.floatArray = malloc(source->arrayLength * sizeof(double)); if(target->v.floatArray == NULL){ return 0; } memset(target->v.floatArray,0,source->arrayLength * sizeof(double)); target->arrayLength = source->arrayLength; } if(source->v.floatArray != NULL){ for(i = 0; i < source->arrayLength; i++){ target->v.floatArray[i] = source->v.floatArray[i]; } } break; case HIPOBJ: case HIPFUNC: target->v.obj = source->v.obj; break; default: /* * unknown data type */ assert(0); break; } return 1; } /*----------------------------------------------------------------------------*/ int SetHipadabaPar(pHdb node, hdbValue v, void *callData){ int status; status = InvokeCallbackChain(node->writeCallbacks, node, callData, v); return status; } /*-----------------------------------------------------------------------------*/ int UpdateHipadabaPar(pHdb node, hdbValue v, void *callData){ int status; status = InvokeCallbackChain(node->updateCallbacks, node, callData, v); if(status != 1 ){ return status; } copyHdbValue(&v,&node->value); return 1; } /*-----------------------------------------------------------------------------*/ int NotifyHipadabaPar(pHdb node,void *callData){ int status; status = InvokeCallbackChain(node->updateCallbacks, node, callData, node->value); if(status != 1 ){ return status; } return 1; } /*-----------------------------------------------------------------------------*/ int GetHipadabaPar(pHdb node, hdbValue *v, void *callData){ int status; status = InvokeCallbackChain(node->readCallbacks, node, callData, *v); if(status != 1 ){ return status; } v->dataType = node->value.dataType; copyHdbValue(&node->value,v); return 1; } /*----------------------------------------------------------------------------*/ static int calcDataLength(pHdb node, int testLength){ int length = 0; length = getHdbValueLength(node->value); if(node->value.dataType == HIPFLOATVARAR || node->value.dataType == HIPINTVARAR || node->value.dataType == HIPTEXT){ length = testLength; } return length; } /*--------------------------------------------------------------------------*/ int SetHdbPar(pHdb node, int dataType, void *data, int length, void *callData){ int status; hdbValue v; if(node->value.dataType == HIPNONE){ return 1; } if(dataType != node->value.dataType){ return HDBTYPEMISMATCH; } if(length != calcDataLength(node,length)){ return HDBLENGTHMISMATCH; } v = makeHdbData(dataType, length, data); status = InvokeCallbackChain(node->writeCallbacks, node, callData, v); if(status == 1) { copyHdbValue(&v,&node->value); } return status; } /*--------------------------------------------------------------------------*/ int UpdateHdbPar(pHdb node, int dataType, void *data, int length, void *callData){ int status; hdbValue v; if(node->value.dataType == HIPNONE){ return 1; } if(dataType != node->value.dataType){ return HDBTYPEMISMATCH; } if(length != calcDataLength(node,length)){ return HDBLENGTHMISMATCH; } v = makeHdbData(dataType,length,data); status = InvokeCallbackChain(node->updateCallbacks, node, callData, v); if(status == 1) { copyHdbValue(&v,&node->value); } return status; } /*-----------------------------------------------------------------------------*/ int GetHdbPar(pHdb node, int dataType, void *data, int length, void *callData){ int status, toCopy; hdbValue v; if(dataType != node->value.dataType){ return HDBTYPEMISMATCH; } if(length != calcDataLength(node,length)){ return HDBLENGTHMISMATCH; } status = InvokeCallbackChain(node->readCallbacks, node, callData, v); if(status != 1 ){ return status; } switch(dataType){ case HIPNONE: break; case HIPINT: memcpy(data,&node->value.v.intValue,sizeof(int)); break; case HIPFLOAT: memcpy(data,&node->value.v.doubleValue,sizeof(double)); break; case HIPINTAR: case HIPINTVARAR: memcpy(data,node->value.v.intArray, node->value.arrayLength*sizeof(int)); break; case HIPTEXT: toCopy = strlen(node->value.v.text); if(toCopy > length){ toCopy = length; } memcpy(data,&node->value.v.text, toCopy); break; case HIPFLOATAR: case HIPFLOATVARAR: memcpy(data,node->value.v.floatArray, node->value.arrayLength*sizeof(double)); break; case HIPOBJ: memcpy(data,&node->value.v.obj,sizeof(void *)); break; default: assert(0); break; } return 1; } /*============================= Property Functions ==========================*/ void SetHdbProperty(pHdb node, char *key, char *value){ if(node != NULL && key != NULL && node->properties != NULL){ if(StringDictExists(node->properties, key)){ StringDictUpdate(node->properties,key,value); } else { StringDictAddPair(node->properties,key,value); } } } /*---------------------------------------------------------------------------*/ int GetHdbProperty(pHdb node, char *key, char *value, int len){ if(node != NULL && node->properties != NULL){ return StringDictGet(node->properties,key,value,len); } else { return 0; } } /*---------------------------------------------------------------------------*/ void InitHdbPropertySearch(pHdb node){ if(node != NULL && node->properties != NULL){ StringDictKillScan(node->properties); } } /*--------------------------------------------------------------------------*/ const char *GetNextHdbProperty(pHdb node, char *value ,int len){ if(node != NULL && node->properties != NULL) { return StringDictGetNext(node->properties, value, len); } else { return NULL; } }