| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt or https://cmake.org/licensing for details. */ | |
| #include "cmXMLParser.h" | |
| #include <cctype> | |
| #include <cstring> | |
| #include <iostream> | |
| #include <sstream> | |
| #include <cm3p/expat.h> | |
| #include "cmsys/FStream.hxx" | |
| cmXMLParser::cmXMLParser() | |
| { | |
| this->Parser = nullptr; | |
| this->ParseError = 0; | |
| this->ReportCallback = nullptr; | |
| this->ReportCallbackData = nullptr; | |
| } | |
| cmXMLParser::~cmXMLParser() | |
| { | |
| if (this->Parser) { | |
| this->CleanupParser(); | |
| } | |
| } | |
| int cmXMLParser::Parse(const char* string) | |
| { | |
| return this->InitializeParser() && | |
| this->ParseChunk(string, strlen(string)) && this->CleanupParser(); | |
| } | |
| int cmXMLParser::ParseFile(const char* file) | |
| { | |
| if (!file) { | |
| return 0; | |
| } | |
| cmsys::ifstream ifs(file); | |
| if (!ifs) { | |
| return 0; | |
| } | |
| std::ostringstream str; | |
| str << ifs.rdbuf(); | |
| return this->Parse(str.str().c_str()); | |
| } | |
| int cmXMLParser::InitializeParser() | |
| { | |
| if (this->Parser) { | |
| std::cerr << "Parser already initialized" << std::endl; | |
| this->ParseError = 1; | |
| return 0; | |
| } | |
| // Create the expat XML parser. | |
| this->Parser = XML_ParserCreate(nullptr); | |
| XML_SetElementHandler(static_cast<XML_Parser>(this->Parser), | |
| &cmXMLParserStartElement, &cmXMLParserEndElement); | |
| XML_SetCharacterDataHandler(static_cast<XML_Parser>(this->Parser), | |
| &cmXMLParserCharacterDataHandler); | |
| XML_SetUserData(static_cast<XML_Parser>(this->Parser), this); | |
| this->ParseError = 0; | |
| return 1; | |
| } | |
| int cmXMLParser::ParseChunk(const char* inputString, | |
| std::string::size_type length) | |
| { | |
| if (!this->Parser) { | |
| std::cerr << "Parser not initialized" << std::endl; | |
| this->ParseError = 1; | |
| return 0; | |
| } | |
| int res; | |
| res = this->ParseBuffer(inputString, length); | |
| if (res == 0) { | |
| this->ParseError = 1; | |
| } | |
| return res; | |
| } | |
| int cmXMLParser::CleanupParser() | |
| { | |
| if (!this->Parser) { | |
| std::cerr << "Parser not initialized" << std::endl; | |
| this->ParseError = 1; | |
| return 0; | |
| } | |
| int result = !this->ParseError; | |
| if (result) { | |
| // Tell the expat XML parser about the end-of-input. | |
| if (!XML_Parse(static_cast<XML_Parser>(this->Parser), "", 0, 1)) { | |
| this->ReportXmlParseError(); | |
| result = 0; | |
| } | |
| } | |
| // Clean up the parser. | |
| XML_ParserFree(static_cast<XML_Parser>(this->Parser)); | |
| this->Parser = nullptr; | |
| return result; | |
| } | |
| int cmXMLParser::ParseBuffer(const char* buffer, std::string::size_type count) | |
| { | |
| // Pass the buffer to the expat XML parser. | |
| if (!XML_Parse(static_cast<XML_Parser>(this->Parser), buffer, | |
| static_cast<int>(count), 0)) { | |
| this->ReportXmlParseError(); | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| int cmXMLParser::ParseBuffer(const char* buffer) | |
| { | |
| return this->ParseBuffer(buffer, static_cast<int>(strlen(buffer))); | |
| } | |
| int cmXMLParser::ParsingComplete() | |
| { | |
| // Default behavior is to parse to end of stream. | |
| return 0; | |
| } | |
| void cmXMLParser::StartElement(const std::string& name, const char** /*atts*/) | |
| { | |
| std::cout << "Start element: " << name << std::endl; | |
| } | |
| void cmXMLParser::EndElement(const std::string& name) | |
| { | |
| std::cout << "End element: " << name << std::endl; | |
| } | |
| void cmXMLParser::CharacterDataHandler(const char* /*inData*/, | |
| int /*inLength*/) | |
| { | |
| } | |
| int cmXMLParser::IsSpace(char c) | |
| { | |
| return isspace(c); | |
| } | |
| const char* cmXMLParser::FindAttribute(const char** atts, | |
| const char* attribute) | |
| { | |
| if (atts && attribute) { | |
| for (const char** a = atts; *a && *(a + 1); a += 2) { | |
| if (strcmp(*a, attribute) == 0) { | |
| return *(a + 1); | |
| } | |
| } | |
| } | |
| return nullptr; | |
| } | |
| void cmXMLParserStartElement(void* parser, const char* name, const char** atts) | |
| { | |
| // Begin element handler that is registered with the XML_Parser. | |
| // This just casts the user data to a cmXMLParser and calls | |
| // StartElement. | |
| static_cast<cmXMLParser*>(parser)->StartElement(name, atts); | |
| } | |
| void cmXMLParserEndElement(void* parser, const char* name) | |
| { | |
| // End element handler that is registered with the XML_Parser. This | |
| // just casts the user data to a cmXMLParser and calls EndElement. | |
| static_cast<cmXMLParser*>(parser)->EndElement(name); | |
| } | |
| void cmXMLParserCharacterDataHandler(void* parser, const char* data, | |
| int length) | |
| { | |
| // Character data handler that is registered with the XML_Parser. | |
| // This just casts the user data to a cmXMLParser and calls | |
| // CharacterDataHandler. | |
| static_cast<cmXMLParser*>(parser)->CharacterDataHandler(data, length); | |
| } | |
| void cmXMLParser::ReportXmlParseError() | |
| { | |
| XML_Parser parser = static_cast<XML_Parser>(this->Parser); | |
| this->ReportError(static_cast<int>(XML_GetCurrentLineNumber(parser)), | |
| static_cast<int>(XML_GetCurrentColumnNumber(parser)), | |
| XML_ErrorString(XML_GetErrorCode(parser))); | |
| } | |
| void cmXMLParser::ReportError(int line, int /*unused*/, const char* msg) | |
| { | |
| if (this->ReportCallback) { | |
| this->ReportCallback(line, msg, this->ReportCallbackData); | |
| } else { | |
| std::cerr << "Error parsing XML in stream at line " << line << ": " << msg | |
| << std::endl; | |
| } | |
| } |