- use dig for resolving host names - ascon.c: fix terminator parsing - property callback: change property before callback - logger.c:default for logger period must be the old value instead of 1 - add frappy type history writing - increase max. logreader line length - HIPNONE returns "null" with json protocol - encode strings properly in formatNameValue - fix memory leak in json2tcl - scriptcontext: do not show debug messages when script starts with underscore or when the "send" property is empty - scriptcontext: remove args for action timestamp - scriptcontext: "que" function will replace an already queued action, e.g. for 'halt - introduced updatestatus script
1221 lines
28 KiB
C
1221 lines
28 KiB
C
/*-------------------------------------------------------------------------
|
|
The hierarchical parameter database code. For more information, see
|
|
hipadaba.h
|
|
|
|
copyright: GPL
|
|
|
|
Mark Koennecke, June 2006
|
|
---------------------------------------------------------------------------*/
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#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" };
|
|
static char propertyChange[] = { "propertyChange" };
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
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;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
pHdbPropertyChange GetPropertyChangeMessage(pHdbMessage toTest)
|
|
{
|
|
if (toTest->type == propertyChange) {
|
|
return (pHdbPropertyChange)toTest;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*================== internal functions ===================================*/
|
|
void DeleteCallbackChain(pHdb node)
|
|
{
|
|
pHdbCallback current = NULL, thisEntry;
|
|
hdbMessage killNodeMsg;
|
|
|
|
killNodeMsg.type = killNode;
|
|
InvokeCallbackChain(node, &killNodeMsg);
|
|
|
|
current = node->callBackChain;
|
|
node->callBackChain = NULL;
|
|
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, next = NULL;
|
|
|
|
if (node == NULL) {
|
|
return;
|
|
}
|
|
|
|
DeleteCallbackChain(node);
|
|
if (node->properties != NULL) {
|
|
DeleteStringDict(node->properties);
|
|
}
|
|
|
|
if (node->name != NULL) {
|
|
free(node->name);
|
|
}
|
|
if (node->path != NULL) {
|
|
free(node->path);
|
|
}
|
|
ReleaseHdbValue(&node->value);
|
|
node->magic = 000000;
|
|
|
|
while (node->child != NULL) {
|
|
tmp = node->child;
|
|
next = node->child->next;
|
|
DeleteNodeData(tmp);
|
|
node->child = next;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
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;
|
|
|
|
if(root == NULL){
|
|
return 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;
|
|
result.doNotFree = 0;
|
|
return result;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
hdbValue MakeHdbFloat(double initValue)
|
|
{
|
|
hdbValue result;
|
|
|
|
result.dataType = HIPFLOAT;
|
|
result.arrayLength = 1;
|
|
result.v.doubleValue = initValue;
|
|
result.doNotFree = 0;
|
|
return result;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
hdbValue MakeHdbText(char *initText)
|
|
{
|
|
hdbValue result;
|
|
|
|
result.dataType = HIPTEXT;
|
|
result.v.text = initText; /* no strdup here ! */
|
|
result.arrayLength = strlen(initText);
|
|
result.doNotFree = 0;
|
|
return result;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
hdbValue MakeHdbIntArray(int length, int *data)
|
|
{
|
|
hdbValue result;
|
|
|
|
result.dataType = HIPINTAR;
|
|
result.arrayLength = length;
|
|
result.v.intArray = data;
|
|
result.doNotFree = 0;
|
|
return result;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
hdbValue MakeHdbFloatArray(int length, double *data)
|
|
{
|
|
hdbValue result;
|
|
|
|
result.dataType = HIPFLOATAR;
|
|
result.arrayLength = length;
|
|
result.v.floatArray = data;
|
|
result.doNotFree = 0;
|
|
return result;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
hdbValue MakeHdbFunc(voidFunc * func)
|
|
{
|
|
hdbValue result;
|
|
|
|
result.dataType = HIPFUNC;
|
|
result.v.func = func;
|
|
result.doNotFree = 0;
|
|
return result;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
hdbValue MakeHdbObj(void *obj)
|
|
{
|
|
hdbValue result;
|
|
|
|
result.dataType = HIPOBJ;
|
|
result.v.obj = obj;
|
|
result.doNotFree = 0;
|
|
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;
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------------------------*/
|
|
static unsigned short fletcher16( char *data, size_t len)
|
|
{
|
|
unsigned short sum1 = 0xff, sum2 = 0xff, result;
|
|
unsigned char checkA, checkB;
|
|
|
|
if(data == NULL){
|
|
return 0;
|
|
}
|
|
while (len) {
|
|
size_t tlen = len > 21 ? 21 : len;
|
|
len -= tlen;
|
|
do {
|
|
sum1 += *data++;
|
|
sum2 += sum1;
|
|
} while (--tlen);
|
|
sum1 = (sum1 & 0xff) + (sum1 >> 8);
|
|
sum2 = (sum2 & 0xff) + (sum2 >> 8);
|
|
}
|
|
/* Second reduction step to reduce sums to 8 bits */
|
|
sum1 = (sum1 & 0xff) + (sum1 >> 8);
|
|
sum2 = (sum2 & 0xff) + (sum2 >> 8);
|
|
checkA = (unsigned char)sum1;
|
|
checkB = (unsigned char)sum2;
|
|
result = checkA;
|
|
result = result << 8 | checkB;
|
|
return result ;
|
|
}
|
|
#define MAXLEN 65536
|
|
/*------------------------------------------------------------------------*/
|
|
static unsigned short longfletcher16(char *data, size_t len)
|
|
{
|
|
char buffer[MAXLEN];
|
|
int i, j, div, count;
|
|
char *pPtr;
|
|
|
|
if(len < MAXLEN){
|
|
return fletcher16(data,len);
|
|
}
|
|
|
|
/**
|
|
* sum together to run the more complex checksum on
|
|
* more juicy data
|
|
*/
|
|
div = (int)trunc((float)len/(float)MAXLEN);
|
|
for(i = 0; i < MAXLEN; i++){
|
|
pPtr = data + div*i;
|
|
for(j = 0; j < div; j++){
|
|
buffer[i] += *(pPtr + j);
|
|
}
|
|
}
|
|
return fletcher16(buffer,MAXLEN);
|
|
}
|
|
/*------------------------------------------------------------------------*/
|
|
unsigned short getHdbCheckSum(hdbValue *val)
|
|
{
|
|
char *data;
|
|
size_t len;
|
|
|
|
len = getHdbValueLength(*val);
|
|
/*
|
|
if(len > MAXLEN){
|
|
len = MAXLEN;
|
|
}
|
|
*/
|
|
switch (val->dataType) {
|
|
case HIPNONE:
|
|
return 0;
|
|
break;
|
|
case HIPINT:
|
|
data = (char *)&val->v.intValue;
|
|
return fletcher16(data,len);
|
|
break;
|
|
case HIPFLOAT:
|
|
data = (char *)&val->v.doubleValue;
|
|
return fletcher16(data,len);
|
|
break;
|
|
case HIPTEXT:
|
|
data = val->v.text;
|
|
return fletcher16(data,len);
|
|
break;
|
|
case HIPINTAR:
|
|
case HIPINTVARAR:
|
|
data = (char *)val->v.intArray;
|
|
return fletcher16(data,len);
|
|
break;
|
|
case HIPFLOATAR:
|
|
case HIPFLOATVARAR:
|
|
data = (char *)val->v.floatArray;
|
|
return fletcher16(data,len);
|
|
break;
|
|
case HIPOBJ:
|
|
data = (char *)val->v.obj;
|
|
return fletcher16(data,len);
|
|
break;
|
|
case HIPFUNC:
|
|
data = (char *)val->v.func;
|
|
return fletcher16(data,len);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
/*------------------------------------------------------------------------*/
|
|
int compareHdbValue(hdbValue v1, hdbValue v2)
|
|
{
|
|
int i;
|
|
|
|
if (v1.dataType != v2.dataType) {
|
|
return 0;
|
|
}
|
|
|
|
if((v1.doNotFree == 1 && v1.v.obj == NULL)
|
|
|| (v2.doNotFree == 1 && v2.v.obj == NULL)){
|
|
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) < .0001) { /* DFC */
|
|
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;
|
|
|
|
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);
|
|
}
|
|
node->path = pPtr;
|
|
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 == HIPINTAR &&
|
|
(source->dataType == HIPINTAR || source->dataType == HIPINTVARAR)
|
|
&& target->arrayLength == source->arrayLength){
|
|
return 1;
|
|
}
|
|
if (target->dataType == HIPFLOATVARAR) {
|
|
if (source->dataType == HIPFLOATAR ||
|
|
source->dataType == HIPFLOATVARAR) {
|
|
return 1;
|
|
}
|
|
}
|
|
if(target->dataType == HIPFLOATAR &&
|
|
(source->dataType == HIPFLOATAR || source->dataType == HIPFLOATVARAR)
|
|
&& target->arrayLength == source->arrayLength){
|
|
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];
|
|
}
|
|
*/
|
|
memcpy(target->v.intArray,source->v.intArray,source->arrayLength*sizeof(int));
|
|
}
|
|
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;
|
|
unsigned short checksum;
|
|
|
|
memset(v,0,sizeof(hdbValue));
|
|
v->dataType = node->value.dataType;
|
|
|
|
checksum = getHdbCheckSum(&node->value);
|
|
status = SendDataMessage(node, get, v, callData);
|
|
copyHdbValue(&node->value, v);
|
|
if(getHdbCheckSum(&node->value) != checksum){
|
|
NotifyHipadabaPar(node, callData);
|
|
}
|
|
if (status != 1) {
|
|
return status;
|
|
}
|
|
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)
|
|
{
|
|
hdbPropertyChange propMes;
|
|
|
|
if (node != NULL && key != NULL && node->properties != NULL) {
|
|
propMes.type = propertyChange;
|
|
propMes.key = key;
|
|
propMes.value = value;
|
|
InvokeCallbackChain(node,(pHdbMessage)&propMes);
|
|
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 HasHdbProperty(pHdb node, char *key)
|
|
{
|
|
if (node != NULL && node->properties != NULL) {
|
|
return StringDictExists(node->properties, key);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
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 && isHdbNodeValid(node) && 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;
|
|
}
|
|
}
|