diff options
Diffstat (limited to 'glcdskin/parser.c')
-rw-r--r-- | glcdskin/parser.c | 304 |
1 files changed, 237 insertions, 67 deletions
diff --git a/glcdskin/parser.c b/glcdskin/parser.c index d42cfb8..5aa5515 100644 --- a/glcdskin/parser.c +++ b/glcdskin/parser.c @@ -16,28 +16,33 @@ #include <vector> #include <string> +#include <clocale> + #include "parser.h" #include "xml.h" #include "skin.h" +/* workaround for thread-safe parsing */ +#include <pthread.h> + namespace GLCD { #define TAG_ERR_REMAIN(_context) do { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Unexpected tag %s within %s", \ - name.c_str(), _context); \ + errorDetail = "Unexpected tag "+name+" within "+ _context; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } while (0) #define TAG_ERR_CHILD(_context) do { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: No child tag %s expected within %s", \ - name.c_str(), _context); \ + errorDetail = "No child tag "+name+" expected within "+ _context; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } while (0) #define TAG_ERR_END(_context) do { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Unexpected closing tag for %s within %s", \ - name.c_str(), _context); \ + errorDetail = "Unexpected closing tag for "+name+" within "+ _context; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } while (0) @@ -49,8 +54,8 @@ namespace GLCD #define ATTRIB_MAN_STRING(_attr,_target) \ ATTRIB_OPT_STRING(_attr,_target) \ else { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \ - _attr, name.c_str()); \ + errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+ name; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } @@ -59,8 +64,8 @@ namespace GLCD char *_e; const char *_t = attrs[_attr].c_str(); \ long _l = strtol(_t, &_e, 10); \ if (_e ==_t || *_e != '\0') { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Invalid numeric value \"%s\" in attribute %s", \ - _t, _attr); \ + errorDetail = "Invalid numeric value \""+ (std::string)_t +"\" in attribute "+ (std::string)_attr; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } else \ _target = _l; \ @@ -69,8 +74,8 @@ namespace GLCD #define ATTRIB_MAN_NUMBER(_attr,_target) \ ATTRIB_OPT_NUMBER(_attr,_target) \ else { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \ - _attr, name.c_str()); \ + errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+name; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } @@ -81,8 +86,8 @@ namespace GLCD else if (attrs[_attr] == "no") \ _target = false; \ else { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Invalid boolean value \"%s\" in attribute %s", \ - attrs[_attr].c_str(), _attr); \ + errorDetail = "Invalid boolean value \""+ attrs[_attr] +"\" in attribute "+ _attr; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } \ } @@ -90,16 +95,16 @@ namespace GLCD #define ATTRIB_MAN_BOOL(_attr,_target) \ ATTRIB_OPT_BOOL(_attr,_target) \ else { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \ - _attr, name.c_str()); \ + errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+name; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } #define ATTRIB_OPT_FUNC(_attr,_func) \ if (attrs.find(_attr) != attrs.end()) { \ if (!_func(attrs[_attr])) { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Unexpected value %s for attribute %s", \ - attrs[_attr].c_str(), _attr); \ + errorDetail = "Unexpected value \""+ attrs[_attr] +"\" for attribute "+ (std::string)_attr; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } \ } @@ -107,16 +112,16 @@ namespace GLCD #define ATTRIB_MAN_FUNC(_attr,_func) \ ATTRIB_OPT_FUNC(_attr,_func) \ else { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \ - _attr, name.c_str()); \ + errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+name; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } #define ATTRIB_OPT_FUNC_PARAM(_attr,_func,_param) \ if (attrs.find(_attr) != attrs.end()) { \ if (!_func(attrs[_attr],_param)) { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Unexpected value %s for attribute %s", \ - attrs[_attr].c_str(), _attr); \ + errorDetail = "Unexpected value "+ attrs[_attr] +" for attribute "+ (std::string)_attr; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } \ } @@ -124,8 +129,8 @@ namespace GLCD #define ATTRIB_MAN_FUNC_PARAM(_attr,_func,_param) \ ATTRIB_OPT_FUNC_PARAM(_attr,_func,_param) \ else { \ - syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \ - _attr, name.c_str()); \ + errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+name; \ + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \ return false; \ } @@ -133,31 +138,140 @@ static std::vector<std::string> context; static cSkin * skin = NULL; static cSkinFont * font = NULL; static cSkinVariable * variable = NULL; +static cSkinVariable * variable_default = NULL; static cSkinDisplay * display = NULL; static std::vector <cSkinObject *> parents; static cSkinObject * object = NULL; static uint32_t oindex = 0; +static std::string errorDetail = ""; +static std::string condblock_cond = ""; + +// support for including files (templates, ...) in the skin definition +// max. depth supported for file inclusion (-> detect recursive inclusions) +#define MAX_INCLUDEDEPTH 5 +static int includeDepth = 0; +static std::string subErrorDetail = ""; + +bool StartElem(const std::string & name, std::map<std::string,std::string> & attrs); +bool CharData(const std::string & text); +bool EndElem(const std::string & name); + + + +static bool CheckSkinVersion(const std::string & version) { + float currv; + char* ecptr = NULL; + const char* verscstr = version.c_str(); + // only accept floating point numbers with '.' as separator, no ',' + char* curr_locale = setlocale(LC_NUMERIC, "C"); + + currv = strtof(verscstr, &ecptr); + setlocale(LC_NUMERIC, curr_locale); + + if ( (*ecptr != '\0') || (ecptr == NULL) /*|| (ecptr != verscstr)*/ || + ((int)(GLCDSKIN_SKIN_VERSION * 100.0) < (int)(currv * 100.0)) + ) + { + return false; + } + return true; +} + + bool StartElem(const std::string & name, std::map<std::string,std::string> & attrs) { //printf("start element: %s\n", name.c_str()); - +// if (context.size() > 0) fprintf(stderr, "context: %s\n", context[context.size() - 1].c_str()); if (context.size() == 0) { if (name == "skin") { ATTRIB_MAN_STRING("version", skin->version); ATTRIB_MAN_STRING("name", skin->title); + ATTRIB_OPT_FUNC("enable", skin->ParseEnable); + + if (! CheckSkinVersion(skin->version) ) { + errorDetail = "skin version '"+ skin->version +"' not supported."; + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); + return false; + } } else TAG_ERR_REMAIN("document"); } + else if (name == "include") + { + if (includeDepth + 1 < MAX_INCLUDEDEPTH) { + cSkinObject* tmpobj = new cSkinObject(new cSkinDisplay(skin)); + cSkinString* path = new cSkinString(tmpobj, false); + ATTRIB_MAN_FUNC("path", path->Parse); + std::string strpath = path->Evaluate(); + // is path relative? -> prepend skinpath + if (strpath[0] != '/') { + strpath = skin->Config().SkinPath() + "/" + strpath; + } + path = NULL; + tmpobj = NULL; + + includeDepth++; + cXML incxml(strpath, skin->Config().CharSet()); + incxml.SetNodeStartCB(StartElem); + incxml.SetNodeEndCB(EndElem); + incxml.SetCDataCB(CharData); + if (incxml.Parse() != 0) { + errorDetail = "error when parsing included xml file '"+strpath+"'"+ ( (subErrorDetail == "") ? "" : " ("+subErrorDetail+")"); + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); + return false; + } + includeDepth--; + } else { + subErrorDetail = "max. include depth reached"; + return false; + } + } + else if (name == "condblock") + { + int i = context.size() - 1; + while (i >= 0) { + if (context[i] == "condblock") { + errorDetail = "'condblock' must not be nested in another 'condblock'."; + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); + return false; + } + i--; + } + ATTRIB_MAN_STRING("condition", condblock_cond); + } else if (name == "variable") { variable = new cSkinVariable(skin); ATTRIB_MAN_STRING("id", variable->mId); + ATTRIB_OPT_FUNC("evaluate", variable->ParseEvalMode); ATTRIB_MAN_FUNC("value", variable->ParseValue); - ATTRIB_OPT_FUNC("condition", variable->ParseCondition); + if (context[context.size() - 1] == "condblock") { + if (attrs.find("condition") != attrs.end()) { + errorDetail = "variable \""+variable->mId+"\" must not contain a condition when context = 'condblock'."; + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); + return false; + } else { + if (! variable->ParseCondition(condblock_cond)) { + errorDetail = "Unexpected value \""+ attrs["condition"] +"\" for attribute "+ (std::string)"condition"; + syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); + return false; + } + } + } else { + ATTRIB_OPT_FUNC("condition", variable->ParseCondition); + } + // if a 'default' value is set, create a second variable w/o condition: will be used if condition is not true + // as variables all have global scope (no matter where defined) and will always be sought from the start of the array, + // the default variable will be inserted _after_ the variable containing the condition. + if (attrs.find("default") != attrs.end()) { + variable_default = new cSkinVariable(skin); + ATTRIB_MAN_STRING("id", variable_default->mId); + ATTRIB_MAN_FUNC("default", variable_default->ParseValue); + } } else if (context[context.size() - 1] == "skin") { @@ -187,83 +301,98 @@ bool StartElem(const std::string & name, std::map<std::string,std::string> & att } object = new cSkinObject(display); + + /* default settings */ + object->ParseColor("transparent", object->mBackgroundColor); + if (object->ParseType(name)) { - ATTRIB_OPT_FUNC_PARAM("x1", object->ParseIntParam, object->mPos1.x); - ATTRIB_OPT_FUNC_PARAM("y1", object->ParseIntParam, object->mPos1.y); - ATTRIB_OPT_FUNC_PARAM("x2", object->ParseIntParam, object->mPos2.x); - ATTRIB_OPT_FUNC_PARAM("y2", object->ParseIntParam, object->mPos2.y); - ATTRIB_OPT_FUNC("width", object->ParseWidth); - ATTRIB_OPT_FUNC("height", object->ParseHeight); + object->mX1.Parse("0"); + object->mY1.Parse("0"); + object->mX2.Parse("-1"); + object->mY2.Parse("-1"); + object->mWidth.Parse("0"); + object->mHeight.Parse("0"); + ATTRIB_OPT_FUNC("x", object->mX1.Parse); + ATTRIB_OPT_FUNC("y", object->mY1.Parse); + ATTRIB_OPT_FUNC("x1", object->mX1.Parse); + ATTRIB_OPT_FUNC("y1", object->mY1.Parse); + ATTRIB_OPT_FUNC("x2", object->mX2.Parse); + ATTRIB_OPT_FUNC("y2", object->mY2.Parse); + ATTRIB_OPT_FUNC("width", object->mWidth.Parse); + ATTRIB_OPT_FUNC("height", object->mHeight.Parse); ATTRIB_OPT_FUNC("condition", object->ParseCondition); + ATTRIB_OPT_STRING("action", object->mAction); if (name == "image") { - ATTRIB_OPT_FUNC_PARAM("x", object->ParseIntParam, object->mPos1.x); - ATTRIB_OPT_FUNC_PARAM("y", object->ParseIntParam, object->mPos1.y); - ATTRIB_OPT_FUNC_PARAM("x", object->ParseIntParam, object->mPos2.x); - ATTRIB_OPT_FUNC_PARAM("y", object->ParseIntParam, object->mPos2.y); - ATTRIB_OPT_FUNC("color", object->ParseColor); + //ATTRIB_OPT_FUNC_PARAM("x", object->ParseIntParam, object->mPos2.x); + //ATTRIB_OPT_FUNC_PARAM("y", object->ParseIntParam, object->mPos2.y); + ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor); + ATTRIB_OPT_FUNC_PARAM("bgcolor", object->ParseColor, object->mBackgroundColor); ATTRIB_MAN_FUNC("path", object->mPath.Parse); ATTRIB_OPT_FUNC("loop", object->ParseScrollLoopMode); + ATTRIB_OPT_FUNC_PARAM("opacity", object->ParseIntParam, object->mOpacity); + ATTRIB_OPT_FUNC("scale", object->ParseScale); } else if (name == "text" || name == "scrolltext") { - ATTRIB_OPT_FUNC("color", object->ParseColor); + ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor); + ATTRIB_OPT_FUNC_PARAM("bgcolor", object->ParseColor, object->mBackgroundColor); ATTRIB_OPT_FUNC("align", object->ParseAlignment); ATTRIB_OPT_FUNC("valign", object->ParseVerticalAlignment); - ATTRIB_OPT_FUNC("font", object->ParseFontFace); + ATTRIB_MAN_FUNC("font", object->ParseFontFace); ATTRIB_OPT_BOOL("multiline", object->mMultiline); + ATTRIB_OPT_FUNC("mlrelscroll", object->mMultilineRelScroll.Parse); ATTRIB_OPT_FUNC("scrollmode", object->ParseScrollLoopMode); ATTRIB_OPT_FUNC("scrollspeed", object->ParseScrollSpeed); ATTRIB_OPT_FUNC("scrolltime", object->ParseScrollTime); ATTRIB_OPT_STRING("alttext", object->mAltText); ATTRIB_OPT_FUNC("altcondition", object->ParseAltCondition); -#if 0 - if (name == "blink") - { - ATTRIB_OPT_NUMBER("delay", object->mDelay); - - if (object->mDelay == 0) - object->mDelay = 1000; - } - else if (name == "marquee") - { - ATTRIB_OPT_NUMBER("delay", object->mDelay); - - if (object->mDelay == 0) - object->mDelay = 500; - } -#endif + ATTRIB_OPT_FUNC_PARAM("effectcolor", object->ParseColor, object->mEffectColor); + ATTRIB_OPT_FUNC("effect", object->ParseEffect); + ATTRIB_OPT_NUMBER("radius", object->mRadius); + } + else if (name == "button") + { + ATTRIB_OPT_FUNC_PARAM("labelcolor", object->ParseColor, object->mColor); + ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mBackgroundColor); + ATTRIB_MAN_FUNC("font", object->ParseFontFace); + ATTRIB_OPT_NUMBER("radius", object->mRadius); } else if (name == "pixel") { - ATTRIB_OPT_FUNC("color", object->ParseColor); + ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor); } else if (name == "line") { - ATTRIB_OPT_FUNC("color", object->ParseColor); + ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor); } else if (name == "rectangle") { - ATTRIB_OPT_FUNC("color", object->ParseColor); + ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor); ATTRIB_OPT_BOOL("filled", object->mFilled); ATTRIB_OPT_NUMBER("radius", object->mRadius); } else if (name == "ellipse" || name == "slope") { - ATTRIB_OPT_FUNC("color", object->ParseColor); + ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor); ATTRIB_OPT_BOOL("filled", object->mFilled); ATTRIB_OPT_NUMBER("arc", object->mArc); } else if (name == "progress" || name == "scrollbar") { - ATTRIB_OPT_FUNC("color", object->ParseColor); - ATTRIB_OPT_NUMBER("direction", object->mDirection); - ATTRIB_OPT_FUNC("current", object->mCurrent.Parse); - ATTRIB_OPT_FUNC("total", object->mTotal.Parse); + ATTRIB_OPT_FUNC_PARAM( "color", object->ParseColor, object->mColor); + ATTRIB_OPT_NUMBER( "direction", object->mDirection); + ATTRIB_OPT_FUNC( "current", object->mCurrent.Parse); + ATTRIB_OPT_FUNC( "total", object->mTotal.Parse); + ATTRIB_OPT_FUNC( "peak", object->mPeak.Parse); + ATTRIB_OPT_FUNC_PARAM( "peakcolor", object->ParseColor, object->mPeakGradientColor); + ATTRIB_OPT_FUNC( "gradient", object->ParseGradient); + ATTRIB_OPT_FUNC_PARAM( "gradientcolor", object->ParseColor, object->mPeakGradientColor); + ATTRIB_OPT_NUMBER( "radius", object->mRadius); } #if 0 else if (name == "item") { @@ -271,6 +400,12 @@ bool StartElem(const std::string & name, std::map<std::string,std::string> & att --object->mPos2.y; } #endif + // range checks + if (object->mOpacity < 0) + object->mOpacity = 0; + else if (object->mOpacity > 255) + object->mOpacity = 255; + } else TAG_ERR_REMAIN(context[context.size() - 1].c_str()); @@ -291,13 +426,14 @@ bool CharData(const std::string & text) //printf("context: %s\n", context[context.size() - 1].c_str()); if (context[context.size() - 1] == "text" - || context[context.size() - 1] == "scrolltext") + || context[context.size() - 1] == "scrolltext" + || context[context.size() - 1] == "button") { if (!object->mText.Parse(text)) return false; } else - syslog(LOG_ERR, "ERROR: Bad character data"); + syslog(LOG_ERR, "ERROR: graphlcd/skin: Bad character data"); return true; } @@ -314,7 +450,13 @@ bool EndElem(const std::string & name) else if (name == "variable") { skin->mVariables.push_back(variable); +//fprintf(stderr, " variable '%s', value: %s\n", variable->mId.c_str(), ((std::string)variable->Value()).c_str()); variable = NULL; + if (variable_default != NULL) { + skin->mVariables.push_back(variable_default); +//fprintf(stderr, " variable default '%s', value: %s\n", variable_default->mId.c_str(), ((std::string)variable_default->Value()).c_str()); + variable_default = NULL; + } } else if (name == "display") { @@ -367,29 +509,57 @@ bool EndElem(const std::string & name) return true; } -cSkin * XmlParse(cSkinConfig & Config, const std::string & Name, const std::string & fileName) +static pthread_mutex_t parse_mutex; // temp. workaround of thread-safe parsing problem + +cSkin * XmlParse(cSkinConfig & Config, const std::string & Name, const std::string & fileName, std::string & errorString) { + pthread_mutex_lock(&parse_mutex); // temp. workaround + //fprintf(stderr, ">>>>> XmlParse, Config: %s, Name: %s\n", Config.GetDriver()->ConfigName().c_str(), Name.c_str()); skin = new cSkin(Config, Name); context.clear(); - cXML xml(fileName); + { // temp. workaround for thread-safe parsing + font = NULL; + variable = NULL; + variable_default = NULL; + display = NULL; + parents.clear(); + object = NULL; + oindex = 0; + errorDetail = ""; + condblock_cond = ""; + includeDepth = 0; + subErrorDetail = ""; + } + cXML xml(fileName, skin->Config().CharSet()); xml.SetNodeStartCB(StartElem); xml.SetNodeEndCB(EndElem); xml.SetCDataCB(CharData); if (xml.Parse() != 0) { + char buff[8]; + snprintf(buff, 7, "%d", xml.LineNr()); syslog(LOG_ERR, "ERROR: graphlcd/skin: Parse error in %s, line %d", fileName.c_str(), xml.LineNr()); + // shorter version outgoing errorString (eg. displaying errorString on the display) + errorString = "Parse error in skin "+Name+", line "+buff; + if (errorDetail != "") + errorString += ":\n"+errorDetail; delete skin; skin = NULL; delete display; display = NULL; delete object; object = NULL; + //fprintf(stderr, "<<<<< XmlParse ERROR, Config: %s, Name: %s\n", Config.GetDriver()->ConfigName().c_str(), Name.c_str()); + pthread_mutex_unlock(&parse_mutex); return NULL; } cSkin * result = skin; skin = NULL; + errorString = ""; + //fprintf(stderr, "<<<<< XmlParse, Config: %s, Name: %s\n", Config.GetDriver()->ConfigName().c_str(), Name.c_str()); + pthread_mutex_unlock(&parse_mutex); return result; } |