| //===-- 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_LIBXML2_ENABLED |
| #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_LIBXML2_ENABLED |
| 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_LIBXML2_ENABLED |
| |
| 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"); |
| } |