cmWIXSourceWriter.cxx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2012 Kitware, Inc.
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmStandardIncludes.h"
  11. #include "cmWIXSourceWriter.h"
  12. #include <CPack/cmCPackGenerator.h>
  13. #include <windows.h>
  14. cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger,
  15. std::string const& filename,
  16. bool isIncludeFile):
  17. Logger(logger),
  18. File(filename.c_str()),
  19. State(DEFAULT),
  20. SourceFilename(filename)
  21. {
  22. WriteXMLDeclaration();
  23. if(isIncludeFile)
  24. {
  25. BeginElement("Include");
  26. }
  27. else
  28. {
  29. BeginElement("Wix");
  30. }
  31. AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi");
  32. }
  33. cmWIXSourceWriter::~cmWIXSourceWriter()
  34. {
  35. if(Elements.size() > 1)
  36. {
  37. cmCPackLogger(cmCPackLog::LOG_ERROR,
  38. Elements.size() - 1 << " WiX elements were still open when closing '" <<
  39. SourceFilename << "'" << std::endl);
  40. return;
  41. }
  42. EndElement(Elements.back());
  43. }
  44. void cmWIXSourceWriter::BeginElement(std::string const& name)
  45. {
  46. if(State == BEGIN)
  47. {
  48. File << ">";
  49. }
  50. File << "\n";
  51. Indent(Elements.size());
  52. File << "<" << name;
  53. Elements.push_back(name);
  54. State = BEGIN;
  55. }
  56. void cmWIXSourceWriter::EndElement(std::string const& name)
  57. {
  58. if(Elements.empty())
  59. {
  60. cmCPackLogger(cmCPackLog::LOG_ERROR,
  61. "can not end WiX element with no open elements in '" <<
  62. SourceFilename << "'" << std::endl);
  63. return;
  64. }
  65. if(Elements.back() != name)
  66. {
  67. cmCPackLogger(cmCPackLog::LOG_ERROR,
  68. "WiX element <" << Elements.back() <<
  69. "> can not be closed by </" << name << "> in '" <<
  70. SourceFilename << "'" << std::endl);
  71. return;
  72. }
  73. if(State == DEFAULT)
  74. {
  75. File << "\n";
  76. Indent(Elements.size()-1);
  77. File << "</" << Elements.back() << ">";
  78. }
  79. else
  80. {
  81. File << "/>";
  82. }
  83. Elements.pop_back();
  84. State = DEFAULT;
  85. }
  86. void cmWIXSourceWriter::AddTextNode(std::string const& text)
  87. {
  88. if(State == BEGIN)
  89. {
  90. File << ">";
  91. }
  92. if(Elements.empty())
  93. {
  94. cmCPackLogger(cmCPackLog::LOG_ERROR,
  95. "can not add text without open WiX element in '" <<
  96. SourceFilename << "'" << std::endl);
  97. return;
  98. }
  99. File << this->EscapeAttributeValue(text);
  100. State = DEFAULT;
  101. }
  102. void cmWIXSourceWriter::AddProcessingInstruction(
  103. std::string const& target, std::string const& content)
  104. {
  105. if(State == BEGIN)
  106. {
  107. File << ">";
  108. }
  109. File << "\n";
  110. Indent(Elements.size());
  111. File << "<?" << target << " " << content << "?>";
  112. State = DEFAULT;
  113. }
  114. void cmWIXSourceWriter::AddAttribute(
  115. std::string const& key, std::string const& value)
  116. {
  117. std::string utf8 = CMakeEncodingToUtf8(value);
  118. File << " " << key << "=\"" << EscapeAttributeValue(utf8) << '"';
  119. }
  120. void cmWIXSourceWriter::AddAttributeUnlessEmpty(
  121. std::string const& key, std::string const& value)
  122. {
  123. if(!value.empty())
  124. {
  125. AddAttribute(key, value);
  126. }
  127. }
  128. std::string cmWIXSourceWriter::CMakeEncodingToUtf8(std::string const& value)
  129. {
  130. #ifdef CMAKE_ENCODING_UTF8
  131. return value;
  132. #else
  133. if(value.empty())
  134. {
  135. return std::string();
  136. }
  137. int characterCount = MultiByteToWideChar(
  138. CP_ACP, 0, value.c_str(), static_cast<int>(value.size()), 0, 0);
  139. if(characterCount == 0)
  140. {
  141. return std::string();
  142. }
  143. std::vector<wchar_t> utf16(characterCount);
  144. MultiByteToWideChar(
  145. CP_ACP, 0, value.c_str(), static_cast<int>(value.size()),
  146. &utf16[0], static_cast<int>(utf16.size()));
  147. int utf8ByteCount = WideCharToMultiByte(
  148. CP_UTF8, 0, &utf16[0], static_cast<int>(utf16.size()), 0, 0, 0, 0);
  149. if(utf8ByteCount == 0)
  150. {
  151. return std::string();
  152. }
  153. std::vector<char> utf8(utf8ByteCount);
  154. WideCharToMultiByte(CP_UTF8, 0, &utf16[0], static_cast<int>(utf16.size()),
  155. &utf8[0], static_cast<int>(utf8.size()), 0, 0);
  156. return std::string(&utf8[0], utf8.size());
  157. #endif
  158. }
  159. void cmWIXSourceWriter::WriteXMLDeclaration()
  160. {
  161. File << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
  162. }
  163. void cmWIXSourceWriter::Indent(size_t count)
  164. {
  165. for(size_t i = 0; i < count; ++i)
  166. {
  167. File << " ";
  168. }
  169. }
  170. std::string cmWIXSourceWriter::EscapeAttributeValue(
  171. std::string const& value)
  172. {
  173. std::string result;
  174. result.reserve(value.size());
  175. char c = 0;
  176. for(size_t i = 0 ; i < value.size(); ++i)
  177. {
  178. c = value[i];
  179. switch(c)
  180. {
  181. case '<':
  182. result += "&lt;";
  183. break;
  184. case '>':
  185. result += "&gt;";
  186. break;
  187. case '&':
  188. result +="&amp;";
  189. break;
  190. case '"':
  191. result += "&quot;";
  192. break;
  193. default:
  194. result += c;
  195. break;
  196. }
  197. }
  198. return result;
  199. }