/* * GraphLCD skin library * * function.c - skin functions class * * This file is released under the GNU General Public License. Refer * to the COPYING file distributed with this package. * * based on text2skin * */ #include #include #include "skin.h" #include "function.h" namespace GLCD { static const char * Internals[] = { "not", "and", "or", "equal", "eq", "gt", "lt", "ge", "le", "ne", "file", "trans", "add", "sub", "mul", "div", "FontTotalWidth", "FontTotalHeight", "FontTotalAscent", "FontSpaceBetween", "FontLineHeight", "FontTextWidth", "FontTextHeight", "ImageWidth", "ImageHeight", "QueryFeature", NULL }; cSkinFunction::cSkinFunction(cSkinObject *Parent) : mObject(Parent), mSkin(Parent->Skin()), mType(string), mString(mObject, false), mNumber(0), mNumParams(0) { } cSkinFunction::cSkinFunction(const cSkinString & String) : mObject(String.Object()), mSkin(String.Skin()), mType(string), mString(String), mNumber(0), mNumParams(0) { } cSkinFunction::cSkinFunction(const cSkinFunction & Src) : mObject(Src.mObject), mSkin(Src.mSkin), mType(Src.mType), mString(Src.mString), mNumber(Src.mNumber), mNumParams(Src.mNumParams) { for (uint32_t i = 0; i < mNumParams; ++i) mParams[i] = new cSkinFunction(*Src.mParams[i]); } cSkinFunction::~cSkinFunction() { for (uint32_t i = 0; i < mNumParams; ++i) delete mParams[i]; } bool cSkinFunction::Parse(const std::string & Text, bool reparse) { const char *text = Text.c_str(); const char *ptr = text, *last = text; eType type = undefined_function; int inExpr = 0; cSkinFunction *expr; if (*ptr == '\'' || *ptr == '{') { // must be string if (strlen(ptr) < 2 || (*ptr == '\'' && *(ptr + strlen(ptr) - 1) != '\'') || (*ptr == '{' && *(ptr + strlen(ptr) - 1) != '}')) { if (!reparse) // only log this error when not reparsing syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Unmatched string end\n"); return false; } std::string temp; if (*ptr == '\'') temp.assign(ptr + 1, strlen(ptr) - 2); else temp.assign(ptr); int pos = -1; while ((pos = temp.find("\\'", pos + 1)) != -1) temp.replace(pos, 2, "'"); if (!mString.Parse(temp)) return false; mType = string; } if (*ptr == '#') { // must be a variable id if (strlen(ptr) < 2) { if (!reparse) // only log this error when not reparsing syslog(LOG_ERR, "ERROR: graphlcd/skin/function: No variable id given\n"); return false; } mVariableId.assign(ptr + 1); mType = variable; } else if ((*ptr >= '0' && *ptr <= '9') || *ptr == '-' || *ptr == '+') { // must be number char *end; int num = strtol(ptr, &end, 10); if (end == ptr || *end != '\0') { // don't log this because when parsing a string starting with a digit (eg: 0%) this may result in a load of false positives //syslog(LOG_ERR, "ERROR: Invalid numeric value (%s)\n", Text.c_str()); return false; } mNumber = num; mType = number; } else { bool inToken = false; // expression for (; *ptr; ++ptr) { if (*ptr == '{') inToken = true; else if (inToken && *ptr == '}') inToken = false; if (*ptr == '(') { if (inExpr++ == 0) { int i; for (i = 0; Internals[i] != NULL; ++i) { if ((size_t)(ptr - last) == strlen(Internals[i]) && memcmp(last, Internals[i], ptr - last) == 0) { type = (eType)(INTERNAL + i); break; } } if (Internals[i] == NULL) { if (!reparse) // only log this error when not reparsing syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Unknown function %.*s", (int)(ptr - last), last); return false; } last = ptr + 1; } } else if ( ( (!inToken) && (*ptr == ',') ) || *ptr == ')') { if (inExpr == 0) { if (!reparse) // only log this error when not reparsing syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Unmatched '%c' in expression (%s)", *ptr, Text.c_str()); return false; } if (inExpr == 1) { expr = new cSkinFunction(mObject); if (!expr->Parse(std::string(last, ptr - last))) { delete expr; return false; } if (mNumParams == MAXPARAMETERS) { if (!reparse) // only log this error when not reparsing syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Too many parameters to function, maximum is %d", MAXPARAMETERS); return false; } mType = type; mParams[mNumParams++] = expr; last = ptr + 1; } if (*ptr == ')') { if (inExpr == 1) { int params = 0; switch (mType) { case fun_and: case fun_or: params = -1; break; case fun_equal: case fun_eq: case fun_ne: case fun_gt: case fun_lt: case fun_ge: case fun_le: params = 2; break; case fun_not: case fun_trans: case fun_file: params = 1; break; case funAdd: case funSub: case funMul: params = -1; break; case funDiv: params = 2; break; case funFontTotalWidth: case funFontTotalHeight: case funFontTotalAscent: case funFontSpaceBetween: case funFontLineHeight: params = 1; break; case funFontTextWidth: case funFontTextHeight: params = 2; break; case funImageWidth: case funImageHeight: params = 1; break; case funQueryFeature: params = 1; break; default: break; } if (params != -1 && mNumParams != (uint32_t) params) { syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Wrong number of parameters to %s, " "expecting %d", Internals[mType - INTERNAL], params); return false; } } --inExpr; } } } if (inExpr > 0) { // only log this error when not reparsing if (!reparse) syslog(LOG_ERR, "ERROR: Expecting ')' in expression"); return false; } } return true; } cType cSkinFunction::FunFile(const cType & Param) const { cImageCache * cache = mSkin->ImageCache(); GLCD::cImage * image = cache->Get(Param); return image ? (cType) Param : (cType) false; } cType cSkinFunction::FunFont(eType Function, const cType &Param1, const cType &Param2) const { cSkinFont * skinFont = mSkin->GetFont(Param1); if (!skinFont) return false; const cFont * font = skinFont->Font(); if (!font) return false; switch (Function) { case funFontTotalWidth: return font->TotalWidth(); case funFontTotalHeight: return font->TotalHeight(); case funFontTotalAscent: return font->TotalAscent(); case funFontSpaceBetween: return font->SpaceBetween(); case funFontLineHeight: return font->LineHeight(); case funFontTextWidth: return font->Width((const std::string) Param2); case funFontTextHeight: return font->Height((const std::string) Param2); default: break; } return false; } cType cSkinFunction::FunImage(eType Function, const cType &Param) const { cImageCache * cache = mSkin->ImageCache(); GLCD::cImage * image = cache->Get(Param); if (!image) return false; switch (Function) { case funImageWidth: return (int) image->Width(); case funImageHeight: return (int) image->Height(); default: break; } return false; } cType cSkinFunction::Evaluate(void) const { switch (mType) { case string: return mString.Evaluate(); case number: return mNumber; case variable: { cSkinVariable * variable = mSkin->GetVariable(mVariableId); if (variable) { cType rv = variable->Value(); if (rv.IsString()) { std::string val = rv; if (val.find("{") != std::string::npos || val.find("#") != std::string::npos) { cSkinString *result = new cSkinString(mObject, false); if (result->Parse(val)) { val = (std::string) result->Evaluate(); rv = cType(val); } delete result; } } return rv; } return false; } case fun_not: return !mParams[0]->Evaluate(); case fun_and: for (uint32_t i = 0; i < mNumParams; ++i) { if (!mParams[i]->Evaluate()) return false; } return true; case fun_or: for (uint32_t i = 0; i < mNumParams; ++i) { if (mParams[i]->Evaluate()) return true; } return false; case fun_equal: case fun_eq: return (std::string) mParams[0]->Evaluate() == (std::string) mParams[1]->Evaluate(); case fun_ne: return (std::string) mParams[0]->Evaluate() != (std::string) mParams[1]->Evaluate(); case fun_gt: return (int) mParams[0]->Evaluate() > (int) mParams[1]->Evaluate(); case fun_lt: return (int) mParams[0]->Evaluate() < (int) mParams[1]->Evaluate(); case fun_ge: return (int) mParams[0]->Evaluate() >= (int) mParams[1]->Evaluate(); case fun_le: return (int) mParams[0]->Evaluate() <= (int) mParams[1]->Evaluate(); case fun_file: return FunFile(mParams[0]->Evaluate()); case fun_trans: return mSkin->Config().Translate(mParams[0]->Evaluate()); case funAdd: { int result = 0; for (uint32_t i = 0; i < mNumParams; ++i) { result += (int) mParams[i]->Evaluate(); } return result; } case funSub: if (mNumParams > 0) { int result = (int) mParams[0]->Evaluate(); for (uint32_t i = 1; i < mNumParams; ++i) { result -= (int) mParams[i]->Evaluate(); } return result; } return 0; case funMul: { int result = 1; for (uint32_t i = 0; i < mNumParams; ++i) { result *= (int) mParams[i]->Evaluate(); } return result; } case funDiv: return ((int) mParams[0]->Evaluate() / (int) mParams[1]->Evaluate()); case funFontTotalWidth: case funFontTotalHeight: case funFontTotalAscent: case funFontSpaceBetween: case funFontLineHeight: return FunFont(mType, mParams[0]->Evaluate(), ""); case funFontTextWidth: case funFontTextHeight: return FunFont(mType, mParams[0]->Evaluate(), mParams[1]->Evaluate()); case funImageWidth: case funImageHeight: return FunImage(mType, mParams[0]->Evaluate()); case funQueryFeature: { int value; if (mSkin->Config().GetDriver()->GetFeature((const std::string)(mParams[0]->Evaluate()), value)) { return (value) ? true : false; } else { return false; } } default: //Dprintf("unknown function code\n"); syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Unknown function code called (this shouldn't happen)"); break; } return false; } } // end of namespace