diff options
author | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
---|---|---|
committer | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
commit | 06e5c21a835c0e923506c4ff27929f34e00761c2 (patch) | |
tree | 75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/libs/llvm12/lib/WindowsManifest | |
parent | 03f024c4412e3aa613bb543cf1660176320ba8f4 (diff) | |
download | ydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz |
fix ya.make
Diffstat (limited to 'contrib/libs/llvm12/lib/WindowsManifest')
-rw-r--r-- | contrib/libs/llvm12/lib/WindowsManifest/WindowsManifestMerger.cpp | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/contrib/libs/llvm12/lib/WindowsManifest/WindowsManifestMerger.cpp b/contrib/libs/llvm12/lib/WindowsManifest/WindowsManifestMerger.cpp new file mode 100644 index 0000000000..54adb0ae2b --- /dev/null +++ b/contrib/libs/llvm12/lib/WindowsManifest/WindowsManifestMerger.cpp @@ -0,0 +1,729 @@ +//===-- WindowsManifestMerger.cpp ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// +// +// This file implements the .manifest merger class. +// +//===---------------------------------------------------------------------===// + +#include "llvm/WindowsManifest/WindowsManifestMerger.h" +#include "llvm/Config/config.h" +#include "llvm/Support/MemoryBuffer.h" + +#include <map> + +#if LLVM_ENABLE_LIBXML2 +#error #include <libxml/xmlreader.h> +#endif + +#define TO_XML_CHAR(X) reinterpret_cast<const unsigned char *>(X) +#define FROM_XML_CHAR(X) reinterpret_cast<const char *>(X) + +using namespace llvm; +using namespace windows_manifest; + +char WindowsManifestError::ID = 0; + +WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {} + +void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; } + +class WindowsManifestMerger::WindowsManifestMergerImpl { +public: + ~WindowsManifestMergerImpl(); + Error merge(const MemoryBuffer &Manifest); + std::unique_ptr<MemoryBuffer> getMergedManifest(); + +private: + static void errorCallback(void *Ctx, const char *Format, ...); + Error getParseError(); +#if LLVM_ENABLE_LIBXML2 + xmlDocPtr CombinedDoc = nullptr; + std::vector<xmlDocPtr> MergedDocs; + + bool Merged = false; + struct XmlDeleter { + void operator()(xmlChar *Ptr) { xmlFree(Ptr); } + void operator()(xmlDoc *Ptr) { xmlFreeDoc(Ptr); } + }; + int BufferSize = 0; + std::unique_ptr<xmlChar, XmlDeleter> Buffer; +#endif + bool ParseErrorOccurred = false; +}; + +#if LLVM_ENABLE_LIBXML2 + +static constexpr std::pair<StringLiteral, StringLiteral> MtNsHrefsPrefixes[] = { + {"urn:schemas-microsoft-com:asm.v1", "ms_asmv1"}, + {"urn:schemas-microsoft-com:asm.v2", "ms_asmv2"}, + {"urn:schemas-microsoft-com:asm.v3", "ms_asmv3"}, + {"http://schemas.microsoft.com/SMI/2005/WindowsSettings", + "ms_windowsSettings"}, + {"urn:schemas-microsoft-com:compatibility.v1", "ms_compatibilityv1"}}; + +static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) { + // Handle null pointers. Comparison of 2 null pointers returns true because + // this indicates the prefix of a default namespace. + if (!A || !B) + return A == B; + return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0; +} + +static bool isMergeableElement(const unsigned char *ElementName) { + for (StringRef S : {"application", "assembly", "assemblyIdentity", + "compatibility", "noInherit", "requestedExecutionLevel", + "requestedPrivileges", "security", "trustInfo"}) { + if (S == FROM_XML_CHAR(ElementName)) { + return true; + } + } + return false; +} + +static xmlNodePtr getChildWithName(xmlNodePtr Parent, + const unsigned char *ElementName) { + for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) { + if (xmlStringsEqual(Child->name, ElementName)) { + return Child; + } + } + return nullptr; +} + +static xmlAttrPtr getAttribute(xmlNodePtr Node, + const unsigned char *AttributeName) { + for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr; + Attribute = Attribute->next) { + if (xmlStringsEqual(Attribute->name, AttributeName)) { + return Attribute; + } + } + return nullptr; +} + +// Check if namespace specified by HRef1 overrides that of HRef2. +static bool namespaceOverrides(const unsigned char *HRef1, + const unsigned char *HRef2) { + auto HRef1Position = llvm::find_if( + MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) { + return xmlStringsEqual(HRef1, TO_XML_CHAR(Element.first.data())); + }); + auto HRef2Position = llvm::find_if( + MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) { + return xmlStringsEqual(HRef2, TO_XML_CHAR(Element.first.data())); + }); + return HRef1Position < HRef2Position; +} + +// Search for prefix-defined namespace specified by HRef, starting on Node and +// continuing recursively upwards. Returns the namespace or nullptr if not +// found. +static xmlNsPtr search(const unsigned char *HRef, xmlNodePtr Node) { + for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { + if (Def->prefix && xmlStringsEqual(Def->href, HRef)) { + return Def; + } + } + if (Node->parent) { + return search(HRef, Node->parent); + } + return nullptr; +} + +// Return the prefix that corresponds to the HRef. If HRef is not a recognized +// URI, then just return the HRef itself to use as the prefix. +static const unsigned char *getPrefixForHref(const unsigned char *HRef) { + for (auto &Ns : MtNsHrefsPrefixes) { + if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.data()))) { + return TO_XML_CHAR(Ns.second.data()); + } + } + return HRef; +} + +// Search for prefix-defined namespace specified by HRef, starting on Node and +// continuing recursively upwards. If it is found, then return it. If it is +// not found, then prefix-define that namespace on the node and return a +// reference to it. +static Expected<xmlNsPtr> searchOrDefine(const unsigned char *HRef, + xmlNodePtr Node) { + if (xmlNsPtr Def = search(HRef, Node)) + return Def; + if (xmlNsPtr Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef))) + return Def; + return make_error<WindowsManifestError>("failed to create new namespace"); +} + +// Set the namespace of OrigionalAttribute on OriginalNode to be that of +// AdditionalAttribute's. +static Error copyAttributeNamespace(xmlAttrPtr OriginalAttribute, + xmlNodePtr OriginalNode, + xmlAttrPtr AdditionalAttribute) { + + Expected<xmlNsPtr> ExplicitOrError = + searchOrDefine(AdditionalAttribute->ns->href, OriginalNode); + if (!ExplicitOrError) + return ExplicitOrError.takeError(); + OriginalAttribute->ns = std::move(ExplicitOrError.get()); + return Error::success(); +} + +// Return the corresponding namespace definition for the prefix, defined on the +// given Node. Returns nullptr if there is no such definition. +static xmlNsPtr getNamespaceWithPrefix(const unsigned char *Prefix, + xmlNodePtr Node) { + if (Node == nullptr) + return nullptr; + for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { + if (xmlStringsEqual(Def->prefix, Prefix)) { + return Def; + } + } + return nullptr; +} + +// Search for the closest inheritable default namespace, starting on (and +// including) the Node and traveling upwards through parent nodes. Returns +// nullptr if there are no inheritable default namespaces. +static xmlNsPtr getClosestDefault(xmlNodePtr Node) { + if (xmlNsPtr Ret = getNamespaceWithPrefix(nullptr, Node)) + return Ret; + if (Node->parent == nullptr) + return nullptr; + return getClosestDefault(Node->parent); +} + +// Merge the attributes of AdditionalNode into OriginalNode. If attributes +// with identical types are present, they are not duplicated but rather if +// their values are not consistent and error is thrown. In addition, the +// higher priority namespace is used for each attribute, EXCEPT in the case +// of merging two default namespaces and the lower priority namespace +// definition occurs closer than the higher priority one. +static Error mergeAttributes(xmlNodePtr OriginalNode, + xmlNodePtr AdditionalNode) { + xmlNsPtr ClosestDefault = getClosestDefault(OriginalNode); + for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute; + Attribute = Attribute->next) { + if (xmlAttrPtr OriginalAttribute = + getAttribute(OriginalNode, Attribute->name)) { + if (!xmlStringsEqual(OriginalAttribute->children->content, + Attribute->children->content)) { + return make_error<WindowsManifestError>( + Twine("conflicting attributes for ") + + FROM_XML_CHAR(OriginalNode->name)); + } + if (!Attribute->ns) { + continue; + } + if (!OriginalAttribute->ns) { + if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, + Attribute)) { + return E; + } + continue; + } + if (namespaceOverrides(OriginalAttribute->ns->href, + Attribute->ns->href)) { + // In this case, the original attribute has a higher priority namespace + // than the incomiing attribute, however the namespace definition of + // the lower priority namespace occurs first traveling upwards in the + // tree. Therefore the lower priority namespace is applied. + if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix && + ClosestDefault && + xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) { + if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, + Attribute)) { + return E; + } + continue; + } + continue; + // This covers the case where the incoming attribute has the higher + // priority. The higher priority namespace is applied in all cases + // EXCEPT when both of the namespaces are default inherited, and the + // closest inherited default is the lower priority one. + } + if (Attribute->ns->prefix || OriginalAttribute->ns->prefix || + (ClosestDefault && !xmlStringsEqual(OriginalAttribute->ns->href, + ClosestDefault->href))) { + if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, + Attribute)) { + return E; + } + continue; + } + continue; + } + // If the incoming attribute is not already found on the node, append it + // to the end of the properties list. Also explicitly apply its + // namespace as a prefix because it might be contained in a separate + // namespace that doesn't use the attribute. + xmlAttrPtr NewProp = + xmlNewProp(OriginalNode, Attribute->name, Attribute->children->content); + Expected<xmlNsPtr> ExplicitOrError = + searchOrDefine(Attribute->ns->href, OriginalNode); + if (!ExplicitOrError) + return ExplicitOrError.takeError(); + NewProp->ns = std::move(ExplicitOrError.get()); + } + return Error::success(); +} + +// Given two nodes, return the one with the higher priority namespace. +static xmlNodePtr getDominantNode(xmlNodePtr Node1, xmlNodePtr Node2) { + + if (!Node1 || !Node1->ns) + return Node2; + if (!Node2 || !Node2->ns) + return Node1; + if (namespaceOverrides(Node1->ns->href, Node2->ns->href)) + return Node1; + return Node2; +} + +// Checks if this Node's namespace is inherited or one it defined itself. +static bool hasInheritedNs(xmlNodePtr Node) { + return Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node); +} + +// Check if this Node's namespace is a default namespace that it inherited, as +// opposed to defining itself. +static bool hasInheritedDefaultNs(xmlNodePtr Node) { + return hasInheritedNs(Node) && Node->ns->prefix == nullptr; +} + +// Check if this Node's namespace is a default namespace it defined itself. +static bool hasDefinedDefaultNamespace(xmlNodePtr Node) { + return Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node)); +} + +// For the given explicit prefix-definition of a namespace, travel downwards +// from a node recursively, and for every implicit, inherited default usage of +// that namespace replace it with that explicit prefix use. This is important +// when namespace overriding occurs when merging, so that elements unique to a +// namespace will still stay in that namespace. +static void explicateNamespace(xmlNsPtr PrefixDef, xmlNodePtr Node) { + // If a node as its own default namespace definition it clearly cannot have + // inherited the given default namespace, and neither will any of its + // children. + if (hasDefinedDefaultNamespace(Node)) + return; + if (Node->ns && xmlStringsEqual(Node->ns->href, PrefixDef->href) && + hasInheritedDefaultNs(Node)) + Node->ns = PrefixDef; + for (xmlAttrPtr Attribute = Node->properties; Attribute; + Attribute = Attribute->next) { + if (Attribute->ns && + xmlStringsEqual(Attribute->ns->href, PrefixDef->href)) { + Attribute->ns = PrefixDef; + } + } + for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { + explicateNamespace(PrefixDef, Child); + } +} + +// Perform the namespace merge between two nodes. +static Error mergeNamespaces(xmlNodePtr OriginalNode, + xmlNodePtr AdditionalNode) { + // Save the original default namespace definition in case the incoming node + // overrides it. + const unsigned char *OriginalDefinedDefaultHref = nullptr; + if (xmlNsPtr OriginalDefinedDefaultNs = + getNamespaceWithPrefix(nullptr, OriginalNode)) { + OriginalDefinedDefaultHref = xmlStrdup(OriginalDefinedDefaultNs->href); + } + const unsigned char *NewDefinedDefaultHref = nullptr; + // Copy all namespace definitions. There can only be one default namespace + // definition per node, so the higher priority one takes precedence in the + // case of collision. + for (xmlNsPtr Def = AdditionalNode->nsDef; Def; Def = Def->next) { + if (xmlNsPtr OriginalNsDef = + getNamespaceWithPrefix(Def->prefix, OriginalNode)) { + if (!Def->prefix) { + if (namespaceOverrides(Def->href, OriginalNsDef->href)) { + NewDefinedDefaultHref = TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href))); + } + } else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) { + return make_error<WindowsManifestError>( + Twine("conflicting namespace definitions for ") + + FROM_XML_CHAR(Def->prefix)); + } + } else { + xmlNsPtr NewDef = xmlCopyNamespace(Def); + NewDef->next = OriginalNode->nsDef; + OriginalNode->nsDef = NewDef; + } + } + + // Check whether the original node or the incoming node has the higher + // priority namespace. Depending on which one is dominant, we will have + // to recursively apply namespace changes down to children of the original + // node. + xmlNodePtr DominantNode = getDominantNode(OriginalNode, AdditionalNode); + xmlNodePtr NonDominantNode = + DominantNode == OriginalNode ? AdditionalNode : OriginalNode; + if (DominantNode == OriginalNode) { + if (OriginalDefinedDefaultHref) { + xmlNsPtr NonDominantDefinedDefault = + getNamespaceWithPrefix(nullptr, NonDominantNode); + // In this case, both the nodes defined a default namespace. However + // the lower priority node ended up having a higher priority default + // definition. This can occur if the higher priority node is prefix + // namespace defined. In this case we have to define an explicit + // prefix for the overridden definition and apply it to all children + // who relied on that definition. + if (NonDominantDefinedDefault && + namespaceOverrides(NonDominantDefinedDefault->href, + OriginalDefinedDefaultHref)) { + Expected<xmlNsPtr> EC = + searchOrDefine(OriginalDefinedDefaultHref, DominantNode); + if (!EC) { + return EC.takeError(); + } + xmlNsPtr PrefixDominantDefinedDefault = std::move(EC.get()); + explicateNamespace(PrefixDominantDefinedDefault, DominantNode); + } + // In this case the node with a higher priority namespace did not have a + // default namespace definition, but the lower priority node did. In this + // case the new default namespace definition is copied. A side effect of + // this is that all children will suddenly find themselves in a different + // default namespace. To maintain correctness we need to ensure that all + // children now explicitly refer to the namespace that they had previously + // implicitly inherited. + } else if (getNamespaceWithPrefix(nullptr, NonDominantNode)) { + if (DominantNode->parent) { + xmlNsPtr ClosestDefault = getClosestDefault(DominantNode->parent); + Expected<xmlNsPtr> EC = + searchOrDefine(ClosestDefault->href, DominantNode); + if (!EC) { + return EC.takeError(); + } + xmlNsPtr ExplicitDefault = std::move(EC.get()); + explicateNamespace(ExplicitDefault, DominantNode); + } + } + } else { + // Covers case where the incoming node has a default namespace definition + // that overrides the original node's namespace. This always leads to + // the original node receiving that new default namespace. + if (hasDefinedDefaultNamespace(DominantNode)) { + NonDominantNode->ns = getNamespaceWithPrefix(nullptr, NonDominantNode); + } else { + // This covers the case where the incoming node either has a prefix + // namespace, or an inherited default namespace. Since the namespace + // may not yet be defined in the original tree we do a searchOrDefine + // for it, and then set the namespace equal to it. + Expected<xmlNsPtr> EC = + searchOrDefine(DominantNode->ns->href, NonDominantNode); + if (!EC) { + return EC.takeError(); + } + xmlNsPtr Explicit = std::move(EC.get()); + NonDominantNode->ns = Explicit; + } + // This covers cases where the incoming dominant node HAS a default + // namespace definition, but MIGHT NOT NECESSARILY be in that namespace. + if (xmlNsPtr DominantDefaultDefined = + getNamespaceWithPrefix(nullptr, DominantNode)) { + if (OriginalDefinedDefaultHref) { + if (namespaceOverrides(DominantDefaultDefined->href, + OriginalDefinedDefaultHref)) { + // In this case, the incoming node's default definition overrides + // the original default definition, all children who relied on that + // definition must be updated accordingly. + Expected<xmlNsPtr> EC = + searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode); + if (!EC) { + return EC.takeError(); + } + xmlNsPtr ExplicitDefault = std::move(EC.get()); + explicateNamespace(ExplicitDefault, NonDominantNode); + } + } else { + // The original did not define a default definition, however the new + // default definition still applies to all children, so they must be + // updated to explicitly refer to the namespace they had previously + // been inheriting implicitly. + xmlNsPtr ClosestDefault = getClosestDefault(NonDominantNode); + Expected<xmlNsPtr> EC = + searchOrDefine(ClosestDefault->href, NonDominantNode); + if (!EC) { + return EC.takeError(); + } + xmlNsPtr ExplicitDefault = std::move(EC.get()); + explicateNamespace(ExplicitDefault, NonDominantNode); + } + } + } + if (NewDefinedDefaultHref) { + xmlNsPtr OriginalNsDef = getNamespaceWithPrefix(nullptr, OriginalNode); + xmlFree(const_cast<unsigned char *>(OriginalNsDef->href)); + OriginalNsDef->href = NewDefinedDefaultHref; + } + xmlFree(const_cast<unsigned char *>(OriginalDefinedDefaultHref)); + return Error::success(); +} + +static bool isRecognizedNamespace(const unsigned char *NsHref) { + for (auto &Ns : MtNsHrefsPrefixes) { + if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.data()))) { + return true; + } + } + return false; +} + +static bool hasRecognizedNamespace(xmlNodePtr Node) { + return isRecognizedNamespace(Node->ns->href); +} + +// Ensure a node's inherited namespace is actually defined in the tree it +// resides in. +static Error reconcileNamespaces(xmlNodePtr Node) { + if (!Node) { + return Error::success(); + } + if (hasInheritedNs(Node)) { + Expected<xmlNsPtr> ExplicitOrError = searchOrDefine(Node->ns->href, Node); + if (!ExplicitOrError) { + return ExplicitOrError.takeError(); + } + xmlNsPtr Explicit = std::move(ExplicitOrError.get()); + Node->ns = Explicit; + } + for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { + if (auto E = reconcileNamespaces(Child)) { + return E; + } + } + return Error::success(); +} + +// Recursively merge the two given manifest trees, depending on which elements +// are of a mergeable type, and choose namespaces according to which have +// higher priority. +static Error treeMerge(xmlNodePtr OriginalRoot, xmlNodePtr AdditionalRoot) { + if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot)) + return E; + if (auto E = mergeNamespaces(OriginalRoot, AdditionalRoot)) + return E; + xmlNodePtr AdditionalFirstChild = AdditionalRoot->children; + xmlNode StoreNext; + for (xmlNodePtr Child = AdditionalFirstChild; Child; Child = Child->next) { + xmlNodePtr OriginalChildWithName; + if (!isMergeableElement(Child->name) || + !(OriginalChildWithName = + getChildWithName(OriginalRoot, Child->name)) || + !hasRecognizedNamespace(Child)) { + StoreNext.next = Child->next; + xmlUnlinkNode(Child); + if (!xmlAddChild(OriginalRoot, Child)) { + return make_error<WindowsManifestError>(Twine("could not merge ") + + FROM_XML_CHAR(Child->name)); + } + if (auto E = reconcileNamespaces(Child)) { + return E; + } + Child = &StoreNext; + } else if (auto E = treeMerge(OriginalChildWithName, Child)) { + return E; + } + } + return Error::success(); +} + +static void stripComments(xmlNodePtr Root) { + xmlNode StoreNext; + for (xmlNodePtr Child = Root->children; Child; Child = Child->next) { + if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) { + stripComments(Child); + continue; + } + StoreNext.next = Child->next; + xmlNodePtr Remove = Child; + Child = &StoreNext; + xmlUnlinkNode(Remove); + xmlFreeNode(Remove); + } +} + +// libxml2 assumes that attributes do not inherit default namespaces, whereas +// the original mt.exe does make this assumption. This function reconciles +// this by setting all attributes to have the inherited default namespace. +static void setAttributeNamespaces(xmlNodePtr Node) { + for (xmlAttrPtr Attribute = Node->properties; Attribute; + Attribute = Attribute->next) { + if (!Attribute->ns) { + Attribute->ns = getClosestDefault(Node); + } + } + for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { + setAttributeNamespaces(Child); + } +} + +// The merging process may create too many prefix defined namespaces. This +// function removes all unnecessary ones from the tree. +static void checkAndStripPrefixes(xmlNodePtr Node, + std::vector<xmlNsPtr> &RequiredPrefixes) { + for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { + checkAndStripPrefixes(Child, RequiredPrefixes); + } + if (Node->ns && Node->ns->prefix != nullptr) { + xmlNsPtr ClosestDefault = getClosestDefault(Node); + if (ClosestDefault && + xmlStringsEqual(ClosestDefault->href, Node->ns->href)) { + Node->ns = ClosestDefault; + } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { + RequiredPrefixes.push_back(Node->ns); + } + } + for (xmlAttrPtr Attribute = Node->properties; Attribute; + Attribute = Attribute->next) { + if (Attribute->ns && Attribute->ns->prefix != nullptr) { + xmlNsPtr ClosestDefault = getClosestDefault(Node); + if (ClosestDefault && + xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) { + Attribute->ns = ClosestDefault; + } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { + RequiredPrefixes.push_back(Attribute->ns); + } + } + } + xmlNsPtr Prev; + xmlNs Temp; + for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { + if (!Def->prefix || llvm::is_contained(RequiredPrefixes, Def)) { + Prev = Def; + continue; + } + if (Def == Node->nsDef) { + Node->nsDef = Def->next; + } else { + Prev->next = Def->next; + } + Temp.next = Def->next; + xmlFreeNs(Def); + Def = &Temp; + } +} + +WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { + for (auto &Doc : MergedDocs) + xmlFreeDoc(Doc); +} + +Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( + const MemoryBuffer &Manifest) { + if (Merged) + return make_error<WindowsManifestError>( + "merge after getMergedManifest is not supported"); + if (Manifest.getBufferSize() == 0) + return make_error<WindowsManifestError>( + "attempted to merge empty manifest"); + xmlSetGenericErrorFunc((void *)this, + WindowsManifestMergerImpl::errorCallback); + xmlDocPtr ManifestXML = xmlReadMemory( + Manifest.getBufferStart(), Manifest.getBufferSize(), "manifest.xml", + nullptr, XML_PARSE_NOBLANKS | XML_PARSE_NODICT); + xmlSetGenericErrorFunc(nullptr, nullptr); + if (auto E = getParseError()) + return E; + xmlNodePtr AdditionalRoot = xmlDocGetRootElement(ManifestXML); + stripComments(AdditionalRoot); + setAttributeNamespaces(AdditionalRoot); + if (CombinedDoc == nullptr) { + CombinedDoc = ManifestXML; + } else { + xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); + if (!xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) || + !isMergeableElement(AdditionalRoot->name) || + !hasRecognizedNamespace(AdditionalRoot)) { + return make_error<WindowsManifestError>("multiple root nodes"); + } + if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { + return E; + } + } + MergedDocs.push_back(ManifestXML); + return Error::success(); +} + +std::unique_ptr<MemoryBuffer> +WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { + if (!Merged) { + Merged = true; + + if (!CombinedDoc) + return nullptr; + + xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); + std::vector<xmlNsPtr> RequiredPrefixes; + checkAndStripPrefixes(CombinedRoot, RequiredPrefixes); + std::unique_ptr<xmlDoc, XmlDeleter> OutputDoc( + xmlNewDoc((const unsigned char *)"1.0")); + xmlDocSetRootElement(OutputDoc.get(), CombinedRoot); + assert(0 == xmlDocGetRootElement(CombinedDoc)); + + xmlKeepBlanksDefault(0); + xmlChar *Buff = nullptr; + xmlDocDumpFormatMemoryEnc(OutputDoc.get(), &Buff, &BufferSize, "UTF-8", 1); + Buffer.reset(Buff); + } + + return BufferSize ? MemoryBuffer::getMemBufferCopy(StringRef( + FROM_XML_CHAR(Buffer.get()), (size_t)BufferSize)) + : nullptr; +} + +bool windows_manifest::isAvailable() { return true; } + +#else + +WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { +} + +Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( + const MemoryBuffer &Manifest) { + return make_error<WindowsManifestError>("no libxml2"); +} + +std::unique_ptr<MemoryBuffer> +WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { + return nullptr; +} + +bool windows_manifest::isAvailable() { return false; } + +#endif + +WindowsManifestMerger::WindowsManifestMerger() + : Impl(std::make_unique<WindowsManifestMergerImpl>()) {} + +WindowsManifestMerger::~WindowsManifestMerger() {} + +Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) { + return Impl->merge(Manifest); +} + +std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() { + return Impl->getMergedManifest(); +} + +void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback( + void *Ctx, const char *Format, ...) { + auto *Merger = (WindowsManifestMergerImpl *)Ctx; + Merger->ParseErrorOccurred = true; +} + +Error WindowsManifestMerger::WindowsManifestMergerImpl::getParseError() { + if (!ParseErrorOccurred) + return Error::success(); + return make_error<WindowsManifestError>("invalid xml document"); +} |