File indexing completed on 2024-04-28 15:35:13

0001 /***************************************************************************
0002  *   Copyright (C) 2004 by Tomas Mecir                                     *
0003  *   kmuddy@kmuddy.org                                                     *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU Library General Public License as       *
0007  *   published by the Free Software Foundation; either version 2 of the    *
0008  *   License, or (at your option) any later version.                       *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU Library General Public License for more details.                  *
0014  ***************************************************************************/
0015 
0016 #include "celementmanager.h"
0017 
0018 #include "centitymanager.h"
0019 #include "cmxpcolors.h"
0020 #include "cmxpparser.h"
0021 #include "cmxpstate.h"
0022 #include "cresulthandler.h"
0023 #include "stringops.h"
0024 
0025 #include <stdlib.h>
0026 
0027 cElementManager::cElementManager (cMXPState *st, cResultHandler *res, cEntityManager *enm)
0028 {
0029   state = st;
0030   results = res;
0031   entities = enm;
0032 
0033   paramexpander = new cEntityManager (true);
0034   parser = new cMXPParser;
0035   
0036   reset ();
0037 
0038   createInternalElements ();
0039 }
0040 
0041 cElementManager::~cElementManager ()
0042 {
0043   delete paramexpander;
0044   paramexpander = 0;
0045   delete parser;
0046   parser = 0;
0047   
0048   removeAll ();
0049 
0050   //internal elements
0051   map<string, sInternalElement *>::iterator it;
0052   for (it = ielements.begin(); it != ielements.end(); ++it)
0053   {
0054     it->second->attlist.clear ();
0055     it->second->attdefault.clear ();
0056     delete it->second;
0057   }
0058   ielements.clear ();
0059   aliases.clear ();
0060 }
0061 
0062 void cElementManager::reset ()
0063 {
0064   lastLineTag = 0;
0065   removeAll ();
0066 }
0067   
0068 void cElementManager::assignMXPState (cMXPState *st)
0069 {
0070   state = st;
0071 }
0072 
0073 void cElementManager::createInternalElements ()
0074 {
0075   //the list doesn't contain information on whether an argument is required or not
0076   //processor of the tag implements this functionality
0077 
0078   //create all internal elements
0079   sInternalElement *e;
0080 
0081   //!element
0082   e = new sInternalElement;
0083   e->empty = true;
0084   e->open = false;
0085   e->attlist.push_back ("name");  //this name is not in the spec!
0086   e->attlist.push_back ("definition");  //this name is not in the spec!
0087   e->attlist.push_back ("att");
0088   e->attlist.push_back ("tag");
0089   e->attlist.push_back ("flag");
0090   e->attlist.push_back ("open");
0091   e->attlist.push_back ("delete");
0092   e->attlist.push_back ("empty");
0093   e->attdefault["open"] = "";  //flags
0094   e->attdefault["delete"] = "";
0095   e->attdefault["empty"] = "";
0096   ielements["!element"] = e;
0097 
0098   //!attlist
0099   e = new sInternalElement;
0100   e->empty = true;
0101   e->open = false;
0102   e->attlist.push_back ("name");  //this name is not in the spec!
0103   e->attlist.push_back ("att");
0104   ielements["!attlist"] = e;
0105 
0106   //!entity
0107   e = new sInternalElement;
0108   e->empty = true;
0109   e->open = false;
0110   e->attlist.push_back ("name");  //this name is not in the spec!
0111   e->attlist.push_back ("value");  //this name is not in the spec!
0112   e->attlist.push_back ("desc");
0113   e->attlist.push_back ("private");
0114   e->attlist.push_back ("publish");
0115   e->attlist.push_back ("add");
0116   e->attlist.push_back ("delete");
0117   e->attlist.push_back ("remove");
0118   e->attdefault["private"] = "";  //flags
0119   e->attdefault["publish"] = "";
0120   e->attdefault["delete"] = "";
0121   e->attdefault["add"] = "";
0122   e->attdefault["remove"] = "";
0123   ielements["!entity"] = e;
0124 
0125   //var
0126   e = new sInternalElement;
0127   e->empty = false;
0128   e->open = false;
0129   e->attlist.push_back ("name");  //this name is not in the spec!
0130   e->attlist.push_back ("desc");
0131   e->attlist.push_back ("private");
0132   e->attlist.push_back ("publish");
0133   e->attlist.push_back ("add");
0134   e->attlist.push_back ("delete");
0135   e->attlist.push_back ("remove");
0136   e->attdefault["private"] = "";  //flags
0137   e->attdefault["publish"] = "";
0138   e->attdefault["delete"] = "";
0139   e->attdefault["add"] = "";
0140   e->attdefault["remove"] = "";
0141   ielements["var"] = e;
0142 
0143   //b
0144   e = new sInternalElement;
0145   e->empty = false;
0146   e->open = true;
0147   ielements["b"] = e;
0148 
0149   //i
0150   e = new sInternalElement;
0151   e->empty = false;
0152   e->open = true;
0153   ielements["i"] = e;
0154 
0155   //u
0156   e = new sInternalElement;
0157   e->empty = false;
0158   e->open = true;
0159   ielements["u"] = e;
0160 
0161   //s
0162   e = new sInternalElement;
0163   e->empty = false;
0164   e->open = true;
0165   ielements["s"] = e;
0166 
0167   //c
0168   e = new sInternalElement;
0169   e->empty = false;
0170   e->open = true;
0171   e->attlist.push_back ("fore");
0172   e->attlist.push_back ("back");
0173   ielements["c"] = e;
0174 
0175   //h
0176   e = new sInternalElement;
0177   e->empty = false;
0178   e->open = true;
0179   ielements["h"] = e;
0180 
0181   //font
0182   e = new sInternalElement;
0183   e->empty = false;
0184   e->open = true;
0185   e->attlist.push_back ("face");
0186   e->attlist.push_back ("size");
0187   e->attlist.push_back ("color");
0188   e->attlist.push_back ("back");
0189   ielements["font"] = e;
0190 
0191   //nobr
0192   e = new sInternalElement;
0193   e->empty = true;
0194   e->open = false;
0195   ielements["nobr"] = e;
0196 
0197   //p
0198   e = new sInternalElement;
0199   e->empty = false;
0200   e->open = false;
0201   ielements["p"] = e;
0202 
0203   //br
0204   e = new sInternalElement;
0205   e->empty = true;
0206   e->open = false;
0207   ielements["br"] = e;
0208 
0209   //sbr
0210   e = new sInternalElement;
0211   e->empty = true;
0212   e->open = false;
0213   ielements["sbr"] = e;
0214 
0215   //a
0216   e = new sInternalElement;
0217   e->empty = false;
0218   e->open = false;
0219   e->attlist.push_back ("href");
0220   e->attlist.push_back ("hint");
0221   e->attlist.push_back ("expire");
0222   ielements["a"] = e;
0223 
0224   //send
0225   e = new sInternalElement;
0226   e->empty = false;
0227   e->open = false;
0228   e->attlist.push_back ("href");
0229   e->attlist.push_back ("hint");
0230   e->attlist.push_back ("prompt");
0231   e->attlist.push_back ("expire");
0232   e->attdefault["prompt"] = "";  //flags
0233   ielements["send"] = e;
0234 
0235   //expire
0236   e = new sInternalElement;
0237   e->empty = true;
0238   e->open = false;
0239   e->attlist.push_back ("name");  //this name is not in the spec!
0240   ielements["expire"] = e;
0241 
0242   //version
0243   e = new sInternalElement;
0244   e->empty = true;
0245   e->open = false;
0246   ielements["version"] = e;
0247 
0248   //support
0249   e = new sInternalElement;
0250   e->empty = true;
0251   e->open = false;
0252   ielements["support"] = e;
0253 
0254   //h1
0255   e = new sInternalElement;
0256   e->empty = false;
0257   e->open = false;
0258   ielements["h1"] = e;
0259 
0260   //h2
0261   e = new sInternalElement;
0262   e->empty = false;
0263   e->open = false;
0264   ielements["h2"] = e;
0265 
0266   //h3
0267   e = new sInternalElement;
0268   e->empty = false;
0269   e->open = false;
0270   ielements["h3"] = e;
0271 
0272   //h4
0273   e = new sInternalElement;
0274   e->empty = false;
0275   e->open = false;
0276   ielements["h4"] = e;
0277 
0278   //h5
0279   e = new sInternalElement;
0280   e->empty = false;
0281   e->open = false;
0282   ielements["h5"] = e;
0283 
0284   //h6
0285   e = new sInternalElement;
0286   e->empty = false;
0287   e->open = false;
0288   ielements["h6"] = e;
0289 
0290   //hr
0291   e = new sInternalElement;
0292   e->empty = true;
0293   e->open = false;
0294   ielements["hr"] = e;
0295 
0296   //small
0297   e = new sInternalElement;
0298   e->empty = false;
0299   e->open = false;
0300   ielements["small"] = e;
0301 
0302   //tt
0303   e = new sInternalElement;
0304   e->empty = false;
0305   e->open = false;
0306   ielements["tt"] = e;
0307 
0308   //sound
0309   e = new sInternalElement;
0310   e->empty = true;
0311   e->open = false;
0312   e->attlist.push_back ("fname");
0313   e->attlist.push_back ("v");
0314   e->attlist.push_back ("l");
0315   e->attlist.push_back ("p");
0316   e->attlist.push_back ("t");
0317   e->attlist.push_back ("u");
0318   e->attdefault["v"] = "100";
0319   e->attdefault["l"] = "1";
0320   e->attdefault["p"] = "50";
0321   ielements["sound"] = e;
0322 
0323   //music
0324   e = new sInternalElement;
0325   e->empty = true;
0326   e->open = false;
0327   e->attlist.push_back ("fname");
0328   e->attlist.push_back ("v");
0329   e->attlist.push_back ("l");
0330   e->attlist.push_back ("c");
0331   e->attlist.push_back ("t");
0332   e->attlist.push_back ("u");
0333   e->attdefault["v"] = "100";
0334   e->attdefault["l"] = "1";
0335   e->attdefault["c"] = "1";
0336   ielements["music"] = e;
0337 
0338   //gauge
0339   e = new sInternalElement;
0340   e->empty = true;
0341   e->open = false;
0342   e->attlist.push_back ("entity");  //this name is not in the spec!
0343   e->attlist.push_back ("max");
0344   e->attlist.push_back ("caption");
0345   e->attlist.push_back ("color");
0346   ielements["gauge"] = e;
0347 
0348   //stat
0349   e = new sInternalElement;
0350   e->empty = true;
0351   e->open = false;
0352   e->attlist.push_back ("entity");  //this name is not in the spec!
0353   e->attlist.push_back ("max");
0354   e->attlist.push_back ("caption");
0355   ielements["stat"] = e;
0356 
0357   //frame
0358   e = new sInternalElement;
0359   e->empty = true;
0360   e->open = false;
0361   e->attlist.push_back ("name");
0362   e->attlist.push_back ("action");
0363   e->attlist.push_back ("title");
0364   e->attlist.push_back ("internal");
0365   e->attlist.push_back ("align");
0366   e->attlist.push_back ("left");
0367   e->attlist.push_back ("top");
0368   e->attlist.push_back ("width");
0369   e->attlist.push_back ("height");
0370   e->attlist.push_back ("scrolling");
0371   e->attlist.push_back ("floating");
0372   e->attdefault["action"] = "open";
0373   e->attdefault["align"] = "top";
0374   e->attdefault["left"] = "0";
0375   e->attdefault["top"] = "0";
0376   e->attdefault["internal"] = "";  //flags
0377   e->attdefault["scrolling"] = "";
0378   e->attdefault["floating"] = "";
0379   ielements["frame"] = e;
0380 
0381   //dest
0382   e = new sInternalElement;
0383   e->empty = false;
0384   e->open = false;
0385   e->attlist.push_back ("name");  //this name is not in the spec!
0386   e->attlist.push_back ("x");
0387   e->attlist.push_back ("y");
0388   e->attlist.push_back ("eol");
0389   e->attlist.push_back ("eof");
0390   e->attdefault["eol"] = "";  //flags
0391   e->attdefault["eof"] = "";
0392   ielements["dest"] = e;
0393 
0394   //relocate
0395   e = new sInternalElement;
0396   e->empty = true;
0397   e->open = false;
0398   e->attlist.push_back ("name");  //this name is not in the spec!
0399   e->attlist.push_back ("port");  //this name is not in the spec!
0400   ielements["relocate"] = e;
0401 
0402   //user
0403   e = new sInternalElement;
0404   e->empty = true;
0405   e->open = false;
0406   ielements["user"] = e;
0407 
0408   //password
0409   e = new sInternalElement;
0410   e->empty = true;
0411   e->open = false;
0412   ielements["password"] = e;
0413 
0414   //image
0415   e = new sInternalElement;
0416   e->empty = true;
0417   e->open = false;
0418   e->attlist.push_back ("fname");
0419   e->attlist.push_back ("url");
0420   e->attlist.push_back ("t");
0421   e->attlist.push_back ("h");
0422   e->attlist.push_back ("w");
0423   e->attlist.push_back ("hspace");
0424   e->attlist.push_back ("vspace");
0425   e->attlist.push_back ("align");
0426   e->attlist.push_back ("ismap");
0427   e->attdefault["align"] = "top";
0428   e->attdefault["ismap"] = "";  //flags
0429   ielements["image"] = e;
0430 
0431   //filter
0432   e = new sInternalElement;
0433   e->empty = true;
0434   e->open = false;
0435   e->attlist.push_back ("src");
0436   e->attlist.push_back ("dest");
0437   e->attlist.push_back ("name");
0438   ielements["filter"] = e;
0439 
0440   //finally, define some aliases for internal elements
0441   aliases["!el"] = "!element";
0442   aliases["!at"] = "!attlist";
0443   aliases["!en"] = "!entity";
0444   aliases["v"] = "var";
0445   aliases["bold"] = "b";
0446   aliases["strong"] = "b";
0447   aliases["italic"] = "i";
0448   aliases["em"] = "i";
0449   aliases["underline"] = "u";
0450   aliases["strikeout"] = "s";
0451   aliases["high"] = "h";
0452   aliases["color"] = "c";
0453   aliases["destination"] = "dest";
0454 }
0455 
0456 bool cElementManager::elementDefined (const string &name)
0457 {
0458   return ((elements.count (name) != 0) || (ielements.count (name) != 0) ||
0459       (aliases.count (name) != 0));
0460 }
0461 
0462 bool cElementManager::internalElement (const string &name)
0463 {
0464   return ((ielements.count (name) != 0) || (aliases.count (name) != 0));
0465 }
0466 
0467 bool cElementManager::customElement (const string &name)
0468 {
0469   return (elements.count (name) != 0);
0470 }
0471 
0472 bool cElementManager::openElement (const string &name)
0473 {
0474   if (!elementDefined (name))
0475     return false;
0476   if (internalElement (name))
0477   {
0478     string n = name;
0479     if (aliases.count (name))
0480       n = aliases[name];
0481     return ielements[n]->open;
0482   }
0483   else
0484     return elements[name]->open;
0485 }
0486 
0487 bool cElementManager::emptyElement (const string &name)
0488 {
0489   if (!elementDefined (name))
0490     return false;
0491   if (internalElement (name))
0492   {
0493     string n = name;
0494     if (aliases.count (name))
0495       n = aliases[name];
0496     return ielements[n]->empty;
0497   }
0498   else
0499     return elements[name]->empty;
0500 }
0501 
0502 enum tagParserState {
0503   tagBegin,
0504   tagName,
0505   tagParam,
0506   tagParamValue,
0507   tagQuotedParam,
0508   tagAfterQuotedParam,
0509   tagBetweenParams
0510 };
0511 
0512 void cElementManager::gotTag (const string &tag)
0513 {
0514   string tagname;
0515   list<sParam> params;
0516   sParam param;
0517   param.flag = false;
0518   char quote;
0519   tagParserState pstate = tagBegin;
0520   string::const_iterator it;
0521   for (it = tag.begin(); it != tag.end(); ++it)
0522   {
0523     char ch = *it;
0524 
0525     //process character
0526     switch (pstate) {
0527       case tagBegin: {
0528         if (ch != ' ')
0529         {
0530           pstate = tagName;
0531           tagname += ch;
0532         }
0533         break;
0534       }
0535       case tagName: {
0536         if (ch == ' ')
0537           pstate = tagBetweenParams;
0538         else
0539           tagname += ch;
0540         break;
0541       }
0542       case tagParam: {
0543         if (ch == '=')
0544           pstate = tagParamValue;
0545         else if (ch == ' ')
0546         {
0547           //one parameter, value only (it could also be a flag, we'll check that later)
0548           param.value = param.name;
0549           param.name = "";
0550           //add a new parameter :-)
0551           params.push_back (param);
0552           param.value = "";
0553           pstate = tagBetweenParams;
0554         }
0555         else
0556           param.name += ch;
0557         break;
0558       }
0559       case tagParamValue: {
0560         if (ch == ' ')
0561         {
0562           //add a new parameter :-)
0563           params.push_back (param);
0564           param.name = "";
0565           param.value = "";
0566           pstate = tagBetweenParams;
0567         }
0568         else if (param.value.empty() && ((ch == '\'') || (ch == '"')))
0569         {
0570           pstate = tagQuotedParam;
0571           quote = ch;
0572         }
0573         else
0574           param.value += ch;
0575         break;
0576       }
0577       case tagQuotedParam: {
0578         if (ch == quote)
0579         {
0580           //add a new parameter :-)
0581           params.push_back (param);
0582           param.name = "";
0583           param.value = "";
0584           pstate = tagAfterQuotedParam;
0585         }
0586         else
0587           param.value += ch;
0588         break;
0589       }
0590       case tagAfterQuotedParam: {
0591         if (ch == ' ')    //ignore everything up to some space...
0592           pstate = tagBetweenParams;
0593         break;
0594       }
0595       case tagBetweenParams: {
0596         if (ch != ' ')
0597         {
0598           if ((ch == '\'') || (ch == '"'))
0599           {
0600             pstate = tagQuotedParam;
0601             param.name = "";
0602             quote = ch;
0603           }
0604           else
0605           {
0606             pstate = tagParam;
0607             param.name += ch;
0608           }
0609         }
0610         break;
0611       }
0612     };
0613   }
0614 
0615   //last parameter...
0616   switch (pstate) {
0617     case tagBegin:
0618       results->addToList (results->createError ("Received a tag with no body!"));
0619       break;
0620     case tagParam: {
0621       param.value = param.name;
0622       param.name = "";
0623       params.push_back (param);
0624       }
0625       break;
0626     case tagParamValue:
0627       params.push_back (param);
0628       break;
0629     case tagQuotedParam:
0630       results->addToList (results->createError ("Received tag " + tagname +
0631           " with unfinished quoted parameter!"));
0632       break;
0633   };
0634 
0635   //nothing more to do if the tag has no contents...
0636   if (pstate == tagBegin) return;
0637   
0638   //convert tag name to lowercase
0639   tagname = lcase (tagname);
0640   
0641   //handle closing tag...
0642   if (tagname[0] == '/')
0643   {
0644     if (!params.empty())
0645       results->addToList (results->createError ("Received closing tag " + tagname +
0646           " with parametrs!"));
0647     //remove that '/'
0648     tagname.erase (tagname.begin());
0649     //and call closing tag processing
0650     handleClosingTag (tagname);
0651     return;
0652   }
0653   
0654   //convert all parameter names to lower-case
0655   list<sParam>::iterator parit;
0656   for (parit = params.begin(); parit != params.end(); ++parit)
0657     (*parit).name = lcase ((*parit).name);
0658   
0659   //now we check the type of the tag and act accordingly
0660   if (!elementDefined (tagname))
0661   {
0662     params.clear();
0663     results->addToList (results->createError ("Received undefined tag " + tagname + "!"));
0664     return;
0665   }
0666 
0667   mxpMode m = state->getMXPMode ();
0668   //mode can be open or secure; locked mode is not possible here (or we're in a bug)
0669   if (m == openMode)
0670     //open mode - only open tags allowed
0671     if (!openElement (tagname))
0672     {
0673     params.clear();
0674       results->addToList (results->createError ("Received secure tag " + tagname +
0675           " in open mode!"));
0676       return;
0677     }
0678 
0679   if (internalElement (tagname))
0680   {
0681     //if the name is an alias for another tag, change the name
0682     if (aliases.count (tagname))
0683       tagname = aliases[tagname];
0684     //the <support> tag has to be handled separately :(
0685     if (tagname == "support")
0686     {
0687       processSupport (params);
0688       return;
0689     }
0690     //identify all flags in the tag
0691     identifyFlags (ielements[tagname]->attdefault, params);
0692     //correctly identify all parameters (assign names where necessary)
0693     handleParams (tagname, params, ielements[tagname]->attlist, ielements[tagname]->attdefault);
0694     //separate out all the flags (flags are only valid for internal tags)
0695     list<string> flags;
0696     parit = params.begin();
0697     while (parit != params.end())
0698     {
0699       if ((*parit).flag)
0700       {
0701         flags.push_back ((*parit).name);
0702         parit = params.erase (parit);
0703       }
0704       else
0705         ++parit;
0706     }
0707     //okay, parsing done - send the tag for further processing
0708     processInternalTag (tagname, params, flags);
0709   }
0710   else
0711   {
0712     handleParams (tagname, params, elements[tagname]->attlist, elements[tagname]->attdefault);
0713     processCustomTag (tagname, params);
0714   }
0715   
0716   params.clear ();
0717 }
0718 
0719 void cElementManager::identifyFlags (const map<string, string> &attdefault,
0720     list<sParam> &args)
0721 {
0722   list<sParam>::iterator it;
0723   for (it = args.begin(); it != args.end(); ++it)
0724     if ((*it).name.empty())
0725     {
0726       string s = lcase ((*it).value);
0727       if ((attdefault.count (s) != 0) && (attdefault.find(s)->second == ""))
0728       {
0729         //this one is a flag
0730         (*it).name = s;
0731         (*it).value = "";
0732         (*it).flag = true;
0733       }
0734     }
0735 }
0736 
0737 void cElementManager::handleParams (const string &tagname, list<sParam> &args,
0738     const list<string> &attlist, const map<string, string> &attdefault)
0739 {
0740   list<string>::const_iterator cur = attlist.begin();
0741   list<sParam>::iterator it;
0742   for (it = args.begin(); it != args.end(); ++it)
0743   {
0744     //flag?
0745     if ((*it).flag)
0746     {
0747       //only advance parameter iterator
0748       cur++;
0749     }
0750     //not a flag
0751     else
0752     {
0753       //empty name?
0754       if ((*it).name.empty())
0755       {
0756         //set the parameter name:
0757         
0758         //find the correct attribute name, skipping all flags
0759         while (cur != attlist.end())
0760         {
0761           if ((attdefault.count (*cur) != 0) && (attdefault.find(*cur)->second == ""))  //flag
0762             cur++;
0763           else
0764             break;  //okay, found the correct parameter
0765         }
0766         if (cur == attlist.end())    //ARGH! Parameter not found :(
0767         {
0768           results->addToList (results->createError ("Received too many parameters for tag " +
0769               tagname + "!"));
0770           continue;  //continue with the next parameter...
0771         }
0772       }
0773       //non-empty name?
0774       else
0775       {
0776         //set "cur" to the parameter following the given one
0777         
0778         //to speed things up a bit, first look if the iterator is pointing at the right parameter
0779         // (we won't need to traverse the whole list, if it does)
0780         if ((cur == attlist.end()) || ((*it).name != *cur))
0781         {
0782           list<string>::const_iterator cur2 = cur;  //remember old "cur" value
0783           for (cur = attlist.begin(); cur != attlist.end(); ++cur)
0784             if ((*it).name == *cur)
0785               break;
0786           if (cur == attlist.end())    //parameter name not found
0787           {
0788             //restore old iterator value
0789             cur = cur2;
0790             results->addToList (results->createError ("Received unknown parameter " +
0791                 (*it).name + " in tag " + tagname + "!"));
0792             //clear name/value to avoid confusion in later stages
0793             (*it).name = "";
0794             (*it).value = "";
0795             //proceed with next parameter
0796             continue;
0797           }
0798           //if cur isn't attlist.end(), it's now set to the correct value...
0799         }
0800       }
0801       
0802       //things common to all non-flag parameters...
0803       
0804       //set parameter name
0805       (*it).name = *cur;
0806       //if parameter value is empty, set it to default value (if any)
0807       if ((*it).value.empty() && (attdefault.count (*cur) != 0))
0808         (*it).value = attdefault.find(*cur)->second;
0809       //advance parameter iterator
0810       cur++;
0811     }
0812   }
0813   
0814   //finally, we add default parameter values to the beginning of the list... these shall get
0815   //overridden by given values, if any (those shall be later in the list)
0816   sParam s;
0817   map<string, string>::const_iterator mit;
0818   for (mit = attdefault.begin(); mit != attdefault.end(); ++mit)
0819     if (mit->second != "")  //not a flag
0820     {
0821       s.flag = false;
0822       s.name = mit->first;
0823       s.value = mit->second;
0824       args.push_front (s);
0825     }
0826 }
0827 
0828 void cElementManager::gotLineTag (int number)
0829 {
0830   if ((number < 20) || (number > 99))
0831   {
0832     lastLineTag = 0;
0833     return;
0834   }
0835   if (lineTags.count (number) == 0)
0836   {
0837     lastLineTag = 0;
0838     return;
0839   }
0840   string tag = lineTags[number];
0841   lastLineTag = number;
0842   //behave as if we've just gotten the appropriate tag
0843   gotTag (tag);
0844 }
0845 
0846 void cElementManager::gotNewLine ()
0847 {
0848   if ((lastLineTag < 20) || (lastLineTag > 99))
0849   {
0850     lastLineTag = 0;
0851     return;
0852   }
0853   if (lineTags.count (lastLineTag) == 0)
0854   {
0855     lastLineTag = 0;
0856     return;
0857   }
0858   string tag = lineTags[lastLineTag];
0859   lastLineTag = 0;
0860   if (emptyElement (tag))
0861     //no closing tag needed
0862     return;
0863   //okay, send out the appropriate closing tag
0864   handleClosingTag (tag);
0865 }
0866 
0867 void cElementManager::handleClosingTag (const string &name)
0868 {
0869   string n = lcase (name);
0870   if (!elementDefined (n))
0871   {
0872     results->addToList (results->createError ("Received unknown closing tag </" + n + ">!"));
0873     return;
0874   }
0875   if (emptyElement (n))
0876   {
0877     results->addToList (results->createError ("Received closing tag for tag " + n +
0878         ", which doesn't need a closing tag!"));
0879     return;
0880   }
0881   if (internalElement (n))
0882   {
0883     //if the name is an alias for another tag, change the name
0884     if (aliases.count (n))
0885       n = aliases[n];
0886     state->gotClosingTag (n);
0887   }
0888   else
0889   {
0890     //send closing flag, if needed
0891     if (!elements[n]->flag.empty())
0892       state->gotFlag (false, elements[n]->flag);
0893     
0894     //expand the closing tag...
0895     list<string>::iterator cit;
0896     for (cit = elements[n]->closingseq.begin(); cit != elements[n]->closingseq.end(); ++cit)
0897       handleClosingTag (*cit);
0898   }
0899 }
0900 
0901 void cElementManager::processInternalTag (const string &name, const list<sParam> &params,
0902     const list<string> &flags)
0903 {
0904   list<sParam>::const_iterator it;
0905   list<string>::const_iterator it2;
0906   if (name == "!element")
0907   {
0908     string name, definition, att, flag;
0909     int tag;
0910     bool fopen = false, fdelete = false, fempty = false;
0911     for (it = params.begin(); it != params.end(); ++it)
0912     {
0913       string s = (*it).name;
0914       if (s == "name") name = lcase ((*it).value);
0915       if (s == "definition") definition = (*it).value;
0916       if (s == "att") att = (*it).value;
0917       if (s == "flag") flag = (*it).value;
0918       if (s == "tag") tag = atoi ((*it).value.c_str());
0919     }
0920     for (it2 = flags.begin(); it2 != flags.end(); ++it2)
0921     {  
0922       if (*it2 == "open") fopen = true;
0923       if (*it2 == "delete") fdelete = true;
0924       if (*it2 == "empty") fempty = true;
0925     }
0926     
0927     if (name.empty())
0928     {
0929       results->addToList (results->createError (
0930           "Received an <!element> tag with no element name!"));
0931       return;
0932     }
0933     //definition can be empty, that's no problem...
0934 
0935     //if we want to delete the tag...
0936     if (fdelete)
0937     {
0938       //sanity check
0939       if (!elements.count (name))
0940       {
0941         results->addToList (results->createWarning (
0942             "Received request to remove an undefined tag " + name + "!"));
0943         return;
0944       }
0945       removeElement (name);
0946       return;
0947     }
0948     
0949     //parse tag definition
0950     parser->simpleParse (definition);
0951     list<sElementPart *> tagcontents;
0952     while (parser->hasNext ())
0953     {
0954       chunk ch = parser->getNext ();
0955       if (ch.chk == chunkError)
0956         results->addToList (results->createError (ch.text));
0957       else
0958       {
0959         //create a new element part
0960         sElementPart *part = new sElementPart;
0961         part->text = ch.text;
0962         part->istag = (ch.chk == chunkTag) ? true : false;
0963         tagcontents.push_back (part);
0964       }
0965     }
0966     
0967     //parse attribute list
0968     list<string> attlist;
0969     map<string, string> attdefault;
0970     processParamList (att, attlist, attdefault);
0971     
0972     //and do the real work
0973     addElement (name, tagcontents, attlist, attdefault, fopen, fempty, tag, flag);
0974   }
0975   else if (name == "!attlist")
0976   {
0977     string name, att;
0978     for (it = params.begin(); it != params.end(); ++it)
0979     {
0980       string s = (*it).name;
0981       if (s == "name") name = (*it).value;
0982       if (s == "att") att = (*it).value;
0983     }
0984     
0985     if (name.empty())
0986     {
0987       results->addToList (results->createError (
0988           "Received an <!attlist> tag with no element name!"));
0989       return;
0990     }
0991 
0992     //parse attribute list
0993     list<string> attlist;
0994     map<string, string> attdefault;
0995     processParamList (att, attlist, attdefault);
0996     
0997     //and do the real work
0998     setAttList (name, attlist, attdefault);
0999   }
1000   else if (name == "!entity")
1001   {
1002     string name, value, desc;
1003     bool fpriv = false, fpub = false, fadd = false, fdel = false, frem = false;
1004     for (it = params.begin(); it != params.end(); ++it)
1005     {
1006       string s = (*it).name;
1007       if (s == "name") name = (*it).value;
1008       if (s == "value") value = (*it).value;
1009       if (s == "desc") desc = (*it).value;
1010     }
1011     for (it2 = flags.begin(); it2 != flags.end(); ++it2)
1012     {  
1013       if (*it2 == "private") fpriv = true;
1014       if (*it2 == "publish") fpub = true;
1015       if (*it2 == "delete") fdel = true;
1016       if (*it2 == "add") fadd = true;
1017       if (*it2 == "remove") frem = true;
1018     }
1019     
1020     if (name.empty())
1021     {
1022       results->addToList (results->createError (
1023           "Received an <!entity> tag with no variable name!"));
1024       return;
1025     }
1026     
1027     //fpub is IGNORED...
1028     //fadd and frem is IGNORED...
1029     if (!(fadd) && !(frem))
1030     {
1031       if (fdel)
1032       {
1033         entities->deleteEntity (name);
1034         if (!fpriv) //do not announce PRIVATE entities
1035           state->gotVariable (name, "", true);
1036       }
1037       else
1038       {
1039         //we now have a new variable...
1040         entities->addEntity (name, value);
1041         if (!fpriv) //do not announce PRIVATE entities
1042           state->gotVariable (name, value);
1043       }
1044     }
1045     else
1046       results->addToList (results->createWarning (
1047           "Ignored <!ENTITY> tag with ADD or REMOVE flag."));
1048   }
1049   else if (name == "var")
1050   {
1051     //this is very similar to the !entity handler above...
1052     
1053     string name, desc;
1054     bool fpriv = false, fpub = false, fadd = false, fdel = false, frem = false;
1055     for (it = params.begin(); it != params.end(); ++it)
1056     {
1057       string s = (*it).name;
1058       if (s == "name") name = (*it).value;
1059       if (s == "desc") desc = (*it).value;
1060     }
1061     for (it2 = flags.begin(); it2 != flags.end(); ++it2)
1062     {  
1063       if (*it2 == "private") fpriv = true;
1064       if (*it2 == "publish") fpub = true;
1065       if (*it2 == "add") fadd = true;
1066       if (*it2 == "delete") fdel = true;
1067       if (*it2 == "remove") frem = true;
1068     }
1069     
1070     if (name.empty())
1071     {
1072       results->addToList (results->createError (
1073           "Received an <var> tag with no variable name!"));
1074       return;
1075     }
1076     
1077     //fpriv and fpub is IGNORED...
1078     //fadd and fdel is IGNORED...
1079     if (!(fadd) && !(fdel))
1080       state->gotVAR (name);
1081     else
1082       results->addToList (results->createWarning ("Ignored <VAR> tag with ADD or REMOVE flag."));
1083   }
1084   else if (name == "b")
1085     state->gotBOLD();
1086   else if (name == "i")
1087     state->gotITALIC();
1088   else if (name == "u")
1089     state->gotUNDERLINE();
1090   else if (name == "s")
1091     state->gotSTRIKEOUT();
1092   else if (name == "c")
1093   {
1094     string fore, back;
1095     for (it = params.begin(); it != params.end(); ++it)
1096     {
1097       string s = (*it).name;
1098       if (s == "fore") fore = (*it).value;
1099       if (s == "back") back = (*it).value;
1100     }
1101     RGB fg = state->fgColor();
1102     RGB bg = state->bgColor();
1103     if (!fore.empty())
1104       fg = cMXPColors::self()->color (fore);
1105     if (!back.empty())
1106       bg = cMXPColors::self()->color (back);
1107     state->gotCOLOR (fg, bg);
1108   }
1109   else if (name == "h")
1110     state->gotHIGH();
1111   else if (name == "font")
1112   {
1113     string face, fore, back;
1114     int size = 0;
1115     for (it = params.begin(); it != params.end(); ++it)
1116     {
1117       string s = (*it).name;
1118       if (s == "face") face = (*it).value;
1119       if (s == "size") size = atoi ((*it).value.c_str());
1120       if (s == "color") fore = (*it).value;
1121       if (s == "back") back = (*it).value;
1122     }
1123     if (face.empty())
1124       face = state->fontFace();
1125     if (size == 0)
1126       size = state->fontSize();
1127     RGB fg = state->fgColor();
1128     RGB bg = state->bgColor();
1129     if (!fore.empty())
1130       fg = cMXPColors::self()->color (fore);
1131     if (!back.empty())
1132       bg = cMXPColors::self()->color (back);
1133     state->gotFONT (face, size, fg, bg);
1134   }
1135   else if (name == "p")
1136     state->gotP();
1137   else if (name == "br")
1138     state->gotBR();
1139   else if (name == "nobr")
1140     state->gotNOBR();
1141   else if (name == "sbr")
1142     state->gotSBR();
1143   else if (name == "a")
1144   {
1145     string href, hint, expire;
1146     for (it = params.begin(); it != params.end(); ++it)
1147     {
1148       string s = (*it).name;
1149       if (s == "href") href = (*it).value;
1150       if (s == "hint") hint = (*it).value;
1151       if (s == "expire") expire = (*it).value;
1152     }
1153     state->gotA (href, hint, expire);
1154   }
1155   else if (name == "send")
1156   {
1157     string href, hint, expire;
1158     bool prompt = false;
1159     for (it = params.begin(); it != params.end(); ++it)
1160     {
1161       string s = (*it).name;
1162       if (s == "href") href = (*it).value;
1163       if (s == "hint") hint = (*it).value;
1164       if (s == "expire") expire = (*it).value;
1165     }
1166     for (it2 = flags.begin(); it2 != flags.end(); ++it2)
1167       if (*it2 == "prompt") prompt = true;
1168     state->gotSEND (href, hint, prompt, expire);
1169   }
1170   else if (name == "expire")
1171   {
1172     string name;
1173     for (it = params.begin(); it != params.end(); ++it)
1174     {
1175       string s = (*it).name;
1176       if (s == "name") name = (*it).value;
1177     }
1178     //name can be empty - all named links shall then expire
1179     state->gotEXPIRE (name);
1180   }
1181   else if (name == "version")
1182     state->gotVERSION();
1183   else if (name == "h1")
1184     state->gotHtag (1);
1185   else if (name == "h2")
1186     state->gotHtag (2);
1187   else if (name == "h3")
1188     state->gotHtag (3);
1189   else if (name == "h4")
1190     state->gotHtag (4);
1191   else if (name == "h5")
1192     state->gotHtag (5);
1193   else if (name == "h6")
1194     state->gotHtag (6);
1195   else if (name == "hr")
1196     state->gotHR();
1197   else if (name == "small")
1198     state->gotSMALL();
1199   else if (name == "tt")
1200     state->gotTT();
1201   else if (name == "sound")
1202   {
1203     string fname, t, u;
1204     int v = 0, l = 0, p = 0;  //shall be overridden by defaults...
1205     for (it = params.begin(); it != params.end(); ++it)
1206     {
1207       string s = (*it).name;
1208       if (s == "fname") fname = (*it).value;
1209       if (s == "t") t = (*it).value;
1210       if (s == "u") u = (*it).value;
1211       if (s == "v") v = atoi ((*it).value.c_str());
1212       if (s == "l") l = atoi ((*it).value.c_str());
1213       if (s == "p") p = atoi ((*it).value.c_str());
1214     }
1215     if (fname.empty())
1216     {
1217       results->addToList (results->createError ("Received SOUND tag with no file name!"));
1218       return;
1219     }
1220     if ((v < 0) ||  (v > 100))
1221     {
1222       results->addToList (results->createWarning ("Ignoring incorrect V param for SOUND tag."));
1223       v = 100;  //set default value
1224     }
1225     if ((l < -1) || (l > 100) || (l == 0))
1226     {
1227       results->addToList (results->createWarning ("Ignoring incorrect L param for SOUND tag."));
1228       l = 1;  //set default value
1229     }
1230     if ((p < 0) ||  (p > 100))
1231     {
1232       results->addToList (results->createWarning ("Ignoring incorrect P param for SOUND tag."));
1233       p = 50;  //set default value
1234     }
1235     state->gotSOUND (fname, v, l, p, t, u);
1236   }
1237   else if (name == "music")
1238   {
1239     string fname, t, u;
1240     int v = 0, l = 0, c = 0;  //shall be overridden by defaults...
1241     for (it = params.begin(); it != params.end(); ++it)
1242     {
1243       string s = (*it).name;
1244       if (s == "fname") fname = (*it).value;
1245       if (s == "t") t = (*it).value;
1246       if (s == "u") u = (*it).value;
1247       if (s == "v") v = atoi ((*it).value.c_str());
1248       if (s == "l") l = atoi ((*it).value.c_str());
1249       if (s == "c") c = atoi ((*it).value.c_str());
1250     }
1251     if (fname.empty())
1252     {
1253       results->addToList (results->createError ("Received MUSIC tag with no file name!"));
1254       return;
1255     }
1256     if ((v < 0) ||  (v > 100))
1257     {
1258       results->addToList (results->createWarning ("Ignoring incorrect V param for MUSIC tag."));
1259       v = 100;  //set default value
1260     }
1261     if ((l < -1) || (l > 100) || (l == 0))
1262     {
1263       results->addToList (results->createWarning ("Ignoring incorrect L param for MUSIC tag."));
1264       l = 1;  //set default value
1265     }
1266     if ((c != 0) && (c != 1))
1267     {
1268       results->addToList (results->createWarning ("Ignoring incorrect C param for MUSIC tag."));
1269       c = 1;  //set default value
1270     }
1271     state->gotMUSIC (fname, v, l, (c!=0), t, u);
1272   }
1273   else if (name == "gauge")
1274   {
1275     string entity, max, caption, color;
1276     for (it = params.begin(); it != params.end(); ++it)
1277     {
1278       string s = (*it).name;
1279       if (s == "entity") entity = (*it).value;
1280       if (s == "max") max = (*it).value;
1281       if (s == "caption") caption = (*it).value;
1282       if (s == "color") color = (*it).value;
1283     }
1284     if (entity.empty())
1285     {
1286       results->addToList (results->createError ("Received GAUGE with no entity name!"));
1287       return;
1288     }
1289     RGB c;
1290     if (color.empty()) color = "white";
1291     c = cMXPColors::self()->color (color);
1292     state->gotGAUGE (entity, max, caption, c);
1293   }
1294   else if (name == "stat")
1295   {
1296     string entity, max, caption;
1297     for (it = params.begin(); it != params.end(); ++it)
1298     {
1299       string s = (*it).name;
1300       if (s == "entity") entity = (*it).value;
1301       if (s == "max") max = (*it).value;
1302       if (s == "caption") caption = (*it).value;
1303     }
1304     if (entity.empty())
1305     {
1306       results->addToList (results->createError ("Received STAT with no entity name!"));
1307       return;
1308     }
1309     state->gotSTAT (entity, max, caption);
1310   }
1311   else if (name == "frame")
1312   {
1313     string name, action, title, align;
1314     int left = 0, top = 0, width = 0, height = 0;
1315     bool finternal = false, fscroll = false, ffloat = false;
1316     for (it = params.begin(); it != params.end(); ++it)
1317     {
1318       string s = (*it).name;
1319       if (s == "name") name = (*it).value;
1320       if (s == "action") action = (*it).value;
1321       if (s == "title") title = (*it).value;
1322       if (s == "align") align = (*it).value;
1323       if (s == "left") left = state->computeCoord ((*it).value.c_str(), true);
1324       if (s == "top") top = state->computeCoord ((*it).value.c_str(), false);
1325       if (s == "width") width = state->computeCoord ((*it).value.c_str(), true);
1326       if (s == "height") height = state->computeCoord ((*it).value.c_str(), false);
1327     }
1328     for (it2 = flags.begin(); it2 != flags.end(); ++it2)
1329     {
1330       if (*it2 == "internal") finternal = true;
1331       if (*it2 == "scrolling") fscroll = true;
1332       if (*it2 == "floating") ffloat = true;
1333     }
1334     if (name.empty())
1335     {
1336       results->addToList (results->createError ("Received FRAME tag with no frame name!"));
1337       return;
1338     }
1339     state->gotFRAME (name, action, title, finternal, align, left, top, width, height,
1340         fscroll, ffloat);
1341   }
1342   else if (name == "dest")
1343   {
1344     string name;
1345     int x = 0, y = 0;
1346     bool feol = false, feof = false;
1347     for (it = params.begin(); it != params.end(); ++it)
1348     {
1349       string s = (*it).name;
1350       if (s == "name") name = (*it).value;
1351       if (s == "x") x = atoi ((*it).value.c_str());
1352       if (s == "y") y = atoi ((*it).value.c_str());
1353     }
1354     for (it2 = flags.begin(); it2 != flags.end(); ++it2)
1355     {
1356       if (*it2 == "eol") feol = true;
1357       if (*it2 == "eof") feof = true;
1358     }
1359     if (name.empty())
1360     {
1361       results->addToList (results->createError ("Received DEST tag with no frame name!"));
1362       return;
1363     }
1364     state->gotDEST (name, x, y, feol, feof);
1365   }
1366   else if (name == "relocate")
1367   {
1368     string name;
1369     int port = 0;
1370     for (it = params.begin(); it != params.end(); ++it)
1371     {
1372       string s = (*it).name;
1373       if (s == "name") name = (*it).value;
1374       if (s == "port") port = atoi ((*it).value.c_str());
1375     }
1376     if (name.empty())
1377     {
1378       results->addToList (results->createError ("Received RELOCATE tag with no server name!"));
1379       return;
1380     }
1381     if (port == 0)
1382     {
1383       results->addToList (results->createError ("Received RELOCATE tag with no server port!"));
1384       return;
1385     }
1386     state->gotRELOCATE (name, port);
1387   }
1388   else if (name == "user")
1389     state->gotUSER();
1390   else if (name == "password")
1391     state->gotPASSWORD();
1392   else if (name == "image")
1393   {
1394     string name, url, t, align;
1395     int h = 0, w = 0, hspace = 0, vspace = 0;
1396     bool fismap = false;
1397     for (it = params.begin(); it != params.end(); ++it)
1398     {
1399       string s = (*it).name;
1400       if (s == "fname") name = (*it).value;
1401       if (s == "url") url = (*it).value;
1402       if (s == "t") t = (*it).value;
1403       if (s == "align") align = (*it).value;
1404       if (s == "h") h = state->computeCoord ((*it).value.c_str(), true, true);
1405       if (s == "w") w = state->computeCoord ((*it).value.c_str(), false, true);
1406       if (s == "hspace") hspace = atoi ((*it).value.c_str());
1407       if (s == "vspace") vspace = atoi ((*it).value.c_str());
1408     }
1409     for (it2 = flags.begin(); it2 != flags.end(); ++it2)
1410       if (*it2 == "ismap") fismap = true;
1411     if (name.empty())
1412     {
1413       results->addToList (results->createError ("Received IMAGE tag with no image name!"));
1414       return;
1415     }
1416     state->gotIMAGE (name, url, t, h, w, hspace, vspace, align, fismap);
1417   }
1418   else if (name == "filter")
1419   {
1420 /*
1421     string src, dest, name;
1422     for (it = params.begin(); it != params.end(); ++it)
1423     {
1424       string s = (*it).name;
1425       if (s == "src") src = (*it).value;
1426       if (s == "dest") dest = (*it).value;
1427       if (s == "name") name = (*it).value;
1428     }
1429     state->gotFILTER (src, dest, name);
1430 */
1431     results->addToList (results->createWarning ("Ignoring unsupported FILTER tag."));
1432   }
1433 }
1434 
1435 void cElementManager::processSupport (const list<sParam> &params)
1436 {
1437   list<string> pars;
1438   list<sParam>::const_iterator it;
1439   for (it = params.begin(); it != params.end(); ++it)
1440     pars.push_back ((*it).value);
1441   state->gotSUPPORT (pars);
1442 }
1443 
1444 void cElementManager::processCustomTag (const string &name, const list<sParam> &params)
1445 {
1446   //generate a mapping with all parameter values
1447   paramexpander->reset();
1448   list<sParam>::const_iterator itp;
1449   for (itp = params.begin(); itp != params.end(); ++itp)
1450     //assign parameter value... default values and stuff were already expanded
1451     paramexpander->addEntity ((*itp).name, /*"'" +*/ (*itp).value /*+ "'"*/);
1452   
1453   //process tag contents one by one
1454   list<sElementPart *>::iterator it;
1455   for (it = elements[name]->element.begin(); it != elements[name]->element.end(); ++it)
1456   {
1457     sElementPart *sep = *it;
1458     string part = sep->text;
1459     
1460     //expand tag parameters first
1461     part = paramexpander->expandEntities (part, true);
1462     
1463     //parameters are expanded, process this part
1464     if (sep->istag)
1465       //this part is another tag - expand it recursively
1466       gotTag (part);
1467     else
1468       //this part is regular text
1469       state->gotText (part);
1470   }
1471 
1472   //tag processed, send flag information, is any
1473   if (!elements[name]->flag.empty())
1474     state->gotFlag (true, elements[name]->flag);
1475 }
1476 
1477 enum paramParserState {
1478   parNone,
1479   parName,
1480   parValue,
1481   parQuotedValue
1482 };
1483 
1484 
1485 void cElementManager::processParamList (const string &params, list<string> &attlist,
1486     map<string, string> &attdefault)
1487 {
1488   //this is similar to the parser in gotTag(), but it's a bit simpler...
1489   
1490   string name, value;
1491   char quote;
1492   paramParserState state = parNone;
1493   string::const_iterator it;
1494   for (it = params.begin(); it != params.end(); ++it)
1495   {
1496     char ch = *it;
1497 
1498     //process character
1499     switch (state) {
1500       case parNone: {
1501         if (ch != ' ')
1502         {
1503           state = parName;
1504           name += ch;
1505         }
1506         break;
1507       }
1508       case parName: {
1509         if (ch == '=')
1510           state = parValue;
1511         else if (ch == ' ')
1512         {
1513           //new parameter, no default value
1514           attlist.push_back (lcase (name));
1515           name = "";
1516           state = parNone;
1517         }
1518         else
1519           name += ch;
1520         break;
1521       }
1522       case parValue: {
1523         if (ch == ' ')
1524         {
1525           //new parameter, with default value
1526           attlist.push_back (lcase (name));
1527           attdefault[name] = value;
1528           name = "";
1529           value = "";
1530           state = parNone;
1531         }
1532         else if (value.empty() && ((ch == '\'') || (ch == '"')))
1533         {
1534           state = parQuotedValue;
1535           quote = ch;
1536         }
1537         else
1538           value += ch;
1539         break;
1540       }
1541       case parQuotedValue: {
1542         if (ch == quote)
1543         {
1544           //new parameter, with default value
1545           attlist.push_back (lcase (name));
1546           attdefault[name] = value;
1547           name = "";
1548           value = "";
1549           state = parNone;
1550         }
1551         else
1552           value += ch;
1553         break;
1554       }
1555     };
1556   }
1557 
1558   //last parameter...
1559   switch (state) {
1560     case parName: {
1561       //new parameter, no default value
1562       attlist.push_back (lcase (name));
1563     }
1564     break;
1565     case parValue: {
1566       //new parameter, with default value
1567       attlist.push_back (lcase (name));
1568       attdefault[name] = value;
1569       break;
1570     }
1571     case parQuotedValue:
1572       results->addToList (results->createWarning (
1573           "Received tag definition with unfinished quoted default parameter value!"));
1574       //new parameter, with default value (unfinished, but hey...)
1575       attlist.push_back (lcase (name));
1576       attdefault[name] = value;
1577     break;
1578   };
1579   
1580   //everything done...
1581 }
1582 
1583 void cElementManager::addElement (const string &name, list<sElementPart *> contents,
1584     list<string> attlist, map<string, string> attdefault, bool open, bool empty,
1585     int tag, string flag)
1586 {
1587   //sanity checks
1588   if (elementDefined (name))
1589   {
1590     results->addToList (results->createError ("Multiple definition of element " + name + "!"));
1591     return;
1592   }
1593 
1594   sElement *e = new sElement;
1595   e->open = open;
1596   e->empty = empty;
1597   if ((tag >= 20) && (tag <= 99))
1598   {
1599     e->tag = tag;
1600     if (lineTags.count (tag))
1601       results->addToList (results->createError ("Element " + name +
1602           " uses an already assigned line tag!"));
1603     lineTags[tag] = name;
1604   }
1605   else
1606     e->tag = 0;
1607   e->flag = flag;
1608 
1609   //assign element contents, generating the list of closing tags
1610   e->element.clear();
1611   list<sElementPart *>::iterator it;
1612   for (it = contents.begin(); it != contents.end(); ++it)
1613   {
1614     sElementPart *ep = *it;
1615     if (ep->istag)
1616     {
1617       string tag = lcase (firstword (ep->text));
1618       if (elementDefined (tag))
1619       {
1620         if (open && !(openElement (tag)))
1621         {
1622           delete ep;
1623           results->addToList (results->createError ("Definition of open " + name +
1624               " tag contains secure tag " + tag + "!"));
1625         }
1626         else if (empty && !(emptyElement (tag)))
1627         {
1628           delete ep;
1629           results->addToList (results->createError ("Definition of empty " + name +
1630               " tag contains non-empty tag " + tag + "!"));
1631         }
1632         else
1633         {
1634           e->element.push_back (ep);
1635           if (!emptyElement(tag)) e->closingseq.push_front (tag);
1636         }
1637       }
1638       else
1639       {
1640         //element doesn't exist yet - we must believe that it's correct
1641         e->element.push_back (ep);
1642         if (!empty) e->closingseq.push_front (tag);
1643         results->addToList (results->createWarning ("Definition of element " + name +
1644             " contains undefined element " + tag + "!"));
1645       }
1646     }
1647     else
1648       e->element.push_back (ep);
1649   }
1650 
1651   //assign the element definition
1652   elements[name] = e;
1653 
1654   //set attribute list
1655   setAttList (name, attlist, attdefault);
1656 }
1657 
1658 void cElementManager::setAttList (const string &name, list<string> attlist,
1659     map<string, string> attdefault)
1660 {
1661   //sanity check
1662   if (!elements.count (name))
1663   {
1664     results->addToList (results->createWarning ("Received attribute list for undefined tag " +
1665         name + "!"));
1666     return;
1667   }
1668 
1669   sElement *e = elements[name];
1670   e->attlist.clear();
1671   e->attdefault.clear();
1672   e->attlist = attlist;
1673   e->attdefault = attdefault;
1674 }
1675 
1676 void cElementManager::removeElement (const string &name)
1677 {
1678   //external elements only
1679   if (elements.count (name))
1680   {
1681     sElement *e = elements[name];
1682     list<sElementPart *>::iterator it;
1683     for (it = e->element.begin(); it != e->element.end(); ++it)
1684       delete *it;
1685     e->element.clear();
1686     e->attlist.clear();
1687     e->attdefault.clear();
1688     e->closingseq.clear();
1689     if (e->tag)
1690       lineTags.erase (e->tag);
1691     delete e;
1692     elements.erase (name);
1693   }
1694 }
1695 
1696 void cElementManager::removeAll ()
1697 {
1698   //external elements only
1699   
1700   list<string> names;
1701   //we cannot delete definitions directly, because that would invalidate our iterator
1702   map<string, sElement *>::iterator it;
1703   for (it = elements.begin(); it != elements.end(); ++it)
1704     names.push_back (it->first);
1705   list<string>::iterator it2;
1706   for (it2 = names.begin(); it2 != names.end(); ++it2)
1707     removeElement (*it2);
1708   names.clear ();
1709 }