<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/ns/xproc-step" xmlns:c="http://www.w3.org/ns/xproc-step" exclude-result-prefixes="#all" version="2.0" xpath-default-namespace="http://www.w3.org/ns/xproc-step">
<xsl:variable name="NameStartChar" select="':A-Z_a-zÀ-ÖØ-öø-˿Ͱ-ͽͿ--⁰-Ⰰ-、-豈-﷏ﷰ-�𐀀-'"/>
<xsl:variable name="NameChar" select="concat($NameStartChar,'\-\.0-9·̀-ͯ‿-⁀')"/>
<xsl:variable name="PubidCharNoApos" select="'
a-zA-Z0-9\-\(\)\+,\./:=\?;!\*#@\$_%'"/>
<xsl:variable name="PubidChar" select="concat($PubidCharNoApos,'''')"/>
<xsl:variable name="Name" select="concat('[',$NameStartChar,'][',$NameChar,']*')"/>
<xsl:variable name="S" select="'[
]+'"/>
<xsl:variable name="SystemLiteral" select="'("[^"]*"|''[^'']*'')'"/>
<xsl:variable name="PubidLiteral" select="concat('("[',$PubidChar,']*"|''[',$PubidCharNoApos,']*'')')"/>
<xsl:template match="/*">
<c:result>
<xsl:attribute name="xml:space" select="'preserve'"/>
<xsl:text>
</xsl:text>
<xsl:variable name="bom">
<xsl:if test="@bom-type">
<xsl:text> </xsl:text>
<c:bom type="{@bom-type}">
<xsl:if test="@bom-base64">
<xsl:attribute name="base64" select="@bom-base64"/>
</xsl:if>
<xsl:if test="@bom-hex">
<xsl:attribute name="hex" select="@bom-hex"/>
</xsl:if>
</c:bom>
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:variable name="result">
<xsl:analyze-string select="text()" regex="<[?!][^>]*>[^<]*">
<xsl:matching-substring>
<xsl:choose>
<xsl:when test="starts-with(.,'<?')">
<c:processing-instruction>
<xsl:value-of select="replace(.,'\s+$','')"/>
</c:processing-instruction>
</xsl:when>
<xsl:when test="starts-with(.,'<!--')">
<c:comment>
<xsl:value-of select="replace(.,'\s+$','')"/>
</c:comment>
</xsl:when>
<xsl:otherwise>
<c:doctype>
<xsl:if test="upper-case(substring(.,1,9))='<!DOCTYPE'">
<xsl:attribute name="doctype-start" select="'true'"/>
</xsl:if>
<xsl:value-of select="."/>
</c:doctype>
</xsl:otherwise>
</xsl:choose>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:choose>
<xsl:when test="contains(.,'<')">
<xsl:variable name="before-root-element" select="substring-before(.,'<')"/>
<xsl:if test="$before-root-element">
<c:other>
<xsl:value-of select="$before-root-element"/>
</c:other>
</xsl:if>
<c:root-element>
<xsl:value-of select="concat('<',replace(substring-after(.,'<'),'>[^>]*$','>'))"/>
</c:root-element>
</xsl:when>
<xsl:when test="normalize-space(.)">
<c:other>
<xsl:value-of select="."/>
</c:other>
</xsl:when>
</xsl:choose>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:variable name="result">
<xsl:for-each-group select="$result/*" group-adjacent="self::c:doctype or self::c:other and preceding-sibling::*[not(self::c:other)][1]/self::c:doctype">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:for-each-group select="current-group()" group-starting-with="c:doctype[@doctype-start='true']">
<c:doctype>
<xsl:copy-of select="current-group()"/>
</c:doctype>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:for-each-group select="current-group()" group-adjacent="self::c:comment or self::c:other and preceding-sibling::*[not(self::c:other)][1]/self::c:comment">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:for-each-group select="current-group()" group-starting-with="c:comment[starts-with(.,'<!--')]">
<c:comment>
<xsl:copy-of select="current-group()"/>
</c:comment>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:variable>
<xsl:variable name="result">
<xsl:for-each select="$result/*">
<xsl:choose>
<xsl:when test="self::c:other and position() = 1">
<c:bom>
<xsl:copy-of select="$bom/*/@*"/>
<xsl:value-of select="."/>
</c:bom>
</xsl:when>
<xsl:when test="self::c:processing-instruction">
<xsl:element name="c:{replace(text(),concat('^<\?(',$Name,')(',$S,'|\?).*$'),'$1','s')}">
<xsl:analyze-string select="text()" regex="{concat($Name,'\s*=\s*("[^"]*"|''[^'']*'')')}">
<xsl:matching-substring>
<xsl:attribute name="{tokenize(.,'=')[1]}" select="replace(replace(.,concat($Name,'\s*=\s*("[^"]*"|''[^'']*'')'),'$1'),'^.(.*).$','$1','s')"/>
</xsl:matching-substring>
</xsl:analyze-string>
<xsl:copy-of select="text()"/>
</xsl:element>
</xsl:when>
<xsl:when test="self::c:doctype">
<xsl:copy>
<xsl:variable name="doctype" select="replace(string-join(*/text(),''),'\s+$','','s')"/>
<xsl:attribute name="name" select="replace($doctype,concat('<!DOCTYPE',$S,'(',$Name,')','[\s>].*'),'$1','s')"/>
<xsl:variable name="public" select="if (matches($doctype,concat('^<!DOCTYPE',$S,$Name,$S,'PUBLIC',$S,$PubidLiteral,$S,$SystemLiteral,'.*$'))) then replace(replace($doctype,concat('^<!DOCTYPE',$S,$Name,$S,'PUBLIC',$S,'(',$PubidLiteral,')',$S,'.*$'),'$1'),'^.(.*).$','$1') else ()"/>
<xsl:variable name="system" select="if (matches($doctype,concat('^<!DOCTYPE',$S,$Name,$S,'PUBLIC',$S,$PubidLiteral,$S,$SystemLiteral,'.*$'))) then replace(replace($doctype,concat('^<!DOCTYPE',$S,$Name,$S,'PUBLIC',$S,$PubidLiteral,$S,'(',$SystemLiteral,')(',$S,'|\[|>).*$'),'$2'),'^.(.*).$','$1') else ()"/>
<xsl:variable name="system" select="if (not($system) and matches($doctype,concat('^<!DOCTYPE',$S,$Name,$S,'SYSTEM',$S,$SystemLiteral,'.*$'))) then replace(replace($doctype,concat('^<!DOCTYPE',$S,$Name,$S,'SYSTEM',$S,'(',$SystemLiteral,')(',$S,'|\[|>).*$'),'$1'),'^.(.*).$','$1') else $system"/>
<xsl:variable name="internal" select="if (contains($doctype,'[')) then replace($doctype,'^[^\[]*\[\s*(.*?)\s*\][^\]]*$','$1','s') else ()"/>
<xsl:if test="$public">
<xsl:attribute name="public" select="$public"/>
</xsl:if>
<xsl:if test="$system">
<xsl:attribute name="system" select="$system"/>
</xsl:if>
<xsl:if test="$internal or $internal=''">
<xsl:attribute name="internal" select="$internal"/>
</xsl:if>
<xsl:value-of select="$doctype"/>
</xsl:copy>
</xsl:when>
<xsl:when test="self::c:comment">
<xsl:copy>
<xsl:variable name="comment" select="string-join(*/text(),'')"/>
<xsl:attribute name="text" select="substring-after(substring-before($comment,'-->'),'<!--')"/>
<xsl:value-of select="$comment"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:if test="not($result/c:bom and $bom)">
<xsl:copy-of select="$bom"/>
</xsl:if>
<xsl:for-each select="$result/*">
<xsl:text> </xsl:text>
<xsl:copy-of select="."/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</c:result>
</xsl:template>
</xsl:stylesheet>