From 3305678c7c954ba11be0ed089f7acd2b09af0779 Mon Sep 17 00:00:00 2001 From: Dan Sinclair Date: Wed, 8 Nov 2017 19:38:22 +0000 Subject: Convert XFA_PACKET to an enum class This CL converts the XFA_PACKET enum to the XFA_PacketType enum class. Change-Id: Ic65e61c2ee681750ca0e672bc4206d676556a862 Reviewed-on: https://pdfium-review.googlesource.com/18110 Commit-Queue: dsinclair Reviewed-by: Tom Sepez --- xfa/fxfa/fxfa_basic.h | 71 +++++++++++--------- xfa/fxfa/parser/cxfa_dataexporter.cpp | 2 +- xfa/fxfa/parser/cxfa_document.cpp | 2 +- xfa/fxfa/parser/cxfa_simple_parser.cpp | 114 +++++++++++++++++---------------- xfa/fxfa/parser/xfa_utils.cpp | 4 +- xfa/fxfa/parser/xfa_utils.h | 2 +- 6 files changed, 105 insertions(+), 90 deletions(-) diff --git a/xfa/fxfa/fxfa_basic.h b/xfa/fxfa/fxfa_basic.h index 8695b48df4..5f1369906c 100644 --- a/xfa/fxfa/fxfa_basic.h +++ b/xfa/fxfa/fxfa_basic.h @@ -47,42 +47,48 @@ enum XFA_HashCode : uint32_t { XFA_HASHCODE_Xmpmeta = 0x132a8fbc }; -enum XFA_PACKET { - XFA_PACKET_USER, - XFA_PACKET_SourceSet, - XFA_PACKET_Pdf, - XFA_PACKET_Xdc, - XFA_PACKET_XDP, - XFA_PACKET_Xmpmeta, - XFA_PACKET_Xfdf, - XFA_PACKET_Config, - XFA_PACKET_LocaleSet, - XFA_PACKET_Stylesheet, - XFA_PACKET_Template, - XFA_PACKET_Signature, - XFA_PACKET_Datasets, - XFA_PACKET_Form, - XFA_PACKET_ConnectionSet, +enum class XFA_PacketType : uint8_t { + User, + SourceSet, + Pdf, + Xdc, + Xdp, + Xmpmeta, + Xfdf, + Config, + LocaleSet, + Stylesheet, + Template, + Signature, + Datasets, + Form, + ConnectionSet, }; enum XFA_XDPPACKET { XFA_XDPPACKET_UNKNOWN = 0, - XFA_XDPPACKET_Config = 1 << XFA_PACKET_Config, - XFA_XDPPACKET_Template = 1 << XFA_PACKET_Template, - XFA_XDPPACKET_Datasets = 1 << XFA_PACKET_Datasets, - XFA_XDPPACKET_Form = 1 << XFA_PACKET_Form, - XFA_XDPPACKET_LocaleSet = 1 << XFA_PACKET_LocaleSet, - XFA_XDPPACKET_ConnectionSet = 1 << XFA_PACKET_ConnectionSet, - XFA_XDPPACKET_SourceSet = 1 << XFA_PACKET_SourceSet, - XFA_XDPPACKET_Xdc = 1 << XFA_PACKET_Xdc, - XFA_XDPPACKET_Pdf = 1 << XFA_PACKET_Pdf, - XFA_XDPPACKET_Xfdf = 1 << XFA_PACKET_Xfdf, - XFA_XDPPACKET_Xmpmeta = 1 << XFA_PACKET_Xmpmeta, - XFA_XDPPACKET_Signature = 1 << XFA_PACKET_Signature, - XFA_XDPPACKET_Stylesheet = 1 << XFA_PACKET_Stylesheet, - XFA_XDPPACKET_USER = 1 << XFA_PACKET_USER, - XFA_XDPPACKET_XDP = 1 << XFA_PACKET_XDP, + XFA_XDPPACKET_Config = 1 << static_cast(XFA_PacketType::Config), + XFA_XDPPACKET_Template = 1 << static_cast(XFA_PacketType::Template), + XFA_XDPPACKET_Datasets = 1 << static_cast(XFA_PacketType::Datasets), + XFA_XDPPACKET_Form = 1 << static_cast(XFA_PacketType::Form), + XFA_XDPPACKET_LocaleSet = 1 + << static_cast(XFA_PacketType::LocaleSet), + XFA_XDPPACKET_ConnectionSet = + 1 << static_cast(XFA_PacketType::ConnectionSet), + XFA_XDPPACKET_SourceSet = 1 + << static_cast(XFA_PacketType::SourceSet), + XFA_XDPPACKET_Xdc = 1 << static_cast(XFA_PacketType::Xdc), + XFA_XDPPACKET_Pdf = 1 << static_cast(XFA_PacketType::Pdf), + XFA_XDPPACKET_Xfdf = 1 << static_cast(XFA_PacketType::Xfdf), + XFA_XDPPACKET_Xmpmeta = 1 << static_cast(XFA_PacketType::Xmpmeta), + XFA_XDPPACKET_Signature = 1 + << static_cast(XFA_PacketType::Signature), + XFA_XDPPACKET_Stylesheet = + 1 << static_cast(XFA_PacketType::Stylesheet), + XFA_XDPPACKET_USER = 1 << static_cast(XFA_PacketType::User), + XFA_XDPPACKET_XDP = 1 << static_cast(XFA_PacketType::Xdp), }; + enum XFA_XDPPACKET_FLAGS { XFA_XDPPACKET_FLAGS_COMPLETEMATCH = 1, XFA_XDPPACKET_FLAGS_PREFIXMATCH = 2, @@ -90,6 +96,7 @@ enum XFA_XDPPACKET_FLAGS { XFA_XDPPACKET_FLAGS_SUPPORTONE = 8, XFA_XDPPACKET_FLAGS_SUPPORTMANY = 16, }; + struct XFA_PACKETINFO { uint32_t uHash; const wchar_t* pName; @@ -931,6 +938,7 @@ enum class XFA_Element : int32_t { Tagged, Items }; + struct XFA_ELEMENTINFO { uint32_t uHash; const wchar_t* pName; @@ -1006,6 +1014,7 @@ struct XFA_NOTSUREATTRIBUTE { }; typedef void (CJX_Object::*XFA_METHOD_CALLBACK)(CFXJSE_Arguments* pArguments); + struct XFA_METHODINFO { uint32_t uHash; const wchar_t* pName; diff --git a/xfa/fxfa/parser/cxfa_dataexporter.cpp b/xfa/fxfa/parser/cxfa_dataexporter.cpp index bc6506d6d3..8b16ae745c 100644 --- a/xfa/fxfa/parser/cxfa_dataexporter.cpp +++ b/xfa/fxfa/parser/cxfa_dataexporter.cpp @@ -399,7 +399,7 @@ void XFA_DataExporter_RegenerateFormFile( } pStream->WriteString(L" xmlns=\""); - const wchar_t* pURI = XFA_GetPacketByIndex(XFA_PACKET_Form)->pURI; + const wchar_t* pURI = XFA_GetPacketByIndex(XFA_PacketType::Form)->pURI; pStream->WriteString(WideStringView(pURI, wcslen(pURI))); WideString wsVersionNumber; diff --git a/xfa/fxfa/parser/cxfa_document.cpp b/xfa/fxfa/parser/cxfa_document.cpp index 0489ae696c..c80dec979a 100644 --- a/xfa/fxfa/parser/cxfa_document.cpp +++ b/xfa/fxfa/parser/cxfa_document.cpp @@ -301,7 +301,7 @@ CFXJSE_Engine* CXFA_Document::GetScriptContext() { XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber( const WideString& wsTemplateNS) { WideStringView wsTemplateURIPrefix = - XFA_GetPacketByIndex(XFA_PACKET_Template)->pURI; + XFA_GetPacketByIndex(XFA_PacketType::Template)->pURI; size_t nPrefixLength = wsTemplateURIPrefix.GetLength(); if (WideStringView(wsTemplateNS.c_str(), wsTemplateNS.GetLength()) != wsTemplateURIPrefix) { diff --git a/xfa/fxfa/parser/cxfa_simple_parser.cpp b/xfa/fxfa/parser/cxfa_simple_parser.cpp index eca93bd6bc..79214356fe 100644 --- a/xfa/fxfa/parser/cxfa_simple_parser.cpp +++ b/xfa/fxfa/parser/cxfa_simple_parser.cpp @@ -168,25 +168,26 @@ bool FindAttributeWithNS(CFX_XMLElement* pElement, CFX_XMLNode* GetDataSetsFromXDP(CFX_XMLNode* pXMLDocumentNode) { if (MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->pName, - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->eFlags)) { + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->eFlags)) { return pXMLDocumentNode; } if (!MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_XDP)->pName, - XFA_GetPacketByIndex(XFA_PACKET_XDP)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_XDP)->eFlags)) { + XFA_GetPacketByIndex(XFA_PacketType::Xdp)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Xdp)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Xdp)->eFlags)) { return nullptr; } for (CFX_XMLNode* pDatasetsNode = pXMLDocumentNode->GetNodeItem(CFX_XMLNode::FirstChild); pDatasetsNode; pDatasetsNode = pDatasetsNode->GetNodeItem(CFX_XMLNode::NextSibling)) { - if (!MatchNodeName(pDatasetsNode, - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->pName, - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->eFlags)) { + if (!MatchNodeName( + pDatasetsNode, + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->eFlags)) { continue; } return pDatasetsNode; @@ -448,9 +449,9 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_XDP( CFX_XMLNode* pXMLDocumentNode, XFA_XDPPACKET ePacketID) { if (!MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_XDP)->pName, - XFA_GetPacketByIndex(XFA_PACKET_XDP)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_XDP)->eFlags)) { + XFA_GetPacketByIndex(XFA_PacketType::Xdp)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Xdp)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Xdp)->eFlags)) { return nullptr; } @@ -478,7 +479,8 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_XDP( pXMLDocumentNode->GetNodeItem(CFX_XMLNode::FirstChild); pChildItem; pChildItem = pChildItem->GetNodeItem(CFX_XMLNode::NextSibling)) { - const XFA_PACKETINFO* pPacketInfo = XFA_GetPacketByIndex(XFA_PACKET_Config); + const XFA_PACKETINFO* pPacketInfo = + XFA_GetPacketByIndex(XFA_PacketType::Config); if (!MatchNodeName(pChildItem, pPacketInfo->pName, pPacketInfo->pURI, pPacketInfo->eFlags)) { continue; @@ -577,9 +579,9 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Config( CFX_XMLNode* pXMLDocumentNode, XFA_XDPPACKET ePacketID) { if (!MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_Config)->pName, - XFA_GetPacketByIndex(XFA_PACKET_Config)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_Config)->eFlags)) { + XFA_GetPacketByIndex(XFA_PacketType::Config)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Config)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Config)->eFlags)) { return nullptr; } CXFA_Node* pNode = @@ -588,7 +590,7 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Config( return nullptr; pNode->JSNode()->SetCData(XFA_Attribute::Name, - XFA_GetPacketByIndex(XFA_PACKET_Config)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Config)->pName, false, false); if (!NormalLoader(pNode, pXMLDocumentNode, ePacketID, true)) return nullptr; @@ -603,17 +605,17 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_TemplateForm( CXFA_Node* pNode = nullptr; if (ePacketID == XFA_XDPPACKET_Template) { if (MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_Template)->pName, - XFA_GetPacketByIndex(XFA_PACKET_Template)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_Template)->eFlags)) { + XFA_GetPacketByIndex(XFA_PacketType::Template)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Template)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Template)->eFlags)) { pNode = m_pFactory->CreateNode(XFA_XDPPACKET_Template, XFA_Element::Template); if (!pNode) return nullptr; pNode->JSNode()->SetCData( - XFA_Attribute::Name, XFA_GetPacketByIndex(XFA_PACKET_Template)->pName, - false, false); + XFA_Attribute::Name, + XFA_GetPacketByIndex(XFA_PacketType::Template)->pName, false, false); if (m_bDocumentParser) { CFX_XMLElement* pXMLDocumentElement = static_cast(pXMLDocumentNode); @@ -628,9 +630,9 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_TemplateForm( } } else if (ePacketID == XFA_XDPPACKET_Form) { if (MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_Form)->pName, - XFA_GetPacketByIndex(XFA_PACKET_Form)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_Form)->eFlags)) { + XFA_GetPacketByIndex(XFA_PacketType::Form)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Form)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Form)->eFlags)) { CFX_XMLElement* pXMLDocumentElement = static_cast(pXMLDocumentNode); WideString wsChecksum = pXMLDocumentElement->GetString(L"checksum"); @@ -654,9 +656,9 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_TemplateForm( if (!pNode) return nullptr; - pNode->JSNode()->SetCData(XFA_Attribute::Name, - XFA_GetPacketByIndex(XFA_PACKET_Form)->pName, - false, false); + pNode->JSNode()->SetCData( + XFA_Attribute::Name, + XFA_GetPacketByIndex(XFA_PacketType::Form)->pName, false, false); pNode->JSNode()->SetAttribute(XFA_Attribute::Checksum, wsChecksum.AsStringView(), false); CXFA_Node* pTemplateRoot = @@ -691,9 +693,9 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Data( if (!pNode) return nullptr; - pNode->JSNode()->SetCData(XFA_Attribute::Name, - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->pName, - false, false); + pNode->JSNode()->SetCData( + XFA_Attribute::Name, + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->pName, false, false); if (!DataLoader(pNode, pDatasetsXMLNode, false)) return nullptr; @@ -703,8 +705,8 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Data( CFX_XMLNode* pDataXMLNode = nullptr; if (MatchNodeName(pXMLDocumentNode, L"data", - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_Datasets)->eFlags)) { + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Datasets)->eFlags)) { static_cast(pXMLDocumentNode) ->RemoveAttribute(L"xmlns:xfa"); pDataXMLNode = pXMLDocumentNode; @@ -751,10 +753,11 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_LocaleConnectionSourceSet( XFA_XDPPACKET ePacketID) { CXFA_Node* pNode = nullptr; if (ePacketID == XFA_XDPPACKET_LocaleSet) { - if (MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_LocaleSet)->pName, - XFA_GetPacketByIndex(XFA_PACKET_LocaleSet)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_LocaleSet)->eFlags)) { + if (MatchNodeName( + pXMLDocumentNode, + XFA_GetPacketByIndex(XFA_PacketType::LocaleSet)->pName, + XFA_GetPacketByIndex(XFA_PacketType::LocaleSet)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::LocaleSet)->eFlags)) { pNode = m_pFactory->CreateNode(XFA_XDPPACKET_LocaleSet, XFA_Element::LocaleSet); if (!pNode) @@ -762,15 +765,16 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_LocaleConnectionSourceSet( pNode->JSNode()->SetCData( XFA_Attribute::Name, - XFA_GetPacketByIndex(XFA_PACKET_LocaleSet)->pName, false, false); + XFA_GetPacketByIndex(XFA_PacketType::LocaleSet)->pName, false, false); if (!NormalLoader(pNode, pXMLDocumentNode, ePacketID, true)) return nullptr; } } else if (ePacketID == XFA_XDPPACKET_ConnectionSet) { - if (MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_ConnectionSet)->pName, - XFA_GetPacketByIndex(XFA_PACKET_ConnectionSet)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_ConnectionSet)->eFlags)) { + if (MatchNodeName( + pXMLDocumentNode, + XFA_GetPacketByIndex(XFA_PacketType::ConnectionSet)->pName, + XFA_GetPacketByIndex(XFA_PacketType::ConnectionSet)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::ConnectionSet)->eFlags)) { pNode = m_pFactory->CreateNode(XFA_XDPPACKET_ConnectionSet, XFA_Element::ConnectionSet); if (!pNode) @@ -778,15 +782,17 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_LocaleConnectionSourceSet( pNode->JSNode()->SetCData( XFA_Attribute::Name, - XFA_GetPacketByIndex(XFA_PACKET_ConnectionSet)->pName, false, false); + XFA_GetPacketByIndex(XFA_PacketType::ConnectionSet)->pName, false, + false); if (!NormalLoader(pNode, pXMLDocumentNode, ePacketID, true)) return nullptr; } } else if (ePacketID == XFA_XDPPACKET_SourceSet) { - if (MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_SourceSet)->pName, - XFA_GetPacketByIndex(XFA_PACKET_SourceSet)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_SourceSet)->eFlags)) { + if (MatchNodeName( + pXMLDocumentNode, + XFA_GetPacketByIndex(XFA_PacketType::SourceSet)->pName, + XFA_GetPacketByIndex(XFA_PacketType::SourceSet)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::SourceSet)->eFlags)) { pNode = m_pFactory->CreateNode(XFA_XDPPACKET_SourceSet, XFA_Element::SourceSet); if (!pNode) @@ -794,7 +800,7 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_LocaleConnectionSourceSet( pNode->JSNode()->SetCData( XFA_Attribute::Name, - XFA_GetPacketByIndex(XFA_PACKET_SourceSet)->pName, false, false); + XFA_GetPacketByIndex(XFA_PacketType::SourceSet)->pName, false, false); if (!NormalLoader(pNode, pXMLDocumentNode, ePacketID, true)) return nullptr; } @@ -808,9 +814,9 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Xdc( CFX_XMLNode* pXMLDocumentNode, XFA_XDPPACKET ePacketID) { if (!MatchNodeName(pXMLDocumentNode, - XFA_GetPacketByIndex(XFA_PACKET_Xdc)->pName, - XFA_GetPacketByIndex(XFA_PACKET_Xdc)->pURI, - XFA_GetPacketByIndex(XFA_PACKET_Xdc)->eFlags)) + XFA_GetPacketByIndex(XFA_PacketType::Xdc)->pName, + XFA_GetPacketByIndex(XFA_PacketType::Xdc)->pURI, + XFA_GetPacketByIndex(XFA_PacketType::Xdc)->eFlags)) return nullptr; CXFA_Node* pNode = @@ -819,8 +825,8 @@ CXFA_Node* CXFA_SimpleParser::ParseAsXDPPacket_Xdc( return nullptr; pNode->JSNode()->SetCData(XFA_Attribute::Name, - XFA_GetPacketByIndex(XFA_PACKET_Xdc)->pName, false, - false); + XFA_GetPacketByIndex(XFA_PacketType::Xdc)->pName, + false, false); pNode->SetXMLMappingNode(pXMLDocumentNode); return pNode; } diff --git a/xfa/fxfa/parser/xfa_utils.cpp b/xfa/fxfa/parser/xfa_utils.cpp index e5091eec0f..122cfb0a1a 100644 --- a/xfa/fxfa/parser/xfa_utils.cpp +++ b/xfa/fxfa/parser/xfa_utils.cpp @@ -464,8 +464,8 @@ const XFA_ATTRIBUTEENUMINFO* XFA_GetAttributeEnumByName( return nullptr; } -const XFA_PACKETINFO* XFA_GetPacketByIndex(XFA_PACKET ePacket) { - return g_XFAPacketData + ePacket; +const XFA_PACKETINFO* XFA_GetPacketByIndex(XFA_PacketType ePacket) { + return g_XFAPacketData + static_cast(ePacket); } const XFA_PACKETINFO* XFA_GetPacketByID(uint32_t dwPacket) { diff --git a/xfa/fxfa/parser/xfa_utils.h b/xfa/fxfa/parser/xfa_utils.h index 359a2d8660..a7498561e1 100644 --- a/xfa/fxfa/parser/xfa_utils.h +++ b/xfa/fxfa/parser/xfa_utils.h @@ -189,7 +189,7 @@ const XFA_ATTRIBUTEINFO* XFA_GetAttributeByName(const WideStringView& wsName); const XFA_ATTRIBUTEINFO* XFA_GetAttributeByID(XFA_Attribute eName); const XFA_ATTRIBUTEENUMINFO* XFA_GetAttributeEnumByName( const WideStringView& wsName); -const XFA_PACKETINFO* XFA_GetPacketByIndex(XFA_PACKET ePacket); +const XFA_PACKETINFO* XFA_GetPacketByIndex(XFA_PacketType ePacket); const XFA_PACKETINFO* XFA_GetPacketByID(uint32_t dwPacket); #endif // XFA_FXFA_PARSER_XFA_UTILS_H_ -- cgit v1.2.3