diff options
author | lordjaxom <lordjaxom> | 2004-12-08 18:48:39 +0000 |
---|---|---|
committer | lordjaxom <lordjaxom> | 2004-12-08 18:48:39 +0000 |
commit | 5382d18d05d358bb1c313c642395e835aa44a6a0 (patch) | |
tree | 2b5ef58620b3640c5b21e8eafe92ee4b266b1d30 /xml | |
parent | eb2f2c9600e8f69788232582191b141002bcd522 (diff) | |
download | vdr-plugin-text2skin-5382d18d05d358bb1c313c642395e835aa44a6a0.tar.gz vdr-plugin-text2skin-5382d18d05d358bb1c313c642395e835aa44a6a0.tar.bz2 |
1.0-pre1v1.0-pre1
Diffstat (limited to 'xml')
-rw-r--r-- | xml/display.c | 42 | ||||
-rw-r--r-- | xml/display.h | 58 | ||||
-rw-r--r-- | xml/function.c | 212 | ||||
-rw-r--r-- | xml/function.h | 66 | ||||
-rw-r--r-- | xml/object.c | 169 | ||||
-rw-r--r-- | xml/object.h | 148 | ||||
-rw-r--r-- | xml/parser.c | 310 | ||||
-rw-r--r-- | xml/parser.h | 14 | ||||
-rw-r--r-- | xml/skin.c | 44 | ||||
-rw-r--r-- | xml/skin.h | 64 | ||||
-rw-r--r-- | xml/string.c | 161 | ||||
-rw-r--r-- | xml/string.h | 128 | ||||
-rw-r--r-- | xml/type.c | 20 | ||||
-rw-r--r-- | xml/type.h | 42 | ||||
-rw-r--r-- | xml/xml.c | 295 | ||||
-rw-r--r-- | xml/xml.h | 63 |
16 files changed, 1836 insertions, 0 deletions
diff --git a/xml/display.c b/xml/display.c new file mode 100644 index 0000000..4790b2d --- /dev/null +++ b/xml/display.c @@ -0,0 +1,42 @@ +/* + * $Id: display.c,v 1.3 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#include "xml/display.h" + +static const std::string DisplayNames[] = + { "channelInfo", "channelSmall", "volume", "message", "replayInfo", "replaySmall", "menu" }; + +cxDisplay::cxDisplay(cxSkin *parent): + mType((eType)__COUNT_DISPLAY__), + mNumWindows(0), + mSkin(parent) +{ +} + +bool cxDisplay::ParseType(const std::string &Text) +{ + for (int i = 0; i < (int)__COUNT_DISPLAY__; ++i) { + if (DisplayNames[i].length() > 0 && DisplayNames[i] == Text) { + mType = (eType)i; + return true; + } + } + return false; +} + +const std::string &cxDisplay::GetType(void) const +{ + return DisplayNames[mType]; +} + +cxDisplays::cxDisplays(void) +{ +} + +cxDisplays::~cxDisplays() +{ + iterator it = begin(); + while (it != end()) + (delete (*it).second, ++it); +} diff --git a/xml/display.h b/xml/display.h new file mode 100644 index 0000000..6c233b2 --- /dev/null +++ b/xml/display.h @@ -0,0 +1,58 @@ +/* + * $Id: display.h,v 1.3 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#ifndef VDR_TEXT2SKIN_DISPLAY_H +#define VDR_TEXT2SKIN_DISPLAY_H + +#include "xml/object.h" +#include <string> +#include <map> + +class cxSkin; + +class cxDisplay { + friend bool xStartElem(const std::string &name, std::map<std::string,std::string> &attrs); + friend bool xEndElem(const std::string &name); + +public: + enum eType { + channelInfo, + channelSmall, + volume, + message, + replayInfo, + replaySmall, + menu, +#define __COUNT_DISPLAY__ (menu + 1) + }; + +private: + eType mType; + txWindow mWindows[MAXOSDAREAS]; + int mNumWindows; + cxObjects mObjects; + cxSkin *mSkin; + +public: + cxDisplay(cxSkin *parent); + + bool ParseType(const std::string &Text); + const std::string &GetType(void) const; + + eType Type(void) const { return mType; } + const txWindow *Windows(void) const { return mWindows; } + int NumWindows(void) const { return mNumWindows; } + cxSkin *Skin(void) const { return mSkin; } + + uint Objects(void) const { return mObjects.size(); } + const cxObject *GetObject(int n) const { return mObjects[n]; } +}; + +class cxDisplays: public std::map<cxDisplay::eType,cxDisplay*> { +public: + cxDisplays(void); + ~cxDisplays(); +}; + +#endif // VDR_TEXT2SKIN_DISPLAY_H diff --git a/xml/function.c b/xml/function.c new file mode 100644 index 0000000..e6e321c --- /dev/null +++ b/xml/function.c @@ -0,0 +1,212 @@ +/* + * $Id: function.c,v 1.4 2004/12/08 18:47:37 lordjaxom Exp $ + */ + +#include "xml/function.h" +#include "render.h" +#include "common.h" +#include <vdr/tools.h> + +static const char *Internals[] = { + "not", "and", "or", "equal", "file", "trans", NULL +}; + +const std::string cxFunction::False = ""; +const std::string cxFunction::True = "1"; + +cxFunction::cxFunction(void): + mType(string), + mString(), + mNumber(0), + mNumParams(0) +{ +} + +cxFunction::cxFunction(const cxString &String): + mType(string), + mString(String), + mNumber(0), + mNumParams(0) +{ +} + +cxFunction::cxFunction(const cxFunction &Src): + mType(Src.mType), + mString(Src.mString), + mNumber(Src.mNumber), + mNumParams(Src.mNumParams) +{ + for (uint i = 0; i < mNumParams; ++i) + mParams[i] = new cxFunction(*Src.mParams[i]); +} + +cxFunction::~cxFunction() +{ + for (uint i = 0; i < mNumParams; ++i) + delete mParams[i]; +} + +bool cxFunction::Parse(const std::string &Text) +{ + const char *text = Text.c_str(); + const char *ptr = text, *last = text; + eType type = undefined_function; + int inExpr = 0; + cxFunction *expr; + + if (*ptr == '\'' || *ptr == '{') { + // must be string + if (strlen(ptr) < 2 + || (*ptr == '\'' && *(ptr + strlen(ptr) - 1) != '\'') + || (*ptr == '{' && *(ptr + strlen(ptr) - 1) != '}')) { + esyslog("ERROR: 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, 1, "\'"); + + if (!mString.Parse(temp)) + return false; + + mType = string; + } + else { + // expression + for (; *ptr; ++ptr) { + 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 + 1 + i); + break; + } + } + + if (Internals[i] == NULL) { + esyslog("ERROR: Unknown function %.*s", (int)(ptr - last), last); + return false; + } + last = ptr + 1; + } + } + else if (*ptr == ',' || *ptr == ')') { + if (inExpr == 0) { + esyslog("ERROR: Unmatched '%c' in expression", *ptr); + return false; + } + + if (inExpr == 1) { + expr = new cxFunction; + if (!expr->Parse(std::string(last, ptr - last))) { + delete expr; + 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_eq: ++params; + case fun_not: + case fun_trans: + case fun_file: ++params; + default: break; + } + + if (params != -1 && mNumParams != (uint)params) { + esyslog("ERROR: Text2Skin: Wrong number of parameters to %s, " + "expecting %d", Internals[mType - 1 - INTERNAL], params); + return false; + } + } + + --inExpr; + } + } + } + + if (inExpr > 0) { + esyslog("ERROR: Expecting ')' in expression"); + return false; + } + } + + return true; +} + +const std::string &cxFunction::FunFile(const std::string &Param) const +{ + std::string path = cText2SkinRender::ImagePath(Param); + Dprintf("checking file(%s)\n", path.c_str()); + if (access(path.c_str(), F_OK) == 0) + return Param; + return False; +} + +std::string cxFunction::Evaluate(void) const +{ + switch (mType) { + case string: + return mString.Evaluate(); + + case fun_not: + return mParams[0]->EvaluateToBool() ? False : True; + + case fun_and: + for (uint i = 0; i < mNumParams; ++i) { + if (!mParams[i]->EvaluateToBool()) + return False; + } + return True; + + case fun_or: + for (uint i = 0; i < mNumParams; ++i) { + if (mParams[i]->EvaluateToBool()) + return True; + } + return False; + + case fun_eq: + return mParams[0]->Evaluate() == mParams[1]->Evaluate() ? True : False; + + case fun_file: + return FunFile(mParams[0]->Evaluate()); + + case fun_trans: + return tr(mParams[0]->Evaluate().c_str()); + + default: + Dprintf("unknown function code\n"); + esyslog("ERROR: Unknown function code called (this shouldn't happen)"); + break; + } + return False; +} + +bool cxFunction::EvaluateToBool(void) +{ + std::string result = Evaluate(); + if (result == False/* || result == "0"*/) + return false; + return true; +} + diff --git a/xml/function.h b/xml/function.h new file mode 100644 index 0000000..a7d8838 --- /dev/null +++ b/xml/function.h @@ -0,0 +1,66 @@ +/* + * $Id: function.h,v 1.4 2004/12/08 18:47:37 lordjaxom Exp $ + */ + +#ifndef VDR_TEXT2SKIN_XML_FUNCTION_H +#define VDR_TEXT2SKIN_XML_FUNCTION_H + +#include "xml/string.h" +#include <string> + +#define STRING 0x01000000 +#define NUMBER 0x02000000 +#define INTERNAL 0x04000000 + +#define MAXPARAMETERS 16 + +class cxFunction { +public: + enum eType { + undefined_function, + + string = STRING, + number = NUMBER, + + fun_not = INTERNAL + 1, + fun_and = INTERNAL + 2, + fun_or = INTERNAL + 3, + fun_eq = INTERNAL + 4, + fun_file = INTERNAL + 5, + fun_trans = INTERNAL + 6 + }; + + static const std::string False; + static const std::string True; + +private: + eType mType; + cxString mString; + int mNumber; + cxFunction *mParams[MAXPARAMETERS]; + uint mNumParams; + +protected: + const std::string &FunFile(const std::string &Param) const; + +public: + cxFunction(void); + cxFunction(const cxString &String); + cxFunction(const cxFunction &Src); + ~cxFunction(); + + bool Parse(const std::string &Text); + std::string Evaluate(void) const; + bool EvaluateToBool(void); + + void SetIndex(uint Index, int Tab); +}; + +inline void cxFunction::SetIndex(uint Index, int Tab) +{ + mString.SetIndex(Index, Tab); + for (uint i = 0; i < mNumParams; ++i) + mParams[i]->SetIndex(Index, Tab); +} + +#endif // VDR_TEXT2SKIN_XML_FUNCTION_H diff --git a/xml/object.c b/xml/object.c new file mode 100644 index 0000000..6932106 --- /dev/null +++ b/xml/object.c @@ -0,0 +1,169 @@ +/* + * $Id: object.c,v 1.4 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#include "xml/object.h" +#include "render.h" +#include "font.h" + +static const std::string ObjectNames[] = + { "image", "text", "rectangle", "ellipse", "slope", "progress", "scrolltext", "scrollbar", + "block", "list", "item" }; + +cxObject::cxObject(cxDisplay *parent): + mType((eType)__COUNT_OBJECT__), + mPos1(0, 0), + mPos2(-1, -1), + mAlpha(255), + mArc(0), + mAlign(taDefault), + mCondition(NULL), + mFont(cFont::GetFont(fontOsd)), + mObjects(NULL), + mDisplay(parent), + mSkin(parent->Skin()) +{ +} + +cxObject::cxObject(const cxObject &Src): + mType(Src.mType), + mPos1(Src.mPos1), + mPos2(Src.mPos2), + mAlpha(Src.mAlpha), + mArc(Src.mArc), + mFg(Src.mFg), + mBg(Src.mBg), + mMark(Src.mMark), + mActive(Src.mActive), + mKeep(Src.mKeep), + mPath(Src.mPath), + mText(Src.mText), + mAlign(Src.mAlign), + mCondition(NULL), + mCurrent(Src.mCurrent), + mTotal(Src.mTotal), + mFont(Src.mFont), + mObjects(NULL), + mDisplay(Src.mDisplay), + mSkin(Src.mSkin) +{ + if (Src.mCondition) + mCondition = new cxFunction(*Src.mCondition); + if (Src.mObjects) + mObjects = new cxObjects(*Src.mObjects); +} + +cxObject::~cxObject() +{ + delete mCondition; + delete mObjects; +} + +bool cxObject::ParseType(const std::string &Text) { + for (int i = 0; i < (int)__COUNT_OBJECT__; ++i) { + if (ObjectNames[i] == Text) { + mType = (eType)i; + return true; + } + } + return false; +} + +bool cxObject::ParseCondition(const std::string &Text) { + cxFunction *result = new cxFunction; + if (result->Parse(Text)) { + delete mCondition; + mCondition = result; + return true; + } + return false; +} + +bool cxObject::ParseAlignment(const std::string &Text) { + if (Text == "center") mAlign = (eTextAlignment)(taTop | taCenter); + else if (Text == "right") mAlign = (eTextAlignment)(taTop | taRight); + else if (Text == "left") mAlign = (eTextAlignment)(taTop | taLeft); + else + return false; + return true; +} + +bool cxObject::ParseFontFace(const std::string &Text) { + /*for (int i = 0; i < eDvbFontSize; ++i) { + if (FontNames[i] == Text) { + mFont = cFont::GetFont((eDvbFont)i); + return true; + } + }*/ + + int size = 0, pos; + std::string face = Text; + if ((pos = face.find(':')) != -1) { + size = atoi(face.substr(pos + 1).c_str()); + face.erase(pos); + } + Dprintf("trying: %s %d\n", ((std::string)SkinPath() + "/fonts/" + face).c_str(), size); + if ((mFont = cText2SkinFont::Load(SkinPath() + "/fonts", face, size)) != NULL) + return true; + else if ((mFont = cText2SkinFont::Load(SkinPath() + "/" + mSkin->Name(), face, size)) != NULL) + return true; + return false; +} + +const std::string &cxObject::TypeName(void) const { + return ObjectNames[mType]; +} + +txPoint cxObject::Pos(void) const { + return txPoint(mSkin->BaseOffset().x + (mPos1.x < 0 ? Skin()->BaseSize().w + mPos1.x : mPos1.x), + mSkin->BaseOffset().y + (mPos1.y < 0 ? Skin()->BaseSize().h + mPos1.y : mPos1.y)); +} + +txSize cxObject::Size(void) const { + txPoint p1(mSkin->BaseOffset().x + (mPos1.x < 0 ? Skin()->BaseSize().w + mPos1.x : mPos1.x), + mSkin->BaseOffset().y + (mPos1.y < 0 ? Skin()->BaseSize().h + mPos1.y : mPos1.y)); + txPoint p2(mSkin->BaseOffset().x + (mPos2.x < 0 ? Skin()->BaseSize().w + mPos2.x : mPos2.x), + mSkin->BaseOffset().y + (mPos2.y < 0 ? Skin()->BaseSize().h + mPos2.y : mPos2.y)); + return txSize(p2.x - p1.x + 1, p2.y - p1.y + 1); +} + +const tColor *cxObject::Fg(void) const +{ + static tColor Fg; + return cText2SkinRender::ItemColor(mFg, Fg) ? &Fg : NULL; +} + +const tColor *cxObject::Bg(void) const +{ + static tColor Bg; + return cText2SkinRender::ItemColor(mBg, Bg) ? &Bg : NULL; +} + +const tColor *cxObject::Mark(void) const +{ + static tColor Mark; + return cText2SkinRender::ItemColor(mMark, Mark) ? &Mark : NULL; +} + +const tColor *cxObject::Active(void) const +{ + static tColor Active; + return cText2SkinRender::ItemColor(mActive, Active) ? &Active : NULL; +} + +const tColor *cxObject::Keep(void) const +{ + static tColor Keep; + return cText2SkinRender::ItemColor(mKeep, Keep) ? &Keep : NULL; +} + +cxObjects::cxObjects(void) +{ +} + +cxObjects::~cxObjects() +{ + for (uint i = 0; i < size(); ++i) + delete operator[](i); +} + diff --git a/xml/object.h b/xml/object.h new file mode 100644 index 0000000..8b134a9 --- /dev/null +++ b/xml/object.h @@ -0,0 +1,148 @@ +/* + * $Id: object.h,v 1.4 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#ifndef VDR_TEXT2SKIN_XML_OBJECT_H +#define VDR_TEXT2SKIN_XML_OBJECT_H + +#include "xml/string.h" +#include "xml/function.h" +#include <vdr/osd.h> +#include <vector> +#include <string> +#include <map> + +class cxDisplay; +class cxSkin; + +struct txPoint { + int x, y; + txPoint(int _x = 0, int _y = 0) { x = _x; y = _y; } +}; + +struct txSize { + int w, h; + txSize(int _w = 0, int _h = 0) { w = _w; h = _h; } +}; + +struct txWindow { + txPoint pos1; + txPoint pos2; + int bpp; + txWindow(int _x1 = 0, int _y1 = 0, int _x2 = -1, int _y2 = -1, int _bpp=4): + pos1(_x1, _y2), pos2(_x2, _y2), bpp(_bpp) {} +}; + +class cxObjects; + +class cxObject { + friend class cText2SkinRender; + friend bool xStartElem(const std::string &name, std::map<std::string,std::string> &attrs); + friend bool xCharData(const std::string &text); + friend bool xEndElem(const std::string &name); + + /* Skin Editor */ + friend class VSkinnerItem; + +public: + enum eType { + image, + text, + rectangle, + ellipse, + slope, + progress, + scrolltext, + scrollbar, + block, + list, + item, +#define __COUNT_OBJECT__ (item + 1) + }; + +private: + eType mType; + txPoint mPos1; + txPoint mPos2; + int mAlpha; + int mArc; + std::string mFg; + std::string mBg; + std::string mMark; + std::string mActive; + std::string mKeep; + cxString mPath; + cxString mText; + eTextAlignment mAlign; + cxFunction *mCondition; + cxString mCurrent; + cxString mTotal; + const cFont *mFont; + cxObjects *mObjects; // used for block objects such as <list> + cxDisplay *mDisplay; + cxSkin *mSkin; + +public: + cxObject(cxDisplay *parent); + cxObject(const cxObject &Src); + ~cxObject(); + + bool ParseType (const std::string &Text); + bool ParseCondition(const std::string &Text); + bool ParseAlignment(const std::string &Text); + bool ParseFontFace (const std::string &Text); + + void SetIndex(uint Index, int Tab); + + eType Type(void) const { return mType; } + cxFunction *Condition(void) const { return mCondition; } + int Alpha(void) const { return mAlpha; } + eTextAlignment Align(void) const { return mAlign; } + int Arc(void) const { return mArc; } + std::string Path(void) const { return mPath.Evaluate(); } + std::string Text(void) const { return mText.Evaluate(); } + int Current(void) const { return mCurrent.Evaluate(); } + int Total(void) const { return mTotal.Evaluate(); } + const cFont *Font(void) const { return mFont; } + cxDisplay *Display(void) const { return mDisplay; } + cxSkin *Skin(void) const { return mSkin; } + + const std::string &TypeName(void) const; + txPoint Pos(void) const; + txSize Size(void) const; + const tColor *Fg(void) const; + const tColor *Bg(void) const; + const tColor *Mark(void) const; + const tColor *Active(void) const; + const tColor *Keep(void) const; + + uint Objects(void) const; + const cxObject *GetObject(uint Index) const; +}; + +inline void cxObject::SetIndex(uint Index, int Tab) +{ + mText.SetIndex(Index, Tab); + mPath.SetIndex(Index, Tab); + if (mCondition != NULL) + mCondition->SetIndex(Index, Tab); +} + +class cxObjects: public std::vector<cxObject*> { +public: + cxObjects(void); + ~cxObjects(); +}; + +// recursive dependancy +inline uint cxObject::Objects(void) const +{ + return mObjects ? mObjects->size() : 0; +} + +inline const cxObject *cxObject::GetObject(uint Index) const +{ + return mObjects ? (*mObjects)[Index] : NULL; +} + +#endif // VDR_TEXT2SKIN_XML_OBJECT_H diff --git a/xml/parser.c b/xml/parser.c new file mode 100644 index 0000000..1349769 --- /dev/null +++ b/xml/parser.c @@ -0,0 +1,310 @@ +/* + * $Id: parser.c,v 1.4 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#include "xml/parser.h" +#include "xml/xml.h" +#include "xml/skin.h" +#include "common.h" +#include <vdr/tools.h> +#include <stdio.h> +#include <vector> +#include <string> + +#define STR_SKIN "skin" +#define STR_DISPLAY "display" +#define STR_ID "id" +#define STR_WINDOW "window" +#define STR_X1 "x1" +#define STR_Y1 "y1" +#define STR_X "x" +#define STR_Y "y" +#define STR_X2 "x2" +#define STR_Y2 "y2" +#define STR_BPP "bpp" +#define STR_IMAGE "image" +#define STR_PATH "path" +#define STR_ALPHA "alpha" +#define STR_TEXT "text" +#define STR_CONDITION "condition" +#define STR_NOT "not" +#define STR_FILE "file" +#define STR_COLOR "color" +#define STR_ALIGN "align" +#define STR_RECTANGLE "rectangle" +#define STR_ELLIPSE "ellipse" +#define STR_SLOPE "slope" +#define STR_PROGRESS "progress" +#define STR_BGCOLOR "bgcolor" +#define STR_CURRENT "current" +#define STR_TOTAL "total" +#define STR_VERSION "version" +#define STR_NAME "name" +#define STR_SCREENBASE "screenBase" +#define STR_FONT "font" +#define STR_ARC "arc" + +#define MSG_BADTAG "ERROR: The tag %s was not expected in this context" +#define MSG_BADENDTAG "ERROR: The tag %s was not expected in this context" +#define MSG_BADATTR "ERROR: The attribute %s was not expected in tag %s" +#define MSG_MISSATTR "ERROR: The tag %s lacks the attribute %s" +#define MSG_BADVALUE "ERROR: %s is not allowed for attribute %s" +#define MSG_PARSERR "ERROR: Parser error in %s:%d: %s" +#define MSG_BADCDATA "ERROR: Bad character data" +#define MSG_NOFILE "ERROR: Couldn't read %s: %m" +#define MSG_MANYWINS "ERROR: Too many windows" + +#define TAG_ERR_REMAIN(_context) do { \ + esyslog("ERROR: Text2Skin: Unexpected tag %s within %s", \ + name.c_str(), _context); \ + return false; \ + } while (0) + +#define TAG_ERR_CHILD(_context) do { \ + esyslog("ERROR: Text2Skin: No child tag %s expected within %s", \ + name.c_str(), _context); \ + return false; \ + } while (0) + +#define TAG_ERR_END(_context) do { \ + esyslog("ERROR: Text2Skin: Unexpected closing tag for %s within %s", \ + name.c_str(), _context); \ + return false; \ + } while (0) + +#define ATTRIB_OPT_STRING(_attr,_target) \ + if (attrs.find(_attr) != attrs.end()) { \ + _target = attrs[_attr]; \ + attrs.erase(_attr); \ + } + +#define ATTRIB_MAN_STRING(_attr,_target) \ + ATTRIB_OPT_STRING(_attr,_target) \ + else { \ + esyslog("ERROR: Text2Skin: Mandatory attribute %s missing in tag %s", \ + _attr, name.c_str()); \ + return false; \ + } + +#define ATTRIB_OPT_NUMBER(_attr,_target) \ + if (attrs.find(_attr) != attrs.end()) { \ + char *_e; const char *_t = attrs[_attr].c_str(); \ + long _l = strtol(_t, &_e, 10); \ + if (_e ==_t || *_e != '\0') { \ + esyslog("ERROR: Text2Skin: Invalid numeric value \"%s\" in attribute %s", \ + _t, _attr); \ + return false; \ + } else \ + _target = _l; \ + attrs.erase(_attr); \ + } + +#define ATTRIB_MAN_NUMBER(_attr,_target) \ + ATTRIB_OPT_NUMBER(_attr,_target) \ + else { \ + esyslog("ERROR: Text2Skin: Mandatory attribute %s missing in tag %s", \ + _attr, name.c_str()); \ + return false; \ + } + +#define ATTRIB_OPT_FUNC(_attr,_func) \ + if (attrs.find(_attr) != attrs.end()) { \ + if (!_func(attrs[_attr])) { \ + esyslog("ERROR: Text2Skin: Unexpected value %s for attribute %s", \ + attrs[_attr].c_str(), _attr); \ + return false; \ + } \ + attrs.erase(_attr); \ + } + +#define ATTRIB_MAN_FUNC(_attr,_func) \ + ATTRIB_OPT_FUNC(_attr,_func) \ + else { \ + esyslog("ERROR: Text2Skin: Mandatory attribute %s missing in tag %s", \ + _attr, name.c_str()); \ + return false; \ + } + +static std::vector<std::string> context; +static cxSkin *skin = NULL; +static cxDisplay *display = NULL; +static cxObject *parent = NULL; +static cxObject *object = NULL; + +bool xStartElem(const std::string &name, std::map<std::string,std::string> &attrs) { + Dprintf("start element: %s\n", name.c_str()); + + if (context.size() == 0) { + if (name == "skin") { + ATTRIB_MAN_STRING("version", skin->mVersion); + ATTRIB_MAN_STRING("name", skin->mTitle); + ATTRIB_MAN_FUNC ("screenBase", skin->ParseBase); + } + else + TAG_ERR_REMAIN("document"); + } + else if (context[context.size() - 1] == "skin") { + if (name == "display") { + display = new cxDisplay(skin); + ATTRIB_MAN_FUNC ("id", display->ParseType); + } + else + TAG_ERR_REMAIN("skin"); + } + else if (context[context.size() - 1] == "display" + || context[context.size() - 1] == "list" + || context[context.size() - 1] == "block") { + if (object != NULL) { + parent = object; + object = NULL; + } + + if (name == "window") { + if (display->mNumWindows < MAXOSDAREAS) { + txWindow window; + ATTRIB_OPT_NUMBER("x1", window.pos1.x); + ATTRIB_OPT_NUMBER("y1", window.pos1.y); + ATTRIB_OPT_NUMBER("x2", window.pos2.x); + ATTRIB_OPT_NUMBER("y2", window.pos2.y); + ATTRIB_OPT_NUMBER("bpp", window.bpp); + display->mWindows[display->mNumWindows++] = window; + } else + esyslog("ERROR: Text2Skin: Too many windows in display"); + } + else { + object = new cxObject(display); + if (object->ParseType(name)) { + ATTRIB_OPT_NUMBER("x1", object->mPos1.x); + ATTRIB_OPT_NUMBER("y1", object->mPos1.y); + ATTRIB_OPT_NUMBER("x2", object->mPos2.x); + ATTRIB_OPT_NUMBER("y2", object->mPos2.y); + ATTRIB_OPT_FUNC ("condition", object->ParseCondition); + + if (name == "image") { + ATTRIB_OPT_NUMBER("x", object->mPos1.x); + ATTRIB_OPT_NUMBER("y", object->mPos1.y); + ATTRIB_OPT_NUMBER("alpha", object->mAlpha); + ATTRIB_OPT_STRING("color", object->mFg); + ATTRIB_OPT_STRING("bgColor", object->mBg); + ATTRIB_MAN_FUNC ("path", object->mPath.Parse); + } + else if (name == "text" + || name == "scrolltext") { + ATTRIB_OPT_STRING("color", object->mFg); + ATTRIB_OPT_FUNC ("align", object->ParseAlignment); + ATTRIB_OPT_FUNC ("font", object->ParseFontFace); + } + else if (name == "rectangle") { + ATTRIB_OPT_STRING("color", object->mFg); + } + else if (name == "ellipse" || name == "slope") { + ATTRIB_OPT_STRING("color", object->mFg); + ATTRIB_OPT_NUMBER("arc", object->mArc); + } + else if (name == "progress" + || name == "scrollbar") { + ATTRIB_OPT_STRING("color", object->mFg); + ATTRIB_OPT_STRING("bgColor", object->mBg); + ATTRIB_OPT_FUNC ("current", object->mCurrent.Parse); + ATTRIB_OPT_FUNC ("total", object->mTotal.Parse); + } + else if (name == "item") { + ATTRIB_MAN_NUMBER("height", object->mPos2.y); + --object->mPos2.y; + } + } else + TAG_ERR_REMAIN(context[context.size() - 1].c_str()); + } + } else + TAG_ERR_CHILD(context[context.size() - 1].c_str()); + context.push_back(name); + return true; +} + +bool xCharData(const std::string &text) { + int start = 0, end = text.length() - 1; + + Dprintf("char data before: %s\n", text.c_str()); + + while (text[start] == '\012' || text[start] == '\015' || text[start] == ' ' || text[start] == '\t') + ++start; + + while (text[end] == '\012' || text[end] == '\015' || text[end] == ' ' || text[end] == '\t') + --end; + + Dprintf("char data after: %s\n", text.substr(start, end - start + 1).c_str()); + + if (end - start + 1 > 0) { + Dprintf("context: %s\n", context[context.size() - 1].c_str()); + if (context[context.size() - 1] == "text" + || context[context.size() - 1] == "scrolltext") { + if (!object->mText.Parse(text.substr(start, end - start + 1))) + return false; + } else + esyslog(MSG_BADCDATA); + } + return true; +} + +bool xEndElem(const std::string &name) { + Dprintf("end element: %s\n", name.c_str()); + if (context[context.size() - 1] == name) { + if (name == "display") { + skin->mDisplays[display->Type()] = display; + display = NULL; + } + else if (object != NULL || parent != NULL) { + if (object == NULL) { + Dprintf("rotating parent to object\n"); + object = parent; + parent = NULL; + } + + if (object->mCondition == NULL) { + switch (object->mType) { + case cxObject::text: + object->mCondition = new cxFunction(object->mText); + break; + + default: + break; + } + } + + if (parent != NULL) { + Dprintf("pushing to parent\n"); + if (parent->mObjects == NULL) + parent->mObjects = new cxObjects(); + parent->mObjects->push_back(object); + } + else + display->mObjects.push_back(object); + object = NULL; + } + context.pop_back(); + } else + TAG_ERR_END(context[context.size() - 1].c_str()); + return true; +} + +cxSkin *xmlParse(const std::string &name, const std::string &fileName) { + skin = new cxSkin(name); + context.clear(); + + XML xml(fileName); + xml.nodeStartCB(xStartElem); + xml.nodeEndCB(xEndElem); + xml.cdataCB(xCharData); + if (xml.parse() != 0) { + esyslog("ERROR: Text2Skin: Parse error in %s, line %d", fileName.c_str(), xml.lineNr()); + DELETENULL(skin); + DELETENULL(display); + DELETENULL(object); + return NULL; + } + + cxSkin *result = skin; + skin = NULL; + return result; +} + diff --git a/xml/parser.h b/xml/parser.h new file mode 100644 index 0000000..60fe30c --- /dev/null +++ b/xml/parser.h @@ -0,0 +1,14 @@ +/* + * $Id: parser.h,v 1.1.1.1 2004/11/19 16:45:31 lordjaxom Exp $ + */ + +#ifndef VDR_TEXT2SKIN_PARSER_H +#define VDR_TEXT2SKIN_PARSER_H + +#include <string> + +class cxSkin; + +cxSkin *xmlParse(const std::string &name, const std::string &fileName); + +#endif // VDR_TEXT2SKIN_PARSER_H diff --git a/xml/skin.c b/xml/skin.c new file mode 100644 index 0000000..0cc753b --- /dev/null +++ b/xml/skin.c @@ -0,0 +1,44 @@ +/* + * $Id: skin.c,v 1.2 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#include "xml/skin.h" +#include <vdr/tools.h> +#include <vdr/config.h> + +const std::string ScreenBases[] = { "relative", "absolute" }; + +cxSkin::cxSkin(const std::string &Name): + mName(Name) { +} + +void cxSkin::SetBase(eScreenBase Base) { + mBase = Base; + switch (mBase) { + case relative: + mBaseOffset = txPoint(Setup.OSDLeft, Setup.OSDTop); + mBaseSize = txSize(Setup.OSDWidth, Setup.OSDHeight); + break; + + case absolute: + mBaseOffset = txPoint(0, 0); + mBaseSize = txSize(720, 576); //XXX + break; + + default: + break; + } +} + +bool cxSkin::ParseBase(const std::string &Text) { + int i; + for (i = 0; i < (int)__COUNT_BASE__; ++i) { + if (ScreenBases[i] == Text) + break; + } + if (i < (int)__COUNT_BASE__) { + SetBase((eScreenBase)i); + return true; + } + return false; +} diff --git a/xml/skin.h b/xml/skin.h new file mode 100644 index 0000000..40e25e7 --- /dev/null +++ b/xml/skin.h @@ -0,0 +1,64 @@ +/* + * $Id: skin.h,v 1.3 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#ifndef VDR_TEXT2SKIN_XML_SKIN_H +#define VDR_TEXT2SKIN_XML_SKIN_H + +#include "xml/display.h" +#include <vdr/osd.h> +#include <expat.h> +#include <map> +#include <string> + +// --- cxSkin ----------------------------------------------------------------- + +class cxSkin { + friend bool xStartElem(const std::string &name, std::map<std::string,std::string> &attrs); + friend bool xEndElem(const std::string &name); + + /* Skin Editor */ + friend class VSkinnerView; + +public: + enum eScreenBase { + relative, + absolute, +#define __COUNT_BASE__ (absolute + 1) + }; + +private: + eScreenBase mBase; + txPoint mBaseOffset; + txSize mBaseSize; + std::string mName; + std::string mTitle; + std::string mVersion; + + cxDisplays mDisplays; + +protected: + void SetBase(eScreenBase Base); + +public: + cxSkin(const std::string &Name); + + cxDisplay *Get(cxDisplay::eType Type); + + bool ParseBase(const std::string &Text); + + eScreenBase Base(void) const { return mBase; } + const txPoint &BaseOffset(void) const { return mBaseOffset; } + const txSize &BaseSize(void) const { return mBaseSize; } + const std::string &Name(void) const { return mName; } + const std::string &Title(void) const { return mTitle; } + const std::string &Version(void) const { return mVersion; } +}; + +inline cxDisplay *cxSkin::Get(cxDisplay::eType Type) { + if (mDisplays.find(Type) != mDisplays.end()) + return mDisplays[Type]; + return NULL; +} + +#endif // VDR_TEXT2SKIN_XML_SKIN_H diff --git a/xml/string.c b/xml/string.c new file mode 100644 index 0000000..051a003 --- /dev/null +++ b/xml/string.c @@ -0,0 +1,161 @@ +/* + * $Id: string.c,v 1.5 2004/12/08 17:22:28 lordjaxom Exp $ + */ + +#include "xml/string.h" +#include "render.h" +#include <vdr/tools.h> + +static const char *Tokens[__COUNT_TOKEN__] = { + "DateTime", + + // Channel Display + "ChannelNumber", "ChannelName", "ChannelShortName", "ChannelBouquet", "PresentStartDateTime", + "PresentVPSDateTime", "PresentEndDateTime", "PresentDuration", "PresentProgress", + "PresentTitle", "PresentShortText", "PresentDescription", "FollowingStartDateTime", + "FollowingVPSDateTime", "FollowingEndDateTime", "FollowingDuration", + "FollowingTitle", "FollowingShortText", "FollowingDescription", "Language", + "HasTeletext", "HasMultilang", "HasDolby", "IsEncrypted", "IsRadio", "IsRecording", "HasVPS", + "HasTimer", "IsRunning", + + // Volume Display + "VolumeCurrent", "VolumeTotal", "IsMute", + + // Message Display + "Message", "MessageStatus", "MessageInfo", "MessageWarning", + "MessageError", + + // Replay Display + "ReplayTitle", "ReplayPositionIndex", "ReplayDurationIndex", "ReplayPrompt", "IsPlaying", + "IsFastForward", "IsFastRewind", "IsSlowForward", "IsSlowRewind", "IsPausing", + "ReplayPosition", "ReplayDuration", "ReplayMode", + + // Menu Page + "MenuTitle", "MenuGroup", "IsMenuGroup", "MenuItem", "IsMenuItem", "MenuCurrent", "IsMenuCurrent", + "MenuText", "ButtonRed", "ButtonGreen", "ButtonYellow", "ButtonBlue", "CanScrollUp", + "CanScrollDown" +}; + +std::string txToken::Token(const txToken &Token) { + std::string result = (std::string)"{" + Tokens[Token.Type]; + if (Token.Attrib.length() > 0) + result += ":" + Token.Attrib; + result += "}"; + + return result; +} + +cxString::cxString(void) { +} + +bool cxString::Parse(const std::string &Text) { + const char *text = Text.c_str(); + const char *ptr = text, *last = text; + bool inToken = false; + bool inAttrib = false; + int offset = 0; + + Dprintf("parsing: %s\n", Text.c_str()); + + for (; *ptr; ++ptr) { + if (inToken && *ptr == '\\') { + if (*(ptr + 1) == '\0') { + esyslog("ERROR: Stray \\ in token attribute\n"); + return false; + } + + ++ptr; + continue; + } + else if (*ptr == '{') { + if (inToken) { + esyslog("ERROR: Unexpected '{' in token"); + return false; + } + + mText.append(last, ptr - last); + last = ptr + 1; + inToken = true; + } + else if (*ptr == '}' || (inToken && *ptr == ':')) { + if (!inToken) { + esyslog("ERROR: Unexpected '}' outside of token"); + return false; + } + + if (inAttrib) { + if (*ptr == ':') { + esyslog("ERROR: Unexpected ':' inside of token attribute"); + return false; + } + + int pos = -1; + txToken &lastToken = mTokens[mTokens.size() - 1]; + lastToken.Attrib.assign(last, ptr - last); + while ((pos = lastToken.Attrib.find('\\', pos + 1)) != -1) { + switch (lastToken.Attrib[pos + 1]) { + case 'n': + lastToken.Attrib.replace(pos, 2, "\n"); + break; + + default: + lastToken.Attrib.erase(pos, 1); + } + } + + inAttrib = false; + inToken = false; + } else { + int i; + for (i = 0; i < (int)__COUNT_TOKEN__; ++i) { + if ((size_t)(ptr - last) == strlen(Tokens[i]) + && memcmp(last, Tokens[i], ptr - last) == 0) { + txToken token((exToken)i, offset, ""); + mTokens.push_back(token); + break; + } + } + + if (i == (int)__COUNT_TOKEN__) { + esyslog("ERROR: Unexpected token {%.*s}", (int)(ptr - last), last); + return false; + } + + if (*ptr == ':') + inAttrib = true; + else + inToken = false; + } + + last = ptr + 1; + } + else if (!inToken) + ++offset; + } + + if (inToken) { + esyslog("ERROR: Expecting '}' in token"); + return false; + } + + mText.append(last, ptr - last); + return true; +} + +cxType cxString::Evaluate(void) const +{ + std::string result; + int offset = 0; + + if (mText.length() == 0 && mTokens.size() == 1) + return cText2SkinRender::GetToken(mTokens[0]); + + for (uint i = 0; i < mTokens.size(); ++i) { + result.append(mText.c_str() + offset, mTokens[i].Offset - offset); + result.append(cText2SkinRender::GetToken(mTokens[i])); + offset = mTokens[i].Offset; + } + result.append(mText.c_str() + offset); + return result; +} + diff --git a/xml/string.h b/xml/string.h new file mode 100644 index 0000000..5c89d13 --- /dev/null +++ b/xml/string.h @@ -0,0 +1,128 @@ +/* + * $Id: string.h,v 1.5 2004/12/08 18:47:37 lordjaxom Exp $ + */ + +#ifndef VDR_TEXT2SKIN_XML_STRING_H +#define VDR_TEXT2SKIN_XML_STRING_H + +#include "xml/type.h" +#include <string> +#include <vector> + +enum exToken { + tDateTime, + + // Channel Display + tChannelNumber, + tChannelName, + tChannelShortName, + tChannelBouquet, + // next 8 also in Menu + tPresentStartDateTime, + tPresentVPSDateTime, + tPresentEndDateTime, + tPresentDuration, + tPresentProgress, + tPresentTitle, + tPresentShortText, + tPresentDescription, + tFollowingStartDateTime, + tFollowingVPSDateTime, + tFollowingEndDateTime, + tFollowingDuration, + tFollowingTitle, + tFollowingShortText, + tFollowingDescription, + tLanguage, + tHasTeletext, + tHasMultilang, + tHasDolby, + tIsEncrypted, + tIsRadio, + tIsRecording, + // next 3 also in Menu + tHasVPS, + tHasTimer, + tIsRunning, + + // VolumeDisplay + tVolumeCurrent, + tVolumeTotal, + tIsMute, + + // Message Display + tMessage, + tMessageStatus, + tMessageInfo, + tMessageWarning, + tMessageError, + + // Replay Display + tReplayTitle, + tReplayPositionIndex, + tReplayDurationIndex, + tReplayPrompt, + tIsPlaying, + tIsFastForward, + tIsFastRewind, + tIsSlowForward, + tIsSlowRewind, + tIsPausing, + tReplayPosition, + tReplayDuration, + tReplayMode, + + // Menu Page + tMenuTitle, + tMenuGroup, + tIsMenuGroup, + tMenuItem, + tIsMenuItem, + tMenuCurrent, + tIsMenuCurrent, + tMenuText, + tButtonRed, + tButtonGreen, + tButtonYellow, + tButtonBlue, + tCanScrollUp, + tCanScrollDown, + +#define __COUNT_TOKEN__ (tCanScrollDown + 1) +}; + +struct txToken { + exToken Type; + uint Offset; + std::string Attrib; + int Index; + int Tab; + + txToken(void): Index(-1), Tab(-1) {} + txToken(exToken t, uint o, const std::string &a): Type(t), Offset(o), Attrib(a), Index(-1), Tab(-1) {} + static std::string Token(const txToken &Token); +}; + +class cxString { +private: + std::string mText; + std::vector<txToken> mTokens; + +public: + cxString(void); + + bool Parse(const std::string &Text); + cxType Evaluate(void) const; + + void SetIndex(uint Index, int Tab); +}; + +inline void cxString::SetIndex(uint Index, int Tab) +{ + for (uint i = 0; i < mTokens.size(); ++i) { + mTokens[i].Index = Index; + mTokens[i].Tab = Tab; + } +} + +#endif // VDR_TEXT2SKIN_XML_STRING_H diff --git a/xml/type.c b/xml/type.c new file mode 100644 index 0000000..d311b30 --- /dev/null +++ b/xml/type.c @@ -0,0 +1,20 @@ +/* + * $Id: type.c,v 1.2 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#include "xml/type.h" +#include "xml/function.h" +#include <stdio.h> + +const std::string &cxType::String(void) { + static char buffer[50]; + if (mType == number) { + snprintf(buffer, sizeof(buffer), "%d", mNumber); + mString = buffer; + mType = string; + } else if (mType == boolean) { + mString = mNumber ? cxFunction::True : cxFunction::False; + mType = string; + } + return mString; +} diff --git a/xml/type.h b/xml/type.h new file mode 100644 index 0000000..1b1113c --- /dev/null +++ b/xml/type.h @@ -0,0 +1,42 @@ +/* + * $Id: type.h,v 1.3 2004/12/08 17:13:26 lordjaxom Exp $ + */ + +#ifndef VDR_TEXT2SKIN_XML_TYPE_H +#define VDR_TEXT2SKIN_XML_TYPE_H + +#include <string> + +class cxType { +public: + enum eType { + string, + number, + boolean + }; + +private: + eType mType; + std::string mString; + int mNumber; + +public: + cxType(const char *String): mType(string), mString(String ?: "") {} + cxType(std::string String): mType(string), mString(String) {} + cxType(int Number): mType(number), mNumber(Number) {} + cxType(time_t Number): mType(number), mNumber(Number) {} + cxType(bool Value): mType(boolean), mNumber(Value ? 1 : 0) {} + + const std::string &String(void); + int Number(void) const; + + operator std::string () { return String(); } + operator int () { return Number(); } + operator bool () { return Number(); } +}; + +inline int cxType::Number(void) const { + return mType == number ? mNumber : 0; +} + +#endif // VDR_TEXT2SKIN_XML_TYPE_H diff --git a/xml/xml.c b/xml/xml.c new file mode 100644 index 0000000..07dae8c --- /dev/null +++ b/xml/xml.c @@ -0,0 +1,295 @@ +/* + * $Id: xml.c,v 1.2 2004/12/06 15:01:02 lordjaxom Exp $ + * This module was kindly provided by Clemens Kirchgatterer + */ + +#include <ctype.h> + +#include <iostream> +#include <fstream> + +#include "xml.h" + +using namespace std; + +enum { + LOOK4START, // looking for first element start + LOOK4TAG, // looking for element tag + INTAG, // reading tag + LOOK4ATTRN, // looking for attr name, > or / + INATTRN, // reading attr name + LOOK4ATTRV, // looking for attr value + SAWSLASH, // saw / in element opening + INATTRV, // in attr value + LOOK4CLOSETAG, // looking for closing tag after < + INCLOSETAG, // reading closing tag +}; + +XML::XML(const string &file) { + char *buffer; + long size; + + ifstream f(file.c_str(), ios::in|ios::binary|ios::ate); + size = f.tellg(); + f.seekg(0, ios::beg); + buffer = new char [size]; + f.read(buffer, size); + f.close(); + data = buffer; + delete[] buffer; + nodestartcb = NULL; + nodeendcb = NULL; + cdatacb = NULL; + parseerrorcb = NULL; + progresscb = NULL; +} + +XML::XML(const char *mem, unsigned int len) { + data.assign(mem, len); + nodestartcb = NULL; + nodeendcb = NULL; + cdatacb = NULL; + parseerrorcb = NULL; + progresscb = NULL; +} + +void +XML::nodeStartCB(XML_NODE_START_CB(cb)) { + nodestartcb = cb; +} + +void +XML::nodeEndCB(XML_NODE_END_CB(cb)) { + nodeendcb = cb; +} + +void +XML::cdataCB(XML_CDATA_CB(cb)) { + cdatacb = cb; +} + +void +XML::parseErrorCB(XML_PARSE_ERROR_CB(cb)) { + parseerrorcb = cb; +} + +void +XML::progressCB(XML_PROGRESS_CB(cb)) { + progresscb = cb; +} + +int +XML::parse(void) { + float percent = 0; + unsigned int len; + int last = 0; + + state = LOOK4START; + linenr = 1; + skipping = false; + len = data.length(); + for (unsigned int i=0; i<len; i++) { + if (readChar(data[i])) return (-1); + if (progresscb) { + percent = ((float)i/len)*100; + if ((int)percent>last) { + progresscb((int)percent); + last = ((int)percent); + } + } + } + return (0); +} + +bool +XML::isTokenChar(bool start, int c) { + return (isalpha(c) || c == '_' || (!start && isdigit(c))); +} + +int +XML::readChar(int c) { + // new line? + if (c == '\n') linenr++; + + switch (state) { + + // looking for element start + case LOOK4START: + if (c == '<') { + if (cdatacb) { + int pos = 0; + while ((pos = cdata.find('&', pos)) != -1) { + if (cdata.substr(pos, 4) == "<") + cdata.replace(pos, 4, "<"); + else if (cdata.substr(pos, 4) == ">") + cdata.replace(pos, 4, ">"); + else if (cdata.substr(pos, 5) == "&") + cdata.replace(pos, 5, "&"); + ++pos; + } + if (!cdatacb(cdata)) + return (-1); + } + cdata = ""; + attr.clear(); + tag = ""; + state = LOOK4TAG; + } else + cdata += c; + // silently ignore until resync + break; + + // looking for element tag + case LOOK4TAG: + // skip comments and declarations. + if (skipping) { + if (c == '>') { + skipping = false; + state = LOOK4START; + } + break; + } else { + if (c == '?' || c == '!') { + skipping = true; + break; + } + } + if (isTokenChar(1, c)) { + tag += c; + state = INTAG; + } else if (c == '/') { + state = LOOK4CLOSETAG; + } else if (!isspace(c)) { + if (parseerrorcb) { + parseerrorcb(linenr, "Bogus tag char", c); + } + return (-1); + } + break; + + // reading tag + case INTAG: + if (isTokenChar(0, c)) { + tag += c; + } else if (c == '>') { + if (nodestartcb) + if (!nodestartcb(tag, attr)) + return (-1); + state = LOOK4START; + } else if (c == '/') { + state = SAWSLASH; + } else { + state = LOOK4ATTRN; + } + break; + + // looking for attr name, > or / + case LOOK4ATTRN: + if (c == '>') { + if (nodestartcb) + if (!nodestartcb(tag, attr)) + return (-1); + state = LOOK4START; + } else if (c == '/') { + state = SAWSLASH; + } else if (isTokenChar(1, c)) { + attrn = ""; + attrn += c; + state = INATTRN; + } else if (!isspace(c)) { + if (parseerrorcb) { + parseerrorcb(linenr, "Bogus 1st attr name char", c); + } + return (-2); + } + break; + + // saw / in element opening + case SAWSLASH: + if (c == '>') { + if (nodestartcb) + if (!nodestartcb(tag, attr)) + return (-1); + if (nodeendcb) + if (!nodeendcb(tag)) + return (-1); + state = LOOK4START; + } else { + if (parseerrorcb) { + parseerrorcb(linenr, "Bogus char before >", c); + } + return (-3); + } + break; + + // reading attr name + case INATTRN: + if (isTokenChar(0, c)) { + attrn += c; + } else if (isspace(c) || c == '=') { + state = LOOK4ATTRV; + } else { + if (parseerrorcb) { + parseerrorcb(linenr, "Bogus attr name char", c); + } + return (-4); + } + break; + + // looking for attr value + case LOOK4ATTRV: + if (c == '\'' || c == '"') { + delim = c; + attrv = ""; + state = INATTRV; + } else if (!(isspace(c) || c == '=')) { + if (parseerrorcb) { + parseerrorcb(linenr, "No attribute value", c); + } + return (-5); + } + break; + + // in attr value + case INATTRV: + if (c == delim) { + attr[attrn] = attrv; + state = LOOK4ATTRN; + } else if (!iscntrl(c)) { + attrv += c; + } + break; + + // looking for closing tag after < + case LOOK4CLOSETAG: + if (isTokenChar(1, c)) { + tag += c; + state = INCLOSETAG; + } else if (!isspace(c)) { + if (parseerrorcb) { + parseerrorcb(linenr, "Bogus preend tag char", c); + } + return (-6); + } + break; + + // reading closing tag + case INCLOSETAG: + if (isTokenChar(0, c)) { + tag += c; + } else if (c == '>') { + if (nodeendcb) + if (!nodeendcb(tag)) + return false; + state = LOOK4START; + } else if (!isspace(c)) { + if (parseerrorcb) { + parseerrorcb(linenr, "Bogus end tag char", c); + } + return (-7); + } + break; + } + + return (0); +} diff --git a/xml/xml.h b/xml/xml.h new file mode 100644 index 0000000..9abc641 --- /dev/null +++ b/xml/xml.h @@ -0,0 +1,63 @@ +/* + * $Id: xml.h,v 1.1.1.1 2004/11/19 16:45:31 lordjaxom Exp $ + * This module was kindly provided by Clemens Kirchgatterer + */ + +#ifndef _XML_H_ +#define _XML_H_ + +#include <string> +#include <map> + +#define XML_NODE_START_CB(CB) \ +bool (*CB)(const std::string &tag, std::map<std::string, std::string> &attr) +#define XML_NODE_END_CB(CB) \ +bool (*CB)(const std::string &tag) +#define XML_CDATA_CB(CB) \ +bool (*CB)(const std::string &text) +#define XML_PARSE_ERROR_CB(CB) \ +void (*CB)(int line, const char *txt, char c) +#define XML_PROGRESS_CB(CB) \ +void (*CB)(int percent) + +class XML { + +public: + + XML(const std::string &file); + XML(const char *mem, unsigned int len); + + void nodeStartCB(XML_NODE_START_CB(cb)); + void nodeEndCB(XML_NODE_END_CB(cb)); + void cdataCB(XML_CDATA_CB(cb)); + void parseErrorCB(XML_PARSE_ERROR_CB(cb)); + void progressCB(XML_PROGRESS_CB(cb)); + + int parse(void); + + int lineNr(void) const { return linenr; } + +protected: + + bool isTokenChar(bool start, int c); + int readChar(int c); + +private: + + bool skipping; + int state; + int linenr; + int delim; + + std::string data, cdata, tag, attrn, attrv; + std::map<std::string, std::string> attr; + + XML_NODE_START_CB(nodestartcb); + XML_NODE_END_CB(nodeendcb); + XML_CDATA_CB(cdatacb); + XML_PARSE_ERROR_CB(parseerrorcb); + XML_PROGRESS_CB(progresscb); + +}; + +#endif // _XML_H_ |