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 880f8e3e0..3bd349ca9 100644 --- a/examples/src/main.cpp +++ b/examples/src/main.cpp @@ -1,19 +1,16 @@ #include #include #include "CIMModel.hpp" +#include "CimConstants.hpp" #include "BaseClass.hpp" #include "IdentifiedObject.hpp" +#include "version.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) { @@ -21,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; } @@ -52,7 +50,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 +62,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; +} 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/CIMContentHandler.cpp b/src/CIMContentHandler.cpp index 3537c3b12..93aaea382 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) @@ -244,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)) 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; }; 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 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}"