#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 cNumericExpr::GetRefFactors(void) { vector 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 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 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; }