先看看下面这个HTML字符串。
 <HTML>=====<SPARATOR>s-value</SPARATOR>++++++<BODY A="AA">{123}<BR>[456]</BODY><IMG>*****</HTML>
<HTML>=====<SPARATOR>s-value</SPARATOR>++++++<BODY A="AA">{123}<BR>[456]</BODY><IMG>*****</HTML>
    很不标准吧?但浏览器却是能够正常显示的。Lingoes的结果里面充斥了大量的这样的字符串,但就如我常说的 -- “代码在手,天下我有”一样,修改了TinyHtmlParser,咱也能解析了~
 
      这下不怕了,嘿嘿。。。下面是修改后的TinyHtmlParser,修改了CParserData结构,使其更加容易理解,而“容易理解”对于我来说很重要,因为我喜欢--第一感觉该如何实现,那么代码就该按照感觉去写。代码只是现实的一种描述而已,越容易,越简单,越好。
 #ifndef __TINYHTMLPARSER_H__
#ifndef __TINYHTMLPARSER_H__
 #define __TINYHTMLPARSER_H__
#define __TINYHTMLPARSER_H__

 #include <iostream>
#include <iostream>
 #include <string>
#include <string>
 #include <queue>
#include <queue>
 #include <stack>
#include <stack>

 namespace TinyHtmlParser
namespace TinyHtmlParser


 {
{


 enum ExceptionNumber
enum ExceptionNumber  { EN_UNKNOWN = -1, EN_ATTRIB_VALUEMISS = 0, EN_ATTRIB_VALUEBROKEN,
{ EN_UNKNOWN = -1, EN_ATTRIB_VALUEMISS = 0, EN_ATTRIB_VALUEBROKEN, 
 EN_DOCUMENT_FORMATERROR
                    EN_DOCUMENT_FORMATERROR
 };
                    };

 class CExceptionObject
class CExceptionObject


 {
{
 public:
public:
 CExceptionObject(ExceptionNumber type, const std::wstring& info);
    CExceptionObject(ExceptionNumber type, const std::wstring& info);
 CExceptionObject(const CExceptionObject& right);
    CExceptionObject(const CExceptionObject& right);

 virtual ~CExceptionObject()
    virtual ~CExceptionObject()  {}
{}


 int Number() const
    int Number() const  { return _type; }
{ return _type; }

 const std::wstring& Info() const
    const std::wstring& Info() const  { return _info; }
{ return _info; }

 protected:
protected:
 ExceptionNumber _type;
    ExceptionNumber _type;
 std::wstring _info;
    std::wstring _info;
 private:
private:

 CExceptionObject& operator = (const CExceptionObject& right)
    CExceptionObject& operator = (const CExceptionObject& right)  { return *this; }
{ return *this; }
 };
};



 enum ElementType
enum ElementType  { ET_UNKNOWN = -1, ET_TAG = 0, ET_NODE, ET_ELEMENT };//0:just a tag, 1:no value, 2:have value
{ ET_UNKNOWN = -1, ET_TAG = 0, ET_NODE, ET_ELEMENT };//0:just a tag, 1:no value, 2:have value

 class CAttributeObject
class CAttributeObject


 {
{
 public:
public:
 CAttributeObject(const std::wstring& a, const std::wstring& v)
    CAttributeObject(const std::wstring& a, const std::wstring& v)
 : attr(a), value(v), next(NULL)
    : attr(a), value(v), next(NULL)

 
     {
{
 }
    }

 virtual ~CAttributeObject()
    virtual ~CAttributeObject()  {}
{}

 void Show(std::wostream& os) const;
    void Show(std::wostream& os) const;
 public:
public:
 std::wstring attr;
    std::wstring attr;
 std::wstring value;
    std::wstring value;
 CAttributeObject* next;
    CAttributeObject* next;
 };
};

 class CElementObject
class CElementObject


 {
{
 public:
public:
 CElementObject();
    CElementObject();
 virtual ~CElementObject();
    virtual ~CElementObject();

 virtual int Analyse();
    virtual int Analyse();

 const CAttributeObject* FindAttribute(const std::wstring& attr) const;
    const CAttributeObject* FindAttribute(const std::wstring& attr) const;

 void Show(std::wostream& os) const;
    void Show(std::wostream& os) const;
 protected:
protected:
 int AnalyseAttribute(const std::wstring& attr);
    int AnalyseAttribute(const std::wstring& attr);
 int MakeAttribute(const std::wstring& attr);
    int MakeAttribute(const std::wstring& attr);
 int MakeAttribute(const std::wstring& attr, const std::wstring& value);
    int MakeAttribute(const std::wstring& attr, const std::wstring& value);
 void FreeAnalyseAttribute();
    void FreeAnalyseAttribute();
 int AnalyseValue();
    int AnalyseValue();
 public:
public:
 ElementType type;
    ElementType type; 
 size_t level;
    size_t level;
 CElementObject* parent;
    CElementObject* parent;
 CElementObject* child;
    CElementObject* child;
 CElementObject* sibling;
    CElementObject* sibling;

 CAttributeObject* attrib;
    CAttributeObject* attrib;
 public:
public:
 std::wstring tag;
    std::wstring tag;
 std::wstring value;
    std::wstring value;
 };
};

 class CParserData
class CParserData


 {
{
 public:
public:

 enum DataType
    enum DataType  { DT_UNKNOWN = -1, DT_TAG = 0, DT_VALUE, DT_END, DT_DONE, DT_TAG_VALUE, DT_BROKEN };
{ DT_UNKNOWN = -1, DT_TAG = 0, DT_VALUE, DT_END, DT_DONE, DT_TAG_VALUE, DT_BROKEN };
 typedef std::pair<size_t, size_t> TRange;//start + end;
    typedef std::pair<size_t, size_t> TRange;//start + end;
 typedef std::vector<TRange> TValueVector;
    typedef std::vector<TRange> TValueVector;
 public:
public:
 CParserData()
    CParserData()
 : type(DT_UNKNOWN)
        : type(DT_UNKNOWN)

 
     {
{
 }
    }

 virtual ~CParserData()
    virtual ~CParserData()  {}
{}

 void Show(std::wostream& os) const;
    void Show(std::wostream& os) const;
 void Show(std::wostream& os, const std::wstring& html) const;
    void Show(std::wostream& os, const std::wstring& html) const;
 public:
public:
 DataType type;
    DataType type;
 TRange tag;
    TRange tag;
 TValueVector value;
    TValueVector value;
 };
};

 class CDocumentObject
class CDocumentObject


 {
{
 protected:
protected:
 static const wchar_t TAG_LT        =   L'<';
    static const wchar_t TAG_LT        =   L'<';
 static const wchar_t TAG_GT        =   L'>';
    static const wchar_t TAG_GT        =   L'>';
 static const wchar_t TAG_SLASH     =   L'/';
    static const wchar_t TAG_SLASH     =   L'/';
 static const wchar_t TAG_BSLASH    =   L'\\';
    static const wchar_t TAG_BSLASH    =   L'\\';
 static const wchar_t TAG_AND       =   L'&';
    static const wchar_t TAG_AND       =   L'&';

 typedef std::stack<CParserData> TDataStack;
    typedef std::stack<CParserData> TDataStack;
 typedef std::pair<size_t, CParserData> TNodeData;//level + tag;
    typedef std::pair<size_t, CParserData> TNodeData;//level + tag;
 typedef std::deque<TNodeData> TNodeQueue;
    typedef std::deque<TNodeData> TNodeQueue;
 public:
public:
 typedef std::stack<const CElementObject*> TElementStack;
    typedef std::stack<const CElementObject*> TElementStack;
 public:
public:
 CDocumentObject();
    CDocumentObject();
 virtual ~CDocumentObject();
    virtual ~CDocumentObject();

 int Load(const std::wstring& str, bool strict = true);
    int Load(const std::wstring& str, bool strict = true);


 const CElementObject* Root() const
    const CElementObject* Root() const  { return _root; }
{ return _root; }

 const CElementObject* FindFirstElement(const std::wstring& tag);
    const CElementObject* FindFirstElement(const std::wstring& tag);
 const CElementObject* FindNextElement();
    const CElementObject* FindNextElement();

 const CElementObject* FindFirstElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack);
    const CElementObject* FindFirstElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack);
 const CElementObject* FindNextElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack);
    const CElementObject* FindNextElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack);

 const CAttributeObject* FindAttribute(const CElementObject* element, const std::wstring& attr);
    const CAttributeObject* FindAttribute(const CElementObject* element, const std::wstring& attr);
 
    

 bool IsMistake() const
    bool IsMistake() const  { return _bIsMistake; }
{ return _bIsMistake; }

 void Show(std::wostream& os) const;
    void Show(std::wostream& os) const;
 protected:
protected:
 int PreProcess(const std::wstring& str, std::wstring& html, bool strict);
    int PreProcess(const std::wstring& str, std::wstring& html, bool strict);
 int PreParser(const std::wstring& html, TNodeQueue& que, bool strict);
    int PreParser(const std::wstring& html, TNodeQueue& que, bool strict);
 int Parser(const std::wstring& html, TNodeQueue& que, bool strict);
    int Parser(const std::wstring& html, TNodeQueue& que, bool strict);
 private:
private:
 int PreParserLT(const std::wstring& html, std::wstring::size_type& pos, CParserData& data);
    int PreParserLT(const std::wstring& html, std::wstring::size_type& pos, CParserData& data);

 int PushValueData(CParserData::DataType type, size_t start, size_t end, TDataStack& datastack) const;
    int PushValueData(CParserData::DataType type, size_t start, size_t end, TDataStack& datastack) const;
 int PushTagData(const std::wstring& html, CParserData& data, TDataStack& datastack, TNodeQueue& nodeque) const;
    int PushTagData(const std::wstring& html, CParserData& data, TDataStack& datastack, TNodeQueue& nodeque) const;
 int PreParserBroken(const std::wstring& html, TDataStack& datastack, TNodeQueue& nodeque) const;
    int PreParserBroken(const std::wstring& html, TDataStack& datastack, TNodeQueue& nodeque) const;
 
    
 int CheckSpecialTag(const std::wstring& html, const CParserData& data) const;
    int CheckSpecialTag(const std::wstring& html, const CParserData& data) const;
 int CheckTag(const std::wstring& html, const CParserData& tag, const CParserData& end) const;
    int CheckTag(const std::wstring& html, const CParserData& tag, const CParserData& end) const;
 CElementObject* MakeElement(const std::wstring& html, const TNodeData& node, CElementObject* parent, CElementObject* sibling) const;
    CElementObject* MakeElement(const std::wstring& html, const TNodeData& node, CElementObject* parent, CElementObject* sibling) const;

 void CDocumentObject::ShowElement(std::wostream& os, const CElementObject* e) const;
    void CDocumentObject::ShowElement(std::wostream& os, const CElementObject* e) const;

 void FreeElement(CElementObject* root);
    void FreeElement(CElementObject* root);

 const CElementObject* FindElement(const CElementObject* root, const CElementObject* pe, const std::wstring& tag, TElementStack& stack);
    const CElementObject* FindElement(const CElementObject* root, const CElementObject* pe, const std::wstring& tag, TElementStack& stack);
 private:
private:
 CElementObject* _root;
    CElementObject* _root;
 private:
private:
 std::wstring _findtag;
    std::wstring _findtag;
 TElementStack _findstack;
    TElementStack _findstack;
 private:
private:
 bool _bIsMistake;
    bool _bIsMistake;
 };
};

 }
}

 #endif
#endif

 #include <sstream>
#include <sstream>

 #include "TinyHtmlParser.h"
#include "TinyHtmlParser.h"

 namespace TinyHtmlParser
namespace TinyHtmlParser


 {
{

 CExceptionObject::CExceptionObject(TinyHtmlParser::ExceptionNumber type, const std::wstring &info)
CExceptionObject::CExceptionObject(TinyHtmlParser::ExceptionNumber type, const std::wstring &info)
 : _type(type)
: _type(type)
 , _info(info)
, _info(info)


 {
{
 }
}

 CExceptionObject::CExceptionObject(const TinyHtmlParser::CExceptionObject &right)
CExceptionObject::CExceptionObject(const TinyHtmlParser::CExceptionObject &right)
 : _type(right._type)
: _type(right._type)
 , _info(right._info)
, _info(right._info)


 {
{
 }
}

 #define THROW_EXCEPTION(type, info) \
#define THROW_EXCEPTION(type, info) \


 { \
{ \
 std::wostringstream oss; \
    std::wostringstream oss; \
 oss << info; \
    oss << info; \
 throw CExceptionObject(type, oss.str()); \
    throw CExceptionObject(type, oss.str()); \
 }
}

 /**////////////////
/**////////////////

 void CAttributeObject::Show(std::wostream& os) const
void CAttributeObject::Show(std::wostream& os) const


 {
{
 os << "    attr : " << this->attr << " -- value = " << this->value << std::endl;
    os << "    attr : " << this->attr << " -- value = " << this->value << std::endl;
 }
}

 CElementObject::CElementObject()
CElementObject::CElementObject()
 : type(ET_UNKNOWN)
: type(ET_UNKNOWN)
 , level(0)
, level(0)
 , parent(NULL)
, parent(NULL)
 , child(NULL)
, child(NULL)
 , sibling(NULL)
, sibling(NULL)
 , attrib(NULL)
, attrib(NULL)


 {
{
 }
}

 CElementObject::~CElementObject()
CElementObject::~CElementObject()


 {
{
 FreeAnalyseAttribute();
    FreeAnalyseAttribute();
 }
}

 int CElementObject::Analyse()
int CElementObject::Analyse()


 {
{
 std::wstring str = tag;
    std::wstring str = tag;

 std::wstring::size_type pos = str.find(L" ");
    std::wstring::size_type pos = str.find(L" ");
 if(pos != std::wstring::npos)
    if(pos != std::wstring::npos)

 
     {
{
 tag = str.substr(0, pos);
        tag = str.substr(0, pos);

 str = str.substr(pos + 1);
        str = str.substr(pos + 1);
 if(AnalyseAttribute(str) != 0)
        if(AnalyseAttribute(str) != 0)

 
         {
{
 return -1;
            return -1;
 }
        }
 }
    }
 if(type == ET_ELEMENT)
    if(type == ET_ELEMENT)

 
     {
{
 if(AnalyseValue() != 0)
        if(AnalyseValue() != 0)
 return -1;
            return -1;
 }
    }
 return 0;
    return 0;
 }
}

 int CElementObject::AnalyseAttribute(const std::wstring& attr)
int CElementObject::AnalyseAttribute(const std::wstring& attr)


 {
{
 if(attr.size() == 0)
    if(attr.size() == 0)
 return 0;
        return 0;

 std::wstring a, v;
    std::wstring a, v;
 std::wstring::size_type pos = attr.find(L"="), start = 0;
    std::wstring::size_type pos = attr.find(L"="), start = 0;
 while(pos != std::wstring::npos)
    while(pos != std::wstring::npos)

 
     {
{
 if(pos == attr.size() - 1)
        if(pos == attr.size() - 1)

 
         {
{
 THROW_EXCEPTION(EN_ATTRIB_VALUEMISS, L"Attribue analyse failed - attribute string : " << attr);
            THROW_EXCEPTION(EN_ATTRIB_VALUEMISS, L"Attribue analyse failed - attribute string : " << attr);
 return -1;
            return -1;
 }
        }
 a = attr.substr(start, pos - start);
        a = attr.substr(start, pos - start);
 start = pos + 1;
        start = pos + 1;
 if(attr[pos + 1] == L'\"')
        if(attr[pos + 1] == L'\"')

 
         {
{
 pos = attr.find(L"\"", start + 1);
            pos = attr.find(L"\"", start + 1);
 if(pos == std::wstring::npos)
            if(pos == std::wstring::npos)

 
             {
{
 THROW_EXCEPTION(EN_ATTRIB_VALUEBROKEN, L"Attribue analyse failed - attribute string : " << attr);
                THROW_EXCEPTION(EN_ATTRIB_VALUEBROKEN, L"Attribue analyse failed - attribute string : " << attr);
 return -1;
                return -1;
 }
            }
 v = attr.substr(start, pos - start + 1);
            v = attr.substr(start, pos - start + 1);
 start = pos + 2;
            start = pos + 2;
 }
        }
 else
        else

 
         {
{
 pos = attr.find(L" ", start);
            pos = attr.find(L" ", start);
 if(pos == std::wstring::npos)
            if(pos == std::wstring::npos)
 pos = attr.size();
                pos = attr.size();
 v = attr.substr(start, pos - start);
            v = attr.substr(start, pos - start);
 start = pos + 1;
            start = pos + 1;
 }
        }
 if(MakeAttribute(a, v) != 0)
        if(MakeAttribute(a, v) != 0)
 return -1;
            return -1;

 if(start >= attr.size())
        if(start >= attr.size())
 break;
            break;

 pos = attr.find(L"=", start);
        pos = attr.find(L"=", start);
 }
    }
 return 0;
    return 0;
 }
}

 int CElementObject::MakeAttribute(const std::wstring &attr)
int CElementObject::MakeAttribute(const std::wstring &attr)


 {
{
 std::wstring::size_type pos = attr.find(L"=");
    std::wstring::size_type pos = attr.find(L"=");
 if(pos == std::wstring::npos)
    if(pos == std::wstring::npos)
 return -1;
        return -1;

 return MakeAttribute(attr.substr(0, pos), attr.substr(pos));
    return MakeAttribute(attr.substr(0, pos), attr.substr(pos));
 }
}

 int CElementObject::MakeAttribute(const std::wstring &attr, const std::wstring& value)
int CElementObject::MakeAttribute(const std::wstring &attr, const std::wstring& value)


 {
{
 std::auto_ptr<CAttributeObject> obj(new CAttributeObject(attr, value));//attr.substr(0, pos), attr.substr(pos)));
    std::auto_ptr<CAttributeObject> obj(new CAttributeObject(attr, value));//attr.substr(0, pos), attr.substr(pos)));
 
    
 if(attrib != NULL)
    if(attrib != NULL)

 
     {
{
 CAttributeObject* tmp = attrib;
        CAttributeObject* tmp = attrib;
 while(tmp->next != NULL)
        while(tmp->next != NULL)
 tmp = tmp->next;
            tmp = tmp->next;
 tmp->next = obj.release();
        tmp->next = obj.release();
 }
    }
 else
    else

 
     {
{
 attrib = obj.release();
        attrib = obj.release();
 }
    }
 return 0;
    return 0;
 }
}


 void CElementObject::FreeAnalyseAttribute()
void CElementObject::FreeAnalyseAttribute()


 {
{
 CAttributeObject* tmp = attrib;
    CAttributeObject* tmp = attrib;
 while(attrib != NULL)
    while(attrib != NULL)

 
     {
{
 tmp = attrib->next;
        tmp = attrib->next;
 delete attrib;
        delete attrib;
 attrib = tmp;
        attrib = tmp;
 }
    }

 }
}

 int CElementObject::AnalyseValue()
int CElementObject::AnalyseValue()


 {
{
 std::wstring::size_type pos = this->value.find(L" ");
    std::wstring::size_type pos = this->value.find(L" ");
 while(pos != std::wstring::npos)
    while(pos != std::wstring::npos)

 
     {
{
 this->value.replace(pos, 6, L" ");
        this->value.replace(pos, 6, L" ");
 pos = this->value.find(L" ", pos + 1);
        pos = this->value.find(L" ", pos + 1);
 }
    }    

 return 0;
    return 0;
 }
}

 const CAttributeObject* CElementObject::FindAttribute(const std::wstring& attr) const
const CAttributeObject* CElementObject::FindAttribute(const std::wstring& attr) const


 {
{
 const CAttributeObject* pa = this->attrib;
    const CAttributeObject* pa = this->attrib;
 while(pa != NULL)
    while(pa != NULL)

 
     {
{
 if(pa->attr == attr)
        if(pa->attr == attr)
 return pa;
            return pa;
 pa = pa->next;
        pa = pa->next;
 }
    }
 return pa;
    return pa;
 }
}

 void CElementObject::Show(std::wostream& os) const
void CElementObject::Show(std::wostream& os) const


 {
{
 os << "[" << this->level << "]" << "Tag : " << this->tag;
    os << "[" << this->level << "]" << "Tag : " << this->tag;
 if(this->type == ET_ELEMENT)
    if(this->type == ET_ELEMENT)

 os << " -- value = " << /**//*std::wstring*/(this->value);
        os << " -- value = " << /**//*std::wstring*/(this->value);
 os << std::endl;
    os << std::endl;

 const CAttributeObject* attr = this->attrib;
    const CAttributeObject* attr = this->attrib;
 while(attr != NULL)
    while(attr != NULL)

 
     {
{
 attr->Show(os);
        attr->Show(os);
 attr = attr->next;
        attr = attr->next;
 }
    }
 os << std::endl;
    os << std::endl;
 }
}
 //
//
 void CParserData::Show(std::wostream &os) const
void CParserData::Show(std::wostream &os) const


 {
{
 os << "\nType = " << this->type;
    os << "\nType = " << this->type;
 os << "\nTag Start = " << this->tag.first << " - End = " << this->tag.second;
    os << "\nTag Start = " << this->tag.first << " - End = " << this->tag.second;

 for(TValueVector::const_iterator it = this->value.begin(); it != this->value.end(); ++ it)
    for(TValueVector::const_iterator it = this->value.begin(); it != this->value.end(); ++ it)

 
     {
{
 os << "\nValue Start = " << it->first << " - End = " << it->second;
        os << "\nValue Start = " << it->first << " - End = " << it->second;
 }
    }
 os << std::endl;
    os << std::endl;
 }
}

 void CParserData::Show(std::wostream& os, const std::wstring& html) const
void CParserData::Show(std::wostream& os, const std::wstring& html) const


 {
{
 os << "\nType = " << this->type;
    os << "\nType = " << this->type;
 os << "\nTag = " << "[" << this->tag.first << "," << this->tag.second << "]" << html.substr(this->tag.first, this->tag.second - this->tag.first + 1);
    os << "\nTag = " << "[" << this->tag.first << "," << this->tag.second << "]" << html.substr(this->tag.first, this->tag.second - this->tag.first + 1);
 for(TValueVector::const_iterator it = this->value.begin(); it != this->value.end(); ++ it)
    for(TValueVector::const_iterator it = this->value.begin(); it != this->value.end(); ++ it)

 
     {
{
 os << "\nValue = " << "[" << it->first << "," << it->second << "]" << html.substr(it->first, it->second - it->first + 1);
        os << "\nValue = " << "[" << it->first << "," << it->second << "]" << html.substr(it->first, it->second - it->first + 1);
 }
    }
 os << std::endl;
    os << std::endl;
 }
}
 //
//
 CDocumentObject::CDocumentObject()
CDocumentObject::CDocumentObject()
 : _root(NULL)
: _root(NULL)
 , _bIsMistake(false)
, _bIsMistake(false)


 {
{
 }
}

 CDocumentObject::~CDocumentObject()
CDocumentObject::~CDocumentObject()


 {
{
 if(_root != NULL)
    if(_root != NULL)
 FreeElement(_root);
        FreeElement(_root);
 }
}

 int CDocumentObject::Load(const std::wstring &str, bool strict)
int CDocumentObject::Load(const std::wstring &str, bool strict)


 {
{
 std::wstring html;
    std::wstring html;
 if(PreProcess(str, html, strict) != 0)
    if(PreProcess(str, html, strict) != 0)
 return -1;
        return -1;
 TNodeQueue que;
    TNodeQueue que;
 if(PreParser(html, que, strict) != 0)
    if(PreParser(html, que, strict) != 0)
 return -1;
        return -1;
 if(Parser(html, que, strict) != 0)
    if(Parser(html, que, strict) != 0)
 return -1;
        return -1;
 return 0;
    return 0;
 }
}

 int CDocumentObject::PreProcess(const std::wstring& str, std::wstring& html, bool strict)
int CDocumentObject::PreProcess(const std::wstring& str, std::wstring& html, bool strict)


 {
{
 //html = str;
    //html = str;
 bool tag = false;
    bool tag = false;
 for(std::wstring::const_iterator it = str.begin(); it != str.end(); ++ it)
    for(std::wstring::const_iterator it = str.begin(); it != str.end(); ++ it)

 
     {
{
 if(*it == TAG_LT)
        if(*it == TAG_LT)

 
         {
{
 if(tag == true)
            if(tag == true)

 
             {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Double '<'.");
                THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Double '<'.");
 return -1;
                return -1;
 }
            }
 tag = true;
            tag = true;
 }
        }
 else if(*it == TAG_GT)
        else if(*it == TAG_GT)

 
         {
{
 if(tag == false)
            if(tag == false)

 
             {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Miss '<' before '>'.");
                THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Miss '<' before '>'.");
 return -1;
                return -1;
 }
            }
 tag = false;
            tag = false;
 }
        }
 else
        else

 
         {
{
 if(tag == false)
            if(tag == false)

 
             {
{
 //if(isspace((unsigned char)*it) != 0)
                //if(isspace((unsigned char)*it) != 0)
 //    continue;
                //    continue;
 if((unsigned char)(*it) == '\r' || (unsigned char)(*it) == '\n')
                if((unsigned char)(*it) == '\r' || (unsigned char)(*it) == '\n')
 continue;
                    continue;
 }
            }
 }
        }
 html += *it;
        html += *it;
 }
    }

 return 0;
    return 0;
 }
}

 int CDocumentObject::PreParser(const std::wstring& html, CDocumentObject::TNodeQueue& que, bool strict)
int CDocumentObject::PreParser(const std::wstring& html, CDocumentObject::TNodeQueue& que, bool strict)


 {
{
 std::wstring::size_type pos = 0;
    std::wstring::size_type pos = 0;

 if(html.size() == 0)
    if(html.size() == 0)

 
     {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"HTML is empty.");
        THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"HTML is empty.");
 return -1;
        return -1;
 }
    }
 if(html[pos] != TAG_LT)
    if(html[pos] != TAG_LT)

 
     {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"First character of HTML is NOT '<' - pos : " << pos);
        THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"First character of HTML is NOT '<' - pos : " << pos);
 return -1;
        return -1;
 }
    }

 TDataStack datastack;
    TDataStack datastack;
 CParserData data;
    CParserData data;
 size_t start = 0;
    size_t start = 0;

 while(pos < html.size())
    while(pos < html.size())

 
     {
{
 if(html[pos] == TAG_LT)
        if(html[pos] == TAG_LT)

 
         {
{
 if(pos > start)
            if(pos > start)

 
             {
{
 if(PushValueData(CParserData::DT_VALUE, start, pos -1, datastack) != 0)
                if(PushValueData(CParserData::DT_VALUE, start, pos -1, datastack) != 0)
 return -1;
                    return -1;
 }
            }

 if(PreParserLT(html, pos, data) != 0)
            if(PreParserLT(html, pos, data) != 0)
 break;
                break;
 if(PushTagData(html, data, datastack, que) != 0)
            if(PushTagData(html, data, datastack, que) != 0)
 return -1;
                return -1;
 
            
 ++ pos;
            ++ pos;
 start = pos;
            start = pos;
 }
        }
 //else if(html[pos] == TAG_GT || html[pos] == TAG_SLASH)
        //else if(html[pos] == TAG_GT || html[pos] == TAG_SLASH)
 //{
        //{
 //    return -1;
        //    return -1;
 //}
        //}
 else
        else

 
         {
{
 ++ pos;
            ++ pos;
 }
        }
 }
    }

 if(datastack.size() > 0)
    if(datastack.size() > 0)

 
     {
{
 if(strict)
        if(strict)

 
         {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Tags do NOT match each other.");
            THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Tags do NOT match each other.");
 return -1;
            return -1;
 }
        }

 if(pos > start)
        if(pos > start)

 
         {
{
 if(PushValueData(CParserData::DT_BROKEN, start, pos, datastack) != 0)
            if(PushValueData(CParserData::DT_BROKEN, start, pos, datastack) != 0)
 return -1;
                return -1;
 }
        }

 if(PreParserBroken(html, datastack, que) != 0)
        if(PreParserBroken(html, datastack, que) != 0)
 return -1;
            return -1;
 }
    }

 return 0;
    return 0;
 }
}

 int CDocumentObject::Parser(const std::wstring& html, CDocumentObject::TNodeQueue& que, bool strict)
int CDocumentObject::Parser(const std::wstring& html, CDocumentObject::TNodeQueue& que, bool strict)


 {
{
 CElementObject *pe = NULL,  *pp = NULL, *ps = NULL;
    CElementObject *pe = NULL,  *pp = NULL, *ps = NULL;
 size_t level = 0;
    size_t level = 0;
 while(que.size()> 0)
    while(que.size()> 0)

 
     {
{
 const TNodeData &node = que.front();
        const TNodeData &node = que.front();
 if(level < node.first)
        if(level < node.first)

 
         {
{
 pp = pe;
            pp = pe;
 ps = NULL;
            ps = NULL;
 }
        }
 else if(level == node.first)
        else if(level == node.first)

 
         {
{
 ps = pe;
            ps = pe;
 }
        }
 else//>
        else//>

 
         {
{
 ps = pe;
            ps = pe;
 pp = pe->parent;
            pp = pe->parent;
 int t = level - node.first;
            int t = level - node.first;
 while(t > 0)
            while(t > 0)

 
             {
{
 ps = ps->parent;
                ps = ps->parent;
 pp = pp->parent;
                pp = pp->parent;
 -- t;
                -- t;
 }
            }
 }
        }        
 level = node.first;
        level = node.first;

 pe = MakeElement(html, node, pp, ps);
        pe = MakeElement(html, node, pp, ps);

 if(pe == NULL)
        if(pe == NULL)
 return -1;
            return -1;

 que.pop_front();
        que.pop_front();
 }
    }

 if(pp != NULL)
    if(pp != NULL)

 
     {
{
 while(pp->parent != NULL)
        while(pp->parent != NULL)
 pp = pp->parent;
            pp = pp->parent;
 _root = pp;
        _root = pp;
 }
    }
 else
    else
 _root = pe;
        _root = pe;

 return 0;
    return 0;
 }
}

 int CDocumentObject::PreParserLT(const std::wstring& html, std::wstring::size_type& pos, CParserData& data)
int CDocumentObject::PreParserLT(const std::wstring& html, std::wstring::size_type& pos, CParserData& data)


 {
{
 if(pos == html.size() - 1)
    if(pos == html.size() - 1)

 
     {
{
 //THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"'<' is the last character.");
        //THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"'<' is the last character.");
 return -1;
        return -1;
 }
    }
 
    
 data.tag.first = pos;
    data.tag.first = pos;

 ++ pos;
    ++ pos;

 if(html[pos] != TAG_SLASH)
    if(html[pos] != TAG_SLASH)

 
     {
{
 data.type = CParserData::DT_TAG;
       data.type = CParserData::DT_TAG;
 }
    }
 else
    else

 
     {
{
 data.type = CParserData::DT_END;
        data.type = CParserData::DT_END;
 ++ pos;
        ++ pos;
 }
    }

 while(pos < html.size())
   while(pos < html.size())

 
     {
{
 if(html[pos] == TAG_GT)
        if(html[pos] == TAG_GT)

 
         {
{
 if(html[pos - 1] == TAG_SLASH)
            if(html[pos - 1] == TAG_SLASH)

 
             {
{
 data.type = CParserData::DT_DONE;
                data.type = CParserData::DT_DONE;
 }
            }

 data.tag.second = pos;
            data.tag.second = pos;
 
     
 return 0;
            return 0;
 }
        }
 else if(html[pos] == TAG_LT)
        else if(html[pos] == TAG_LT)

 
         {
{
 //THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"'<' follows '<'.");
            //THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"'<' follows '<'.");
 return -1;
            return -1;
 }
        }

 ++ pos;
        ++ pos;
 }
    }
 //THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Miss '>' after '<'");
    //THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Miss '>' after '<'");
 return -1;
    return -1;
 }
}

 int CDocumentObject::PushValueData(CParserData::DataType type, size_t start, size_t end, TDataStack& datastack) const
int CDocumentObject::PushValueData(CParserData::DataType type, size_t start, size_t end, TDataStack& datastack) const


 {
{
 if(datastack.size() == 0)
    if(datastack.size() == 0)

 
     {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Miss '<' before got value - pos : " << start);
        THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Miss '<' before got value - pos : " << start);
 return -1;
        return -1;
 }
    } 

 CParserData& prev = datastack.top();
    CParserData& prev = datastack.top();
 if(prev.type == CParserData::DT_TAG)
    if(prev.type == CParserData::DT_TAG)

 
     {
{
 prev.value.push_back(std::make_pair(start, end));
        prev.value.push_back(std::make_pair(start, end));

 prev.type = CParserData::DT_TAG_VALUE;
        prev.type = CParserData::DT_TAG_VALUE;
 }
    }
 else if(prev.type == CParserData::DT_TAG_VALUE)
    else if(prev.type == CParserData::DT_TAG_VALUE)

 
     {
{
 prev.value.push_back(std::make_pair(start, end));
        prev.value.push_back(std::make_pair(start, end));
 }
    }
 else
    else

 
     {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Type does match : " << prev.type << " - pos : " << start);
        THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Type does match : " << prev.type << " - pos : " << start);
 return -1;
        return -1;
 }
    }

 return 0;
    return 0;
 }
}

 int CDocumentObject::PushTagData(const std::wstring& html, CParserData& data, CDocumentObject::TDataStack& datastack, CDocumentObject::TNodeQueue& nodeque) const
int CDocumentObject::PushTagData(const std::wstring& html, CParserData& data, CDocumentObject::TDataStack& datastack, CDocumentObject::TNodeQueue& nodeque) const


 {
{
 if(CheckSpecialTag(html, data) == 0)
    if(CheckSpecialTag(html, data) == 0)

 
     {
{
 data.type = CParserData::DT_DONE;
        data.type = CParserData::DT_DONE;
 }
    }

 if(data.type == CParserData::DT_TAG)
    if(data.type == CParserData::DT_TAG)

 
     {
{
 datastack.push(data);
        datastack.push(data);
 }
    }
 else if(data.type == CParserData::DT_END)
    else if(data.type == CParserData::DT_END)

 
     {
{
 if(datastack.size() == 0)
        if(datastack.size() == 0)

 
         {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Miss start-tag before end-tag - pos : " << data.tag.first);
            THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Miss start-tag before end-tag - pos : " << data.tag.first);
 return -1;
            return -1;
 }
        }

 if(CheckTag(html, datastack.top(), data) != 0)
        if(CheckTag(html, datastack.top(), data) != 0)
 return -1;
            return -1;
 
        
 nodeque.push_front(std::make_pair(datastack.size() - 1, datastack.top()));
        nodeque.push_front(std::make_pair(datastack.size() - 1, datastack.top()));
 datastack.pop();
        datastack.pop();
 }
    }
 else if(data.type == CParserData::DT_DONE)
    else if(data.type == CParserData::DT_DONE)

 
     {
{
 nodeque.push_front(std::make_pair(datastack.size(), data));
        nodeque.push_front(std::make_pair(datastack.size(), data));
 }
    }
 else
    else

 
     {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Wrong tag type : " << data.type << L" - pos : " << data.tag.first);
        THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Wrong tag type : " << data.type << L" - pos : " << data.tag.first);
 return -1;
        return -1;
 }
    }
 return 0;
    return 0;
 }
}

 int CDocumentObject::PreParserBroken(const std::wstring& html, TDataStack& datastack, TNodeQueue& nodeque) const
int CDocumentObject::PreParserBroken(const std::wstring& html, TDataStack& datastack, TNodeQueue& nodeque) const


 {
{
 while(datastack.size() > 0)
    while(datastack.size() > 0)

 
     {
{
 CParserData& data = datastack.top();
        CParserData& data = datastack.top();
 if(data.type == CParserData::DT_TAG || data.type == CParserData::DT_TAG_VALUE)
        if(data.type == CParserData::DT_TAG || data.type == CParserData::DT_TAG_VALUE)

 
         {
{
 nodeque.push_front(std::make_pair(datastack.size() - 1, data));
            nodeque.push_front(std::make_pair(datastack.size() - 1, data));

 datastack.pop();
            datastack.pop();
 }
        }
 else if(data.type == CParserData::DT_BROKEN)
        else if(data.type == CParserData::DT_BROKEN)

 
         {
{
 nodeque.push_front(std::make_pair(datastack.size() - 1, data));
            nodeque.push_front(std::make_pair(datastack.size() - 1, data));

 datastack.pop();
            datastack.pop();        
 }
        }
 else
        else

 
         {
{
 return -1;
            return -1;
 }
        }
 }
    }

 return 0;
    return 0;    
 }
}

 int CDocumentObject::CheckSpecialTag(const std::wstring& html, const CParserData& data) const
int CDocumentObject::CheckSpecialTag(const std::wstring& html, const CParserData& data) const


 {
{
 std::wstring tag = html.substr(data.tag.first + 1, data.tag.second - data.tag.first - 1);
    std::wstring tag = html.substr(data.tag.first + 1, data.tag.second - data.tag.first - 1);
 std::wstring::size_type pos = tag.find(L" ");
    std::wstring::size_type pos = tag.find(L" ");
 if(pos != std::wstring::npos)
    if(pos != std::wstring::npos)
 tag = tag.substr(0, pos);
        tag = tag.substr(0, pos);   
 
   
 if(tag == L"IMG")
    if(tag == L"IMG")
 return 0;
        return 0;
 if(tag == L"PARAM")
    if(tag == L"PARAM")
 return 0;
        return 0;
 if(tag == L"BR")
    if(tag == L"BR")
 return 0;
        return 0;
 if(tag == L"HR")
    if(tag == L"HR")
 return 0;
        return 0;
 if(tag == L"P")
    if(tag == L"P")
 return 0;
        return 0;

 return -1;
    return -1;
 }
}

 int CDocumentObject::CheckTag(const std::wstring& html, const CParserData& tag, const CParserData& end) const
int CDocumentObject::CheckTag(const std::wstring& html, const CParserData& tag, const CParserData& end) const


 {
{
 std::wstring str = html.substr(tag.tag.first + 1, tag.tag.second - tag.tag.first - 1);
    std::wstring str = html.substr(tag.tag.first + 1, tag.tag.second - tag.tag.first - 1);
 std::wstring::size_type pos = str.find(L" ");
    std::wstring::size_type pos = str.find(L" ");
 if(pos != std::wstring::npos)
    if(pos != std::wstring::npos)
 str = str.substr(0, pos);
        str = str.substr(0, pos);
 
    
 if(str != html.substr(end.tag.first + 2, end.tag.second - end.tag.first - 2))
    if(str != html.substr(end.tag.first + 2, end.tag.second - end.tag.first - 2))

 
     {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"do NOT match tag : " << str << L" and " << html.substr(end.tag.first + 2, end.tag.second - end.tag.first - 2));
        THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"do NOT match tag : " << str << L" and " << html.substr(end.tag.first + 2, end.tag.second - end.tag.first - 2));
 return -1;
        return -1;
 }
    }
 return 0;
   return 0;
 }
}

 CElementObject* CDocumentObject::MakeElement(const std::wstring& html, const CDocumentObject::TNodeData &node, CElementObject *parent, CElementObject *sibling) const
CElementObject* CDocumentObject::MakeElement(const std::wstring& html, const CDocumentObject::TNodeData &node, CElementObject *parent, CElementObject *sibling) const


 {
{
 std::auto_ptr<CElementObject> ele(new CElementObject);
    std::auto_ptr<CElementObject> ele(new CElementObject);
 
    
 ele->level = node.first;
    ele->level = node.first;

 if(node.second.type == CParserData::DT_TAG)
    if(node.second.type == CParserData::DT_TAG)

 
     {
{
 ele->type = ET_NODE;
        ele->type = ET_NODE;
 ele->tag = html.substr(node.second.tag.first + 1, node.second.tag.second - node.second.tag.first - 1);
        ele->tag = html.substr(node.second.tag.first + 1, node.second.tag.second - node.second.tag.first - 1);
 }
    }
 else if(node.second.type == CParserData::DT_DONE)
    else if(node.second.type == CParserData::DT_DONE)

 
     {
{
 ele->type = ET_TAG;
        ele->type = ET_TAG;
 ele->tag = html.substr(node.second.tag.first + 1, node.second.tag.second - node.second.tag.first - 1);
        ele->tag = html.substr(node.second.tag.first + 1, node.second.tag.second - node.second.tag.first - 1);
 }
    }
 else if(node.second.type == CParserData::DT_TAG_VALUE)
    else if(node.second.type == CParserData::DT_TAG_VALUE)

 
     {
{
 ele->type = ET_ELEMENT;
        ele->type = ET_ELEMENT;
 ele->tag = html.substr(node.second.tag.first + 1, node.second.tag.second - node.second.tag.first - 1);
        ele->tag = html.substr(node.second.tag.first + 1, node.second.tag.second - node.second.tag.first - 1);
 
        
 ele->value = L"";
        ele->value = L"";
 for(CParserData::TValueVector::const_iterator it = node.second.value.begin(); it != node.second.value.end(); ++ it)
        for(CParserData::TValueVector::const_iterator it = node.second.value.begin(); it != node.second.value.end(); ++ it)

 
         {
{
 ele->value += html.substr(it->first, it->second - it->first + 1);
            ele->value += html.substr(it->first, it->second - it->first + 1);
 }
        }
 }
    }
 else
    else

 
     {
{
 THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Wrong Tag Type : " << node.second.type);
        THROW_EXCEPTION(EN_DOCUMENT_FORMATERROR, L"Wrong Tag Type : " << node.second.type);
 return NULL;
        return NULL;
 }
    }

 if(ele->Analyse() != 0)
    if(ele->Analyse() != 0)

 
     {
{
 return NULL;
        return NULL;
 }
    }

 if(parent != NULL)
    if(parent != NULL)
 parent->child = ele.get();
        parent->child = ele.get();
 ele->parent = parent;
    ele->parent = parent;
 ele->sibling = sibling;
    ele->sibling = sibling;

 return ele.release();
    return ele.release();
 }
}

 void CDocumentObject::Show(std::wostream &os) const
void CDocumentObject::Show(std::wostream &os) const


 {
{
 if(_root != NULL)
    if(_root != NULL)
 ShowElement(os, _root);
        ShowElement(os, _root);
 }
}

 void CDocumentObject::ShowElement(std::wostream& os, const CElementObject* e) const
void CDocumentObject::ShowElement(std::wostream& os, const CElementObject* e) const


 {
{
 const CElementObject* pe = e, *ps = e->sibling;
    const CElementObject* pe = e, *ps = e->sibling;

 pe->Show(os);
    pe->Show(os);
 
    
 pe = pe->child;
    pe = pe->child;
 if(pe != NULL)
    if(pe != NULL)

 
     {
{
 ShowElement(os, pe);
        ShowElement(os, pe);
 }
    }
 if(ps != NULL)
    if(ps != NULL)

 
     {
{
 ShowElement(os, ps);
        ShowElement(os, ps);
 }
    }
 }
}

 void CDocumentObject::FreeElement(CElementObject* root)
void CDocumentObject::FreeElement(CElementObject* root)


 {
{
 CElementObject* pe = root->child, *ps = root->sibling;
    CElementObject* pe = root->child, *ps = root->sibling;

 //    std::cout << "free:" << root->tag << std::endl;
//    std::cout << "free:" << root->tag << std::endl;

 if(root != NULL)
    if(root != NULL)

 
     {
{
 delete root;
        delete root;
 root = NULL;
        root = NULL;
 }
    }

 if(pe != NULL)
    if(pe != NULL)

 
     {
{
 FreeElement(pe);
        FreeElement(pe);
 }
    }
 if(ps != NULL)
    if(ps != NULL)

 
     {
{
 FreeElement(ps);
        FreeElement(ps);
 }
    }
 }
}

 const CElementObject* CDocumentObject::FindFirstElement(const std::wstring &tag)
const CElementObject* CDocumentObject::FindFirstElement(const std::wstring &tag)


 {
{
 if(_root == NULL)
    if(_root == NULL)
 return NULL;
        return NULL;

 _findtag = tag;
    _findtag = tag;
 while(!_findstack.empty())
    while(!_findstack.empty())
 _findstack.pop();
        _findstack.pop();
 
 
 return FindElement(NULL, _root, _findtag, _findstack);
    return FindElement(NULL, _root, _findtag, _findstack);
 }
}

 const CElementObject* CDocumentObject::FindNextElement()
const CElementObject* CDocumentObject::FindNextElement()


 {
{
 if(_findstack.empty())
    if(_findstack.empty())
 return NULL;
        return NULL;

 return FindElement(NULL, _findstack.top()->child, _findtag, _findstack);
    return FindElement(NULL, _findstack.top()->child, _findtag, _findstack);
 }
}

 const CElementObject* CDocumentObject::FindFirstElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack)
const CElementObject* CDocumentObject::FindFirstElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack)


 {
{
 if(element == NULL)
    if(element == NULL)
 return NULL;
        return NULL;

 while(!tmpstack.empty())
    while(!tmpstack.empty())
 tmpstack.pop();
        tmpstack.pop();

 return FindElement(element, element, tag, tmpstack);
    return FindElement(element, element, tag, tmpstack);
 }
}

 const CElementObject* CDocumentObject::FindNextElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack)
const CElementObject* CDocumentObject::FindNextElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack)


 {
{
 if(tmpstack.empty())
    if(tmpstack.empty())
 return NULL;
        return NULL;

 return FindElement(element, tmpstack.top()->child, tag, tmpstack);
    return FindElement(element, tmpstack.top()->child, tag, tmpstack);
 }
}

 const CElementObject* CDocumentObject::FindElement(const CElementObject* root, const CElementObject* pe, const std::wstring& tag, TElementStack& stack)
const CElementObject* CDocumentObject::FindElement(const CElementObject* root, const CElementObject* pe, const std::wstring& tag, TElementStack& stack)


 {
{
 while(pe != NULL)
    while(pe != NULL)

 
     {
{
 stack.push(pe);
        stack.push(pe);
 if(pe->tag == tag)
        if(pe->tag == tag)
 return pe;
            return pe;
 pe = pe->child;
        pe = pe->child;
 }
    }
 
    
 while(!stack.empty() && stack.top() != root && pe == NULL)
    while(!stack.empty() && stack.top() != root && pe == NULL)

 
     {
{
 pe = stack.top()->sibling;
        pe = stack.top()->sibling;  
 stack.pop();
        stack.pop();
 }
    }

 if(pe == NULL)
    if(pe == NULL)
 return NULL;
        return NULL;

 return FindElement(root, pe, tag, stack);
    return FindElement(root, pe, tag, stack);
 }
}

 const CAttributeObject* CDocumentObject::FindAttribute(const TinyHtmlParser::CElementObject *element, const std::wstring &attr)
const CAttributeObject* CDocumentObject::FindAttribute(const TinyHtmlParser::CElementObject *element, const std::wstring &attr)


 {
{
 if(element == NULL)
    if(element == NULL)
 return NULL;
        return NULL;
 
    
 const CAttributeObject* pa = element->attrib;
    const CAttributeObject* pa = element->attrib;
 while(pa != NULL)
    while(pa != NULL)

 
     {
{
 if(pa->attr == attr)
        if(pa->attr == attr)
 return pa;
            return pa;
 pa = pa->next;
        pa = pa->next;
 }
    }
 return pa;
    return pa;
 }
}

 }
}    明天合成到LingosHook中试试,今天就到这里了,累了,而且还要留点时间去看--《The Pacific》。