224 lines
5.3 KiB
C++
224 lines
5.3 KiB
C++
/* 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 <CPack/cmCPackGenerator.h>
|
|
|
|
#include <cmUuid.h>
|
|
|
|
#include <windows.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)
|
|
{
|
|
std::string utf8 = CMakeEncodingToUtf8(value);
|
|
|
|
File << " " << key << "=\"" << EscapeAttributeValue(utf8) << '"';
|
|
}
|
|
|
|
void cmWIXSourceWriter::AddAttributeUnlessEmpty(std::string const& key,
|
|
std::string const& value)
|
|
{
|
|
if (!value.empty()) {
|
|
AddAttribute(key, value);
|
|
}
|
|
}
|
|
|
|
std::string cmWIXSourceWriter::CMakeEncodingToUtf8(std::string const& value)
|
|
{
|
|
#ifdef CMAKE_ENCODING_UTF8
|
|
return value;
|
|
#else
|
|
if (value.empty()) {
|
|
return std::string();
|
|
}
|
|
|
|
int characterCount = MultiByteToWideChar(
|
|
CP_ACP, 0, value.c_str(), static_cast<int>(value.size()), 0, 0);
|
|
|
|
if (characterCount == 0) {
|
|
return std::string();
|
|
}
|
|
|
|
std::vector<wchar_t> utf16(characterCount);
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, value.c_str(), static_cast<int>(value.size()),
|
|
&utf16[0], static_cast<int>(utf16.size()));
|
|
|
|
int utf8ByteCount = WideCharToMultiByte(
|
|
CP_UTF8, 0, &utf16[0], static_cast<int>(utf16.size()), 0, 0, 0, 0);
|
|
|
|
if (utf8ByteCount == 0) {
|
|
return std::string();
|
|
}
|
|
|
|
std::vector<char> utf8(utf8ByteCount);
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, &utf16[0], static_cast<int>(utf16.size()),
|
|
&utf8[0], static_cast<int>(utf8.size()), 0, 0);
|
|
|
|
return std::string(&utf8[0], utf8.size());
|
|
#endif
|
|
}
|
|
|
|
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());
|
|
|
|
char c = 0;
|
|
for (size_t i = 0; i < value.size(); ++i) {
|
|
c = value[i];
|
|
switch (c) {
|
|
case '<':
|
|
result += "<";
|
|
break;
|
|
case '>':
|
|
result += ">";
|
|
break;
|
|
case '&':
|
|
result += "&";
|
|
break;
|
|
case '"':
|
|
result += """;
|
|
break;
|
|
default:
|
|
result += c;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|