| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmWIXSourceWriter.h" |
| |
| #include <windows.h> |
| |
| #include "cmCPackGenerator.h" |
| #include "cmUuid.h" |
| |
| cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger, |
| std::string const& filename, |
| GuidType componentGuidType, |
| RootElementType rootElementType) |
| : Logger(logger) |
| , File(filename.c_str()) |
| , State(DEFAULT) |
| , SourceFilename(filename) |
| , ComponentGuidType(componentGuidType) |
| { |
| WriteXMLDeclaration(); |
| |
| if (rootElementType == INCLUDE_ELEMENT_ROOT) { |
| BeginElement("Include"); |
| } else { |
| BeginElement("Wix"); |
| } |
| |
| AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi"); |
| } |
| |
| cmWIXSourceWriter::~cmWIXSourceWriter() |
| { |
| if (Elements.size() > 1) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| Elements.size() - 1 |
| << " WiX elements were still open when closing '" |
| << SourceFilename << "'" << std::endl); |
| return; |
| } |
| |
| EndElement(Elements.back()); |
| } |
| |
| void cmWIXSourceWriter::BeginElement(std::string const& name) |
| { |
| if (State == BEGIN) { |
| File << ">"; |
| } |
| |
| File << "\n"; |
| Indent(Elements.size()); |
| File << "<" << name; |
| |
| Elements.push_back(name); |
| State = BEGIN; |
| } |
| |
| void cmWIXSourceWriter::EndElement(std::string const& name) |
| { |
| if (Elements.empty()) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "can not end WiX element with no open elements in '" |
| << SourceFilename << "'" << std::endl); |
| return; |
| } |
| |
| if (Elements.back() != name) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "WiX element <" |
| << Elements.back() << "> can not be closed by </" << name |
| << "> in '" << SourceFilename << "'" << std::endl); |
| return; |
| } |
| |
| if (State == DEFAULT) { |
| File << "\n"; |
| Indent(Elements.size() - 1); |
| File << "</" << Elements.back() << ">"; |
| } else { |
| File << "/>"; |
| } |
| |
| Elements.pop_back(); |
| State = DEFAULT; |
| } |
| |
| void cmWIXSourceWriter::AddTextNode(std::string const& text) |
| { |
| if (State == BEGIN) { |
| File << ">"; |
| } |
| |
| if (Elements.empty()) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "can not add text without open WiX element in '" |
| << SourceFilename << "'" << std::endl); |
| return; |
| } |
| |
| File << this->EscapeAttributeValue(text); |
| State = DEFAULT; |
| } |
| |
| void cmWIXSourceWriter::AddProcessingInstruction(std::string const& target, |
| std::string const& content) |
| { |
| if (State == BEGIN) { |
| File << ">"; |
| } |
| |
| File << "\n"; |
| Indent(Elements.size()); |
| File << "<?" << target << " " << content << "?>"; |
| |
| State = DEFAULT; |
| } |
| |
| void cmWIXSourceWriter::AddAttribute(std::string const& key, |
| std::string const& value) |
| { |
| File << " " << key << "=\"" << EscapeAttributeValue(value) << '"'; |
| } |
| |
| void cmWIXSourceWriter::AddAttributeUnlessEmpty(std::string const& key, |
| std::string const& value) |
| { |
| if (!value.empty()) { |
| AddAttribute(key, value); |
| } |
| } |
| |
| std::string cmWIXSourceWriter::CreateGuidFromComponentId( |
| std::string const& componentId) |
| { |
| std::string guid = "*"; |
| if (this->ComponentGuidType == CMAKE_GENERATED_GUID) { |
| std::string md5 = cmSystemTools::ComputeStringMD5(componentId); |
| cmUuid uuid; |
| std::vector<unsigned char> ns; |
| guid = uuid.FromMd5(ns, md5); |
| } |
| return guid; |
| } |
| |
| void cmWIXSourceWriter::WriteXMLDeclaration() |
| { |
| File << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; |
| } |
| |
| void cmWIXSourceWriter::Indent(size_t count) |
| { |
| for (size_t i = 0; i < count; ++i) { |
| File << " "; |
| } |
| } |
| |
| std::string cmWIXSourceWriter::EscapeAttributeValue(std::string const& value) |
| { |
| std::string result; |
| result.reserve(value.size()); |
| |
| for (char c : value) { |
| switch (c) { |
| case '<': |
| result += "<"; |
| break; |
| case '>': |
| result += ">"; |
| break; |
| case '&': |
| result += "&"; |
| break; |
| case '"': |
| result += """; |
| break; |
| default: |
| result += c; |
| break; |
| } |
| } |
| |
| return result; |
| } |