/*------------------------------------------------------------------------- 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); } } /*-----------------------------------------------------------------------*/ static void DeleteNodeData(pHdb node){ pHdb tmp = NULL; if(node == NULL){ return; } DeleteCallbackChain(node->writeCallbacks); DeleteCallbackChain(node->updateCallbacks); DeleteCallbackChain(node->readCallbacks); 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); } /*------------------------------------------------------------------------*/ void RemoveHdbNodeFromParent(pHdb node){ 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; } } /*-----------------------------------------------------------------------*/ 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; } /*-------------------------------------------------------------------------*/ static 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; } /*----------------------------------------------------------------------*/ 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 MakeHdbInt(int initValue){ hdbValue result; result.dataType = HIPINT; result.v.intValue = initValue; return result; } /*-------------------------------------------------------------------------*/ hdbValue MakeHdbFloat(double initValue){ hdbValue result; result.dataType = HIPFLOAT; 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(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; } 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; } for(i = 0; i < v1.arrayLength; i++){ if(ABS(v1.v.floatArray[i] - v2.v.floatArray[i]) > .01){ return 0; } } return 1; 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); } /*================= 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; 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){ 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; } } /*--------------------------------------------------------------------------*/ void DeleteHipadabaNode(pHdb node){ pHdb current = NULL, tmp = NULL; if(node == NULL){ return; } RemoveHdbNodeFromParent(node); 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; 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; 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); 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); 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){ 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; } 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){ 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; } for(i = 0; i < source->arrayLength; i++){ target->v.floatArray[i] = source->v.floatArray[i]; } 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 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; }