From b27d20cbdae756084d52ad3c1f80dd5c021a2d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Sun, 11 May 2025 15:19:20 +0200 Subject: [PATCH 1/5] Fix reading text with umlauts in cpp reader (If an XML text value contains umlauts or entity references the text is splitted into several events and has to be concatenated to get the full text value) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Günther --- src/CIMContentHandler.cpp | 30 ++++++++---------------------- src/CIMContentHandler.hpp | 1 + 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/CIMContentHandler.cpp b/src/CIMContentHandler.cpp index 3537c3b12..c08d22c10 100644 --- a/src/CIMContentHandler.cpp +++ b/src/CIMContentHandler.cpp @@ -88,6 +88,7 @@ void CIMContentHandler::startElement(const std::string &namespaceURI, const std: // Remember last opened tag tagStack.push(qName); + value.clear(); // If there is no RDF ID (an XML attribute!) then we don't have a new CIM // object or RDF relation therefore the XML element will contain a value @@ -148,11 +149,16 @@ void CIMContentHandler::startElement(const std::string &namespaceURI, const std: void CIMContentHandler::endElement(const std::string &namespaceURI, const std::string &localName, const std::string &qName) { // Only process tags in cim namespace - if(qName.find("cim:") == std::string::npos) + if (qName.find("cim:") == std::string::npos) { return; } + if (!tagStack.empty() && !objectStack.empty() && !value.empty()) + { + assign(objectStack.top(), tagStack.top(), value); + } + // Pop Stacks if (tagStack.size() == 0) { std::cerr << "WARNING: Nearly tried to pop empty tag stack for tag: " << qName << std::endl; @@ -174,27 +180,7 @@ void CIMContentHandler::endElement(const std::string &namespaceURI, const std::s void CIMContentHandler::characters(const std::string &characters) { - // Only process tags in "cim" namespace - if(tagStack.empty()) - { - return; - } - if(objectStack.empty()) - { - throw CriticalError("CIMContentHandler: Critical Error: objectStack empty"); - } - -#ifndef DEBUG - assign(objectStack.top(), tagStack.top(), characters); -#else - // Check if the characters only contain whitespace - if(is_only_whitespace(characters)) - { - return; - } - if(!assign(objectStack.top(), tagStack.top(), characters)) - std::cout << "CIMContentHandler: Note: Cannot assign '" << characters << "' to " << tagStack.top() << std::endl; -#endif + value.append(characters); } void CIMContentHandler::ignorableWhitespace(const std::string &ch) diff --git a/src/CIMContentHandler.hpp b/src/CIMContentHandler.hpp index df23d00d0..ea9beb91f 100644 --- a/src/CIMContentHandler.hpp +++ b/src/CIMContentHandler.hpp @@ -50,6 +50,7 @@ class CIMContentHandler : public Arabica::SAX::ContentHandler std::stack objectStack; std::stack tagStack; std::list taskQueue; + std::string value; }; From dda7f5b9b9055d8040fb505d8eb1bcd235dee6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Sun, 11 May 2025 15:20:41 +0200 Subject: [PATCH 2/5] Fix handling of (utf-8) umlauts in main.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Günther --- examples/src/main.cpp | 117 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 106 insertions(+), 11 deletions(-) diff --git a/examples/src/main.cpp b/examples/src/main.cpp index 880f8e3e0..d5f7fbed8 100644 --- a/examples/src/main.cpp +++ b/examples/src/main.cpp @@ -4,16 +4,11 @@ #include "BaseClass.hpp" #include "IdentifiedObject.hpp" -std::string formatName(std::string name) { - if (name.length() > 12) { - name.resize(10, ' '); - name += ".. "; - } - else { - name.resize(14, ' '); - } - return name; -} +// forward declerations +static std::string format_name(std::string name); +static std::size_t utf8_code_point_len(const std::string& txt); +static std::size_t utf8_code_point_len(const char* ptr); +static void utf8_code_point_resize(std::string& txt, std::size_t new_len, char fill); int main(int argc, char** argv) { @@ -52,7 +47,7 @@ int main(int argc, char** argv) if (IdObj->name.initialized) { static unsigned int i = 0; - std::string outputName = formatName(IdObj->name); + std::string outputName = format_name(IdObj->name); std::cout << outputName; i++; if (i % 5 == 0) { @@ -64,3 +59,103 @@ int main(int argc, char** argv) std::cout << std::endl; return 0; } + +static std::string format_name(std::string name) +{ + if (utf8_code_point_len(name) > 12) + { + utf8_code_point_resize(name, 10, ' '); + name += ".. "; + } + else + { + utf8_code_point_resize(name, 14, ' '); + } + return name; +} + +static std::size_t utf8_code_point_len(const std::string& txt) +{ + return utf8_code_point_len(txt.c_str()); +} + +static std::size_t utf8_code_point_len(const char* ptr) +{ + size_t len = 0; + for (; *ptr != 0; ++ptr) + { + if ((*ptr & 0b11000000) != 0b10000000) + { + ++len; + } + } + return len; +} + +static void utf8_code_point_resize(std::string& txt, std::size_t new_len, char fill) +{ + const char* ptr = txt.c_str(); + size_t len = 0; + for (; *ptr != 0 && len <= new_len; ++ptr) + { + if ((*ptr & 0b11000000) != 0b10000000) + { + ++len; + } + } + if (len <= new_len) + { + new_len += txt.length() - len; + } + else + { + new_len = ptr - 1 - txt.c_str(); + } + txt.resize(new_len, fill); +} + +static bool utf8_check_valid(const char* ptr, std::size_t* p_len = nullptr) +{ + bool valid = true; + size_t len = 0; + for (; *ptr != 0; ++ptr) + { + if ((*ptr & 0b11000000) != 0b10000000) + { + ++len; + size_t inner_code_point_len = 0; + if ((*ptr & 0b11100000) == 0b11000000) // 110xxxxx + { + inner_code_point_len = 1; + } + if ((*ptr & 0b11110000) == 0b11100000) // 1110xxxx + { + inner_code_point_len = 2; + } + if ((*ptr & 0b11111000) == 0b11110000) // 11110xxx + { + inner_code_point_len = 3; + } + for (size_t idx = 0; idx < inner_code_point_len; ++idx, ++ptr) + { + if ((*ptr & 0b11000000) != 0b10000000) + { + valid = false; + } + if (*ptr == 0) + { + break; + } + } + } + else + { + valid = false; + } + } + if (p_len != nullptr) + { + *p_len = len; + } + return valid; +} From 004fd188ace888aa671ff27623703ed042fe881f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Sun, 11 May 2025 16:24:28 +0200 Subject: [PATCH 3/5] Fix entity references in cpp writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Günther --- src/CIMWriter.cpp | 25 ++++++++++++++++++++++++- src/CIMWriter.hpp | 3 +++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/CIMWriter.cpp b/src/CIMWriter.cpp index 569496696..cb01fd7e7 100644 --- a/src/CIMWriter.cpp +++ b/src/CIMWriter.cpp @@ -97,7 +97,7 @@ bool CIMWriter::writeCim(std::ostream& rdf, const std::vector& objLi std::stringstream stream; if (func(obj, stream)) { - rdfObj << " <" << attr << ">" << stream.str() << "" << std::endl; + rdfObj << " <" << attr << ">" << xmlEscape(stream.str()) << "" << std::endl; ++attributesCount; } } @@ -232,3 +232,26 @@ CGMESProfile CIMWriter::getAttributeProfile(const BaseClass* obj, const std::str } return UnknownProfile; } + +std::string CIMWriter::xmlEscape(const std::string& txt) +{ + if (txt.find_first_of("&<>'\"") == std::string::npos) + { + return txt; + } + std::string result; + result.reserve(txt.size() + 10); + for (size_t pos = 0; pos != txt.size(); ++pos) + { + switch (txt[pos]) + { + case '&': result.append("&"); break; + case '<': result.append("<"); break; + case '>': result.append(">"); break; + case '\'': result.append("'"); break; + case '"': result.append("""); break; + default: result.append(&txt[pos], 1); break; + } + } + return result; +} diff --git a/src/CIMWriter.hpp b/src/CIMWriter.hpp index dbbce2f1b..c32789a7b 100644 --- a/src/CIMWriter.hpp +++ b/src/CIMWriter.hpp @@ -104,6 +104,9 @@ class CIMWriter */ static CGMESProfile getAttributeProfile(const BaseClass* obj, const std::string& attr, const CGMESProfile& classProfile); + +private: + static std::string xmlEscape(const std::string& txt); }; #endif // CIMWRITER_HPP From 449be637ee5c059c79ebb5a6293774168e7ebe90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Sun, 11 May 2025 17:12:19 +0200 Subject: [PATCH 4/5] Fix parsing enums for cgmes3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Günther --- src/CIMContentHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CIMContentHandler.cpp b/src/CIMContentHandler.cpp index c08d22c10..93aaea382 100644 --- a/src/CIMContentHandler.cpp +++ b/src/CIMContentHandler.cpp @@ -230,7 +230,7 @@ std::string CIMContentHandler::get_rdf_enum(const AttributesT &attributes) { if(attributes.getQName(i) == "rdf:resource") { - std::regex expr("^http[s]*://[a-zA-Z0-9./_]*CIM-schema-cim[0-9]+#([a-zA-z0-9]*).([a-zA-z0-9]*)"); + std::regex expr("^http[s]*://.*#([a-zA-z0-9]*).([a-zA-z0-9]*)"); std::smatch m; std::string str = attributes.getValue(i); if(std::regex_match(str, m, expr)) From f0d045e73031b334930895bb391b6871f8d8b99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Fri, 20 Jun 2025 02:04:27 +0200 Subject: [PATCH 5/5] Add version output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Günther --- .gitignore | 2 ++ CMakeLists.txt | 2 ++ examples/src/main.cpp | 3 +++ examples/src/readwrite.cpp | 3 +++ src/version.hpp.in | 1 + 5 files changed, 11 insertions(+) create mode 100644 src/version.hpp.in diff --git a/.gitignore b/.gitignore index 2c1c9d8c4..d88d1272c 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ cmake-build* *.swp .cache + +src/version.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 580dfb885..57b003098 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,8 @@ set(libcimpp_MINOR_VERSION 2) set(libcimpp_PATCH_VERSION 0) set(libcimpp_VERSION ${libcimpp_MAJOR_VERSION}.${libcimpp_MINOR_VERSION}.${libcimpp_PATCH_VERSION}) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/version.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/src/version.hpp) + # Options option(BUILD_SHARED_LIBS "Build shared library" OFF) option(CIMPP_BUILD_EXAMPLES "Build examples" OFF) diff --git a/examples/src/main.cpp b/examples/src/main.cpp index d5f7fbed8..3bd349ca9 100644 --- a/examples/src/main.cpp +++ b/examples/src/main.cpp @@ -1,8 +1,10 @@ #include #include #include "CIMModel.hpp" +#include "CimConstants.hpp" #include "BaseClass.hpp" #include "IdentifiedObject.hpp" +#include "version.hpp" // forward declerations static std::string format_name(std::string name); @@ -16,6 +18,7 @@ int main(int argc, char** argv) { std::cout << "Usage: example [additional cim files]" << std::endl; std::cout << "Provide at least one cim file" << std::endl; + std::cout << "Version: libcimpp " << RELEASE_VERSION << " (" << CimVersion << ")" << std::endl; return 1; } diff --git a/examples/src/readwrite.cpp b/examples/src/readwrite.cpp index 8adf8e65a..754805986 100644 --- a/examples/src/readwrite.cpp +++ b/examples/src/readwrite.cpp @@ -3,6 +3,8 @@ #include "CIMModel.hpp" #include "CIMWriter.hpp" +#include "CimConstants.hpp" +#include "version.hpp" int main(int argc, char** argv) { @@ -10,6 +12,7 @@ int main(int argc, char** argv) { std::cout << "Usage: readwrite [additional cim files] " << std::endl; std::cout << "Provide at least one cim file and outputfile" << std::endl; + std::cout << "Version: libcimpp " << RELEASE_VERSION << " (" << CimVersion << ")" << std::endl; return 1; } std::string outputfile = argv[argc-1]; diff --git a/src/version.hpp.in b/src/version.hpp.in new file mode 100644 index 000000000..6378cc4cc --- /dev/null +++ b/src/version.hpp.in @@ -0,0 +1 @@ +#define RELEASE_VERSION "${libcimpp_VERSION}"