vdr-plugin-skindesigner/coreengine/complextypes.c

1606 lines
56 KiB
C

#include "complextypes.h"
#include "../config.h"
/******************************************************************
* helpers
******************************************************************/
char *RemoveSpace(char *e) {
if (!e)
return e;
int numSpaces = 0;
int exprLen = strlen(e);
for (int i = 0; i < exprLen; ++i) {
if (isspace(e[i]))
++numSpaces;
}
if (numSpaces == 0)
return e;
char *replaced = (char*)malloc(exprLen - numSpaces + 1);
replaced[exprLen - numSpaces] = '\0';
int j = 0;
for (int i = 0; i < exprLen; ++i) {
if (!isspace(e[i])){
replaced[j++] = e[i];
}
}
free(e);
return replaced;
}
void ReplaceDecimalpoint(char *e) {
int size = (int)strlen(e);
for ( int i = 0; i < size; ++i ) {
if (e[i] == '.')
e[i] = config.decPoint;
}
}
void ReplaceStart(char *e, int num) {
int size = (int)strlen(e);
if (size <= num)
return;
for ( int i = 0; i < size; ++i ) {
if (i < size - num)
e[i] = e[i+num];
else e[i] = 0;
}
}
void ReplaceEnd(char *e, int num) {
int size = (int)strlen(e) - 1;
if (size <= num)
return;
for ( int i = size; i > size-num; --i ) {
e[i] = 0;
}
}
/******************************************************************
* cCond
******************************************************************/
cCond::cCond(const char *expression) {
this->expr = strdup(expression);
operation = eCondOp::tAnd;
type = eCondType::token;
constant = false;
isTrue = false;
tokenIndex = -1;
compareValue = -1;
compareStrValue = NULL;
}
cCond::cCond(const cCond &other) {
expr = strdup(other.expr);
operation = other.operation;
type = other.type;
constant = other.constant;
tokenType = other.tokenType;
isTrue = other.isTrue;
tokenIndex = other.tokenIndex;
compareValue = other.compareValue;
compareStrValue = NULL;
if (other.compareStrValue)
compareStrValue = strdup(other.compareStrValue);
}
cCond::~cCond(void) {
free(expr);
free(compareStrValue);
}
void cCond::Debug(void) {
esyslog("skindesigner: cond %s, operation %s, type %d", expr,
(operation == eCondOp::tAnd) ? "++" : "||",
(int)type);
if (constant)
esyslog("skindesigner: constant cond: %s", isTrue ? "TRUE" : "FALSE");
if (tokenIndex >= 0)
esyslog("skindesigner: token index: %d", tokenIndex);
if (compareValue >= 0)
esyslog("skindesigner: compare value: %d", compareValue);
if (compareStrValue)
esyslog("skindesigner: compare string value: %d", compareStrValue);
}
/******************************************************************
* cCondition
******************************************************************/
cCondition::cCondition(const char *expression) {
expr = strdup(expression);
globals = NULL;
tokenContainer = NULL;
loopInfo = NULL;
}
cCondition::cCondition(const cCondition &other) {
expr = strdup(other.expr);
globals = NULL;
tokenContainer = NULL;
loopInfo = NULL;
for (cCond *cond = other.conds.First(); cond; cond = other.conds.Next(cond))
conds.Add(new cCond(*cond));
}
cCondition::~cCondition(void) {
free(expr);
}
void cCondition::Prepare(void) {
expr = RemoveSpace(expr);
Tokenize();
PrepareTokens();
}
bool cCondition::True(void) {
if (conds.Count() == 0)
return true;
bool ok = true;
for (cCond *c = conds.First(); c; c = conds.Next(c)) {
bool condTrue = true;
//evaluate condition
if (c->constant)
{
condTrue = c->isTrue;
}
else if (c->type == eCondType::token)
{
if (c->tokenType == eCondTokenType::inttoken) {
int tokenVal = tokenContainer->IntToken(c->tokenIndex);
condTrue = (tokenVal > 0) ? true : false;
} else if (c->tokenType == eCondTokenType::stringtoken) {
char *tokenVal = tokenContainer->StringToken(c->tokenIndex);
if (tokenVal)
condTrue = (!strcmp(tokenVal, "1")) ? true : false;
} else if (c->tokenType == eCondTokenType::looptoken) {
if (loopInfo && loopInfo->row >= 0) {
char *tokenVal = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, c->tokenIndex);
if (tokenVal)
condTrue = (!strcmp(tokenVal, "1")) ? true : false;
}
}
}
else if (c->type == eCondType::negtoken)
{
if (c->tokenType == eCondTokenType::inttoken) {
int tokenVal = tokenContainer->IntToken(c->tokenIndex);
condTrue = (tokenVal > 0) ? false : true;
} else if (c->tokenType == eCondTokenType::stringtoken) {
char *tokenVal = tokenContainer->StringToken(c->tokenIndex);
if (tokenVal)
condTrue = (!strcmp(tokenVal, "1")) ? false : true;
} else if (c->tokenType == eCondTokenType::looptoken) {
if (loopInfo && loopInfo->row >= 0) {
char *tokenVal = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, c->tokenIndex);
if (tokenVal)
condTrue = (!strcmp(tokenVal, "1")) ? false : true;
}
}
}
else if (c->type == eCondType::lowerInt || c->type == eCondType::equalInt || c->type == eCondType::greaterInt)
{
if (c->tokenType == eCondTokenType::inttoken) {
int tokenVal = tokenContainer->IntToken(c->tokenIndex);
if (c->type == eCondType::lowerInt)
condTrue = (tokenVal < c->compareValue) ? true : false;
else if (c->type == eCondType::equalInt)
condTrue = (tokenVal == c->compareValue) ? true : false;
else if (c->type == eCondType::greaterInt)
condTrue = (tokenVal > c->compareValue) ? true : false;
} else if (c->tokenType == eCondTokenType::stringtoken) {
char *tokenVal = tokenContainer->StringToken(c->tokenIndex);
if (tokenVal) {
int intVal = atoi(tokenVal);
if (c->type == eCondType::lowerInt)
condTrue = (intVal < c->compareValue) ? true : false;
else if (c->type == eCondType::equalInt)
condTrue = (intVal == c->compareValue) ? true : false;
else if (c->type == eCondType::greaterInt)
condTrue = (intVal > c->compareValue) ? true : false;
}
} else if (c->tokenType == eCondTokenType::looptoken) {
if (loopInfo && loopInfo->row >= 0) {
char *tokenVal = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, c->tokenIndex);
if (tokenVal) {
int intVal = atoi(tokenVal);
if (c->type == eCondType::lowerInt)
condTrue = (intVal < c->compareValue) ? true : false;
else if (c->type == eCondType::equalInt)
condTrue = (intVal == c->compareValue) ? true : false;
else if (c->type == eCondType::greaterInt)
condTrue = (intVal > c->compareValue) ? true : false;
}
}
}
}
else if (c->type == eCondType::isset || c->type == eCondType::empty)
{
if (c->tokenType == eCondTokenType::stringtoken) {
char *tokenVal = tokenContainer->StringToken(c->tokenIndex);
if (tokenVal) {
if (c->type == eCondType::isset)
condTrue = strlen(tokenVal) > 0 ? true : false;
else if (c->type == eCondType::empty)
condTrue = strlen(tokenVal) == 0 ? true : false;
} else {
if (c->type == eCondType::isset)
condTrue = false;
else if (c->type == eCondType::empty)
condTrue = true;
}
} else if (c->tokenType == eCondTokenType::looptoken) {
if (loopInfo && loopInfo->row >= 0) {
char *tokenVal = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, c->tokenIndex);
if (tokenVal) {
if (c->type == eCondType::isset)
condTrue = strlen(tokenVal) > 0 ? true : false;
else if (c->type == eCondType::empty)
condTrue = strlen(tokenVal) == 0 ? true : false;
} else {
if (c->type == eCondType::isset)
condTrue = false;
else if (c->type == eCondType::empty)
condTrue = true;
}
}
}
}
else if (c->type == eCondType::equalString || c->type == eCondType::notEqualString || c->type == eCondType::contains || c->type == eCondType::notContains)
{
if (c->tokenType == eCondTokenType::stringtoken) {
char *tokenVal = tokenContainer->StringToken(c->tokenIndex);
if (tokenVal) {
if (c->type == eCondType::equalString)
condTrue = !strcmp(tokenVal, c->compareStrValue) ? true : false;
else if (c->type == eCondType::notEqualString)
condTrue = strcmp(tokenVal, c->compareStrValue) ? true : false;
else if (c->type == eCondType::contains)
condTrue = strstr(tokenVal, c->compareStrValue) ? true : false;
else if (c->type == eCondType::notContains)
condTrue = !strstr(tokenVal, c->compareStrValue) ? true : false;
}
} else if (c->tokenType == eCondTokenType::looptoken) {
if (loopInfo && loopInfo->row >= 0) {
char *tokenVal = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, c->tokenIndex);
if (tokenVal) {
if (c->type == eCondType::equalString)
condTrue = !strcmp(tokenVal, c->compareStrValue) ? true : false;
else if (c->type == eCondType::notEqualString)
condTrue = strcmp(tokenVal, c->compareStrValue) ? true : false;
else if (c->type == eCondType::contains)
condTrue = strstr(tokenVal, c->compareStrValue) ? true : false;
else if (c->type == eCondType::notContains)
condTrue = !strstr(tokenVal, c->compareStrValue) ? true : false;
}
}
}
} //link
if (c->operation == eCondOp::tAnd) {
ok = ok && condTrue;
} else if (c->operation == eCondOp::tOr) {
ok = ok || condTrue;
}
}
return ok;
}
void cCondition::Debug(void) {
esyslog("skindesigner: condition \"%s\"", expr);
for (cCond *c = conds.First(); c; c = conds.Next(c)) {
c->Debug();
}
esyslog("skindesigner: condition is %s", True() ? "TRUE" : "FALSE");
}
void cCondition::Tokenize(void) {
char *condition = strdup(expr);
char delimiter[] = "+|";
eCondOp operation = eCondOp::tAnd;
char *cond = strtok(condition, delimiter);
while (cond) {
eCondType type = eCondType::token;
if (startswith(cond, "{") && endswith(cond, "}")) {
type = eCondType::token;
} else if (startswith(cond, "not{") && endswith(cond, "}")) {
type = eCondType::negtoken;
} else if (startswith(cond, "lt({") && endswith(cond, ")")) {
type = eCondType::lowerInt;
} else if (startswith(cond, "eq({") && endswith(cond, ")")) {
type = eCondType::equalInt;
} else if (startswith(cond, "gt({") && endswith(cond, ")")) {
type = eCondType::greaterInt;
} else if (startswith(cond, "isset{") && endswith(cond, "}")) {
type = eCondType::isset;
} else if (startswith(cond, "empty{") && endswith(cond, "}")) {
type = eCondType::empty;
} else if (startswith(cond, "strequal({") && endswith(cond, ")")) {
type = eCondType::equalString;
} else if (startswith(cond, "strnotequal({") && endswith(cond, ")")) {
type = eCondType::notEqualString;
} else if (startswith(cond, "strcontains({") && endswith(cond, ")")) {
type = eCondType::contains;
} else if (startswith(cond, "strnotcontains({") && endswith(cond, ")")) {
type = eCondType::notContains;
} else {
esyslog("skindesigner: invalid condition term %s", cond);
cond = strtok(NULL, delimiter);
continue;
}
cCond *c = new cCond(cond);
c->operation = operation;
c->type = type;
conds.Add(c);
if (expr[cond - condition + strlen(cond)] == '+')
operation = eCondOp::tAnd;
else
operation = eCondOp::tOr;
cond = strtok(NULL, delimiter);
}
free(condition);
}
void cCondition::PrepareTokens(void) {
for (cCond *c = conds.First(); c; c = conds.Next(c)) {
switch (c->type) {
case eCondType::token:
case eCondType::negtoken:
SetTokenCond(c);
break;
case eCondType::lowerInt:
case eCondType::equalInt:
case eCondType::greaterInt:
SetIntegerCond(c);
break;
case eCondType::isset:
case eCondType::empty:
SetStringCond(c);
break;
case eCondType::equalString:
case eCondType::notEqualString:
case eCondType::contains:
case eCondType::notContains:
SetStringCompareCond(c);
break;
default:
break;
}
}
}
void cCondition::SetTokenCond(cCond *c) {
if (c->type == eCondType::negtoken) {
ReplaceStart(c->expr, 3);
}
//check globals
int result = 0;
string tmp = c->expr;
tmp = tmp.substr(1, tmp.size()-2);
if (globals->GetInt(tmp, result)) {
c->constant = true;
if (result == 1 && c->type == eCondType::token)
c->isTrue = true;
if (result == 0 && c->type == eCondType::negtoken)
c->isTrue = true;
return;
}
SetTokenIndex(c, c->expr);
}
void cCondition::SetIntegerCond(cCond *c) {
char *tokenStart = strchr(c->expr, '{');
char *tokenEnd = strchr(c->expr, '}');
if (!tokenStart || !tokenEnd)
return;
char token[200] = "";
strncpy(token, tokenStart, tokenEnd - tokenStart + 1);
char *condStart = strchr(c->expr, ',');
char *condEnd = strchr(c->expr, ')');
char strCond[100] = "";
strncpy(strCond, condStart + 1, condEnd - condStart - 1);
c->compareValue = atoi(strCond);
//check globals
int result = 0;
if (globals->GetInt(token, result)) {
c->constant = true;
if (c->type == eCondType::lowerInt && result < c->compareValue)
c->isTrue = true;
else if (c->type == eCondType::equalInt && result == c->compareValue)
c->isTrue = true;
else if (c->type == eCondType::greaterInt && result > c->compareValue)
c->isTrue = true;
return;
}
SetTokenIndex(c, token);
}
void cCondition::SetStringCond(cCond *c) {
ReplaceStart(c->expr, 5);
SetTokenIndex(c, c->expr);
}
void cCondition::SetStringCompareCond(cCond *c) {
char *tokenStart = strchr(c->expr, '{');
char *tokenEnd = strchr(c->expr, '}');
if (!tokenStart || !tokenEnd)
return;
char token[200] = "";
strncpy(token, tokenStart, tokenEnd - tokenStart + 1);
char *condStart = strstr(c->expr, ",'");
char *condEnd = strstr(c->expr, "')");
char strCond[100] = "";
strncpy(strCond, condStart + 2, condEnd - condStart - 2);
c->compareStrValue = strdup(strCond);
SetTokenIndex(c, token);
}
void cCondition::SetTokenIndex(cCond *c, const char *token) {
int tokenIndex = tokenContainer->IntTokenIndex(token);
if (tokenIndex >= 0) {
c->tokenIndex = tokenIndex;
c->tokenType = eCondTokenType::inttoken;
return;
}
tokenIndex = tokenContainer->StringTokenIndex(token);
if (tokenIndex >= 0) {
c->tokenIndex = tokenIndex;
c->tokenType = eCondTokenType::stringtoken;
return;
}
tokenIndex = tokenContainer->LoopTokenIndex(token);
if (tokenIndex >= 0) {
c->tokenIndex = tokenIndex;
c->tokenType = eCondTokenType::looptoken;
}
}
/******************************************************************
* cSummand
******************************************************************/
cSummand::cSummand(const char *summand) {
this->summand = strdup(summand);
}
cSummand::cSummand(const cSummand &other) {
summand = NULL;
if (other.summand)
summand = strdup(other.summand);
positive = other.positive;
for (cFactor *fac = other.factors.First(); fac; fac = other.factors.Next(fac)) {
factors.Add(new cFactor(*fac));
}
}
cSummand::~cSummand(void) {
free(summand);
}
void cSummand::Debug(void) {
esyslog("skindesigner: summand %s, positive: %d", summand, positive);
for (cFactor *f = factors.First(); f; f = factors.Next(f)) {
const char *link = f->multiplication ? "multiplication" : "division";
if (f->type == eFactorType::constant)
esyslog("skindesigner: constant factor %f, %s", f->constValue, link);
else if (f->type == eFactorType::inttoken)
esyslog("skindesigner: IntToken factor, index %d, %s", f->tokenIndex, link);
else if (f->type == eFactorType::stringtoken)
esyslog("skindesigner: StringToken factor, index %d, %s", f->tokenIndex, link);
else if (f->type == eFactorType::looptoken)
esyslog("skindesigner: LoopToken factor, index %d, %s", f->tokenIndex, link);
else if (f->type == eFactorType::xref)
esyslog("skindesigner: posx reference factor, %s, %p, %s, result: %f", f->funcRefName, f->funcRef, link, f->funcRef->FuncX());
else if (f->type == eFactorType::yref)
esyslog("skindesigner: posy reference factor, %s, %p, %s, result: %f", f->funcRefName, f->funcRef, link, f->funcRef->FuncY());
else if (f->type == eFactorType::widthref)
esyslog("skindesigner: width reference factor, %s, %p, %s, result: %f", f->funcRefName, f->funcRef, link, f->funcRef->FuncWidth());
else if (f->type == eFactorType::heightref)
esyslog("skindesigner: height reference factor, %s, %p %s, result: %f", f->funcRefName, f->funcRef, link, f->funcRef->FuncHeight());
else if (f->type == eFactorType::areawidth)
esyslog("skindesigner: {areawidth} factor, %s", link);
else if (f->type == eFactorType::areaheight)
esyslog("skindesigner: {areaheight} factor, %s", link);
else if (f->type == eFactorType::columnwidth)
esyslog("skindesigner: {columnwidth} factor, %s", link);
else if (f->type == eFactorType::rowheight)
esyslog("skindesigner: {rowheight} factor, %s", link);
}
}
/******************************************************************
* cNumericExpr
******************************************************************/
cNumericExpr::cNumericExpr(const char *expression) {
expr = strdup(expression);
globals = NULL;
container = NULL;
tokenContainer = NULL;
loopInfo = NULL;
horizontal = true;
value = 0;
dynamic = false;
}
cNumericExpr::cNumericExpr(const cNumericExpr &other) {
expr = strdup(other.expr);
globals = other.globals;
container = NULL;
tokenContainer = NULL;
loopInfo = NULL;
horizontal = other.horizontal;
value = other.value;
dynamic = other.dynamic;
for (cSummand *s = other.summands.First(); s; s = other.summands.Next(s)) {
summands.Add(new cSummand(*s));
}
}
cNumericExpr::~cNumericExpr(void) {
free(expr);
}
/******************************************************************
* Public Functions
******************************************************************/
bool cNumericExpr::CacheStatic(void) {
expr = RemoveSpace(expr);
if (config.replaceDecPoint)
ReplaceDecimalpoint(expr);
//first check if expression is already an valid integer
if (IsNumeric(expr)) {
value = atoi(expr);
return true;
}
//check if expression is a percent expression
if (PercentValue(expr)) {
return true;
}
//if areawidth or height unknown, percent values have to be replaced
expr = ReplacePercentValue(expr);
//replace {areawidth} and {areaheight}
if (container->Width() >= 0) {
//esyslog("skindesigner: replacing areawidth %s", expr);
expr = ReplaceTokens(expr, "{areawidth}", container->Width());
//esyslog("skindesigner: replaced areawidth %s", expr);
}
if (container->Height() >= 0)
expr = ReplaceTokens(expr, "{areaheight}", container->Height());
//replace globals
string tmp = expr;
globals->ReplaceIntVars(tmp);
globals->ReplaceDoubleVars(tmp);
free(expr);
expr = strdup(tmp.c_str());
if (IsNumeric(expr)) {
value = atoi(expr);
return true;
}
//check if all variables are eliminated
if (IsNumericExpression(expr)) {
value = EvaluateExpression(expr);
return true;
}
//now we have a expression with runtime variables
dynamic = true;
return false;
}
void cNumericExpr::PrepareTokens(void) {
CreateSummands();
CreateFactors();
ConsolidateFactors();
ConsolidateSummand();
}
vector<cFactor*> cNumericExpr::GetRefFactors(void) {
vector<cFactor*> refFactors;
for (cSummand *s = summands.First(); s; s = summands.Next(s)) {
for (cFactor* f = s->factors.First(); f; f = s->factors.Next(f)) {
if (f->type >= eFactorType::xref) {
refFactors.push_back(f);
}
}
}
return refFactors;
}
int cNumericExpr::Calculate(void) {
double result = 0.0f;
for (cSummand *s = summands.First(); s; s = summands.Next(s)) {
double factor = 1.0f;
for (cFactor *f = s->factors.First(); f; f = s->factors.Next(f)) {
double fac = 0.0f;
switch( f->type ) {
case eFactorType::constant:
fac = f->constValue;
break;
case eFactorType::stringtoken: {
char *val = tokenContainer->StringToken(f->tokenIndex);
if (val)
fac = atoi(val);
break; }
case eFactorType::inttoken:
fac = tokenContainer->IntToken(f->tokenIndex);
break;
case eFactorType::looptoken:
if (loopInfo && loopInfo->row >= 0) {
char *val = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, f->tokenIndex);
if (val)
fac = atoi(val);
}
break;
case eFactorType::xref:
if (f->funcRef)
fac = f->funcRef->FuncX();
break;
case eFactorType::yref:
if (f->funcRef)
fac = f->funcRef->FuncY();
break;
case eFactorType::widthref:
if (f->funcRef)
fac = f->funcRef->FuncWidth();
break;
case eFactorType::heightref:
if (f->funcRef)
fac = f->funcRef->FuncHeight();
break;
case eFactorType::areawidth:
fac = container->Width();
break;
case eFactorType::areaheight:
fac = container->Height();
break;
case eFactorType::columnwidth:
if (loopInfo)
fac = loopInfo->colWidth;
break;
case eFactorType::rowheight:
if (loopInfo)
fac = loopInfo->rowHeight;
break;
}
if (f->multiplication)
factor *= fac;
else if (fac)
factor /= fac;
}
if (s->positive)
result += factor;
else
result -= factor;
}
return (int)result;
}
void cNumericExpr::Debug(void) {
esyslog("skindesigner: Numeric Expression \"%s\", Result: %d", expr, Calculate());
for (cSummand *s = summands.First(); s; s = summands.Next(s)) {
s->Debug();
}
}
/******************************************************************
* Private Functions
******************************************************************/
bool cNumericExpr::IsNumeric(const char *e) {
//negativ numbers
if (*e == '-')
++e;
//empty
if (!*e)
return false;
while (*e) {
if (isdigit(*e) || *e == '.' || *e == ',')
++e;
else
return false;
}
return true;
}
bool cNumericExpr::IsNumericExpression(const char *e) {
if (!*e)
return false;
while (*e) {
if (isdigit(*e) || *e == '.' || *e == ',' || *e == '+' || *e == '-' || *e == '*' || *e == '/')
++e;
else
return false;
}
return true;
}
bool cNumericExpr::PercentValue(const char *e) {
const char *hit = strchr(e, '%');
if (!hit)
return false;
char buffer[20] = "";
if (strlen(e) > 20)
return false;
strncpy(buffer, e, strlen(e)-1);
buffer[strlen(e)-1] = '\0';
int val = atoi(buffer);
bool ok = false;
if (horizontal && container->Width() > 0) {
value = container->Width() * val / 100;
ok = true;
} else if (!horizontal && container->Height() > 0){
value = container->Height() * val / 100;
ok = true;
}
return ok;
}
char *cNumericExpr::ReplacePercentValue(char *e) {
const char *hit = strchr(e, '%');
if (!hit)
return e;
char buffer[20] = "";
if (strlen(e) > 20)
return e;
strncpy(buffer, e, strlen(e)-1);
buffer[strlen(e)-1] = '\0';
int val = atoi(buffer);
double percentVal = (double)val/100.0f;
char replacement[50] = "";
if (horizontal) {
sprintf(replacement, "%.5f*{areawidth}", percentVal);
} else {
sprintf(replacement, "%.5f*{areaheight}", percentVal);
}
int len = strlen(replacement) + 1;
char *replaced = (char*)malloc(len);
memset(replaced, 0, len);
strcpy(replaced, replacement);
free(expr);
return replaced;
}
char *cNumericExpr::ReplaceToken(char *e, const char* token, int val) {
char *tokenStart = strstr(e, token);
if (!tokenStart) {
return e;
}
char buffer[20] = "";
sprintf(buffer, "%d", val);
size_t newSize = strlen(e) - strlen(token) + strlen(buffer) + 1;
char *replaced = (char*)malloc(newSize);
memset(replaced, 0, newSize);
size_t beginning = strlen(e) - strlen(tokenStart);
if (beginning > 0)
strncpy(replaced, e, beginning);
strcat(replaced, buffer);
strcat(replaced, tokenStart + strlen(token));
free(e);
return replaced;
}
char *cNumericExpr::ReplaceTokens(char *e, const char* token, int val) {
if (!e)
return e;
while (true) {
char *tokenStart = strstr(e, token);
if (!tokenStart)
break;
e = ReplaceToken(e, token, val);
}
return e;
}
int cNumericExpr::EvaluateExpression(char* e) {
return round(ParseSummands(e));
}
double cNumericExpr::EvaluateExpressionDouble(char *e) {
return ParseSummands(e);
}
double cNumericExpr::ParseAtom(char*& e) {
// Read the number from string
char* end_ptr;
double res = strtod(e, &end_ptr);
// Advance the pointer and return the result
e = end_ptr;
return res;
}
// Parse multiplication and division
double cNumericExpr::ParseFactors(char*& e) {
double num1 = ParseAtom(e);
for(;;) {
// Save the operation
char op = *e;
if(op != '/' && op != '*')
return num1;
e++;
double num2 = ParseAtom(e);
// Perform the saved operation
if(op == '/') {
if (num2 != 0) {
num1 /= num2;
}
} else
num1 *= num2;
}
}
// Parse addition and subtraction
double cNumericExpr::ParseSummands(char*& e) {
double num1 = ParseFactors(e);
for(;;) {
char op = *e;
if(op != '-' && op != '+')
return num1;
e++;
double num2 = ParseFactors(e);
if(op == '-')
num1 -= num2;
else
num1 += num2;
}
}
void cNumericExpr::CreateSummands(void) {
char *sum = strdup(expr);
char delimiter[] = "+-";
bool positive = true;
if (sum && *sum && *sum == '-') {
positive = false;
}
char *summand = strtok(sum, delimiter);
while (summand) {
cSummand *s = new cSummand(summand);
s->positive = positive;
summands.Add(s);
//check next sign
if (expr[summand - sum + strlen(summand)] == '-')
positive = false;
else
positive = true;
summand = strtok(NULL, delimiter);
}
free(sum);
}
void cNumericExpr::CreateFactors(void) {
char delimiterFac[] = "*/";
for (cSummand *s = summands.First(); s; s = summands.Next(s)) {
char *sum = strdup(s->summand);
if (IsNumericExpression(sum)) {
cFactor *f = new cFactor();
f->constValue = EvaluateExpressionDouble(sum);
s->factors.Add(f);
free(sum);
continue;
}
bool multiplication = true;
char *fac = strtok(sum, delimiterFac);
while (fac) {
cFactor *f = new cFactor();
if (IsNumeric(fac)) {
f->constValue = atof(fac);
f->multiplication = multiplication;
s->factors.Add(f);
} else {
//factor has to be a token or a function reference now
if (SetTokenFactor(f, fac)) {
f->multiplication = multiplication;
s->factors.Add(f);
} else if (SetReferenceFactor(f, fac)) {
f->multiplication = multiplication;
s->factors.Add(f);
} else if (SetGeometryFactor(f, fac)) {
f->multiplication = multiplication;
s->factors.Add(f);
} else {
esyslog("skindesigner: invalid factor %s in expression \"%s\"", fac, expr);
delete f;
}
}
//check next sign
if (s->summand[fac - sum + strlen(fac)] == '/')
multiplication = false;
else
multiplication = true;
fac = strtok(NULL, delimiterFac);
}
free(sum);
}
}
bool cNumericExpr::SetTokenFactor(cFactor *f, char *tokenName) {
int tokenIndex = tokenContainer->IntTokenIndex(tokenName);
if (tokenIndex >= 0) {
f->tokenIndex = tokenIndex;
f->type = eFactorType::inttoken;
return true;
}
tokenIndex = tokenContainer->StringTokenIndex(tokenName);
if (tokenIndex >= 0) {
f->tokenIndex = tokenIndex;
f->type = eFactorType::stringtoken;
return true;
}
tokenIndex = tokenContainer->LoopTokenIndex(tokenName);
if (tokenIndex >= 0) {
f->tokenIndex = tokenIndex;
f->type = eFactorType::looptoken;
return true;
}
return false;
}
bool cNumericExpr::SetReferenceFactor(cFactor *f, char *tokenName) {
int start = 0;
if (startswith(tokenName, "{posx(") && endswith(tokenName, ")}")) {
start = 6;
f->type = eFactorType::xref;
} else if (startswith(tokenName, "{posy(") && endswith(tokenName, ")}")) {
start = 6;
f->type = eFactorType::yref;
} else if (startswith(tokenName, "{width(") && endswith(tokenName, ")}")) {
start = 7;
f->type = eFactorType::widthref;
} else if (startswith(tokenName, "{height(") && endswith(tokenName, ")}")) {
start = 8;
f->type = eFactorType::heightref;
}
if (start == 0)
return false;
tokenName += start;
f->funcRefName = strdup(tokenName);
ReplaceEnd(f->funcRefName, 2);
return true;
}
bool cNumericExpr::SetGeometryFactor(cFactor *f, char *tokenName) {
bool ok = false;
if (!strcmp(tokenName, "{areawidth}")) {
f->type = eFactorType::areawidth;
ok = true;
} else if (!strcmp(tokenName, "{areaheight}")) {
f->type = eFactorType::areaheight;
ok = true;
} else if (!strcmp(tokenName, "{columnwidth}")) {
f->type = eFactorType::columnwidth;
ok = true;
} else if (!strcmp(tokenName, "{rowheight}")) {
f->type = eFactorType::rowheight;
ok = true;
}
return ok;
}
void cNumericExpr::ConsolidateSummand(void) {
cSummand *constSummand = NULL;
for (cSummand *s = summands.First(); s; s = summands.Next(s)) {
if (s->factors.Count() == 1 && s->factors.First()->type == eFactorType::constant) {
if (!constSummand) {
constSummand = s;
} else {
if (s->positive)
constSummand->factors.First()->constValue += s->factors.First()->constValue;
else
constSummand->factors.First()->constValue -= s->factors.First()->constValue;
summands.Del(s);
s = constSummand;
}
}
}
}
void cNumericExpr::ConsolidateFactors(void) {
for (cSummand *s = summands.First(); s; s = summands.Next(s)) {
cFactor *constFactor = NULL;
for (cFactor *f = s->factors.First(); f; f = s->factors.Next(f)) {
if (f->type == eFactorType::constant) {
if (!constFactor) {
constFactor = f;
} else {
if (f->multiplication)
constFactor->constValue *= f->constValue;
else
constFactor->constValue /= f->constValue;
s->factors.Del(f);
f = constFactor;
}
}
}
}
}
/******************************************************************
* cColor
******************************************************************/
cColor::cColor(const char *expression) {
globals = NULL;
expr = strdup(expression);
value = 0x00;
}
cColor::cColor(const cColor &other) {
globals = other.globals;
expr = strdup(other.expr);
value = other.value;
}
cColor::~cColor(void) {
free(expr);
}
void cColor::Cache(void) {
tColor colVal = 0x00;
string tmp = expr;
if (globals->GetColor(tmp, colVal)) {
value = colVal;
return;
}
if (strlen(expr) != 8)
return;
std::stringstream str;
str << tmp;
colVal = 0x00;
str >> std::hex >> colVal;
value = colVal;
}
tColor cColor::Color(void) {
return value;
}
void cColor::Debug(void) {
esyslog("skindesigner: Color \"%s\", Value %x", expr, value);
}
/******************************************************************
* cTextExpr
******************************************************************/
cTextExpr::cTextExpr(const char *expression) {
globals = NULL;
expr = strdup(expression);
tokenContainer = NULL;
loopInfo = NULL;
}
cTextExpr::cTextExpr(const cTextExpr &other) {
globals = other.globals;
expr = strdup(other.expr);
tokenContainer = NULL;
loopInfo = NULL;
for (cTextToken* t = other.textTokens.First(); t; t = other.textTokens.Next(t)) {
textTokens.Add(new cTextToken(*t));
}
}
cTextExpr::~cTextExpr(void) {
free(expr);
}
void cTextExpr::CorrectImagePath(void) {
//no absolute pathes allowed
if (!startswith(expr, "{")) {
esyslog("skindesigner: no absolute pathes allowed for images - %s", expr);
}
if (startswith(expr, "{ressourcedir}")) {
string tmp = expr;
tmp = tmp.replace(0, 14, *config.GetSkinRessourcePath());
free(expr);
expr = strdup(tmp.c_str());
}
}
void cTextExpr::Cache(void) {
Translate();
Tokenize();
PrepareTokens();
}
char *cTextExpr::DeterminateText(void) {
//calculate length of complete new string
int textLength = 0;
for (cTextToken* t = textTokens.First(); t; t = textTokens.Next(t)) {
if (t->type == eTexttokenType::constant) {
textLength += strlen(t->constValue);
} else if (t->type == eTexttokenType::stringtoken) {
char *str = tokenContainer->StringToken(t->tokenIndex);
if (str)
textLength += strlen(str);
} else if (t->type == eTexttokenType::inttoken) {
int value = tokenContainer->IntToken(t->tokenIndex);
if (value >= 0) {
cString str = cString::sprintf("%d", value);
textLength += strlen(*str);
}
} else if (t->type == eTexttokenType::looptoken && loopInfo && loopInfo->row >= 0) {
char *str = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, t->tokenIndex);
if (str)
textLength += strlen(str);
} else if (t->type == eTexttokenType::printftoken) {
DeterminatePrintfToken(t);
if (t->printfResult)
textLength += strlen(t->printfResult);
} else if (t->type == eTexttokenType::condstringtoken) {
char *str = tokenContainer->StringToken(t->tokenIndex);
if (str) {
textLength += strlen(str);
if (t->condStart)
textLength += strlen(t->condStart);
if (t->condEnd)
textLength += strlen(t->condEnd);
}
} else if (t->type == eTexttokenType::condinttoken) {
int value = tokenContainer->IntToken(t->tokenIndex);
if (value >= 0) {
cString str = cString::sprintf("%d", value);
textLength += strlen(*str);
if (t->condStart)
textLength += strlen(t->condStart);
if (t->condEnd)
textLength += strlen(t->condEnd);
}
}
}
if (textLength <= 0)
return NULL;
char *retVal = (char*)malloc(textLength+1);
memset(retVal, 0, textLength);
bool first = true;
for (cTextToken* t = textTokens.First(); t; t = textTokens.Next(t)) {
if (first) {
first = false;
if (t->type == eTexttokenType::constant) {
strcpy(retVal, t->constValue);
} else if (t->type == eTexttokenType::stringtoken) {
char *str = tokenContainer->StringToken(t->tokenIndex);
if (str)
strcpy(retVal, str);
} else if (t->type == eTexttokenType::inttoken) {
int value = tokenContainer->IntToken(t->tokenIndex);
if (value >= 0) {
cString str = cString::sprintf("%d", value);
strcpy(retVal, *str);
}
} else if (t->type == eTexttokenType::looptoken && loopInfo && loopInfo->row >= 0) {
char *str = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, t->tokenIndex);
if (str)
strcpy(retVal, str);
} else if (t->type == eTexttokenType::printftoken) {
if (t->printfResult) {
strcpy(retVal, t->printfResult);
free(t->printfResult);
t->printfResult = NULL;
}
} else if (t->type == eTexttokenType::condstringtoken) {
char *str = tokenContainer->StringToken(t->tokenIndex);
if (str) {
if (t->condStart) {
strcpy(retVal, t->condStart);
strcat(retVal, str);
} else {
strcpy(retVal, str);
}
if (t->condEnd)
strcat(retVal, t->condEnd);
}
} else if (t->type == eTexttokenType::condinttoken) {
int value = tokenContainer->IntToken(t->tokenIndex);
if (value >= 0) {
cString str = cString::sprintf("%d", value);
if (t->condStart) {
strcpy(retVal, t->condStart);
strcat(retVal, *str);
} else {
strcpy(retVal, *str);
}
if (t->condEnd)
strcat(retVal, t->condEnd);
}
}
} else {
if (t->type == eTexttokenType::constant) {
strcat(retVal, t->constValue);
} else if (t->type == eTexttokenType::stringtoken) {
char *str = tokenContainer->StringToken(t->tokenIndex);
if (str)
strcat(retVal, str);
} else if (t->type == eTexttokenType::inttoken) {
int value = tokenContainer->IntToken(t->tokenIndex);
cString str = cString::sprintf("%d", value);
strcat(retVal, *str);
} else if (t->type == eTexttokenType::looptoken && loopInfo && loopInfo->row >= 0) {
char *str = tokenContainer->LoopToken(loopInfo->index, loopInfo->row, t->tokenIndex);
if (str)
strcat(retVal, str);
} else if (t->type == eTexttokenType::printftoken) {
if (t->printfResult) {
strcat(retVal, t->printfResult);
free(t->printfResult);
t->printfResult = NULL;
}
} else if (t->type == eTexttokenType::condstringtoken) {
char *str = tokenContainer->StringToken(t->tokenIndex);
if (str) {
if (t->condStart)
strcat(retVal, t->condStart);
strcat(retVal, str);
if (t->condEnd)
strcat(retVal, t->condEnd);
}
} else if (t->type == eTexttokenType::condinttoken) {
int value = tokenContainer->IntToken(t->tokenIndex);
if (value >= 0) {
cString str = cString::sprintf("%d", value);
if (t->condStart)
strcat(retVal, t->condStart);
strcat(retVal, *str);
if (t->condEnd)
strcat(retVal, t->condEnd);
}
}
}
}
return retVal;
}
void cTextExpr::Debug(const char *exprName) {
esyslog("skindesigner: TextExpr %s: \"%s\"", exprName, expr);
char *res = DeterminateText();
esyslog("skindesigner: Result: \"%s\"", res);
free(res);
for (cTextToken *t = textTokens.First(); t; t = textTokens.Next(t)) {
if (t->type == eTexttokenType::constant)
esyslog("skindesigner: constant token \"%s\"", t->constValue);
else if (t->type == eTexttokenType::stringtoken)
esyslog("skindesigner: string token %s, index %d", t->constValue, t->tokenIndex);
else if (t->type == eTexttokenType::inttoken)
esyslog("skindesigner: int token %s, index %d", t->constValue, t->tokenIndex);
else if (t->type == eTexttokenType::looptoken)
esyslog("skindesigner: loop token %s, index %d", t->constValue, t->tokenIndex);
else if (t->type == eTexttokenType::printftoken)
esyslog("skindesigner: printf token %s", t->constValue);
else if (t->type == eTexttokenType::condstringtoken || t->type == eTexttokenType::condinttoken)
esyslog("skindesigner: conditional token %s", t->constValue);
}
}
void cTextExpr::Translate(void) {
string translated = "";
if (!globals->Translate(expr, translated))
return;
free(expr);
expr = strdup(translated.c_str());
}
void cTextExpr::Tokenize(void) {
char *head = expr;
char *tail = expr;
while (*tail) {
if (*tail == '|') {
char *begin = CopyTextPart(head, tail, false);
if (begin) {
cTextToken *t = new cTextToken();
t->constValue = begin;
textTokens.Add(t);
}
head = tail;
tail++;
while (*tail) {
if (*tail == '|') {
char *token = CopyTextPart(head, tail);
cTextToken *t = new cTextToken();
t->type = eTexttokenType::condstringtoken;
t->constValue = token;
textTokens.Add(t);
head = tail+1;
break;
}
tail++;
}
} else if (*tail == '{') {
char *begin = CopyTextPart(head, tail, false);
if (begin) {
cTextToken *t = new cTextToken();
t->constValue = begin;
textTokens.Add(t);
}
head = tail;
tail++;
while (*tail) {
if (*tail == '}') {
char *token = CopyTextPart(head, tail);
cTextToken *t = new cTextToken();
t->type = eTexttokenType::stringtoken;
t->constValue = token;
textTokens.Add(t);
head = tail+1;
break;
}
tail++;
}
}
tail++;
}
char *end = CopyTextPart(head, tail, false);
if (end) {
cTextToken *t = new cTextToken();
t->constValue = end;
textTokens.Add(t);
}
}
void cTextExpr::PrepareTokens(void) {
bool error = false;
for (cTextToken *t = textTokens.First(); t; t = textTokens.Next(t)) {
if (t->type == eTexttokenType::condstringtoken) {
ParseCondToken(t);
continue;
} else if (t->type != eTexttokenType::stringtoken)
continue;
if (ParsePrintfToken(t)) {
continue;
}
if (CheckGlobals(t)) {
continue;
}
int tokenIndex = tokenContainer->IntTokenIndex(t->constValue);
if (tokenIndex >= 0) {
t->tokenIndex = tokenIndex;
t->type = eTexttokenType::inttoken;
continue;
}
tokenIndex = tokenContainer->StringTokenIndex(t->constValue);
if (tokenIndex >= 0) {
t->tokenIndex = tokenIndex;
t->type = eTexttokenType::stringtoken;
continue;
}
tokenIndex = tokenContainer->LoopTokenIndex(t->constValue);
if (tokenIndex >= 0) {
t->tokenIndex = tokenIndex;
t->type = eTexttokenType::looptoken;
continue;
}
esyslog("skindesigner: invalid text token %s in expression \"%s\"", t->constValue, expr);
error = true;
break;
}
if (error) {
textTokens.Clear();
}
}
bool cTextExpr::CheckGlobals(cTextToken *t) {
if (!globals || !t->constValue)
return false;
string name = t->constValue;
name = name.substr(1, name.size()-2);
string replacement = "";
if (globals->GetString(name, replacement)) {
free(t->constValue);
t->constValue = strdup(replacement.c_str());
t->type = eTexttokenType::constant;
return true;
}
int numReplacement = 0;
if (globals->GetInt(name, numReplacement)) {
free(t->constValue);
cString repl = cString::sprintf("%d", numReplacement);
t->constValue = strdup(*repl);
t->type = eTexttokenType::constant;
return true;
}
return false;
}
bool cTextExpr::ParsePrintfToken(cTextToken *t) {
//check valid printftoken
char *start = strstr(t->constValue, "printf(");
if (!start)
return false;
char *end = strchr(start, ')');
if (!end)
return false;
t->type = eTexttokenType::printftoken;
//find printf expression
char *startExpr = strchr(t->constValue, '\'');
if (!startExpr)
return false;
char *endExpr = strchr(startExpr + 1, '\'');
if (!endExpr)
return false;
int expLen = endExpr - startExpr - 1;
char buffer[100];
strncpy(buffer, startExpr+1, expLen);
buffer[expLen] = '\0';
t->printfExpr = strdup(buffer);
//find variables
char *startVar = strchr(t->constValue, ',');
if (!startVar)
return false;
startVar++;
vector<string> varTokens;
char *nextVar = NULL;
while (nextVar = strchr(startVar, ',')) {
while(isspace(*startVar))
startVar++;
int varLen = nextVar - startVar;
buffer[0] = '{';
strncpy((char*)buffer + 1, startVar, varLen);
buffer[varLen+1] = '}';
buffer[varLen+2] = '\0';
int i = 1;
while(isspace(buffer[varLen-i])) {
buffer[varLen-i] = '}';
buffer[varLen-i+1] = '\0';
i++;
}
varTokens.push_back(buffer);
startVar = nextVar + 1;
}
if (startVar+1) {
int varLen = end - startVar;
buffer[0] = '{';
strncpy((char*)buffer + 1, startVar + 1, varLen);
buffer[varLen] = '}';
buffer[varLen+1] = '\0';
varTokens.push_back(buffer);
}
//evaluate variables
bool ok = true;
for (size_t i=0; i < varTokens.size(); i++) {
sPrintfInfo info;
int tokenIndex = tokenContainer->IntTokenIndex(varTokens[i].c_str());
if (tokenIndex >= 0) {
info.type = ePrintfVarType::inttoken;
info.index = tokenIndex;
t->printfVarIndices.push_back(info);
continue;
}
tokenIndex = tokenContainer->StringTokenIndex(varTokens[i].c_str());
if (tokenIndex >= 0) {
info.type = ePrintfVarType::stringtoken;
info.index = tokenIndex;
t->printfVarIndices.push_back(info);
continue;
}
tokenIndex = tokenContainer->LoopTokenIndex(varTokens[i].c_str());
if (tokenIndex >= 0) {
info.type = ePrintfVarType::looptoken;
info.index = tokenIndex;
t->printfVarIndices.push_back(info);
continue;
}
ok = false;
}
return ok;
}
void cTextExpr::DeterminatePrintfToken(cTextToken *t) {
int numVars = t->printfVarIndices.size();
vector<int> results;
for (int i=0; i < numVars; i++) {
if (t->printfVarIndices[i].type == ePrintfVarType::inttoken) {
results.push_back(tokenContainer->IntToken(t->printfVarIndices[i].index));
} else if (t->printfVarIndices[i].type == ePrintfVarType::stringtoken) {
if (tokenContainer->StringToken(t->printfVarIndices[i].index)) {
results.push_back(atoi(tokenContainer->StringToken(t->printfVarIndices[i].index)));
}
} else if (t->printfVarIndices[i].type == ePrintfVarType::looptoken && loopInfo && loopInfo->row >= 0) {
if (tokenContainer->LoopToken(loopInfo->index, loopInfo->row, t->printfVarIndices[i].index)) {
results.push_back(atoi(tokenContainer->LoopToken(loopInfo->index, loopInfo->row, t->printfVarIndices[i].index)));
}
}
}
switch (numVars) {
case 1:
t->printfResult = strdup(*cString::sprintf(t->printfExpr, results[0]));
break;
case 2:
t->printfResult = strdup(*cString::sprintf(t->printfExpr, results[0], results[1]));
break;
case 3:
t->printfResult = strdup(*cString::sprintf(t->printfExpr, results[0], results[1], results[2]));
break;
case 4:
t->printfResult = strdup(*cString::sprintf(t->printfExpr, results[0], results[1], results[2], results[3]));
break;
case 5:
t->printfResult = strdup(*cString::sprintf(t->printfExpr, results[0], results[1], results[2], results[3], results[4]));
break;
case 6:
t->printfResult = strdup(*cString::sprintf(t->printfExpr, results[0], results[1], results[2], results[3], results[4], results[5]));
break;
default:
break;
}
}
void cTextExpr::ParseCondToken(cTextToken *t) {
char *head = t->constValue;
char *tail = t->constValue;
head++;
tail++;
char *token = NULL;
while (*tail) {
if (*tail == '{') {
t->condStart = CopyTextPart(head, tail, false);
head = tail;
tail++;
while (*tail) {
if (*tail == '}') {
token = CopyTextPart(head, tail);
head = tail+1;
break;
}
tail++;
}
}
tail++;
}
tail--;
t->condEnd = CopyTextPart(head, tail, false);
if (!token) {
esyslog("skindesigner: invalid conditional texttoken %s in expression \"%s\"", t->constValue, expr);
return;
}
int tokenIndex = tokenContainer->StringTokenIndex(token);
if (tokenIndex >= 0) {
t->tokenIndex = tokenIndex;
return;
}
tokenIndex = tokenContainer->IntTokenIndex(token);
if (tokenIndex >= 0) {
t->tokenIndex = tokenIndex;
t->type = eTexttokenType::condinttoken;
return;
}
esyslog("skindesigner: invalid token %s in expression\"%s\"", token, expr);
}
char *cTextExpr::CopyTextPart(char *start, char *stop, bool incLastChar) {
if (!start)
return NULL;
if (start >= stop)
return NULL;
char *val = NULL;
size_t len = 0;
if (stop) {
//defined end
len = stop - start + 1;
} else {
//search end of text
char *p = start;
while (*p)
len++; p++;
len++;
}
val = (char*)malloc(len+1);
memset(val, 0, len+1);
if (!incLastChar)
len--;
memcpy(val, start, len);
return val;
}