// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "../lib/vstguibase.h" #if VSTGUI_ENABLE_XML_PARSER #include "cstream.h" #include "icontentprovider.h" /// @cond ignore #if VSTGUI_USE_SYSTEM_EXPAT #include #else #define XML_STATIC 1 #define XML_NS 0 #define XML_DTD 0 #define XML_CONTEXT_BYTES 1024 #define XML_LARGE_SIZE 1 #ifdef BYTEORDER #define OLD_BYTEORDER = BYTEORDER #undef BYTEORDER #endif #if MAC && defined (__BIG_ENDIAN__) #define BYTEORDER 4321 #else #define BYTEORDER 1234 #endif #define HAVE_MEMMOVE namespace VSTGUI { namespace Xml { #include "../thirdparty/expat/expat.h" }} #endif // VSTGUI_USE_SYSTEM_EXPAT #include "xmlparser.h" #include namespace VSTGUI { namespace Xml { //------------------------------------------------------------------------ struct Parser::Impl { XML_ParserStruct* parser {nullptr}; IHandler* handler {nullptr}; }; //------------------------------------------------------------------------ static void XMLCALL gStartElementHandler (void* userData, const char* name, const char** atts) { auto parser = static_cast (userData); IHandler* handler = parser ? parser->getHandler () : nullptr; if (handler) handler->startXmlElement (parser, name, atts); } //------------------------------------------------------------------------ static void XMLCALL gEndElementHandler (void* userData, const char* name) { auto parser = static_cast (userData); IHandler* handler = parser ? parser->getHandler () : nullptr; if (handler) handler->endXmlElement (parser, name); } //------------------------------------------------------------------------ static void XMLCALL gCharacterDataHandler (void* userData, const char* s, int len) { auto parser = static_cast (userData); IHandler* handler = parser ? parser->getHandler () : nullptr; if (handler) handler->xmlCharData (parser, (const int8_t*)s, len); } //------------------------------------------------------------------------ static void XMLCALL gCommentHandler (void* userData, const char* string) { auto parser = static_cast (userData); IHandler* handler = parser ? parser->getHandler () : nullptr; if (handler) handler->xmlComment (parser, string); } //----------------------------------------------------------------------------- Parser::Parser () { pImpl = std::unique_ptr (new Impl ()); pImpl->parser = XML_ParserCreate ("UTF-8"); } //----------------------------------------------------------------------------- Parser::~Parser () noexcept { if (pImpl->parser) XML_ParserFree (pImpl->parser); } //----------------------------------------------------------------------------- IHandler* Parser::getHandler () const { return pImpl->handler; } //----------------------------------------------------------------------------- bool Parser::parse (IContentProvider* provider, IHandler* handler) { if (provider == nullptr || handler == nullptr) return false; pImpl->handler = handler; XML_SetUserData (pImpl->parser, this); XML_SetStartElementHandler (pImpl->parser, gStartElementHandler); XML_SetEndElementHandler (pImpl->parser, gEndElementHandler); XML_SetCharacterDataHandler (pImpl->parser, gCharacterDataHandler); XML_SetCommentHandler (pImpl->parser, gCommentHandler); static const uint32_t kBufferSize = 0x8000; provider->rewind (); while (true) { void* buffer = XML_GetBuffer (pImpl->parser, kBufferSize); if (buffer == nullptr) { pImpl->handler = nullptr; return false; } uint32_t bytesRead = provider->readRawData ((int8_t*)buffer, kBufferSize); if (bytesRead == kStreamIOError) bytesRead = 0; XML_Status status = XML_ParseBuffer (pImpl->parser, static_cast (bytesRead), bytesRead == 0); switch (status) { case XML_STATUS_ERROR: { XML_Error error = XML_GetErrorCode (pImpl->parser); if (error == XML_ERROR_JUNK_AFTER_DOC_ELEMENT) // that's ok { pImpl->handler = nullptr; return true; } #if DEBUG XML_Size currentLineNumber = XML_GetCurrentLineNumber (pImpl->parser); DebugPrint ("XML Parser Error on line: %d\n", currentLineNumber); DebugPrint ("%s\n", XML_ErrorString (XML_GetErrorCode (pImpl->parser))); int offset, size; const char* inputContext = XML_GetInputContext (pImpl->parser, &offset, &size); if (inputContext) { int pos = offset; while (offset > 0 && pos - offset < 20) { if (inputContext[offset] == '\n') { offset++; break; } offset--; } for (int i = offset; i < size && i - offset < 40; i++) { if (inputContext[i] == '\n') break; if (inputContext[i] == '\t') DebugPrint (" "); else DebugPrint ("%c", inputContext[i]); } DebugPrint ("\n"); for (int i = offset; i < pos; i++) { DebugPrint (" "); } DebugPrint ("^\n"); } #endif pImpl->handler = nullptr; return false; } case XML_STATUS_SUSPENDED: { pImpl->handler = nullptr; return true; } default: break; } if (bytesRead == 0) break; } pImpl->handler = nullptr; return true; } //----------------------------------------------------------------------------- bool Parser::stop () { XML_StopParser (pImpl->parser, false); return true; } //------------------------------------------------------------------------ }} // namespaces #if !VSTGUI_USE_SYSTEM_EXPAT //------------------------------------------------------------------------ #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wshadow" #endif namespace VSTGUI { namespace Xml { #include "../thirdparty/expat/xmltok.c" #include "../thirdparty/expat/xmlrole.c" #include "../thirdparty/expat/xmlparse.c" }} #ifdef OLD_BYTEORDER #undef BYTEORDER #define BYTEORDER = OLD_BYTEORDER #endif #ifdef __clang__ #pragma clang diagnostic pop #endif #endif /// @endcond #endif // VSTGUI_ENABLE_XML_PARSER