/***************************************************************************
* Copyright (C) 2004 by Tomas Mecir *
* kmuddy@kmuddy.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
***************************************************************************/
#include "celementmanager.h"
#include "centitymanager.h"
#include "cmxpcolors.h"
#include "cmxpparser.h"
#include "cmxpstate.h"
#include "cresulthandler.h"
#include "stringops.h"
#include <stdlib.h>
cElementManager::cElementManager (cMXPState *st, cResultHandler *res, cEntityManager *enm)
{
state = st;
results = res;
entities = enm;
paramexpander = new cEntityManager (true);
parser = new cMXPParser;
reset ();
createInternalElements ();
}
cElementManager::~cElementManager ()
{
delete paramexpander;
paramexpander = 0;
delete parser;
parser = 0;
removeAll ();
//internal elements
map<string, sInternalElement *>::iterator it;
for (it = ielements.begin(); it != ielements.end(); ++it)
{
it->second->attlist.clear ();
it->second->attdefault.clear ();
delete it->second;
}
ielements.clear ();
aliases.clear ();
}
void cElementManager::reset ()
{
lastLineTag = 0;
removeAll ();
}
void cElementManager::assignMXPState (cMXPState *st)
{
state = st;
}
void cElementManager::createInternalElements ()
{
//the list doesn't contain information on whether an argument is required or not
//processor of the tag implements this functionality
//create all internal elements
sInternalElement *e;
//!element
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("name"); //this name is not in the spec!
e->attlist.push_back ("definition"); //this name is not in the spec!
e->attlist.push_back ("att");
e->attlist.push_back ("tag");
e->attlist.push_back ("flag");
e->attlist.push_back ("open");
e->attlist.push_back ("delete");
e->attlist.push_back ("empty");
e->attdefault["open"] = ""; //flags
e->attdefault["delete"] = "";
e->attdefault["empty"] = "";
ielements["!element"] = e;
//!attlist
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("name"); //this name is not in the spec!
e->attlist.push_back ("att");
ielements["!attlist"] = e;
//!entity
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("name"); //this name is not in the spec!
e->attlist.push_back ("value"); //this name is not in the spec!
e->attlist.push_back ("desc");
e->attlist.push_back ("private");
e->attlist.push_back ("publish");
e->attlist.push_back ("add");
e->attlist.push_back ("delete");
e->attlist.push_back ("remove");
e->attdefault["private"] = ""; //flags
e->attdefault["publish"] = "";
e->attdefault["delete"] = "";
e->attdefault["add"] = "";
e->attdefault["remove"] = "";
ielements["!entity"] = e;
//var
e = new sInternalElement;
e->empty = false;
e->open = false;
e->attlist.push_back ("name"); //this name is not in the spec!
e->attlist.push_back ("desc");
e->attlist.push_back ("private");
e->attlist.push_back ("publish");
e->attlist.push_back ("add");
e->attlist.push_back ("delete");
e->attlist.push_back ("remove");
e->attdefault["private"] = ""; //flags
e->attdefault["publish"] = "";
e->attdefault["delete"] = "";
e->attdefault["add"] = "";
e->attdefault["remove"] = "";
ielements["var"] = e;
//b
e = new sInternalElement;
e->empty = false;
e->open = true;
ielements["b"] = e;
//i
e = new sInternalElement;
e->empty = false;
e->open = true;
ielements["i"] = e;
//u
e = new sInternalElement;
e->empty = false;
e->open = true;
ielements["u"] = e;
//s
e = new sInternalElement;
e->empty = false;
e->open = true;
ielements["s"] = e;
//c
e = new sInternalElement;
e->empty = false;
e->open = true;
e->attlist.push_back ("fore");
e->attlist.push_back ("back");
ielements["c"] = e;
//h
e = new sInternalElement;
e->empty = false;
e->open = true;
ielements["h"] = e;
//font
e = new sInternalElement;
e->empty = false;
e->open = true;
e->attlist.push_back ("face");
e->attlist.push_back ("size");
e->attlist.push_back ("color");
e->attlist.push_back ("back");
ielements["font"] = e;
//nobr
e = new sInternalElement;
e->empty = true;
e->open = false;
ielements["nobr"] = e;
//p
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["p"] = e;
//br
e = new sInternalElement;
e->empty = true;
e->open = false;
ielements["br"] = e;
//sbr
e = new sInternalElement;
e->empty = true;
e->open = false;
ielements["sbr"] = e;
//a
e = new sInternalElement;
e->empty = false;
e->open = false;
e->attlist.push_back ("href");
e->attlist.push_back ("hint");
e->attlist.push_back ("expire");
ielements["a"] = e;
//send
e = new sInternalElement;
e->empty = false;
e->open = false;
e->attlist.push_back ("href");
e->attlist.push_back ("hint");
e->attlist.push_back ("prompt");
e->attlist.push_back ("expire");
e->attdefault["prompt"] = ""; //flags
ielements["send"] = e;
//expire
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("name"); //this name is not in the spec!
ielements["expire"] = e;
//version
e = new sInternalElement;
e->empty = true;
e->open = false;
ielements["version"] = e;
//support
e = new sInternalElement;
e->empty = true;
e->open = false;
ielements["support"] = e;
//h1
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["h1"] = e;
//h2
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["h2"] = e;
//h3
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["h3"] = e;
//h4
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["h4"] = e;
//h5
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["h5"] = e;
//h6
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["h6"] = e;
//hr
e = new sInternalElement;
e->empty = true;
e->open = false;
ielements["hr"] = e;
//small
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["small"] = e;
//tt
e = new sInternalElement;
e->empty = false;
e->open = false;
ielements["tt"] = e;
//sound
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("fname");
e->attlist.push_back ("v");
e->attlist.push_back ("l");
e->attlist.push_back ("p");
e->attlist.push_back ("t");
e->attlist.push_back ("u");
e->attdefault["v"] = "100";
e->attdefault["l"] = "1";
e->attdefault["p"] = "50";
ielements["sound"] = e;
//music
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("fname");
e->attlist.push_back ("v");
e->attlist.push_back ("l");
e->attlist.push_back ("c");
e->attlist.push_back ("t");
e->attlist.push_back ("u");
e->attdefault["v"] = "100";
e->attdefault["l"] = "1";
e->attdefault["c"] = "1";
ielements["music"] = e;
//gauge
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("entity"); //this name is not in the spec!
e->attlist.push_back ("max");
e->attlist.push_back ("caption");
e->attlist.push_back ("color");
ielements["gauge"] = e;
//stat
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("entity"); //this name is not in the spec!
e->attlist.push_back ("max");
e->attlist.push_back ("caption");
ielements["stat"] = e;
//frame
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("name");
e->attlist.push_back ("action");
e->attlist.push_back ("title");
e->attlist.push_back ("internal");
e->attlist.push_back ("align");
e->attlist.push_back ("left");
e->attlist.push_back ("top");
e->attlist.push_back ("width");
e->attlist.push_back ("height");
e->attlist.push_back ("scrolling");
e->attlist.push_back ("floating");
e->attdefault["action"] = "open";
e->attdefault["align"] = "top";
e->attdefault["left"] = "0";
e->attdefault["top"] = "0";
e->attdefault["internal"] = ""; //flags
e->attdefault["scrolling"] = "";
e->attdefault["floating"] = "";
ielements["frame"] = e;
//dest
e = new sInternalElement;
e->empty = false;
e->open = false;
e->attlist.push_back ("name"); //this name is not in the spec!
e->attlist.push_back ("x");
e->attlist.push_back ("y");
e->attlist.push_back ("eol");
e->attlist.push_back ("eof");
e->attdefault["eol"] = ""; //flags
e->attdefault["eof"] = "";
ielements["dest"] = e;
//relocate
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("name"); //this name is not in the spec!
e->attlist.push_back ("port"); //this name is not in the spec!
ielements["relocate"] = e;
//user
e = new sInternalElement;
e->empty = true;
e->open = false;
ielements["user"] = e;
//password
e = new sInternalElement;
e->empty = true;
e->open = false;
ielements["password"] = e;
//image
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("fname");
e->attlist.push_back ("url");
e->attlist.push_back ("t");
e->attlist.push_back ("h");
e->attlist.push_back ("w");
e->attlist.push_back ("hspace");
e->attlist.push_back ("vspace");
e->attlist.push_back ("align");
e->attlist.push_back ("ismap");
e->attdefault["align"] = "top";
e->attdefault["ismap"] = ""; //flags
ielements["image"] = e;
//filter
e = new sInternalElement;
e->empty = true;
e->open = false;
e->attlist.push_back ("src");
e->attlist.push_back ("dest");
e->attlist.push_back ("name");
ielements["filter"] = e;
//finally, define some aliases for internal elements
aliases["!el"] = "!element";
aliases["!at"] = "!attlist";
aliases["!en"] = "!entity";
aliases["v"] = "var";
aliases["bold"] = "b";
aliases["strong"] = "b";
aliases["italic"] = "i";
aliases["em"] = "i";
aliases["underline"] = "u";
aliases["strikeout"] = "s";
aliases["high"] = "h";
aliases["color"] = "c";
aliases["destination"] = "dest";
}
bool cElementManager::elementDefined (const string &name)
{
return ((elements.count (name) != 0) || (ielements.count (name) != 0) ||
(aliases.count (name) != 0));
}
bool cElementManager::internalElement (const string &name)
{
return ((ielements.count (name) != 0) || (aliases.count (name) != 0));
}
bool cElementManager::customElement (const string &name)
{
return (elements.count (name) != 0);
}
bool cElementManager::openElement (const string &name)
{
if (!elementDefined (name))
return false;
if (internalElement (name))
{
string n = name;
if (aliases.count (name))
n = aliases[name];
return ielements[n]->open;
}
else
return elements[name]->open;
}
bool cElementManager::emptyElement (const string &name)
{
if (!elementDefined (name))
return false;
if (internalElement (name))
{
string n = name;
if (aliases.count (name))
n = aliases[name];
return ielements[n]->empty;
}
else
return elements[name]->empty;
}
enum tagParserState {
tagBegin,
tagName,
tagParam,
tagParamValue,
tagQuotedParam,
tagAfterQuotedParam,
tagBetweenParams
};
void cElementManager::gotTag (const string &tag)
{
string tagname;
list<sParam> params;
sParam param;
param.flag = false;
char quote;
tagParserState pstate = tagBegin;
string::const_iterator it;
for (it = tag.begin(); it != tag.end(); ++it)
{
char ch = *it;
//process character
switch (pstate) {
case tagBegin: {
if (ch != ' ')
{
pstate = tagName;
tagname += ch;
}
break;
}
case tagName: {
if (ch == ' ')
pstate = tagBetweenParams;
else
tagname += ch;
break;
}
case tagParam: {
if (ch == '=')
pstate = tagParamValue;
else if (ch == ' ')
{
//one parameter, value only (it could also be a flag, we'll check that later)
param.value = param.name;
param.name = "";
//add a new parameter :-)
params.push_back (param);
param.value = "";
pstate = tagBetweenParams;
}
else
param.name += ch;
break;
}
case tagParamValue: {
if (ch == ' ')
{
//add a new parameter :-)
params.push_back (param);
param.name = "";
param.value = "";
pstate = tagBetweenParams;
}
else if (param.value.empty() && ((ch == '\'') || (ch == '"')))
{
pstate = tagQuotedParam;
quote = ch;
}
else
param.value += ch;
break;
}
case tagQuotedParam: {
if (ch == quote)
{
//add a new parameter :-)
params.push_back (param);
param.name = "";
param.value = "";
pstate = tagAfterQuotedParam;
}
else
param.value += ch;
break;
}
case tagAfterQuotedParam: {
if (ch == ' ') //ignore everything up to some space...
pstate = tagBetweenParams;
break;
}
case tagBetweenParams: {
if (ch != ' ')
{
if ((ch == '\'') || (ch == '"'))
{
pstate = tagQuotedParam;
param.name = "";
quote = ch;
}
else
{
pstate = tagParam;
param.name += ch;
}
}
break;
}
};
}
//last parameter...
switch (pstate) {
case tagBegin:
results->addToList (results->createError ("Received a tag with no body!"));
break;
case tagParam: {
param.value = param.name;
param.name = "";
params.push_back (param);
}
break;
case tagParamValue:
params.push_back (param);
break;
case tagQuotedParam:
results->addToList (results->createError ("Received tag " + tagname +
" with unfinished quoted parameter!"));
break;
};
//nothing more to do if the tag has no contents...
if (pstate == tagBegin) return;
//convert tag name to lowercase
tagname = lcase (tagname);
//handle closing tag...
if (tagname[0] == '/')
{
if (!params.empty())
results->addToList (results->createError ("Received closing tag " + tagname +
" with parametrs!"));
//remove that '/'
tagname.erase (tagname.begin());
//and call closing tag processing
handleClosingTag (tagname);
return;
}
//convert all parameter names to lower-case
list<sParam>::iterator parit;
for (parit = params.begin(); parit != params.end(); ++parit)
(*parit).name = lcase ((*parit).name);
//now we check the type of the tag and act accordingly
if (!elementDefined (tagname))
{
params.clear();
results->addToList (results->createError ("Received undefined tag " + tagname + "!"));
return;
}
mxpMode m = state->getMXPMode ();
//mode can be open or secure; locked mode is not possible here (or we're in a bug)
if (m == openMode)
//open mode - only open tags allowed
if (!openElement (tagname))
{
params.clear();
results->addToList (results->createError ("Received secure tag " + tagname +
" in open mode!"));
return;
}
if (internalElement (tagname))
{
//if the name is an alias for another tag, change the name
if (aliases.count (tagname))
tagname = aliases[tagname];
//the <support> tag has to be handled separately :(
if (tagname == "support")
{
processSupport (params);
return;
}
//identify all flags in the tag
identifyFlags (ielements[tagname]->attdefault, params);
//correctly identify all parameters (assign names where necessary)
handleParams (tagname, params, ielements[tagname]->attlist, ielements[tagname]->attdefault);
//separate out all the flags (flags are only valid for internal tags)
list<string> flags;
parit = params.begin();
while (parit != params.end())
{
if ((*parit).flag)
{
flags.push_back ((*parit).name);
parit = params.erase (parit);
}
else
++parit;
}
//okay, parsing done - send the tag for further processing
processInternalTag (tagname, params, flags);
}
else
{
handleParams (tagname, params, elements[tagname]->attlist, elements[tagname]->attdefault);
processCustomTag (tagname, params);
}
params.clear ();
}
void cElementManager::identifyFlags (const map<string, string> &attdefault,
list<sParam> &args)
{
list<sParam>::iterator it;
for (it = args.begin(); it != args.end(); ++it)
if ((*it).name.empty())
{
string s = lcase ((*it).value);
if ((attdefault.count (s) != 0) && (attdefault.find(s)->second == ""))
{
//this one is a flag
(*it).name = s;
(*it).value = "";
(*it).flag = true;
}
}
}
void cElementManager::handleParams (const string &tagname, list<sParam> &args,
const list<string> &attlist, const map<string, string> &attdefault)
{
list<string>::const_iterator cur = attlist.begin();
list<sParam>::iterator it;
for (it = args.begin(); it != args.end(); ++it)
{
//flag?
if ((*it).flag)
{
//only advance parameter iterator
cur++;
}
//not a flag
else
{
//empty name?
if ((*it).name.empty())
{
//set the parameter name:
//find the correct attribute name, skipping all flags
while (cur != attlist.end())
{
if ((attdefault.count (*cur) != 0) && (attdefault.find(*cur)->second == "")) //flag
cur++;
else
break; //okay, found the correct parameter
}
if (cur == attlist.end()) //ARGH! Parameter not found :(
{
results->addToList (results->createError ("Received too much parameters for tag " +
tagname + "!"));
continue; //continue with the next parameter...
}
}
//non-empty name?
else
{
//set "cur" to the parameter following the given one
//to speed things up a bit, first look if the iterator is pointing at the right parameter
// (we won't need to traverse the whole list, if it does)
if ((cur == attlist.end()) || ((*it).name != *cur))
{
list<string>::const_iterator cur2 = cur; //remember old "cur" value
for (cur = attlist.begin(); cur != attlist.end(); ++cur)
if ((*it).name == *cur)
break;
if (cur == attlist.end()) //parameter name not found
{
//restore old iterator value
cur = cur2;
results->addToList (results->createError ("Received unknown parameter " +
(*it).name + " in tag " + tagname + "!"));
//clear name/value to avoid confusion in later stages
(*it).name = "";
(*it).value = "";
//proceed with next parameter
continue;
}
//if cur isn't attlist.end(), it's now set to the correct value...
}
}
//things common to all non-flag parameters...
//set parameter name
(*it).name = *cur;
//if parameter value is empty, set it to default value (if any)
if ((*it).value.empty() && (attdefault.count (*cur) != 0))
(*it).value = attdefault.find(*cur)->second;
//advance parameter iterator
cur++;
}
}
//finally, we add default parameter values to the beginning of the list... these shall get
//overridden by given values, if any (those shall be later in the list)
sParam s;
map<string, string>::const_iterator mit;
for (mit = attdefault.begin(); mit != attdefault.end(); ++mit)
if (mit->second != "") //not a flag
{
s.flag = false;
s.name = mit->first;
s.value = mit->second;
args.push_front (s);
}
}
void cElementManager::gotLineTag (int number)
{
if ((number < 20) || (number > 99))
{
lastLineTag = 0;
return;
}
if (lineTags.count (number) == 0)
{
lastLineTag = 0;
return;
}
string tag = lineTags[number];
lastLineTag = number;
//behave as if we've just gotten the appropriate tag
gotTag (tag);
}
void cElementManager::gotNewLine ()
{
if ((lastLineTag < 20) || (lastLineTag > 99))
{
lastLineTag = 0;
return;
}
if (lineTags.count (lastLineTag) == 0)
{
lastLineTag = 0;
return;
}
string tag = lineTags[lastLineTag];
lastLineTag = 0;
if (emptyElement (tag))
//no closing tag needed
return;
//okay, send out the appropriate closing tag
handleClosingTag (tag);
}
void cElementManager::handleClosingTag (const string &name)
{
string n = lcase (name);
if (!elementDefined (n))
{
results->addToList (results->createError ("Received unknown closing tag </" + n + ">!"));
return;
}
if (emptyElement (n))
{
results->addToList (results->createError ("Received closing tag for tag " + n +
", which doesn't need a closing tag!"));
return;
}
if (internalElement (n))
{
//if the name is an alias for another tag, change the name
if (aliases.count (n))
n = aliases[n];
state->gotClosingTag (n);
}
else
{
//send closing flag, if needed
if (!elements[n]->flag.empty())
state->gotFlag (false, elements[n]->flag);
//expand the closing tag...
list<string>::iterator cit;
for (cit = elements[n]->closingseq.begin(); cit != elements[n]->closingseq.end(); ++cit)
handleClosingTag (*cit);
}
}
void cElementManager::processInternalTag (const string &name, const list<sParam> ¶ms,
const list<string> &flags)
{
list<sParam>::const_iterator it;
list<string>::const_iterator it2;
if (name == "!element")
{
string name, definition, att, flag;
int tag;
bool fopen = false, fdelete = false, fempty = false;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "name") name = lcase ((*it).value);
if (s == "definition") definition = (*it).value;
if (s == "att") att = (*it).value;
if (s == "flag") flag = (*it).value;
if (s == "tag") tag = atoi ((*it).value.c_str());
}
for (it2 = flags.begin(); it2 != flags.end(); ++it2)
{
if (*it2 == "open") fopen = true;
if (*it2 == "delete") fdelete = true;
if (*it2 == "empty") fempty = true;
}
if (name.empty())
{
results->addToList (results->createError (
"Received an <!element> tag with no element name!"));
return;
}
//definition can be empty, that's no problem...
//if we want to delete the tag...
if (fdelete)
{
//sanity check
if (!elements.count (name))
{
results->addToList (results->createWarning (
"Received request to remove an undefined tag " + name + "!"));
return;
}
removeElement (name);
return;
}
//parse tag definition
parser->simpleParse (definition);
list<sElementPart *> tagcontents;
while (parser->hasNext ())
{
chunk ch = parser->getNext ();
if (ch.chk == chunkError)
results->addToList (results->createError (ch.text));
else
{
//create a new element part
sElementPart *part = new sElementPart;
part->text = ch.text;
part->istag = (ch.chk == chunkTag) ? true : false;
tagcontents.push_back (part);
}
}
//parse attribute list
list<string> attlist;
map<string, string> attdefault;
processParamList (att, attlist, attdefault);
//and do the real work
addElement (name, tagcontents, attlist, attdefault, fopen, fempty, tag, flag);
}
else if (name == "!attlist")
{
string name, att;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "name") name = (*it).value;
if (s == "att") att = (*it).value;
}
if (name.empty())
{
results->addToList (results->createError (
"Received an <!attlist> tag with no element name!"));
return;
}
//parse attribute list
list<string> attlist;
map<string, string> attdefault;
processParamList (att, attlist, attdefault);
//and do the real work
setAttList (name, attlist, attdefault);
}
else if (name == "!entity")
{
string name, value, desc;
bool fpriv = false, fpub = false, fadd = false, fdel = false, frem = false;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "name") name = (*it).value;
if (s == "value") value = (*it).value;
if (s == "desc") desc = (*it).value;
}
for (it2 = flags.begin(); it2 != flags.end(); ++it2)
{
if (*it2 == "private") fpriv = true;
if (*it2 == "publish") fpub = true;
if (*it2 == "delete") fdel = true;
if (*it2 == "add") fadd = true;
if (*it2 == "remove") frem = true;
}
if (name.empty())
{
results->addToList (results->createError (
"Received an <!entity> tag with no variable name!"));
return;
}
//fpub is IGNORED...
//fadd and frem is IGNORED...
if (!(fadd) && !(frem))
{
if (fdel)
{
entities->deleteEntity (name);
if (!fpriv) //do not announce PRIVATE entities
state->gotVariable (name, "", true);
}
else
{
//we now have a new variable...
entities->addEntity (name, value);
if (!fpriv) //do not announce PRIVATE entities
state->gotVariable (name, value);
}
}
else
results->addToList (results->createWarning (
"Ignored <!ENTITY> tag with ADD or REMOVE flag."));
}
else if (name == "var")
{
//this is very similar to the !entity handler above...
string name, desc;
bool fpriv = false, fpub = false, fadd = false, fdel = false, frem = false;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "name") name = (*it).value;
if (s == "desc") desc = (*it).value;
}
for (it2 = flags.begin(); it2 != flags.end(); ++it2)
{
if (*it2 == "private") fpriv = true;
if (*it2 == "publish") fpub = true;
if (*it2 == "add") fadd = true;
if (*it2 == "delete") fdel = true;
if (*it2 == "remove") frem = true;
}
if (name.empty())
{
results->addToList (results->createError (
"Received an <var> tag with no variable name!"));
return;
}
//fpriv and fpub is IGNORED...
//fadd and fdel is IGNORED...
if (!(fadd) && !(fdel))
state->gotVAR (name);
else
results->addToList (results->createWarning ("Ignored <VAR> tag with ADD or REMOVE flag."));
}
else if (name == "b")
state->gotBOLD();
else if (name == "i")
state->gotITALIC();
else if (name == "u")
state->gotUNDERLINE();
else if (name == "s")
state->gotSTRIKEOUT();
else if (name == "c")
{
string fore, back;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "fore") fore = (*it).value;
if (s == "back") back = (*it).value;
}
RGB fg = state->fgColor();
RGB bg = state->bgColor();
if (!fore.empty())
fg = cMXPColors::self()->color (fore);
if (!back.empty())
bg = cMXPColors::self()->color (back);
state->gotCOLOR (fg, bg);
}
else if (name == "h")
state->gotHIGH();
else if (name == "font")
{
string face, fore, back;
int size = 0;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "face") face = (*it).value;
if (s == "size") size = atoi ((*it).value.c_str());
if (s == "color") fore = (*it).value;
if (s == "back") back = (*it).value;
}
if (face.empty())
face = state->fontFace();
if (size == 0)
size = state->fontSize();
RGB fg = state->fgColor();
RGB bg = state->bgColor();
if (!fore.empty())
fg = cMXPColors::self()->color (fore);
if (!back.empty())
bg = cMXPColors::self()->color (back);
state->gotFONT (face, size, fg, bg);
}
else if (name == "p")
state->gotP();
else if (name == "br")
state->gotBR();
else if (name == "nobr")
state->gotNOBR();
else if (name == "sbr")
state->gotSBR();
else if (name == "a")
{
string href, hint, expire;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "href") href = (*it).value;
if (s == "hint") hint = (*it).value;
if (s == "expire") expire = (*it).value;
}
state->gotA (href, hint, expire);
}
else if (name == "send")
{
string href, hint, expire;
bool prompt = false;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "href") href = (*it).value;
if (s == "hint") hint = (*it).value;
if (s == "expire") expire = (*it).value;
}
for (it2 = flags.begin(); it2 != flags.end(); ++it2)
if (*it2 == "prompt") prompt = true;
state->gotSEND (href, hint, prompt, expire);
}
else if (name == "expire")
{
string name;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "name") name = (*it).value;
}
//name can be empty - all named links shall then expire
state->gotEXPIRE (name);
}
else if (name == "version")
state->gotVERSION();
else if (name == "h1")
state->gotHtag (1);
else if (name == "h2")
state->gotHtag (2);
else if (name == "h3")
state->gotHtag (3);
else if (name == "h4")
state->gotHtag (4);
else if (name == "h5")
state->gotHtag (5);
else if (name == "h6")
state->gotHtag (6);
else if (name == "hr")
state->gotHR();
else if (name == "small")
state->gotSMALL();
else if (name == "tt")
state->gotTT();
else if (name == "sound")
{
string fname, t, u;
int v = 0, l = 0, p = 0; //shall be overridden by defaults...
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "fname") fname = (*it).value;
if (s == "t") t = (*it).value;
if (s == "u") u = (*it).value;
if (s == "v") v = atoi ((*it).value.c_str());
if (s == "l") l = atoi ((*it).value.c_str());
if (s == "p") p = atoi ((*it).value.c_str());
}
if (fname.empty())
{
results->addToList (results->createError ("Received SOUND tag with no file name!"));
return;
}
if ((v < 0) || (v > 100))
{
results->addToList (results->createWarning ("Ignoring incorrect V param for SOUND tag."));
v = 100; //set default value
}
if ((l < -1) || (l > 100) || (l == 0))
{
results->addToList (results->createWarning ("Ignoring incorrect L param for SOUND tag."));
l = 1; //set default value
}
if ((p < 0) || (p > 100))
{
results->addToList (results->createWarning ("Ignoring incorrect P param for SOUND tag."));
p = 50; //set default value
}
state->gotSOUND (fname, v, l, p, t, u);
}
else if (name == "music")
{
string fname, t, u;
int v = 0, l = 0, c = 0; //shall be overridden by defaults...
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "fname") fname = (*it).value;
if (s == "t") t = (*it).value;
if (s == "u") u = (*it).value;
if (s == "v") v = atoi ((*it).value.c_str());
if (s == "l") l = atoi ((*it).value.c_str());
if (s == "c") c = atoi ((*it).value.c_str());
}
if (fname.empty())
{
results->addToList (results->createError ("Received MUSIC tag with no file name!"));
return;
}
if ((v < 0) || (v > 100))
{
results->addToList (results->createWarning ("Ignoring incorrect V param for MUSIC tag."));
v = 100; //set default value
}
if ((l < -1) || (l > 100) || (l == 0))
{
results->addToList (results->createWarning ("Ignoring incorrect L param for MUSIC tag."));
l = 1; //set default value
}
if ((c != 0) && (c != 1))
{
results->addToList (results->createWarning ("Ignoring incorrect C param for MUSIC tag."));
c = 1; //set default value
}
state->gotMUSIC (fname, v, l, (c!=0), t, u);
}
else if (name == "gauge")
{
string entity, max, caption, color;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "entity") entity = (*it).value;
if (s == "max") max = (*it).value;
if (s == "caption") caption = (*it).value;
if (s == "color") color = (*it).value;
}
if (entity.empty())
{
results->addToList (results->createError ("Received GAUGE with no entity name!"));
return;
}
RGB c;
if (color.empty()) color = "white";
c = cMXPColors::self()->color (color);
state->gotGAUGE (entity, max, caption, c);
}
else if (name == "stat")
{
string entity, max, caption;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "entity") entity = (*it).value;
if (s == "max") max = (*it).value;
if (s == "caption") caption = (*it).value;
}
if (entity.empty())
{
results->addToList (results->createError ("Received STAT with no entity name!"));
return;
}
state->gotSTAT (entity, max, caption);
}
else if (name == "frame")
{
string name, action, title, align;
int left = 0, top = 0, width = 0, height = 0;
bool finternal = false, fscroll = false, ffloat = false;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "name") name = (*it).value;
if (s == "action") action = (*it).value;
if (s == "title") title = (*it).value;
if (s == "align") align = (*it).value;
if (s == "left") left = state->computeCoord ((*it).value.c_str(), true);
if (s == "top") top = state->computeCoord ((*it).value.c_str(), false);
if (s == "width") width = state->computeCoord ((*it).value.c_str(), true);
if (s == "height") height = state->computeCoord ((*it).value.c_str(), false);
}
for (it2 = flags.begin(); it2 != flags.end(); ++it2)
{
if (*it2 == "internal") finternal = true;
if (*it2 == "scrolling") fscroll = true;
if (*it2 == "floating") ffloat = true;
}
if (name.empty())
{
results->addToList (results->createError ("Received FRAME tag with no frame name!"));
return;
}
state->gotFRAME (name, action, title, finternal, align, left, top, width, height,
fscroll, ffloat);
}
else if (name == "dest")
{
string name;
int x = 0, y = 0;
bool feol = false, feof = false;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "name") name = (*it).value;
if (s == "x") x = atoi ((*it).value.c_str());
if (s == "y") y = atoi ((*it).value.c_str());
}
for (it2 = flags.begin(); it2 != flags.end(); ++it2)
{
if (*it2 == "eol") feol = true;
if (*it2 == "eof") feof = true;
}
if (name.empty())
{
results->addToList (results->createError ("Received DEST tag with no frame name!"));
return;
}
state->gotDEST (name, x, y, feol, feof);
}
else if (name == "relocate")
{
string name;
int port = 0;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "name") name = (*it).value;
if (s == "port") port = atoi ((*it).value.c_str());
}
if (name.empty())
{
results->addToList (results->createError ("Received RELOCATE tag with no server name!"));
return;
}
if (port == 0)
{
results->addToList (results->createError ("Received RELOCATE tag with no server port!"));
return;
}
state->gotRELOCATE (name, port);
}
else if (name == "user")
state->gotUSER();
else if (name == "password")
state->gotPASSWORD();
else if (name == "image")
{
string name, url, t, align;
int h = 0, w = 0, hspace = 0, vspace = 0;
bool fismap = false;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "fname") name = (*it).value;
if (s == "url") url = (*it).value;
if (s == "t") t = (*it).value;
if (s == "align") align = (*it).value;
if (s == "h") h = state->computeCoord ((*it).value.c_str(), true, true);
if (s == "w") w = state->computeCoord ((*it).value.c_str(), false, true);
if (s == "hspace") hspace = atoi ((*it).value.c_str());
if (s == "vspace") vspace = atoi ((*it).value.c_str());
}
for (it2 = flags.begin(); it2 != flags.end(); ++it2)
if (*it2 == "ismap") fismap = true;
if (name.empty())
{
results->addToList (results->createError ("Received IMAGE tag with no image name!"));
return;
}
state->gotIMAGE (name, url, t, h, w, hspace, vspace, align, fismap);
}
else if (name == "filter")
{
/*
string src, dest, name;
for (it = params.begin(); it != params.end(); ++it)
{
string s = (*it).name;
if (s == "src") src = (*it).value;
if (s == "dest") dest = (*it).value;
if (s == "name") name = (*it).value;
}
state->gotFILTER (src, dest, name);
*/
results->addToList (results->createWarning ("Ignoring unsupported FILTER tag."));
}
}
void cElementManager::processSupport (const list<sParam> ¶ms)
{
list<string> pars;
list<sParam>::const_iterator it;
for (it = params.begin(); it != params.end(); ++it)
pars.push_back ((*it).value);
state->gotSUPPORT (pars);
}
void cElementManager::processCustomTag (const string &name, const list<sParam> ¶ms)
{
//generate a mapping with all parameter values
paramexpander->reset();
list<sParam>::const_iterator itp;
for (itp = params.begin(); itp != params.end(); ++itp)
//assign parameter value... default values and stuff were already expanded
paramexpander->addEntity ((*itp).name, (*itp).value);
//process tag contents one by one
list<sElementPart *>::iterator it;
for (it = elements[name]->element.begin(); it != elements[name]->element.end(); ++it)
{
sElementPart *sep = *it;
string part = sep->text;
//expand tag parameters first
part = paramexpander->expandEntities (part, true);
//parameters are expanded, process this part
if (sep->istag)
//this part is another tag - expand it recursively
gotTag (part);
else
//this part is regular text
state->gotText (part);
}
//tag processed, send flag information, is any
if (!elements[name]->flag.empty())
state->gotFlag (true, elements[name]->flag);
}
enum paramParserState {
parNone,
parName,
parValue,
parQuotedValue
};
void cElementManager::processParamList (const string ¶ms, list<string> &attlist,
map<string, string> &attdefault)
{
//this is similar to the parser in gotTag(), but it's a bit simpler...
string name, value;
char quote;
paramParserState state = parNone;
string::const_iterator it;
for (it = params.begin(); it != params.end(); ++it)
{
char ch = *it;
//process character
switch (state) {
case parNone: {
if (ch != ' ')
{
state = parName;
name += ch;
}
break;
}
case parName: {
if (ch == '=')
state = parValue;
else if (ch == ' ')
{
//new parameter, no default value
attlist.push_back (lcase (name));
name = "";
state = parNone;
}
else
name += ch;
break;
}
case parValue: {
if (ch == ' ')
{
//new parameter, with default value
attlist.push_back (lcase (name));
attdefault[name] = value;
name = "";
value = "";
state = parNone;
}
else if (value.empty() && ((ch == '\'') || (ch == '"')))
{
state = parQuotedValue;
quote = ch;
}
else
value += ch;
break;
}
case parQuotedValue: {
if (ch == quote)
{
//new parameter, with default value
attlist.push_back (lcase (name));
attdefault[name] = value;
name = "";
value = "";
state = parNone;
}
else
value += ch;
break;
}
};
}
//last parameter...
switch (state) {
case parName: {
//new parameter, no default value
attlist.push_back (lcase (name));
}
break;
case parValue: {
//new parameter, with default value
attlist.push_back (lcase (name));
attdefault[name] = value;
break;
}
case parQuotedValue:
results->addToList (results->createWarning (
"Received tag definition with unfinished quoted default parameter value!"));
//new parameter, with default value (unfinished, but hey...)
attlist.push_back (lcase (name));
attdefault[name] = value;
break;
};
//everything done...
}
void cElementManager::addElement (const string &name, list<sElementPart *> contents,
list<string> attlist, map<string, string> attdefault, bool open, bool empty,
int tag, string flag)
{
//sanity checks
if (elementDefined (name))
{
results->addToList (results->createError ("Multiple definition of element " + name + "!"));
return;
}
sElement *e = new sElement;
e->open = open;
e->empty = empty;
if ((tag >= 20) && (tag <= 99))
{
e->tag = tag;
if (lineTags.count (tag))
results->addToList (results->createError ("Element " + name +
" uses an already assigned line tag!"));
lineTags[tag] = name;
}
else
e->tag = 0;
e->flag = flag;
//assign element contents, generating the list of closing tags
e->element.clear();
list<sElementPart *>::iterator it;
for (it = contents.begin(); it != contents.end(); ++it)
{
sElementPart *ep = *it;
if (ep->istag)
{
string tag = lcase (firstword (ep->text));
if (elementDefined (tag))
{
if (open && !(openElement (tag)))
{
delete ep;
results->addToList (results->createError ("Definition of open " + name +
" tag contains secure tag " + tag + "!"));
}
else if (empty && !(emptyElement (tag)))
{
delete ep;
results->addToList (results->createError ("Definition of empty " + name +
" tag contains non-empty tag " + tag + "!"));
}
else
{
e->element.push_back (ep);
if (!emptyElement(tag)) e->closingseq.push_front (tag);
}
}
else
{
//element doesn't exist yet - we must believe that it's correct
e->element.push_back (ep);
if (!empty) e->closingseq.push_front (tag);
results->addToList (results->createWarning ("Definition of element " + name +
" contains undefined element " + tag + "!"));
}
}
else
e->element.push_back (ep);
}
//assign the element definition
elements[name] = e;
//set attribute list
setAttList (name, attlist, attdefault);
}
void cElementManager::setAttList (const string &name, list<string> attlist,
map<string, string> attdefault)
{
//sanity check
if (!elements.count (name))
{
results->addToList (results->createWarning ("Received attribute list for undefined tag " +
name + "!"));
return;
}
sElement *e = elements[name];
e->attlist.clear();
e->attdefault.clear();
e->attlist = attlist;
e->attdefault = attdefault;
}
void cElementManager::removeElement (const string &name)
{
//external elements only
if (elements.count (name))
{
sElement *e = elements[name];
list<sElementPart *>::iterator it;
for (it = e->element.begin(); it != e->element.end(); ++it)
delete *it;
e->element.clear();
e->attlist.clear();
e->attdefault.clear();
e->closingseq.clear();
if (e->tag)
lineTags.erase (e->tag);
delete e;
elements.erase (name);
}
}
void cElementManager::removeAll ()
{
//external elements only
list<string> names;
//we cannot delete definitions directly, because that would invalidate our iterator
map<string, sElement *>::iterator it;
for (it = elements.begin(); it != elements.end(); ++it)
names.push_back (it->first);
list<string>::iterator it2;
for (it2 = names.begin(); it2 != names.end(); ++it2)
removeElement (*it2);
names.clear ();
}
syntax highlighted by Code2HTML, v. 0.9.1