📄 gxml.cpp
字号:
/* Copyright (C) 2006, Mike Gashler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. see http://www.gnu.org/copyleft/lesser.html*/#include "GXML.h"#include <stdio.h>#include <string.h>#ifdef WIN32#include <io.h>#else // WIN32#endif // !WIN32// This is a helper class used by GXMLTag::ParseXMLFileclass GXMLParser{protected: static const char* s_szCommentError; GXMLTag* m_pRootTag; GXMLTag* m_pCurrentTag; int m_nPos; const char* m_pFile; int m_nLength; int m_nLine; int m_nLineStart; const char* m_szErrorMessage; int m_nErrorOffset; int m_nErrorLine; int m_nErrorColumn;public: GXMLParser(const char* pFile, int nSize); virtual ~GXMLParser(); GXMLTag* Parse(const char** pszErrorMessage, int* pnErrorOffset, int* pnErrorLine, int* pnErrorColumn);protected: void SetError(const char* szMessage); GXMLTag* ParseTag(); bool ParseCloser(int nNameStart, int nNameLength); int ParseName(); void EatWhitespace(); GXMLAttribute* ParseAttribute(); bool UnescapeAttrValue(const char* pValue, int nLength, char* pBuffer); void MoveIntoNextTag(); GXMLTag* ParseTagInternal();};/*static*/ const char* GXMLParser::s_szCommentError = "The tag is a comment";GXMLParser::GXMLParser(const char* pFile, int nSize){ m_pRootTag = NULL; m_pCurrentTag = NULL; m_nPos = 0; m_nLineStart = 0; m_pFile = pFile; m_nLength = nSize; m_nLine = 1; m_szErrorMessage = NULL; m_nErrorOffset = 0; m_nErrorLine = 0; m_nErrorColumn = 0;}GXMLParser::~GXMLParser(){}inline bool isWhitespace(char c){ return c <= ' ' ? true : false;}void GXMLParser::SetError(const char* szMessage){ GAssert(!m_szErrorMessage, "Error message already set"); m_szErrorMessage = szMessage; m_nErrorOffset = m_nPos; m_nErrorLine = m_nLine; m_nErrorColumn = m_nPos - m_nLineStart;}void GXMLParser::EatWhitespace(){ char c; while(true) { if(m_nPos >= m_nLength) break; c = m_pFile[m_nPos]; if(!isWhitespace(c)) break; if(c == '\n') { m_nLine++; m_nLineStart = m_nPos + 1; } m_nPos++; }}GXMLTag* GXMLParser::Parse(const char** pszErrorMessage, int* pnErrorOffset, int* pnErrorLine, int* pnErrorColumn){ GXMLTag* pRoot = NULL; m_szErrorMessage = s_szCommentError; while(m_szErrorMessage == s_szCommentError) { m_szErrorMessage = NULL; pRoot = ParseTag(); } if(pszErrorMessage) *pszErrorMessage = m_szErrorMessage; if(pnErrorOffset) *pnErrorOffset = m_nErrorOffset; if(pnErrorLine) *pnErrorLine = m_nErrorLine; if(pnErrorColumn) *pnErrorColumn = m_nErrorColumn; return pRoot;}GXMLTag* GXMLParser::ParseTag(){ MoveIntoNextTag(); if(m_nPos >= m_nLength) { SetError("Expected a tag"); return NULL; } return ParseTagInternal();}void GXMLParser::MoveIntoNextTag(){ // Parse the name while(m_nPos < m_nLength && m_pFile[m_nPos] != '<') m_nPos++; if(m_nPos < m_nLength) m_nPos++;}GXMLTag* GXMLParser::ParseTagInternal(){ int nNameStart = m_nPos; int nNameLength = ParseName(); if(nNameLength < 1) { SetError("Expected a tag name"); return NULL; } if(m_pFile[nNameStart] == '?') { while(true) { if(m_nPos >= m_nLength) break; if(m_pFile[m_nPos] == '>') break; else if(m_pFile[m_nPos] == '\n') { m_nLine++; m_nLineStart = m_nPos + 1; } m_nPos++; } if(m_nPos < m_nLength) m_nPos++; SetError(s_szCommentError); return NULL; } else if(nNameLength >= 3 && strncmp(&m_pFile[nNameStart], "!--", 3) == 0) { m_nPos -= nNameLength; // Skip to the end of the comment int nNests = 1; while(true) { if(m_nPos + 3 >= m_nLength) break; if(strncmp(&m_pFile[m_nPos], "<!--", 4) == 0) nNests++; if(strncmp(&m_pFile[m_nPos], "-->", 3) == 0) { nNests--; if(nNests <= 0) break; } else if(m_pFile[m_nPos] == '\n') { m_nLine++; m_nLineStart = m_nPos + 1; } m_nPos++; } m_nPos += 3; SetError(s_szCommentError); return NULL; } GXMLTag* pNewTag = new GXMLTag(&m_pFile[nNameStart], nNameLength); Holder<GXMLTag*> hNewTag(pNewTag); pNewTag->SetLineNumber(m_nLine); int nStartColumn = m_nPos - m_nLineStart + 1; // Parse Attributes while(true) { EatWhitespace(); if(m_nPos >= m_nLength) { SetError("Expected an attribute, a '/', or a '>'"); return NULL; } if(m_pFile[m_nPos] == '/' || m_pFile[m_nPos] == '>') break; GXMLAttribute* pNewAttr = ParseAttribute(); if(!pNewAttr) return NULL; pNewTag->AddAttribute(pNewAttr); } int nEndColumn = m_nPos - m_nLineStart + 1; pNewTag->SetColumnAndWidth(nStartColumn, nEndColumn - nStartColumn); if(m_pFile[m_nPos] == '>') { // Parse the children m_nPos++; while(true) { MoveIntoNextTag(); if(m_nPos >= m_nLength) { SetError("Expected a closer tag"); return NULL; } if(m_pFile[m_nPos] == '/') { if(!ParseCloser(nNameStart, nNameLength)) return NULL; break; } GXMLTag* pChildTag = ParseTagInternal(); if(!pChildTag) { if(m_szErrorMessage == s_szCommentError) { m_szErrorMessage = NULL; continue; } else return NULL; } pNewTag->AddChildTag(pChildTag); } } else { // Parse the end of the tag GAssert(m_pFile[m_nPos] == '/', "internal error"); m_nPos++; EatWhitespace(); if(m_nPos >= m_nLength || m_pFile[m_nPos] != '>') { SetError("Expected a '>'"); return NULL; } m_nPos++; } return hNewTag.Drop();}int GXMLParser::ParseName(){ EatWhitespace(); char c; int nLength = 0; while(true) { if(m_nPos >= m_nLength) break; c = m_pFile[m_nPos]; if(c == '/' || c == '>' || c == '=') break; else if(c == '\n') { m_nLine++; m_nLineStart = m_nPos + 1; break; } else if(isWhitespace(c)) break; m_nPos++; nLength++; } return nLength;}bool GXMLParser::ParseCloser(int nNameStart, int nNameLength){ if(m_nPos >= m_nLength || m_pFile[m_nPos] != '/') { SetError("Expected a '/'"); return false; } m_nPos++; EatWhitespace(); int nCloserStart = m_nPos; int nCloserLength = ParseName(); if(nCloserLength != nNameLength || memcmp(&m_pFile[nNameStart], &m_pFile[nCloserStart], nCloserLength) != 0) { SetError("Closer name doesn't match tag name"); return false; } EatWhitespace(); if(m_nPos >= m_nLength || m_pFile[m_nPos] != '>') { SetError("Expected a '>'"); return false; } m_nPos++; return true;}GXMLAttribute* GXMLParser::ParseAttribute(){ int nNameStart = m_nPos; int nNameLength = ParseName(); if(nNameLength < 1) { SetError("Expected an attribute name"); return NULL; } EatWhitespace(); if(m_nPos >= m_nLength || m_pFile[m_nPos] != '=') { SetError("Expected a '='"); return NULL; } m_nPos++; EatWhitespace(); if(m_nPos >= m_nLength || m_pFile[m_nPos] != '"') { SetError("Expected a '\"'"); return NULL; } m_nPos++; int nValueStart = m_nPos; char c; int nValueLength = 0; while(true) { if(m_nPos >= m_nLength) { SetError("Expected a '\"'"); return NULL; } c = m_pFile[m_nPos]; if(c == '"') break; else if(c == '\n') { SetError("Expected a '\"'"); return NULL; } m_nPos++; nValueLength++; } if(m_nPos >= m_nLength || m_pFile[m_nPos] != '"') { SetError("Expected a '\"'"); return NULL; } m_nPos++; char szTmp[512]; char* szBuff = szTmp; if(nValueLength >= 512) szBuff = new char[nValueLength + 1]; if(!UnescapeAttrValue(&m_pFile[nValueStart], nValueLength, szBuff)) { SetError("Unrecognized escape sequence"); return NULL; } GXMLAttribute* pAttr = new GXMLAttribute(&m_pFile[nNameStart], nNameLength, szBuff); if(szBuff != szTmp) delete[] szBuff; return pAttr;}bool GXMLParser::UnescapeAttrValue(const char* pValue, int nLength, char* pBuffer){ while(nLength > 0) { if(*pValue == '&') { pValue++; nLength--; switch(*pValue) { case '#': { pValue++; nLength--; if(UCHAR(*pValue) != 'X') return false; pValue++; nLength--; int nNum = 0; while(*pValue != ';' && nLength > 0) { if(UCHAR(*pValue) < '0' || UCHAR(*pValue) > 'F') return false; if(UCHAR(*pValue) > '9' && UCHAR(*pValue) < 'A') return false; nNum <<= 4; if(UCHAR(*pValue) <= '9') nNum += UCHAR(*pValue) - '0'; else nNum += UCHAR(*pValue) - 'A' + 10; pValue++; nLength--; } if(nLength < 1) return false; pValue++; nLength--; *pBuffer = nNum; } break; case 'a': case 'A': pValue++; nLength--; if(UCHAR(*pValue) != 'M') return false; pValue++; nLength--; if(UCHAR(*pValue) != 'P') return false; pValue++; nLength--; if(*pValue != ';') return false; *pBuffer = '&'; pValue++; nLength--; break; case 'g': case 'G': pValue++; nLength--; if(UCHAR(*pValue) != 'T') return false; pValue++; nLength--; if(*pValue != ';') return false; *pBuffer = '>'; pValue++; nLength--; break; case 'l': case 'L': pValue++; nLength--; if(UCHAR(*pValue) != 'T') return false; pValue++; nLength--; if(*pValue != ';') return false; *pBuffer = '<'; pValue++; nLength--; break; case 'q': case 'Q': pValue++; nLength--; if(UCHAR(*pValue) != 'U') return false; pValue++; nLength--; if(UCHAR(*pValue) != 'O') return false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -