/*------------------------------------------------------------------------- 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 /*================== Message Stuff ========================================*/ static char set[] = {"set"}; static char get[] = {"get"}; static char update[] = {"update"}; static char treeChange[] = {"treeChange"}; static char dataSearch[] = {"dataSearch"}; static char killNode[] = {"killNode"}; /*------------------------------------------------------------------------*/ pHdbDataMessage GetHdbSetMessage(pHdbMessage toTest){ if(toTest->type == set){ return (pHdbDataMessage)toTest; } return NULL; } /*------------------------------------------------------------------------*/ pHdbDataMessage GetHdbGetMessage(pHdbMessage toTest){ if(toTest->type == get){ return (pHdbDataMessage)toTest; } return NULL; } /*------------------------------------------------------------------------*/ pHdbDataMessage GetHdbUpdateMessage(pHdbMessage toTest){ if(toTest->type == update){ return (pHdbDataMessage)toTest; } return NULL; } /*-------------------------------------------------------------------------*/ pHdbTreeChangeMessage GetHdbTreeChangeMessage(pHdbMessage toTest){ if(toTest->type == treeChange){ return (pHdbTreeChangeMessage)toTest; } return NULL; } /*-------------------------------------------------------------------------*/ pHdbDataSearch GetHdbDataSearchMessage(pHdbMessage toTest){ if(toTest->type == dataSearch){ return (pHdbDataSearch)toTest; } return NULL; } /*-------------------------------------------------------------------------*/ pHdbMessage GetHdbKillNodeMessage(pHdbMessage toTest){ if(toTest->type == killNode){ return toTest; } return NULL; } /*================== 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 RecurseCallbackChains(pHdb node, pHdbMessage message){ pHdb current = NULL; InvokeCallbackChain(node,message); current = node->child; while(current != NULL){ RecurseCallbackChains(current,message); current = current->next; } } /*-----------------------------------------------------------------------*/ void DeleteNodeData(pHdb node){ pHdb tmp = NULL; if(node == NULL){ return; } DeleteCallbackChain(node->callBackChain); 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); } /*------------------------------------------------------------------------*/ static pHdbCallback CleanCallbackChain(pHdbCallback head){ pHdbCallback current = head; pHdbCallback next; pHdbCallback *ptr2last = &head; while(current != NULL){ if(current->killFlag == 1){ next = current->next; /* * unlink */ *ptr2last = next; /* * delete */ if(current->killFunc != NULL){ current->killFunc(current->userData); } free(current); /* * move on */ current = next; } else { ptr2last = ¤t->next; current = current->next; } } return head; } /*-------------------------------------------------------------------------*/ int InvokeCallbackChain(pHdb node, pHdbMessage message){ pHdbCallback current = node->callBackChain; hdbCallbackReturn status; int killFlag = 0; while(current != NULL){ status = current->userCallback(node, current->userData,message); switch(status){ case hdbAbort: return 0; break; case hdbKill: current->killFlag = 1; killFlag = 1; break; case hdbContinue: break; } current = current->next; } if(killFlag == 1){ node->callBackChain = CleanCallbackChain(node->callBackChain); } return 1; } /*-----------------------------------------------------------------------*/ static void SendTreeChangeMessage(pHdb node, void *callData){ hdbTreeChangeMessage treeChangeMes; treeChangeMes.type = treeChange; treeChangeMes.callData = callData; InvokeCallbackChain(node, (pHdbMessage)&treeChangeMes); } /*------------------------------------------------------------------------*/ void RemoveHdbNodeFromParent(pHdb node, void *callData){ pHdb parent = NULL; pHdb current = NULL; hdbTreeChangeMessage treeChangeMes; parent = node->mama; if(parent != NULL){ if(parent->child == node){ parent->child = node->next; } else { current = parent->child; while(current->next != node){ current = current->next; } current->next = current->next->next; } SendTreeChangeMessage(parent,callData); node->mama = NULL; } } /*----------------------------------------------------------------------*/ int CountHdbChildren(pHdb node){ int count = 0; pHdb current = NULL; current = node->child; while(current != NULL){ current = current->next; count++; } return count; } /*----------------------------------------------------------------------*/ 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; val.doNotFree = 0; 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 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; /* no strdup here ! */ 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 MakeHdbFloatArray(int length, double *data){ hdbValue result; result.dataType = HIPFLOATAR; result.arrayLength = length; result.v.floatArray = data; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbFunc(voidFunc *func){ hdbValue result; result.dataType = HIPFUNC; result.v.func = func; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbObj(void *obj){ hdbValue result; result.dataType = HIPOBJ; result.v.obj = obj; return result; } /*-------------------------------------------------------------------------*/ void ReleaseHdbValue(hdbValue *v){ if(v->doNotFree == 1){ return; } 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: if(v2.v.obj == v1.v.obj) { return 1; } else { return 0; } break; case HIPFUNC: if(v2.v.func == v1.v.func) { return 1; } else { return 0; } break; default: assert(0); break; } return 0; } /*-------------------------------------------------------------------------*/ int cloneHdbValue(hdbValue *source, hdbValue *clone){ memset(clone,0,sizeof(hdbValue)); clone->v.text = NULL; /* this sets all pointers in the union to NULL */ 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: length = sizeof(void *); break; case HIPFUNC: length = sizeof(voidFunc *); 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; } SendTreeChangeMessage(parent,callData); } /*--------------------------------------------------------------------------*/ void DeleteHipadabaNode(pHdb node, void *callData){ pHdb current = NULL, tmp = NULL; hdbMessage killNodeMsg; if(node == NULL){ return; } killNodeMsg.type = killNode; InvokeCallbackChain(node, &killNodeMsg); 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){ 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; return pNew; } /*-------------------------------------------------------------------*/ void AppendHipadabaCallback(pHdb node, pHdbCallback newCB){ pHdbCallback current = NULL; assert(node); current = node->callBackChain; newCB->next = NULL; if(current == NULL){ node->callBackChain = newCB; return; } while(current->next != NULL){ current = (pHdbCallback)current->next; } current->next = newCB; } /*---------------------------------------------------------------------------*/ void PrependHipadabaCallback(pHdb node,pHdbCallback newCB){ assert(node != NULL); newCB->next = node->callBackChain; node->callBackChain = newCB; } /*----------------------------------------------------------------------------*/ void *FindHdbCallbackData(pHdb node, void *userPtr){ hdbDataSearch dsm; dsm.type = dataSearch; dsm.testPtr = userPtr; dsm.result = NULL; InvokeCallbackChain(node, (pHdbMessage)&dsm); return dsm.result; } /*=================== parameter interface ====================================*/ static int canCopy(hdbValue *source, hdbValue *target){ if(target->dataType == HIPINTVARAR) { if(source->dataType == HIPINTAR || source->dataType == HIPINTVARAR){ return 1; } } if(target->dataType == HIPFLOATVARAR) { if(source->dataType == HIPFLOATAR || source->dataType == HIPFLOATVARAR){ return 1; } } if(source->dataType != target->dataType){ return 0; } else { return 1; } } /*----------------------------------------------------------------------------*/ int copyHdbValue(hdbValue *source, hdbValue *target){ int i; if(!canCopy(source,target)){ 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: target->v.obj = source->v.obj; break; case HIPFUNC: target->v.func = source->v.func; break; default: /* * unknown data type */ assert(0); break; } return 1; } /*---------------------------------------------------------------------------*/ static int SendDataMessage(pHdb node, char *type, hdbValue v, void *callData){ hdbDataMessage dataMes; assert(type == set || type == get || type == update); dataMes.type = type; dataMes.v = &v; dataMes.callData = callData; return InvokeCallbackChain(node, (pHdbMessage)&dataMes); } /*----------------------------------------------------------------------------*/ int SetHipadabaPar(pHdb node, hdbValue v, void *callData){ return SendDataMessage(node, set, v,callData); } /*-----------------------------------------------------------------------------*/ int UpdateHipadabaPar(pHdb node, hdbValue v, void *callData){ int status; status = SendDataMessage(node, update, v,callData); if(status == 1){ copyHdbValue(&v,&node->value); } return status; } /*-----------------------------------------------------------------------------*/ int NotifyHipadabaPar(pHdb node,void *callData){ SendDataMessage(node, update, node->value,callData); return 1; } /*-----------------------------------------------------------------------------*/ int GetHipadabaPar(pHdb node, hdbValue *v, void *callData){ int status; v->dataType = node->value.dataType; v->doNotFree = 0; v->v.text = NULL; /* this sets all pointers in the union to NULL */ status = SendDataMessage(node, get, *v,callData); if(status != 1 ){ return status; } 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; } /*============================= Property Functions ==========================*/ void SetHdbProperty(pHdb node, char *key, char *value){ if(node != NULL && key != NULL && node->properties != NULL){ if (value == NULL) { StringDictDelete(node->properties, key); } else 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; } } /*---------------------------------------------------------------------------*/ char *GetHdbProp(pHdb node, char *key){ if(node != NULL && node->properties != NULL){ return StringDictGetShort(node->properties,key); } else { return NULL; } } /*---------------------------------------------------------------------------*/ 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; } }