pugixml (empty) → 0.1.0
raw patch · 17 files changed
+13886/−0 lines, 17 filesdep +basedep +bytestringdep +data-default-classsetup-changed
Dependencies added: base, bytestring, data-default-class, pugixml, tasty, tasty-hunit, template-haskell
Files
- LICENSE +20/−0
- Setup.hs +2/−0
- Text/XML/Pugi.hs +589/−0
- Text/XML/Pugi/Const.hs +33/−0
- Text/XML/Pugi/Foreign/Attr.hs +22/−0
- Text/XML/Pugi/Foreign/Const.hs +189/−0
- Text/XML/Pugi/Foreign/Document.hs +124/−0
- Text/XML/Pugi/Foreign/Node.hs +296/−0
- Text/XML/Pugi/Foreign/Types.hs +40/−0
- Text/XML/Pugi/Foreign/XPath.hs +106/−0
- Text/XML/Pugi/Foreign/XPath/Node.hs +47/−0
- cbit/wrapper.cc +290/−0
- pugixml-1.4/pugiconfig.hpp +72/−0
- pugixml-1.4/pugixml.cpp +10639/−0
- pugixml-1.4/pugixml.hpp +1332/−0
- pugixml.cabal +50/−0
- test/test.hs +35/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2014 HirotomoMoriwaki++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Text/XML/Pugi.hs view
@@ -0,0 +1,589 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE CPP #-}++-- | parse xml+--+-- @+-- \> docA <- parse def \"\<a \/\>\"+-- \> docA+-- Right Document \<a \/\>+-- +-- \> parseFile def \"test.xml\"+-- Document \<test \>+-- @+--+-- render xml+--+-- @+-- \> Data.ByteString.Lazy.Char.putStrLn $ either undefined (pretty def) docA+-- \<?xml version=\"1.0\"?\>+-- \<a \/\>+--+-- \> prettyFile def docA+-- @+--+-- create xml+--+-- @+-- testHtml :: IO 'Document'+-- testHtml = 'create' $ \doc -\> do+-- decl <- 'appendDeclaration' \"xml\" doc+-- 'appendAttrs' [(\"version\", \"1.0\"), (\"lang\", \"ja\")] decl+--+-- 'appendDoctype' \"html\" doc+--+-- html <- 'appendElement' \"html\" doc+-- body <- appendElement \"body\" html+-- div_ <- appendElement \"div\" body+-- a <- appendElement \"a\" div_+-- 'appendAttr' \"href\" \"http:\/\/example.com\" a+-- txt <- 'appendPCData' \"example.com\" a+-- return ()+-- @+--+-- @+-- -- testHtml for copy&paste to ghci.+-- \> doc \<- create $ \\doc -\> appendDeclaration \"xml\" doc >>= \\decl -\> appendAttrs [(\"version\", \"1.0\"), (\"lang\", \"ja\")] decl >> appendDoctype \"html\" doc >> appendElement \"html\" doc >>= \\html -\> appendElement \"body\" html >>= \\body -\> appendElement \"div\" body >>= \\div_ -\> appendElement \"a\" div_ >>= \\a -\> appendAttr \"href\" \"http:\/\/example.com\" a >> appendPCData \"example.com\" a >> return ()+--+-- \> doc+-- Document \<?xml version=\"1.0\" lang=\"ja\"?\>\<!DOCTYPE html\>\<html\>\<body\>\<div\>\<a href=\"http:\/\/example.com\"\>example.com\<\/a\>\<\/div\>\<\/body\>\<\/html\>+-- @+--+-- access xml tree+--+-- @+-- \> let Just x = 'child' \"xml\" doc+-- \> x+-- Node \<?xml version=\"1.0\" lang=\"ja\"?\>+--+-- \> 'nextSibling' x+-- Just Node \<!DOCTYPE html\>+--+-- \> Just html = 'nextSiblingByName' "html" x+--+-- \> html+-- Node \<html\>\<body\>\<div\>\<a href=\"http:\/\/example.com\"\>example.com\<\/a\>\<\/div\>\<\/body\>\<\/html\>+--+-- \> 'evaluate' [xpath|string(\/\/a\/@href)|] html+-- \"http:\/\/example.com\"+--+-- \> let ns = evaluate [xpath|\/\/a\/@href|] html+--+-- \> 'nodeSetSize' ns+-- 1+-- \> 'nodeSetIndex' ns 0+-- Right (\"href\",\"http:\/\/example.com\")+-- @+--+-- modify xml+--+-- @+-- modify doc $ \\d -\> 'selectSingleNode' [xpath|\/\/a|] d >>= \\(Left a) -\> setOrAppendAttr \"href\" \"#\" a+-- Document \<?xml version=\"1.0\" lang=\"ja\"?\>\<!DOCTYPE html\>\<html\>\<body\>\<div\>\<a href=\"#\"\>example.com\<\/a\>\<\/div\>\<\/body\>\<\/html\>+-- @+-- ++module Text.XML.Pugi+ ( -- * Document+ Document_, Document, MutableDocument+ -- ** parse+ , D.ParseConfig(..), D.ParseException(..)+ , parse, D.parseFile+ -- ** render+ , D.PrettyConfig(..)+ , D.prettyFile, pretty++ -- * Node+ , Node_, Node, MutableNode+ , NodeKind(..)+ , HasName, HasValue, HasAttribute, HasChildren++ -- ** getter+ , M, NodeLike(..)++ -- ** setter+ , Modify+ , create, modify+ , MutableNodeLike(..)+ , appendAttrs+ , setOrAppendAttr+ -- *** specified append/prepend child+ , appendElement, prependElement+ , appendDeclaration, prependDeclaration+ , appendPCData, prependPCData+ , appendCData, prependCData+ , appendComment, prependComment+ , appendDoctype, prependDoctype+ , appendPi, prependPi++ -- * XPath+ , XPath+ , X.EvalXPath+ , X.xpath+ -- ** NodeSet+ , NodeSet, XPathNode, Attribute+ , X.nodeSetSize+ , nodeSetIndex+ , X.nodeSetMapM, X.nodeSetMapM_, nodeSetMap, nodeSetToList++ -- * reexport+ , module Text.XML.Pugi.Const+ , def+ ) where++import Control.Applicative+import Control.Monad++import Foreign.C.Types++import Text.XML.Pugi.Const+import Text.XML.Pugi.Foreign.Types+import qualified Text.XML.Pugi.Foreign.Document as D+import qualified Text.XML.Pugi.Foreign.Node as N+import qualified Text.XML.Pugi.Foreign.XPath as X+import qualified Text.XML.Pugi.Foreign.XPath.Node as X++import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Char8 as L8+import Data.Default.Class++import System.IO.Unsafe+import Unsafe.Coerce++parse :: D.ParseConfig -> S.ByteString+ -> Either D.ParseException Document+parse cfg str = unsafeDupablePerformIO $ D.parse cfg str++pretty :: D.PrettyConfig -> Document -> L.ByteString+pretty cfg doc = unsafeDupablePerformIO $ D.pretty cfg doc++instance Show Node where+ show = ("Node " ++) . L8.unpack . prettyNode def {D.prettyFlags = formatRaw} 0++instance Show Document where+ show = ("Document " ++) . L8.unpack . prettyNode def {D.prettyFlags = formatRaw} 0++-- |+-- @+-- M Immutable a = a+-- M Mutable a = 'Modify' a+-- @+--+type family M (m :: MutableFlag) a+type instance M Immutable a = a+type instance M Mutable a = Modify a++-- |+-- @+-- instance NodeLike 'Document_' Immutable+-- instance NodeLike 'Node_' Immutable+-- instance NodeLike 'Document_' Mutable+-- instance NodeLike 'Node_' Mutable+-- @+--+class NodeLike n m where+ asNode :: n k m -> M m (Node_ k m)+ forgetNodeKind :: n k m -> n Unknown m+ forgetNodeKind = unsafeCoerce+ {-# INLINE forgetNodeKind #-}+ prettyNode :: D.PrettyConfig -> Int -> n k m -> M m L.ByteString+ hashValue :: n k m -> M m CSize+ nodeType :: n k m -> M m NodeType+ getName :: HasName k => n k m -> M m S.ByteString+ getValue :: HasValue k => n k m -> M m S.ByteString+ parent :: n k m -> M m (Maybe (Node_ Unknown m))+ firstChild :: HasChildren k => n k m -> M m (Maybe (Node_ Unknown m))+ lastChild :: HasChildren k => n k m -> M m (Maybe (Node_ Unknown m))+ nextSibling :: n k m -> M m (Maybe (Node_ Unknown m))+ prevSibling :: n k m -> M m (Maybe (Node_ Unknown m))+ child :: HasChildren k => S.ByteString -> n k m -> M m (Maybe (Node_ Unknown m))+ attribute :: HasAttribute k => S.ByteString -> n k m -> M m (Maybe S.ByteString)+ nextSiblingByName :: S.ByteString -> n k m -> M m (Maybe (Node_ Unknown m))+ prevSiblingByName :: S.ByteString -> n k m -> M m (Maybe (Node_ Unknown m))+ findChildByNameAndAttr :: HasChildren k+ => S.ByteString -- ^ node name+ -> S.ByteString -- ^ attribute name+ -> S.ByteString -- ^ attribute value+ -> n k m -> M m (Maybe (Node_ Unknown m))+ findChildByAttr :: HasChildren k+ => S.ByteString -- ^ attribute name+ -> S.ByteString -- ^ attribute value+ -> n k m -> M m (Maybe (Node_ Unknown m))+ childValue :: HasChildren k => n k m -> M m S.ByteString+ childValueByName :: HasChildren k => S.ByteString -> n k m -> M m S.ByteString+ text :: n k m -> M m S.ByteString+ mapSibling :: (Node_ Unknown m -> a) -> n k m -> M m [a]+ mapAttrs :: HasAttribute k => (S.ByteString -> S.ByteString -> a) -> n k m -> M m [a]+ path :: Char -> n k m -> M m S.ByteString+ firstElementByPath :: Char -> S.ByteString -> n k m -> M m (Maybe (Node_ Unknown m))+ root :: n k m -> M m (Maybe (Node_ Unknown m))+ evaluate :: X.EvalXPath r => XPath r -> n k m -> M m r+ selectSingleNode :: XPath (NodeSet m) -> n k m -> M m (XPathNode m)+ selectNodes :: XPath (NodeSet m) -> n k m -> M m (NodeSet m)++instance NodeLike Document_ Immutable where+ asNode = unsafeDupablePerformIO . N.asNode+ prettyNode cfg dph = unsafeDupablePerformIO . N.prettyNode cfg dph+ hashValue = unsafeDupablePerformIO . N.hashValue+ nodeType = unsafeDupablePerformIO . N.nodeType+ getName = unsafeDupablePerformIO . N.getName+ getValue = unsafeDupablePerformIO . N.getValue+ parent = unsafeDupablePerformIO . N.parent+ firstChild = unsafeDupablePerformIO . N.firstChild+ lastChild = unsafeDupablePerformIO . N.lastChild+ nextSibling = unsafeDupablePerformIO . N.nextSibling+ prevSibling = unsafeDupablePerformIO . N.prevSibling+ child n = unsafeDupablePerformIO . N.child n+ attribute n = unsafeDupablePerformIO . N.attribute n+ nextSiblingByName n = unsafeDupablePerformIO . N.nextSiblingByName n+ prevSiblingByName n = unsafeDupablePerformIO . N.prevSiblingByName n+ findChildByNameAndAttr nn an av =+ unsafeDupablePerformIO . N.findChildByNameAndAttr nn an av+ findChildByAttr an av = unsafeDupablePerformIO . N.findChildByAttr an av+ childValue = unsafeDupablePerformIO . N.childValue+ childValueByName n = unsafeDupablePerformIO . N.childValueByName n+ text = unsafeDupablePerformIO . N.text+ mapSibling f = unsafeDupablePerformIO . N.mapSiblingM (return . f)+ mapAttrs f = unsafeDupablePerformIO . N.mapAttrsM (\k v -> return $ f k v)+ path c = unsafeDupablePerformIO . N.path c+ firstElementByPath c p = unsafeDupablePerformIO . N.firstElementByPath c p+ root = unsafeDupablePerformIO . N.root+ evaluate x = unsafeDupablePerformIO . X.evaluateXPath x+ selectSingleNode x = unsafeDupablePerformIO . N.selectSingleNode x+ selectNodes x = unsafeDupablePerformIO . N.selectNodes x++instance NodeLike Node_ Immutable where+ asNode = unsafeDupablePerformIO . N.asNode+ prettyNode cfg dph = unsafeDupablePerformIO . N.prettyNode cfg dph+ hashValue = unsafeDupablePerformIO . N.hashValue+ nodeType = unsafeDupablePerformIO . N.nodeType+ getName = unsafeDupablePerformIO . N.getName+ getValue = unsafeDupablePerformIO . N.getValue+ parent = unsafeDupablePerformIO . N.parent+ firstChild = unsafeDupablePerformIO . N.firstChild+ lastChild = unsafeDupablePerformIO . N.lastChild+ nextSibling = unsafeDupablePerformIO . N.nextSibling+ prevSibling = unsafeDupablePerformIO . N.prevSibling+ child n = unsafeDupablePerformIO . N.child n+ attribute n = unsafeDupablePerformIO . N.attribute n+ nextSiblingByName n = unsafeDupablePerformIO . N.nextSiblingByName n+ prevSiblingByName n = unsafeDupablePerformIO . N.prevSiblingByName n+ findChildByNameAndAttr nn an av =+ unsafeDupablePerformIO . N.findChildByNameAndAttr nn an av+ findChildByAttr an av = unsafeDupablePerformIO . N.findChildByAttr an av+ childValue = unsafeDupablePerformIO . N.childValue+ childValueByName n = unsafeDupablePerformIO . N.childValueByName n+ text = unsafeDupablePerformIO . N.text+ mapSibling f = unsafeDupablePerformIO . N.mapSiblingM (return . f)+ mapAttrs f = unsafeDupablePerformIO . N.mapAttrsM (\k v -> return $ f k v)+ path c = unsafeDupablePerformIO . N.path c+ firstElementByPath c p = unsafeDupablePerformIO . N.firstElementByPath c p+ root = unsafeDupablePerformIO . N.root+ evaluate x = unsafeDupablePerformIO . X.evaluateXPath x+ selectSingleNode x = unsafeDupablePerformIO . N.selectSingleNode x+ selectNodes x = unsafeDupablePerformIO . N.selectNodes x++instance NodeLike Document_ Mutable where+ asNode = Modify . fmap Right . N.asNode+ prettyNode cfg dph = Modify . fmap Right . N.prettyNode cfg dph+ hashValue = Modify . fmap Right . N.hashValue+ nodeType = Modify . fmap Right . N.nodeType+ getName = Modify . fmap Right . N.getName+ getValue = Modify . fmap Right . N.getValue+ parent = Modify . fmap Right . N.parent+ firstChild = Modify . fmap Right . N.firstChild+ lastChild = Modify . fmap Right . N.lastChild+ nextSibling = Modify . fmap Right . N.nextSibling+ prevSibling = Modify . fmap Right . N.prevSibling+ child n = Modify . fmap Right . N.child n+ attribute n = Modify . fmap Right . N.attribute n+ nextSiblingByName n = Modify . fmap Right . N.nextSiblingByName n+ prevSiblingByName n = Modify . fmap Right . N.prevSiblingByName n+ findChildByNameAndAttr nn an av =+ Modify . fmap Right . N.findChildByNameAndAttr nn an av+ findChildByAttr an av = Modify . fmap Right . N.findChildByAttr an av+ childValue = Modify . fmap Right . N.childValue+ childValueByName n = Modify . fmap Right . N.childValueByName n+ text = Modify . fmap Right . N.text+ mapSibling f = Modify . fmap Right . N.mapSiblingM (return . f)+ mapAttrs f = Modify . fmap Right . N.mapAttrsM (\k v -> return $ f k v)+ path c = Modify . fmap Right . N.path c+ firstElementByPath c p = Modify . fmap Right . N.firstElementByPath c p+ root = Modify . fmap Right . N.root+ evaluate x = Modify . fmap Right . X.evaluateXPath x+ selectSingleNode x = Modify . fmap Right . N.selectSingleNode x+ selectNodes x = Modify . fmap Right . N.selectNodes x++instance NodeLike Node_ Mutable where+ asNode = Modify . fmap Right . N.asNode+ prettyNode cfg dph = Modify . fmap Right . N.prettyNode cfg dph+ hashValue = Modify . fmap Right . N.hashValue+ nodeType = Modify . fmap Right . N.nodeType+ getName = Modify . fmap Right . N.getName+ getValue = Modify . fmap Right . N.getValue+ parent = Modify . fmap Right . N.parent+ firstChild = Modify . fmap Right . N.firstChild+ lastChild = Modify . fmap Right . N.lastChild+ nextSibling = Modify . fmap Right . N.nextSibling+ prevSibling = Modify . fmap Right . N.prevSibling+ child n = Modify . fmap Right . N.child n+ attribute n = Modify . fmap Right . N.attribute n+ nextSiblingByName n = Modify . fmap Right . N.nextSiblingByName n+ prevSiblingByName n = Modify . fmap Right . N.prevSiblingByName n+ findChildByNameAndAttr nn an av =+ Modify . fmap Right . N.findChildByNameAndAttr nn an av+ findChildByAttr an av = Modify . fmap Right . N.findChildByAttr an av+ childValue = Modify . fmap Right . N.childValue+ childValueByName n = Modify . fmap Right . N.childValueByName n+ text = Modify . fmap Right . N.text+ mapSibling f = Modify . fmap Right . N.mapSiblingM (return . f)+ mapAttrs f = Modify . fmap Right . N.mapAttrsM (\k v -> return $ f k v)+ path c = Modify . fmap Right . N.path c+ firstElementByPath c p = Modify . fmap Right . N.firstElementByPath c p+ root = Modify . fmap Right . N.root+ evaluate x = Modify . fmap Right . X.evaluateXPath x+ selectSingleNode x = Modify . fmap Right . N.selectSingleNode x+ selectNodes x = Modify . fmap Right . N.selectNodes x++newtype Modify a = Modify { runModify :: IO (Either String a) }+ deriving Functor++instance Applicative Modify where+ pure = Modify . return . Right+ mf <*> ma = Modify $ runModify mf >>= \case+ Left e -> return (Left e)+ Right f -> runModify ma >>= \case+ Left e -> return (Left e)+ Right a -> return (Right (f a))++instance Monad Modify where+ return = pure+ ma >>= g = Modify $ runModify ma >>= \case+ Left e -> return (Left e)+ Right a -> runModify $ g a+ fail = Modify . return . Left++instance Alternative Modify where+ empty = Modify . return $ Left "empty"+ ma <|> mb = Modify $ runModify ma >>= \case+ Left _ -> runModify mb+ Right a -> return $ Right a++instance MonadPlus Modify where+ mzero = empty+ mplus = (<|>)++mLiftIO :: IO a -> Modify a+mLiftIO io = Modify $ Right <$> io++-- | create document from scratch.+create :: Monad m => (MutableDocument -> Modify ()) -> m Document+create m = either fail (return . D.freezeDocument) . unsafeDupablePerformIO . runModify $ do+ d <- mLiftIO D.createDocument+ m d+ return d++-- | modify document.+modify :: Monad m => Document -> (MutableDocument -> Modify ()) -> m Document+modify prt m = either fail (return . D.freezeDocument) . unsafePerformIO . runModify $ do+ d <- mLiftIO $ D.copyDocument prt+ m d+ return d++appendElement :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode Element)+appendElement n e = appendChild nodeTypeElement e >>= \r -> setName n r >> return r++prependElement :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode Element)+prependElement n e = prependChild nodeTypeElement e >>= \r -> setName n r >> return r++appendDeclaration :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode Declaration)+appendDeclaration n e = appendChild nodeTypeDeclaration e >>= \r -> setName n r >> return r++prependDeclaration :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode Declaration)+prependDeclaration n e = prependChild nodeTypeDeclaration e >>= \r -> setName n r >> return r++appendPCData :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode PCData)+appendPCData n e = appendChild nodeTypePCData e >>= \r -> setValue n r >> return r++prependPCData :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode PCData)+prependPCData n e = prependChild nodeTypePCData e >>= \r -> setValue n r >> return r++appendCData :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode CData)+appendCData n e = appendChild nodeTypeCData e >>= \r -> setValue n r >> return r++prependCData :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode CData)+prependCData n e = prependChild nodeTypeCData e >>= \r -> setValue n r >> return r++appendComment :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode Comment)+appendComment n e = appendChild nodeTypeComment e >>= \r -> setValue n r >> return r++prependComment :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode Comment)+prependComment n e = prependChild nodeTypeComment e >>= \r -> setValue n r >> return r++appendDoctype :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode Doctype)+appendDoctype n e = appendChild nodeTypeDoctype e >>= \r -> setValue n r >> return r++prependDoctype :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> n k Mutable -> Modify (MutableNode Doctype)+prependDoctype n e = prependChild nodeTypeDoctype e >>= \r -> setValue n r >> return r++appendPi :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> S.ByteString -> n k Mutable -> Modify (MutableNode Pi)+appendPi n v e = appendChild nodeTypePi e >>= \r -> setName n r >> setValue v r >> return r++prependPi :: (HasChildren k, MutableNodeLike n)+ => S.ByteString -> S.ByteString -> n k Mutable -> Modify (MutableNode Pi)+prependPi n v e = prependChild nodeTypePi e >>= \r -> setName n r >> setValue v r >> return r++class HasName (k :: NodeKind)+instance HasName Element+instance HasName Declaration+instance HasName Pi+instance HasName Unknown++class HasValue (k :: NodeKind)+instance HasValue PCData+instance HasValue CData+instance HasValue Comment+instance HasValue Doctype+instance HasValue Pi+instance HasValue Unknown++class HasAttribute (k :: NodeKind)+instance HasAttribute Element+instance HasAttribute Declaration+instance HasAttribute Unknown++class HasChildren (k :: NodeKind)+instance HasChildren Element+instance HasChildren Unknown++class MutableNodeLike (n :: NodeKind -> MutableFlag -> *) where+ setName :: HasName k => S.ByteString -> n k Mutable -> Modify ()+ setValue :: HasValue k => S.ByteString -> n k Mutable -> Modify ()+ appendAttr :: HasAttribute k => S.ByteString -> S.ByteString -> n k Mutable -> Modify ()+ prependAttr :: HasAttribute k => S.ByteString -> S.ByteString -> n k Mutable -> Modify ()+ setAttr :: HasAttribute k => S.ByteString -> S.ByteString -> n k Mutable -> Modify ()+ -- | generic appendChild method. Recommend to use 'appendElement' etc...+ appendChild :: HasChildren k => NodeType -> n k Mutable -> Modify (MutableNode l)+ -- | generic prependChild method. Recommend to use 'prependElement' etc...+ prependChild :: HasChildren k => NodeType -> n k Mutable -> Modify (MutableNode l)+ appendCopy :: HasChildren k => Node_ k a -> n l Mutable -> Modify (MutableNode k)+ prependCopy :: HasChildren k => Node_ k a -> n l Mutable -> Modify (MutableNode k)+ removeAttr :: HasAttribute k => S.ByteString -> n k Mutable -> Modify ()+ removeChild :: HasChildren k => Node_ k a -> n l Mutable -> Modify ()+ appendFlagment :: HasChildren k => D.ParseConfig -> S.ByteString -> n k Mutable -> Modify ()++appendAttrs :: (MutableNodeLike n, HasAttribute k) => [Attribute] -> n k Mutable -> Modify ()+appendAttrs as n = mapM_ (\(k,v) -> appendAttr k v n) as++instance MutableNodeLike Node_ where+ setName = isetName+ setValue = isetValue+ appendAttr = iappendAttr+ prependAttr = iprependAttr+ setAttr = isetAttr+ appendChild = iappendChild+ prependChild = iprependChild+ appendCopy = iappendCopy+ prependCopy = iprependCopy+ removeAttr = iremoveAttr+ removeChild = iremoveChild+ appendFlagment = iappendFlagment++instance MutableNodeLike Document_ where+ setName = isetName+ setValue = isetValue+ appendAttr = iappendAttr+ prependAttr = iprependAttr+ setAttr = isetAttr+ appendChild = iappendChild+ prependChild = iprependChild+ appendCopy = iappendCopy+ prependCopy = iprependCopy+ removeAttr = iremoveAttr+ removeChild = iremoveChild+ appendFlagment = iappendFlagment++setOrAppendAttr :: (HasAttribute k, MutableNodeLike n)+ => S.ByteString -> S.ByteString -> n k Mutable -> Modify ()+setOrAppendAttr k v n = setAttr k v n <|> appendAttr k v n++isetName :: N.NodeLike n => S.ByteString -> n k Mutable -> Modify ()+isetName n nd = mLiftIO (N.setName n nd) >>= + flip unless (fail $ "setName: " ++ show n)++isetValue :: N.NodeLike n => S.ByteString -> n k Mutable -> Modify ()+isetValue n nd = mLiftIO (N.setValue n nd) >>=+ flip unless (fail $ "setValue: " ++ show n)++iappendAttr :: N.NodeLike n => S.ByteString -> S.ByteString+ -> n k Mutable -> Modify ()+iappendAttr k v n = mLiftIO (N.appendAttr k v n) >>=+ flip unless (fail $ "appendAttr: " ++ show k ++ " = " ++ show v)++iprependAttr :: N.NodeLike n => S.ByteString -> S.ByteString+ -> n k Mutable -> Modify ()+iprependAttr k v n = mLiftIO (N.prependAttr k v n) >>=+ flip unless (fail $ "appendAttr: " ++ show k ++ " = " ++ show v)++isetAttr :: N.NodeLike n => S.ByteString -> S.ByteString+ -> n k Mutable -> Modify ()+isetAttr k v n = mLiftIO (N.setAttr k v n) >>=+ flip unless (fail $ "setAttr: " ++ show k ++ " = " ++ show v)++iappendChild :: N.NodeLike n => NodeType -> n l Mutable -> Modify (MutableNode k)+iappendChild t n = mLiftIO (N.appendChild t n) >>=+ maybe (fail $ "appendChild: " ++ show t) return++iprependChild :: N.NodeLike n => NodeType -> n l Mutable -> Modify (MutableNode k)+iprependChild t n = mLiftIO (N.prependChild t n) >>=+ maybe (fail $ "prependChild: " ++ show t) return++iappendCopy :: N.NodeLike n => Node_ k a -> n l Mutable -> Modify (MutableNode k)+iappendCopy t n = mLiftIO (N.appendCopy t n) >>=+ maybe (fail "appendCopy") return++iprependCopy :: N.NodeLike n => Node_ k a -> n l Mutable -> Modify (MutableNode k)+iprependCopy t n = mLiftIO (N.prependCopy t n) >>=+ maybe (fail "prependCopy") return++iremoveAttr :: N.NodeLike n => S.ByteString -> n k Mutable -> Modify ()+iremoveAttr n nd = mLiftIO (N.removeAttr n nd) >>=+ flip unless (fail $ "removeAttr: " ++ show n)++iremoveChild :: N.NodeLike n => Node_ l a -> n k Mutable -> Modify ()+iremoveChild n nd = mLiftIO (N.removeChild n nd) >>=+ flip unless (fail "removeChild")++iappendFlagment :: N.NodeLike n => D.ParseConfig -> S.ByteString -> n k Mutable -> Modify ()+iappendFlagment cfg str n = mLiftIO (N.appendBuffer cfg str n) >>=+ flip unless (fail $ "appendFlagment: " ++ show str)++nodeSetIndex :: NodeSet m -> Int -> XPathNode m+nodeSetIndex n = unsafeDupablePerformIO . X.nodeSetIndex n++nodeSetMap :: (XPathNode m -> a) -> NodeSet m -> [a]+nodeSetMap f = unsafeDupablePerformIO . X.nodeSetMapM (return . f)++nodeSetToList :: NodeSet m -> [XPathNode m]+nodeSetToList = nodeSetMap id
+ Text/XML/Pugi/Const.hs view
@@ -0,0 +1,33 @@+module Text.XML.Pugi.Const+ ( -- * NodeType+ NodeType+ , nodeTypeNull, nodeTypeDocument, nodeTypeElement, nodeTypePCData, nodeTypeCData+ , nodeTypeComment, nodeTypePi, nodeTypeDeclaration, nodeTypeDoctype++ -- * ParseStatus+ , ParseStatus+ , parseFileNotFound, parseIoError, parseOutOfMemory, parseInternalError+ , parseUnrecognizedTag, parseBadPi, parseBadComment, parseBadCData+ , parseBadDoctype, parseBadPCData, parseBadStartElement, parseBadAttribute+ , parseBadEndElement, parseEndElementMismatch, parseAppendInvalidRoot+ , parseNoDocumentElement++ -- * Encoding+ , Encoding+ , encodingAuto, encodingUtf8, encodingUtf16be, encodingUtf16le, encodingUtf16+ , encodingUtf32be, encodingUtf32le, encodingUtf32, encodingWChar, encodingLatin1++ -- * FormatFlags+ , FormatFlags+ , formatDefault, formatIndent, formatNoDeclaration, formatNoEscapes+ , formatRaw, formatSaveFileText, formatWriteBom++ -- * ParseFlags+ , ParseFlags+ , parseCData, parseComments, parseDeclaration, parseDefault, parseDoctype+ , parseEol, parseEscapes, parseFragment, parseFull, parseMinimal, parsePi+ , parseTrimPCData, parseWsPCData, parseWsPCDataSingle, parseWconvAttribute+ , parseWnormAttribute+ ) where++import Text.XML.Pugi.Foreign.Const
+ Text/XML/Pugi/Foreign/Attr.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Text.XML.Pugi.Foreign.Attr where++import Control.Monad++import Foreign.C+import Foreign.Ptr++import qualified Data.ByteString as S++import Text.XML.Pugi.Foreign.Types++-- attr+foreign import ccall unsafe delete_attr :: Ptr Attr -> IO ()+foreign import ccall unsafe attr_name :: Ptr Attr -> IO CString+foreign import ccall unsafe attr_value :: Ptr Attr -> IO CString+foreign import ccall unsafe attr_set_value :: Ptr Attr -> CString -> IO CInt++attrName, attrValue :: Ptr Attr -> IO S.ByteString+attrName = attr_name >=> S.packCString+attrValue = attr_value >=> S.packCString
+ Text/XML/Pugi/Foreign/Const.hs view
@@ -0,0 +1,189 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Text.XML.Pugi.Foreign.Const where++import Foreign.C++import Data.Default.Class+import Data.Bits+import Data.List+import Data.Monoid++foreign import ccall unsafe pugixml_version :: CInt++newtype NodeType = NodeType CInt deriving Eq+foreign import ccall unsafe "enum_node_type_null" nodeTypeNull :: NodeType+foreign import ccall unsafe "enum_node_type_document" nodeTypeDocument :: NodeType+foreign import ccall unsafe "enum_node_type_element" nodeTypeElement :: NodeType+foreign import ccall unsafe "enum_node_type_pcdata" nodeTypePCData :: NodeType+foreign import ccall unsafe "enum_node_type_cdata" nodeTypeCData :: NodeType+foreign import ccall unsafe "enum_node_type_comment" nodeTypeComment :: NodeType+foreign import ccall unsafe "enum_node_type_pi" nodeTypePi :: NodeType+foreign import ccall unsafe "enum_node_type_declaration" nodeTypeDeclaration :: NodeType+foreign import ccall unsafe "enum_node_type_doctype" nodeTypeDoctype :: NodeType++instance Show NodeType where+ show n+ | n == nodeTypeNull = "nodeTypeNull"+ | n == nodeTypeDocument = "nodeTypeDocument"+ | n == nodeTypeElement = "nodeTypeElement"+ | n == nodeTypePCData = "nodeTypePCData"+ | n == nodeTypeCData = "nodeTypeCData"+ | n == nodeTypeComment = "nodeTypeComment"+ | n == nodeTypePi = "nodeTypePi"+ | n == nodeTypeDeclaration = "nodeTypeDeclaration"+ | n == nodeTypeDoctype = "nodeTypeDoctype"+ | otherwise = let NodeType i = n in "NodeType " ++ show i++newtype ParseStatus = ParseStatus CInt deriving Eq+foreign import ccall unsafe "enum_parse_status_ok" parseOk :: ParseStatus+foreign import ccall unsafe "enum_parse_status_file_not_found" parseFileNotFound :: ParseStatus+foreign import ccall unsafe "enum_parse_status_io_error" parseIoError :: ParseStatus+foreign import ccall unsafe "enum_parse_status_out_of_memory" parseOutOfMemory :: ParseStatus+foreign import ccall unsafe "enum_parse_status_internal_error" parseInternalError :: ParseStatus+foreign import ccall unsafe "enum_parse_status_unrecognized_tag" parseUnrecognizedTag :: ParseStatus+foreign import ccall unsafe "enum_parse_status_bad_pi" parseBadPi :: ParseStatus+foreign import ccall unsafe "enum_parse_status_bad_comment" parseBadComment :: ParseStatus+foreign import ccall unsafe "enum_parse_status_bad_cdata" parseBadCData :: ParseStatus+foreign import ccall unsafe "enum_parse_status_bad_doctype" parseBadDoctype :: ParseStatus+foreign import ccall unsafe "enum_parse_status_bad_pcdata" parseBadPCData :: ParseStatus+foreign import ccall unsafe "enum_parse_status_bad_start_element" parseBadStartElement :: ParseStatus+foreign import ccall unsafe "enum_parse_status_bad_attribute" parseBadAttribute :: ParseStatus+foreign import ccall unsafe "enum_parse_status_bad_end_element" parseBadEndElement :: ParseStatus+foreign import ccall unsafe "enum_parse_status_end_element_mismatch" parseEndElementMismatch :: ParseStatus+foreign import ccall unsafe "enum_parse_status_append_invalid_root" parseAppendInvalidRoot :: ParseStatus+foreign import ccall unsafe "enum_parse_status_no_document_element" parseNoDocumentElement :: ParseStatus++instance Show ParseStatus where+ show n+ | n == parseOk = "parseOk"+ | n == parseFileNotFound = "parseFileNotFound"+ | n == parseIoError = "parseIoError"+ | n == parseOutOfMemory = "parseOutOfMemory"+ | n == parseInternalError = "parseInternalError"+ | n == parseUnrecognizedTag = "parseUnrecognizedTag"+ | n == parseBadPi = "parseBadPi"+ | n == parseBadComment = "parseBadComment"+ | n == parseBadCData = "parseBadCData"+ | n == parseBadDoctype = "parseBadDoctype"+ | n == parseBadPCData = "parseBadPCData"+ | n == parseBadStartElement = "parseBadStartElement"+ | n == parseBadAttribute = "parseBadAttribute"+ | n == parseBadEndElement = "parseBadEndElement"+ | n == parseEndElementMismatch = "parseEndElementMismatch"+ | n == parseAppendInvalidRoot = "parseAppendInvalidRoot"+ | n == parseNoDocumentElement = "parseNoDocumentElement"+ | otherwise = let ParseStatus i = n in "ParseStatus " ++ show i++newtype Encoding = Encoding CInt deriving Eq+foreign import ccall unsafe "enum_encoding_auto" encodingAuto :: Encoding+foreign import ccall unsafe "enum_encoding_utf8" encodingUtf8 :: Encoding+foreign import ccall unsafe "enum_encoding_utf16_be" encodingUtf16be :: Encoding+foreign import ccall unsafe "enum_encoding_utf16_le" encodingUtf16le :: Encoding+foreign import ccall unsafe "enum_encoding_utf16" encodingUtf16 :: Encoding+foreign import ccall unsafe "enum_encoding_utf32_be" encodingUtf32be :: Encoding+foreign import ccall unsafe "enum_encoding_utf32_le" encodingUtf32le :: Encoding+foreign import ccall unsafe "enum_encoding_utf32" encodingUtf32 :: Encoding+foreign import ccall unsafe "enum_encoding_wchar" encodingWChar :: Encoding+foreign import ccall unsafe "enum_encoding_latin1" encodingLatin1 :: Encoding++instance Show Encoding where+ show n+ | n == encodingAuto = "encodingAuto"+ | n == encodingUtf8 = "encodingUtf8"+ | n == encodingUtf16be = "encodingUtf16be"+ | n == encodingUtf16le = "encodingUtf16le"+ | n == encodingUtf16 = "encodingUtf16"+ | n == encodingUtf32be = "encodingUtf32be"+ | n == encodingUtf32le = "encodingUtf32le"+ | n == encodingUtf32 = "encodingUtf32"+ | n == encodingWChar = "encodingWChar"+ | n == encodingLatin1 = "encodingLatin1"+ | otherwise = let Encoding i = n in "Encoding " ++ show i++instance Default Encoding where+ def = encodingAuto++newtype XPathType = XPathType CInt deriving (Show, Eq)+foreign import ccall unsafe "enum_xpath_value_type_none" xPathTypeNode :: XPathType+foreign import ccall unsafe "enum_xpath_value_type_node_set" xPathTypeNodeSet :: XPathType+foreign import ccall unsafe "enum_xpath_value_type_number" xPathTypeNumber :: XPathType+foreign import ccall unsafe "enum_xpath_value_type_string" xPathString :: XPathType+foreign import ccall unsafe "enum_xpath_value_type_boolean" xPathBoolean :: XPathType++newtype FormatFlags = FormatFlags CUInt deriving (Eq, Bits)+foreign import ccall unsafe "format_flag_default" formatDefault :: FormatFlags+foreign import ccall unsafe "format_flag_indent" formatIndent :: FormatFlags+foreign import ccall unsafe "format_flag_no_declaration" formatNoDeclaration :: FormatFlags+foreign import ccall unsafe "format_flag_no_escapes" formatNoEscapes :: FormatFlags+foreign import ccall unsafe "format_flag_raw" formatRaw :: FormatFlags+foreign import ccall unsafe "format_flag_save_file_text" formatSaveFileText :: FormatFlags+foreign import ccall unsafe "format_flag_write_bom" formatWriteBom :: FormatFlags++formatFlags :: [(String, CUInt)]+formatFlags = [ ("Indent", un formatIndent)+ , ("NoDeclaration", un formatNoDeclaration)+ , ("NoEscapes", un formatNoEscapes)+ , ("Raw", un formatRaw)+ , ("SaveFileText", un formatSaveFileText)+ , ("WriteBom", un formatWriteBom)+ ]+ where un (FormatFlags i) = i++instance Show FormatFlags where+ show (FormatFlags fs) = + let fn = foldl' (\f (n, i) -> if i .&. fs /= 0 then (f . (n:)) else f) id formatFlags+ in "FormatFlags(" ++ intercalate "|" (fn []) ++ ")"++instance Default FormatFlags where+ def = formatDefault++instance Monoid FormatFlags where+ mempty = FormatFlags 0+ mappend = (.|.)++newtype ParseFlags = ParseFlags CUInt deriving (Eq, Bits)+foreign import ccall unsafe "parse_flag_cdata" parseCData :: ParseFlags +foreign import ccall unsafe "parse_flag_comments" parseComments :: ParseFlags +foreign import ccall unsafe "parse_flag_declaration" parseDeclaration :: ParseFlags +foreign import ccall unsafe "parse_flag_default" parseDefault :: ParseFlags +foreign import ccall unsafe "parse_flag_doctype" parseDoctype :: ParseFlags +foreign import ccall unsafe "parse_flag_eol" parseEol :: ParseFlags +foreign import ccall unsafe "parse_flag_escapes" parseEscapes :: ParseFlags +foreign import ccall unsafe "parse_flag_fragment" parseFragment :: ParseFlags +foreign import ccall unsafe "parse_flag_full" parseFull :: ParseFlags +foreign import ccall unsafe "parse_flag_minimal" parseMinimal :: ParseFlags +foreign import ccall unsafe "parse_flag_pi" parsePi :: ParseFlags +foreign import ccall unsafe "parse_flag_trim_pcdata" parseTrimPCData :: ParseFlags+foreign import ccall unsafe "parse_flag_ws_pcdata" parseWsPCData :: ParseFlags+foreign import ccall unsafe "parse_flag_ws_pcdata_single" parseWsPCDataSingle :: ParseFlags+foreign import ccall unsafe "parse_flag_wconv_attribute" parseWconvAttribute :: ParseFlags+foreign import ccall unsafe "parse_flag_wnorm_attribute" parseWnormAttribute :: ParseFlags++parseFlags :: [(String, CUInt)]+parseFlags = [ ("CData" , un parseCData)+ , ("Comments" , un parseComments)+ , ("Declaration" , un parseDeclaration)+ , ("Doctype" , un parseDoctype)+ , ("Eol" , un parseEol)+ , ("Escapes" , un parseEscapes)+ , ("Fragment" , un parseFragment)+ , ("Pi" , un parsePi)+ , ("TrimPCData" , un parseTrimPCData)+ , ("WsPCData" , un parseWsPCData)+ , ("WsPCDataSingle", un parseWsPCDataSingle)+ , ("WconvAttribute", un parseWconvAttribute)+ , ("WnormAttribute", un parseWnormAttribute)+ ]+ where un (ParseFlags i) = i++instance Show ParseFlags where+ show (ParseFlags fs) = + let fn = foldl' (\f (n, i) -> if i .&. fs /= 0 then (f . (n:)) else f) id parseFlags+ in "ParseFlags(" ++ intercalate "|" (fn []) ++ ")"++instance Default ParseFlags where+ def = parseDefault++instance Monoid ParseFlags where+ mempty = ParseFlags 0+ mappend = (.|.)
+ Text/XML/Pugi/Foreign/Document.hs view
@@ -0,0 +1,124 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Text.XML.Pugi.Foreign.Document where++import Control.Applicative+import Control.Monad+import Control.Exception++import Foreign.C+import Foreign.Ptr+import Foreign.ForeignPtr+import Foreign.Marshal.Utils++import Data.Typeable+import Data.IORef+import Data.Default.Class+import qualified Data.ByteString as S+import qualified Data.ByteString.Unsafe as S+import qualified Data.ByteString.Lazy as L++import Text.XML.Pugi.Foreign.Const+import Text.XML.Pugi.Foreign.Types+import Unsafe.Coerce++-- Document+foreign import ccall unsafe new_document :: IO (Ptr MutableDocument)+foreign import ccall unsafe "&delete_document" finalizerDocument+ :: FinalizerPtr (Document_ k m)+foreign import ccall unsafe reset_document_with :: Ptr MutableDocument -> Ptr (Document_ k m) -> IO ()++freezeDocument :: Document_ k m -> Document+freezeDocument = unsafeCoerce+{-# INLINE freezeDocument #-}++createDocument :: IO MutableDocument+createDocument = fmap Document $ newForeignPtr finalizerDocument =<< new_document++copyDocument :: Document_ k m -> IO MutableDocument+copyDocument (Document f) = withForeignPtr f $ \p -> do+ d <- new_document+ reset_document_with d p+ Document <$> newForeignPtr finalizerDocument d++-- Parsing+foreign import ccall unsafe delete_parse_result :: ParseResult -> IO ()+foreign import ccall unsafe parse_is_success :: ParseResult -> IO CInt+foreign import ccall unsafe parse_result_status :: ParseResult -> IO ParseStatus+foreign import ccall unsafe parse_result_offset :: ParseResult -> IO CLong+foreign import ccall unsafe parse_result_encoding :: ParseResult -> IO Encoding+foreign import ccall unsafe parse_result_description :: ParseResult -> IO CString++foreign import ccall load_buffer :: Ptr MutableDocument -> Ptr a -> CSize -> ParseFlags -> Encoding -> IO ParseResult+foreign import ccall load_file :: Ptr MutableDocument -> CString -> ParseFlags -> Encoding -> IO ParseResult++data ParseConfig = ParseConfig+ { parseFlags :: ParseFlags+ , parseEncoding :: Encoding+ } deriving Show++instance Default ParseConfig where+ def = ParseConfig def def++data ParseException = ParseException+ { parseExceptionStatus :: ParseStatus+ , parseExceptionOffset :: CLong+ , parseExceptionEncoding :: Encoding+ , parseExceptionDescripton :: String+ } deriving (Show, Typeable)++instance Exception ParseException++parseCommon :: (ForeignPtr MutableDocument -> a) -> (ParseException -> IO a)+ -> Ptr MutableDocument -> ParseResult -> IO a+parseCommon con err doc res = do+ ok <- parse_is_success res+ if toBool ok+ then con <$> newForeignPtr finalizerDocument doc+ else err =<< ParseException+ <$> parse_result_status res+ <*> parse_result_offset res+ <*> parse_result_encoding res+ <*> (parse_result_description res >>= peekCString)++parse :: ParseConfig -> S.ByteString -> IO (Either ParseException Document)+parse (ParseConfig flags enc) str = S.unsafeUseAsCStringLen str $ \(p,l) -> new_document >>= \doc ->+ bracket (load_buffer doc p (fromIntegral l) flags enc) delete_parse_result $+ parseCommon (Right . freezeDocument . Document) (return . Left) doc++parseFile :: ParseConfig -> FilePath -> IO Document+parseFile (ParseConfig flags enc) path = withCString path $ \p -> new_document >>= \doc ->+ bracket (load_file doc p flags enc) delete_parse_result $+ parseCommon (freezeDocument . Document) throwIO doc++-- format+foreign import ccall save_file :: Ptr Document -> CString -> CString -> FormatFlags -> Encoding -> IO CInt++type Writer = CString -> CSize -> IO ()+foreign import ccall "wrapper" wrap_writer :: Writer -> IO (FunPtr Writer)+foreign import ccall save_string :: Ptr Document -> FunPtr Writer -> CString -> FormatFlags -> Encoding -> IO ()++data PrettyConfig = PrettyConfig+ { prettyIndent :: String+ , prettyFlags :: FormatFlags+ , prettyEncoding :: Encoding+ } deriving Show++instance Default PrettyConfig where+ def = PrettyConfig "\t" def def++prettyFile :: PrettyConfig -> FilePath -> Document -> IO ()+prettyFile (PrettyConfig indent flags enc) path (Document doc) =+ withForeignPtr doc $ \d ->+ withCString indent $ \i ->+ withCString path $ \p ->+ save_file d p i flags enc >>= \r ->+ unless (toBool r) (ioError $ userError "prettyFile failed.")++pretty :: PrettyConfig -> Document -> IO L.ByteString+pretty (PrettyConfig indent flags enc) (Document doc) = withForeignPtr doc $ \d -> withCString indent $ \i -> do+ ref <- newIORef (id :: [S.ByteString] -> [S.ByteString])+ let fun cs s = S.packCStringLen (cs, fromIntegral s) >>= \n -> modifyIORef ref (\a -> a . (n:))+ bracket (wrap_writer fun) freeHaskellFunPtr $ \fp ->+ save_string d fp i flags enc+ readIORef ref >>= \r -> return $ L.fromChunks (r [])
+ Text/XML/Pugi/Foreign/Node.hs view
@@ -0,0 +1,296 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE ImpredicativeTypes #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleInstances #-}+module Text.XML.Pugi.Foreign.Node where++import Control.Applicative+import Control.Monad+import Control.Exception++import Foreign.C+import Foreign.Ptr+import Foreign.ForeignPtr+import Foreign.Marshal++import Data.IORef+import Data.ByteString.Internal (c2w)+import qualified Data.ByteString as S+import qualified Data.ByteString.Unsafe as S+import qualified Data.ByteString.Lazy as L++import Text.XML.Pugi.Foreign.Document+import Text.XML.Pugi.Foreign.Const+import Text.XML.Pugi.Foreign.Types+import Text.XML.Pugi.Foreign.Attr++-- node+foreign import ccall unsafe "&delete_node" finalizerNode+ :: FinalizerPtr (Node_ k m)++foreign import ccall unsafe document_element :: Ptr (Document_ k m) -> IO (Ptr (Node_ k m))++foreign import ccall unsafe node_hash_value :: Ptr n -> IO CSize++foreign import ccall unsafe node_type :: Ptr n -> IO NodeType++foreign import ccall unsafe node_name :: Ptr n -> IO CString+foreign import ccall unsafe node_value :: Ptr n -> IO CString++foreign import ccall unsafe node_parent :: Ptr n -> IO (Ptr (Node_ Unknown a))+foreign import ccall unsafe node_first_child :: Ptr n -> IO (Ptr (Node_ Unknown a))+foreign import ccall unsafe node_last_child :: Ptr n -> IO (Ptr (Node_ Unknown a))+foreign import ccall unsafe node_next_sibling :: Ptr n -> IO (Ptr (Node_ Unknown a))+foreign import ccall unsafe node_previous_sibling :: Ptr n -> IO (Ptr (Node_ Unknown a))++foreign import ccall unsafe node_child :: Ptr n -> CString -> IO (Ptr (Node_ Unknown a))+foreign import ccall unsafe node_attribute :: Ptr n -> CString -> IO (Ptr Attr)+foreign import ccall unsafe node_next_sibling_by_name :: Ptr n -> CString -> IO (Ptr (Node_ Unknown a))+foreign import ccall unsafe node_previous_sibling_by_name :: Ptr n -> CString -> IO (Ptr (Node_ Unknown a))+foreign import ccall unsafe node_find_child_by_name_and_attribute :: Ptr n -> CString -> CString -> CString -> IO (Ptr (Node_ Unknown a))+foreign import ccall unsafe node_find_child_by_attribute :: Ptr n -> CString -> CString -> IO (Ptr (Node_ Unknown a))++foreign import ccall unsafe node_child_value :: Ptr n -> IO CString+foreign import ccall unsafe node_child_value_by_name :: Ptr n -> CString -> IO CString+foreign import ccall unsafe node_text :: Ptr n -> IO CString++type NodeMapper k m = Ptr (Node_ k m) -> IO ()+foreign import ccall unsafe "wrapper" wrap_node_mapper :: NodeMapper k m -> IO (FunPtr (NodeMapper k m))+foreign import ccall node_map_sibling :: Ptr n -> FunPtr (NodeMapper k m) -> IO ()++type AttrMapper = Ptr Attr -> IO ()+foreign import ccall unsafe "wrapper" wrap_attr_mapper :: AttrMapper -> IO (FunPtr AttrMapper)+foreign import ccall node_map_attributes :: Ptr n -> FunPtr AttrMapper -> IO ()++foreign import ccall unsafe node_path :: Ptr n -> CChar -> IO CString -- must be free++foreign import ccall unsafe node_first_element_by_path :: Ptr n -> CString -> CChar -> IO (Ptr (Node_ Unknown a))++foreign import ccall unsafe node_root :: Ptr n -> IO (Ptr (Node_ Unknown a))++foreign import ccall unsafe set_name :: Ptr n -> CString -> IO CInt+foreign import ccall unsafe set_value :: Ptr n -> CString -> IO CInt++foreign import ccall unsafe append_attribute :: Ptr n -> CString -> CString -> IO CInt+foreign import ccall unsafe prepend_attribute :: Ptr n -> CString -> CString -> IO CInt++foreign import ccall unsafe append_child :: Ptr n -> NodeType -> IO (Ptr (Node_ k a))+foreign import ccall unsafe prepend_child :: Ptr n -> NodeType -> IO (Ptr (Node_ k a))++foreign import ccall unsafe append_copy :: Ptr n -> Ptr (Node_ k a) -> IO (Ptr (Node_ k b))+foreign import ccall unsafe prepend_copy :: Ptr n -> Ptr (Node_ k a) -> IO (Ptr (Node_ k b))++foreign import ccall unsafe remove_attribute :: Ptr n -> CString -> IO CInt+foreign import ccall unsafe remove_child :: Ptr n -> Ptr (Node_ k a) -> IO CInt++foreign import ccall unsafe append_buffer :: Ptr n -> Ptr c -> CSize -> CUInt -> Encoding -> IO ParseResult++foreign import ccall unsafe node_print :: Ptr n -> FunPtr Writer -> CString -> FormatFlags -> Encoding -> CUInt -> IO ()++foreign import ccall unsafe select_single_node :: Ptr n -> Ptr (XPath (NodeSet m)) -> IO (Ptr XNode)+foreign import ccall unsafe select_nodes :: Ptr n -> Ptr (XPath (NodeSet m)) -> IO (Ptr (NodeSet m))++nodeCommon :: NodeLike n => n k m -> (Ptr (n k m) -> IO (Ptr (Node_ l m))) -> IO (Maybe (Node_ l m))+nodeCommon n f = withNode n $ \p -> do+ q <- f p+ if q == nullPtr+ then return Nothing+ else Just . Node <$> newForeignPtr finalizerNode q++class NodeLike (n :: NodeKind -> MutableFlag -> *) where+ withNode :: n k m -> (Ptr (n k m) -> IO a) -> IO a++ asNode :: n k m -> IO (Node_ k m)++ hashValue :: n k m -> IO CSize+ hashValue n = withNode n node_hash_value+ nodeType :: n k m -> IO NodeType+ nodeType n = withNode n node_type++ getName :: n k m -> IO S.ByteString+ getName n = withNode n $ node_name >=> S.packCString+ getValue :: n k m -> IO S.ByteString+ getValue n = withNode n $ node_value >=> S.packCString++ parent :: n k m -> IO (Maybe (Node_ Unknown m))+ parent n = nodeCommon n node_parent+ firstChild :: n k m -> IO (Maybe (Node_ Unknown m))+ firstChild n = nodeCommon n node_first_child+ lastChild :: n k m -> IO (Maybe (Node_ Unknown m))+ lastChild n = nodeCommon n node_last_child+ nextSibling :: n k m -> IO (Maybe (Node_ Unknown m))+ nextSibling n = nodeCommon n node_next_sibling+ prevSibling :: n k m -> IO (Maybe (Node_ Unknown m))+ prevSibling n = nodeCommon n node_previous_sibling++ child :: S.ByteString -> n k m -> IO (Maybe (Node_ Unknown m))+ child nm n = nodeCommon n $ \p -> S.useAsCString nm $ node_child p++ attribute :: S.ByteString -> n k m -> IO (Maybe S.ByteString)+ attribute nm n = withNode n $ \p -> S.useAsCString nm $ \a ->+ bracket (node_attribute p a) delete_attr $ \attr ->+ if attr == nullPtr+ then return Nothing+ else fmap Just $ attr_value attr >>= S.packCString++ nextSiblingByName :: S.ByteString -> n k m -> IO (Maybe (Node_ Unknown m))+ nextSiblingByName nm n = nodeCommon n $ \p -> S.useAsCString nm $ node_next_sibling_by_name p+ prevSiblingByName :: S.ByteString -> n k m -> IO (Maybe (Node_ Unknown m))+ prevSiblingByName nm n = nodeCommon n $ \p -> S.useAsCString nm $ node_previous_sibling_by_name p++ findChildByNameAndAttr :: S.ByteString -- ^ node name+ -> S.ByteString -- ^ attribute name+ -> S.ByteString -- ^ attribute value+ -> n k m -> IO (Maybe (Node_ Unknown m))+ findChildByNameAndAttr nn an av n = nodeCommon n $ \p ->+ S.useAsCString nn $ \nn' -> S.useAsCString an $ \an' -> S.useAsCString av $ \av' ->+ node_find_child_by_name_and_attribute p nn' an' av'++ findChildByAttr :: S.ByteString -- ^ attribute name+ -> S.ByteString -- ^ attribute value+ -> n k m -> IO (Maybe (Node_ Unknown m))+ findChildByAttr an av n = nodeCommon n $ \p ->+ S.useAsCString an $ \an' -> S.useAsCString av $ \av' ->+ node_find_child_by_attribute p an' av'+ + childValue :: n k m -> IO S.ByteString+ childValue n = withNode n $ node_child_value >=> S.packCString++ childValueByName :: S.ByteString -> n k m -> IO S.ByteString+ childValueByName nm n = withNode n $ \p -> S.useAsCString nm $ \s ->+ node_child_value_by_name p s >>= S.packCString++ text :: n k m -> IO S.ByteString+ text n = withNode n $ node_text >=> S.packCString++ mapSiblingM_ :: (Node_ Unknown m -> IO ()) -> n k m -> IO ()+ mapSiblingM_ func n = withNode n $ \p -> do+ let f e = func . Node =<< newForeignPtr_ e+ bracket (wrap_node_mapper f) freeHaskellFunPtr $ node_map_sibling p++ mapAttrsM_ :: (S.ByteString -> S.ByteString -> IO ()) -> n k m -> IO ()+ mapAttrsM_ func n = withNode n $ \p -> do+ let f a = do+ nm <- attrName a+ val <- attrValue a+ func nm val+ bracket (wrap_attr_mapper f) freeHaskellFunPtr $ node_map_attributes p++ path :: Char -> n k m -> IO S.ByteString+ path del node = withNode node $ \p ->+ bracket (node_path p (fromIntegral $ c2w del)) free $ S.packCString++ firstElementByPath :: Char -> S.ByteString -> n k m -> IO (Maybe (Node_ Unknown m))+ firstElementByPath del path_ node = nodeCommon node $ \p ->+ S.useAsCString path_ $ \d -> node_first_element_by_path p d (fromIntegral $ c2w del)++ root :: n k m -> IO (Maybe (Node_ Unknown m))+ root node = nodeCommon node $ node_root++ setName :: S.ByteString -> n k Mutable -> IO Bool+ setName n node = withNode node $ \p -> S.useAsCString n $ \q ->+ toBool <$> set_name p q+ setValue :: S.ByteString -> n k Mutable -> IO Bool+ setValue n node = withNode node $ \p -> S.useAsCString n $ \q ->+ toBool <$> set_value p q++ appendAttr :: S.ByteString -> S.ByteString -> n k Mutable -> IO Bool+ appendAttr k v n = withNode n $ \np -> S.useAsCString k $ \kp -> S.useAsCString v $ \vp ->+ toBool <$> append_attribute np kp vp+ prependAttr :: S.ByteString -> S.ByteString -> n k Mutable -> IO Bool+ prependAttr k v n = withNode n $ \np -> S.useAsCString k $ \kp -> S.useAsCString v $ \vp ->+ toBool <$> prepend_attribute np kp vp++ setAttr :: S.ByteString -> S.ByteString -> n k Mutable -> IO Bool+ setAttr k v n = withNode n $ \np -> S.useAsCString k $ \kp ->+ bracket (node_attribute np kp) delete_attr $ \attr ->+ if attr == nullPtr+ then return False+ else toBool <$> S.useAsCString v (attr_set_value attr)++ appendChild :: NodeType -> n k Mutable -> IO (Maybe (Node_ l Mutable))+ appendChild t n = nodeCommon n $ \p -> append_child p t+ prependChild :: NodeType -> n k Mutable -> IO (Maybe (Node_ l Mutable))+ prependChild t n = nodeCommon n $ \p -> prepend_child p t++ appendCopy :: Node_ k a -> n l Mutable -> IO (Maybe (Node_ k Mutable))+ appendCopy t n = nodeCommon n $ \p -> withNode t (append_copy p)+ prependCopy :: Node_ k a -> n l Mutable -> IO (Maybe (Node_ k Mutable))+ prependCopy t n = nodeCommon n $ \p -> withNode t (prepend_copy p)++ removeAttr :: S.ByteString -> n k Mutable -> IO Bool+ removeAttr a n = withNode n $ \p -> S.useAsCString a $ \c ->+ toBool <$> remove_attribute p c++ removeChild :: Node_ k a -> n l Mutable -> IO Bool+ removeChild c n = withNode n $ \p -> withNode c $ \q ->+ toBool <$> remove_child p q++ appendBuffer :: ParseConfig -> S.ByteString -> n k Mutable -> IO Bool+ appendBuffer (ParseConfig (ParseFlags flags) enc) str node = fmap toBool $+ withNode node $ \n -> S.unsafeUseAsCStringLen str $ \(c,l) ->+ bracket (append_buffer n c (fromIntegral l) flags enc) delete_parse_result+ parse_is_success++ selectSingleNode :: XPath (NodeSet m) -> n k m -> IO (XPathNode m)+ selectSingleNode (XPath xp) nd =+ withNode nd $ \n -> withForeignPtr xp $ \x -> do+ select_single_node n x >>= peekXNode++ selectNodes :: XPath (NodeSet m) -> n k m -> IO (NodeSet m)+ selectNodes (XPath xp) nd = withNode nd $ \n -> withForeignPtr xp $ \x -> do+ p <- select_nodes n x+ l <- fromIntegral <$> xpath_node_set_size p+ NodeSet l <$> newForeignPtr finalizerXpathNodeSet p++ prettyNode :: PrettyConfig -> Int -> n k m -> IO L.ByteString+ prettyNode (PrettyConfig indent flags enc) depth node =+ withNode node $ \n -> withCString indent $ \i -> do+ ref <- newIORef id+ let fun cs s = S.packCStringLen (cs, fromIntegral s) >>= \c -> modifyIORef ref (\a -> a . (c:))+ bracket (wrap_writer fun) freeHaskellFunPtr $ \fp ->+ node_print n fp i flags enc (fromIntegral depth)+ readIORef ref >>= \r -> return $ L.fromChunks (r [])++-- xpath_node+foreign import ccall unsafe delete_xpath_node :: Ptr XNode -> IO ()+foreign import ccall unsafe xpath_node_node :: Ptr XNode -> IO (Ptr (Node_ Unknown m))+foreign import ccall unsafe xpath_node_attribute :: Ptr XNode -> IO (Ptr Attr)++foreign import ccall unsafe "&delete_xpath_node_set" finalizerXpathNodeSet :: FinalizerPtr (NodeSet m)+foreign import ccall unsafe xpath_node_set_size :: Ptr (NodeSet m) -> IO CSize ++peekXNode :: Ptr XNode -> IO (XPathNode m)+peekXNode p = do+ n <- xpath_node_node p+ if n == nullPtr+ then bracket (xpath_node_attribute p) delete_attr $ \a -> do+ an <- attrName a+ av <- attrValue a+ return $ Right (an, av)+ else Left . Node <$> newForeignPtr finalizerNode n+++mapSiblingM :: NodeLike n => (Node_ Unknown m -> IO a) -> n l m -> IO [a]+mapSiblingM func node = do+ ref <- newIORef id+ mapSiblingM_ (\n -> func n >>= \a -> modifyIORef ref (\f -> f . (a:))) node+ readIORef ref >>= \a -> return (a [])++mapAttrsM :: NodeLike n => (S.ByteString -> S.ByteString -> IO a) -> n k m -> IO [a]+mapAttrsM func n = do+ ref <- newIORef id+ mapAttrsM_ (\k v -> func k v >>= \a -> modifyIORef ref (\f -> f . (a:))) n+ readIORef ref >>= \a -> return (a [])++instance NodeLike Document_ where+ withNode (Document f) = withForeignPtr f+ asNode d = maybe (error "asNode return NULL") id <$> nodeCommon d document_element++instance NodeLike Node_ where+ withNode (Node f) = withForeignPtr f+ asNode = return
+ Text/XML/Pugi/Foreign/Types.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE KindSignatures #-}+module Text.XML.Pugi.Foreign.Types where++import Foreign.ForeignPtr+import Foreign.Ptr+import qualified Data.ByteString as S++data MutableFlag = Mutable | Immutable++data NodeKind = Element -- ^ \<name\>children\</name\>+ | PCData -- ^ value+ | CData -- ^ \<![CDATA[value]]\>+ | Comment -- ^ \<!--value--\>+ | Pi -- ^ \<?name value?>+ | Declaration -- ^ \<?name?\>+ | Doctype -- ^ \<!DOCTYPE value\>+ | Unknown++newtype Document_ (k :: NodeKind) (m :: MutableFlag) = Document (ForeignPtr (Document_ k m))+type Document = Document_ Unknown Immutable+type MutableDocument = Document_ Unknown Mutable++newtype Node_ (k :: NodeKind) (m :: MutableFlag) = Node (ForeignPtr (Node_ k m))+type Node = Node_ Unknown Immutable+type MutableNode k = Node_ k Mutable++newtype ParseResult = ParseResult (Ptr ParseResult)++newtype XPath rt = XPath (ForeignPtr (XPath rt))++data NodeSet (m :: MutableFlag) = NodeSet Int (ForeignPtr (NodeSet m))+instance Show (NodeSet m) where+ show (NodeSet i _) = "NodeSet " ++ show i ++ " items"++data Attr+data XNode+type Attribute = (S.ByteString, S.ByteString)+type XPathNode m = Either (Node_ Unknown m) Attribute
+ Text/XML/Pugi/Foreign/XPath.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TemplateHaskell #-}+module Text.XML.Pugi.Foreign.XPath where++import Language.Haskell.TH+import Language.Haskell.TH.Quote++import Control.Applicative+import Control.Exception++import Foreign.ForeignPtr+import Foreign.Ptr+import Foreign.C+import Foreign.Marshal+import qualified Data.ByteString as S+import Data.String(IsString(..))+import System.IO.Unsafe++import Text.XML.Pugi.Foreign.Types+import Text.XML.Pugi.Foreign.Const+import Text.XML.Pugi.Foreign.Node++foreign import ccall unsafe delete_xpath_query :: Ptr (XPath a) -> IO ()+foreign import ccall unsafe "&delete_xpath_query" finalizerXpathQuery :: FinalizerPtr (XPath a)+foreign import ccall unsafe new_xpath_query_no_variable :: CString -> IO (Ptr (XPath a))++foreign import ccall unsafe xpath_query_evaluate_boolean :: Ptr (XPath Bool) -> Ptr n -> IO CInt+foreign import ccall unsafe xpath_query_evaluate_number :: Ptr (XPath Double) -> Ptr n -> IO CDouble+foreign import ccall unsafe xpath_query_evaluate_string :: Ptr (XPath S.ByteString) -> Ptr n -> IO CString+foreign import ccall unsafe xpath_query_evaluate_node_set :: Ptr (XPath (NodeSet m)) -> Ptr n -> IO (Ptr (NodeSet m))++foreign import ccall unsafe xpath_query_return_type :: Ptr (XPath a) -> IO XPathType+foreign import ccall unsafe xpath_query_parse_is_success :: Ptr (XPath a) -> IO CInt++createXPath :: S.ByteString -> IO (XPath a)+createXPath query = S.useAsCString query $ \c -> do+ p <- new_xpath_query_no_variable c+ XPath <$> newForeignPtr finalizerXpathQuery p++createXPath' :: String -> XPath a+createXPath' q = unsafeDupablePerformIO $ createXPath (fromString q)++asNodeSet :: XPath (NodeSet m) -> XPath (NodeSet m)+asNodeSet = id++asDouble :: XPath Double -> XPath Double+asDouble = id++asByteString :: XPath S.ByteString -> XPath S.ByteString+asByteString = id++asBool :: XPath Bool -> XPath Bool+asBool = id++xpath' :: String -> ExpQ+xpath' str = do+ rt <- runIO $ withCString str $ \c ->+ bracket (new_xpath_query_no_variable c) delete_xpath_query $ \x ->+ (toBool <$> xpath_query_parse_is_success x) >>= \case+ False -> fail $ "xpath compile failed: " ++ show str+ True -> xpath_query_return_type x+ let as = if+ | rt == xPathTypeNodeSet -> [|asNodeSet|]+ | rt == xPathTypeNumber -> [|asDouble|]+ | rt == xPathString -> [|asByteString|]+ | rt == xPathBoolean -> [|asBool|]+ | otherwise -> fail $ "xpath_type_none"+ [|$as (createXPath' $(stringE str))|]++-- | generate xpath object.+--+-- @+-- [xpath|query|] == ((xpathObject) :: XPath (instance of EvalXPath))+-- @+--+xpath :: QuasiQuoter+xpath = QuasiQuoter + { quoteExp = xpath'+ , quotePat = error "xpath QQ can use as Exp only."+ , quoteType = error "xpath QQ can use as Exp only."+ , quoteDec = error "xpath QQ can use as Exp only."+ }++class EvalXPath a where+ evaluateXPath :: NodeLike n => XPath a -> n k m -> IO a++instance EvalXPath Bool where+ evaluateXPath (XPath xp) nd = withForeignPtr xp $ \x -> withNode nd $ \n ->+ toBool <$> xpath_query_evaluate_boolean x n++instance EvalXPath Double where+ evaluateXPath (XPath xp) nd = withForeignPtr xp $ \x -> withNode nd $ \n ->+ realToFrac <$> xpath_query_evaluate_number x n++instance EvalXPath S.ByteString where+ evaluateXPath (XPath xp) nd = withForeignPtr xp $ \x -> withNode nd $ \n -> do+ s <- xpath_query_evaluate_string x n+ S.packCString s <* free s++instance EvalXPath (NodeSet m) where+ evaluateXPath (XPath xp) nd = withForeignPtr xp $ \x -> withNode nd $ \n -> do+ s <- xpath_query_evaluate_node_set x n+ l <- fromIntegral <$> xpath_node_set_size s+ NodeSet l <$> newForeignPtr finalizerXpathNodeSet s
+ Text/XML/Pugi/Foreign/XPath/Node.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module Text.XML.Pugi.Foreign.XPath.Node where++import Control.Applicative+import Control.Exception++import Foreign.ForeignPtr+import Foreign.Ptr+import Foreign.C+import Foreign.Marshal++import Text.XML.Pugi.Foreign.Types+import Text.XML.Pugi.Foreign.Node++import Data.IORef++foreign import ccall unsafe xpath_node_set_empty :: Ptr (NodeSet m) -> IO CInt+foreign import ccall unsafe xpath_node_set_index :: Ptr (NodeSet m) -> CSize -> IO (Ptr XNode)++foreign import ccall unsafe "wrapper" wrap_xpath_node_mapper :: (Ptr XNode -> IO ()) -> IO (FunPtr (Ptr XNode -> IO ()))+foreign import ccall xpath_node_set_map :: Ptr (NodeSet m) -> FunPtr (Ptr XNode -> IO ()) -> IO ()++nodeSetSize :: NodeSet m -> Int+nodeSetSize (NodeSet l _) = l++nodeSetEmpty :: NodeSet m -> IO Bool+nodeSetEmpty (NodeSet _ fp) = toBool <$>+ withForeignPtr fp xpath_node_set_empty++nodeSetIndex :: NodeSet m -> Int -> IO (XPathNode m)+nodeSetIndex (NodeSet l fp) i+ | l > i = withForeignPtr fp $ \p ->+ bracket (xpath_node_set_index p (fromIntegral i))+ delete_xpath_node peekXNode+ | otherwise = fail "out of range"++nodeSetMapM_ :: (XPathNode m -> IO ()) -> NodeSet m -> IO ()+nodeSetMapM_ f (NodeSet _ fp) = withForeignPtr fp $ \p -> do+ let func x = peekXNode x >>= f+ bracket (wrap_xpath_node_mapper func) freeHaskellFunPtr $ \fn ->+ xpath_node_set_map p fn++nodeSetMapM :: (XPathNode m -> IO a) -> NodeSet m -> IO [a]+nodeSetMapM f n = do+ ref <- newIORef id+ nodeSetMapM_ (\x -> f x >>= \a -> modifyIORef ref (\rf -> rf . (a:))) n+ readIORef ref >>= \l -> return (l [])
+ cbit/wrapper.cc view
@@ -0,0 +1,290 @@+#include <pugixml.hpp>+#include <stdlib.h>+#include <string>+#include <cstring>+#include <sstream>++extern "C" {+ typedef pugi::xml_document Document;+ typedef pugi::xml_node Node;+ typedef pugi::xml_attribute Attr;+ typedef pugi::xml_parse_result ParseResult;+ typedef pugi::xpath_query XPath;+ typedef pugi::xpath_node XPathNode;+ typedef pugi::xpath_node_set NodeSet;++ inline Node* checkNewNode(const Node& n) { if (n) {return new Node(n);} else {return NULL;} }+ inline Attr* checkNewAttr(const Attr& a) { if (a) {return new Attr(a);} else {return NULL;} }+ inline XPathNode* checkNewXPathNode(const XPathNode& n) { if (n) {return new XPathNode(n);} else {return NULL;} }++ int pugixml_version () { return PUGIXML_VERSION; }++ ////// enum xml_node_type+ int enum_node_type_null () {return pugi::node_null;}+ int enum_node_type_document () {return pugi::node_document;}+ int enum_node_type_element () {return pugi::node_element;}+ int enum_node_type_pcdata () {return pugi::node_pcdata;}+ int enum_node_type_cdata () {return pugi::node_cdata;}+ int enum_node_type_comment () {return pugi::node_comment;}+ int enum_node_type_pi () {return pugi::node_pi;}+ int enum_node_type_declaration() {return pugi::node_declaration;}+ int enum_node_type_doctype () {return pugi::node_doctype;}++ ////// enum xml_parse_status+ int enum_parse_status_ok () {return pugi::status_ok;}+ int enum_parse_status_file_not_found () {return pugi::status_file_not_found;}+ int enum_parse_status_io_error () {return pugi::status_io_error;}+ int enum_parse_status_out_of_memory () {return pugi::status_out_of_memory;}+ int enum_parse_status_internal_error () {return pugi::status_internal_error;}+ int enum_parse_status_unrecognized_tag () {return pugi::status_unrecognized_tag;}+ int enum_parse_status_bad_pi () {return pugi::status_bad_pi;}+ int enum_parse_status_bad_comment () {return pugi::status_bad_comment;}+ int enum_parse_status_bad_cdata () {return pugi::status_bad_cdata;}+ int enum_parse_status_bad_doctype () {return pugi::status_bad_doctype;}+ int enum_parse_status_bad_pcdata () {return pugi::status_bad_pcdata;}+ int enum_parse_status_bad_start_element () {return pugi::status_bad_start_element;}+ int enum_parse_status_bad_attribute () {return pugi::status_bad_attribute;}+ int enum_parse_status_bad_end_element () {return pugi::status_bad_end_element;}+ int enum_parse_status_end_element_mismatch() {return pugi::status_end_element_mismatch;}+ int enum_parse_status_append_invalid_root () {return pugi::status_append_invalid_root;}+ int enum_parse_status_no_document_element () {return pugi::status_no_document_element;}++ ////// enum xml_encoding+ int enum_encoding_auto () {return pugi::encoding_auto;}+ int enum_encoding_utf8 () {return pugi::encoding_utf8;}+ int enum_encoding_utf16_le() {return pugi::encoding_utf16_le;}+ int enum_encoding_utf16_be() {return pugi::encoding_utf16_be;}+ int enum_encoding_utf16 () {return pugi::encoding_utf16;}+ int enum_encoding_utf32_le() {return pugi::encoding_utf32_le;}+ int enum_encoding_utf32_be() {return pugi::encoding_utf32_be;}+ int enum_encoding_utf32 () {return pugi::encoding_utf32;}+ int enum_encoding_wchar () {return pugi::encoding_wchar;}+ int enum_encoding_latin1 () {return pugi::encoding_latin1;}++ ////// enum xpath_value_type+ int enum_xpath_value_type_none (){return pugi::xpath_type_none;}+ int enum_xpath_value_type_node_set(){return pugi::xpath_type_node_set;}+ int enum_xpath_value_type_number (){return pugi::xpath_type_number;}+ int enum_xpath_value_type_string (){return pugi::xpath_type_string;}+ int enum_xpath_value_type_boolean (){return pugi::xpath_type_boolean;}++ ////// formatting option bit flags+ unsigned int format_flag_default (){return pugi::format_default;}+ unsigned int format_flag_indent (){return pugi::format_indent;}+ unsigned int format_flag_no_declaration(){return pugi::format_no_declaration;}+ unsigned int format_flag_no_escapes (){return pugi::format_no_escapes;}+ unsigned int format_flag_raw (){return pugi::format_raw;}+ unsigned int format_flag_save_file_text(){return pugi::format_save_file_text;}+ unsigned int format_flag_write_bom (){return pugi::format_write_bom;}++ ////// parsing option bit flags+ unsigned int parse_flag_cdata (){return pugi::parse_cdata;}+ unsigned int parse_flag_comments (){return pugi::parse_comments;}+ unsigned int parse_flag_declaration (){return pugi::parse_declaration;}+ unsigned int parse_flag_default (){return pugi::parse_default;}+ unsigned int parse_flag_doctype (){return pugi::parse_doctype;}+ unsigned int parse_flag_eol (){return pugi::parse_eol;}+ unsigned int parse_flag_escapes (){return pugi::parse_escapes;}+ unsigned int parse_flag_fragment (){return pugi::parse_fragment;}+ unsigned int parse_flag_full (){return pugi::parse_full;}+ unsigned int parse_flag_minimal (){return pugi::parse_minimal;}+ unsigned int parse_flag_pi (){return pugi::parse_pi;}+ unsigned int parse_flag_trim_pcdata (){return pugi::parse_trim_pcdata;}+ unsigned int parse_flag_ws_pcdata (){return pugi::parse_ws_pcdata;}+ unsigned int parse_flag_ws_pcdata_single(){return pugi::parse_ws_pcdata_single;}+ unsigned int parse_flag_wconv_attribute (){return pugi::parse_wconv_attribute;}+ unsigned int parse_flag_wnorm_attribute (){return pugi::parse_wnorm_attribute;}++ ////// methods of document+ Document* new_document() { return new pugi::xml_document(); }+ void delete_document(const Document* doc) { delete doc; }++ void reset_document_with(Document* doc, const Document* proto){+ doc->reset(*proto);+ }++ ParseResult* load_buffer(Document* doc, const void* str, size_t size,+ unsigned int options, pugi::xml_encoding encoding) {+ return new pugi::xml_parse_result(doc->load_buffer(str, size, options, encoding));+ }++ ParseResult* load_file(Document* doc, const char* path,+ unsigned int options, pugi::xml_encoding encoding) {+ return new pugi::xml_parse_result(doc->load_file(path, options, encoding));+ }++ int save_file(const Document* doc, const char* path, const char* indent, + unsigned int flags, pugi::xml_encoding encoding) {+ return doc->save_file(path, indent, flags, encoding);+ }++ struct wrap_writer : pugi::xml_writer {+ void (*func)(const char* data, size_t size);++ virtual void write(const void* data, size_t size) {+ func(static_cast<const char*>(data), size);+ }+ };++ void save_string(const Document* doc, void (*func)(const char*, size_t),+ const char* indent, unsigned int flags, pugi::xml_encoding encoding) {+ wrap_writer wtr;+ wtr.func = func;+ doc->save(wtr, indent, flags, encoding);+ }++ Node* document_element(const Document* doc) { return checkNewNode(doc->document_element()); }++ ////// methods of xml_parse_result+ void delete_parse_result(const ParseResult* r) { delete r; }+ int parse_is_success(const ParseResult* r) { return *r ? true : false; }+ int parse_result_status(const ParseResult* r) { return r->status; }+ long parse_result_offset(const ParseResult* r) { return r->offset; }+ pugi::xml_encoding parse_result_encoding(const ParseResult* r) { return r->encoding; }+ const char* parse_result_description(const ParseResult* r) { return r->description(); }++ ////// methods of xml_attribute+ void delete_attr(const Attr* a) { delete a; }+ size_t attr_hash_value(const Attr* a) { return a->hash_value(); }++ const char* attr_name(const Attr* a) { return a->name(); }+ const char* attr_value(const Attr* a) { return a->value(); }++ int attr_set_value(Attr* a, const char* v) { return a->set_value(v); }++ ////// methods of node+ void delete_node(const Node* n) { delete n; }++ size_t node_hash_value(const void* n) { return static_cast<const Node*>(n)->hash_value(); }++ int node_type(const void* n) { return static_cast<const Node*>(n)->type(); }++ const char* node_name(const void* n) { return static_cast<const Node*>(n)->name(); }+ const char* node_value(const void* n) { return static_cast<const Node*>(n)->value(); }++ Node* node_parent(const void* n) { return checkNewNode(static_cast<const Node*>(n)->parent()); }+ Node* node_first_child(const void* n) { return checkNewNode(static_cast<const Node*>(n)->first_child()); }+ Node* node_last_child(const void* n) { return checkNewNode(static_cast<const Node*>(n)->last_child()); }+ Node* node_next_sibling(const void* n) { return checkNewNode(static_cast<const Node*>(n)->next_sibling()); }+ Node* node_previous_sibling(const void* n) { return checkNewNode(static_cast<const Node*>(n)->previous_sibling()); }++ Node* node_child(const void* n, const char* name) { return checkNewNode(static_cast<const Node*>(n)->child(name)); }+ Attr* node_attribute(const void* n, const char* name) { return checkNewAttr(static_cast<const Node*>(n)->attribute(name)); }+ Node* node_next_sibling_by_name(const void* n, const char* name) { return checkNewNode(static_cast<const Node*>(n)->next_sibling(name)); }+ Node* node_previous_sibling_by_name(const void* n, const char* name) { return checkNewNode(static_cast<const Node*>(n)->previous_sibling(name)); }+ Node* node_find_child_by_name_and_attribute(const void* n, const char* name, const char* attr_name, const char* attr_value) { return checkNewNode(static_cast<const Node*>(n)->find_child_by_attribute(name, attr_name, attr_value)); }+ Node* node_find_child_by_attribute(const void* n, const char* attr_name, const char* attr_value) { return checkNewNode(static_cast<const Node*>(n)->find_child_by_attribute(attr_name, attr_value)); }++ const char* node_child_value(const void* n) { return static_cast<const Node*>(n)->child_value(); }+ const char* node_child_value_by_name(const void* n, const char* name) { return static_cast<const Node*>(n)->child_value(name); }+ const char* node_text(const void* n) { return static_cast<const Node*>(n)->text().get(); }++ void node_map_sibling (const void* node, void (*fun)(Node*)) {+ const Node* n = static_cast<const Node*>(node);+ for(pugi::xml_node_iterator it = n->begin(); it != n->end(); ++it) {+ fun(&*it);+ }+ }+ void node_map_attributes (const void* node, void (*fun)(Attr*)) {+ const Node* n = static_cast<const Node*>(node);+ for(pugi::xml_attribute_iterator it = n->attributes_begin(); it != n->attributes_end(); ++it) {+ fun(&*it);+ }+ }++ char* node_path(const void* node, const char del) { + std::string s = static_cast<const Node*>(node)->path(del);+ char* ret = (char*)malloc(s.length() + 1);+ std::strcpy(ret, s.c_str());+ return ret;+ }++ Node* node_first_element_by_path(const void* node, const char* path, char del) {+ return checkNewNode(static_cast<const Node*>(node)->first_element_by_path(path, del));+ }++ Node* node_root(const void* n) {return checkNewNode(static_cast<const Node*>(n)->root()); }++ int set_name(void* n, const char* name) { return static_cast<Node*>(n)->set_name(name); }+ int set_value(void* n, const char* name) { return static_cast<Node*>(n)->set_value(name); }++ int append_attribute(void* n, const char* name, const char* val) { return static_cast<Node*>(n)->append_attribute(name).set_value(val); }+ int prepend_attribute(void* n, const char* name, const char* val) { return static_cast<Node*>(n)->append_attribute(name).set_value(val); }++ Node* append_child(void* n, pugi::xml_node_type typ) { return checkNewNode(static_cast<Node*>(n)->append_child(typ)); }+ Node* prepend_child(void* n, pugi::xml_node_type typ) { return checkNewNode(static_cast<Node*>(n)->prepend_child(typ)); }++ Node* append_copy(void* n, const Node* proto) { return checkNewNode(static_cast<Node*>(n)->append_copy(*proto)); }+ Node* prepend_copy(void* n, const Node* proto) { return checkNewNode(static_cast<Node*>(n)->prepend_copy(*proto)); }++ int remove_attribute(void* n, const char* name) { return static_cast<Node*>(n)->remove_attribute(name); }+ int remove_child (void* n, const Node* cld) { return static_cast<Node*>(n)->remove_child(*cld); }++ ParseResult* append_buffer(void* node, const void* cont,+ size_t size, unsigned int options, pugi::xml_encoding enc) {+ return new pugi::xml_parse_result(static_cast<Node*>(node)->append_buffer(cont, size, options, enc));+ }++ void node_print(const void* node, void (*func)(const char*, size_t),+ const char* indent, unsigned int flags, pugi::xml_encoding encoding,+ unsigned int depth) {+ wrap_writer wtr;+ wtr.func = func;+ static_cast<const Node*>(node)->print(wtr, indent, flags, encoding, depth);+ }++ XPathNode* select_single_node(const void* node, const XPath* x) {+ return checkNewXPathNode(static_cast<const Node*>(node)->select_single_node(*x));+ }+ NodeSet* select_nodes(const void* node, const XPath* x) {+ return new NodeSet(static_cast<const Node*>(node)->select_nodes(*x));+ }++ ////// methods of xpath_node+ void delete_xpath_node(const XPathNode* p) { delete p; }+ Node* xpath_node_node(const XPathNode* p) { return checkNewNode(p->node()); }+ Attr* xpath_node_attribute(const XPathNode* p) { return checkNewAttr(p->attribute()); }++ ////// methods of xpath_node_set+ void delete_xpath_node_set(const NodeSet* p) { delete p; }+ size_t xpath_node_set_size (const NodeSet* p) { return p->size(); }+ int xpath_node_set_empty (const NodeSet* p) { return p->empty(); }+ XPathNode* xpath_node_set_index (const NodeSet* p, size_t i) { + return checkNewXPathNode((*p)[i]);+ }+ void xpath_node_set_map(const NodeSet* p, void (*func)(const XPathNode*)) {+ for(pugi::xpath_node_set::const_iterator it = p->begin(); it != p->end(); ++it) {+ func(&*it);+ }+ }++ ////// methods of xpath_query+ void delete_xpath_query(const XPath* p) { delete p; }+ XPath* new_xpath_query_no_variable(const char* query) { return new pugi::xpath_query(query); }++ int xpath_query_evaluate_boolean(const XPath* p, const void* n) {+ XPathNode xn(*static_cast<const Node*>(n));+ return p->evaluate_boolean(xn); + }+ double xpath_query_evaluate_number(const XPath* p, const void* n) {+ XPathNode xn(*static_cast<const Node*>(n));+ return p->evaluate_number(xn);+ }++ char* xpath_query_evaluate_string(const XPath* p, const void* n) {+ XPathNode xn(*static_cast<const Node*>(n));+ std::string s = p->evaluate_string(xn);+ char* ret = (char*)malloc(s.length() + 1);+ std::strcpy(ret, s.c_str());+ return ret;+ }++ NodeSet* xpath_query_evaluate_node_set(const XPath* p, const void* n) {+ XPathNode xn(*static_cast<const Node*>(n));+ return new NodeSet(p->evaluate_node_set(xn));+ }++ pugi::xpath_value_type xpath_query_return_type(const XPath* p) { return p->return_type(); }+ int xpath_query_parse_is_success(const XPath* p) { return static_cast<bool>(*p); }++}
+ pugixml-1.4/pugiconfig.hpp view
@@ -0,0 +1,72 @@+/**+ * pugixml parser - version 1.4+ * --------------------------------------------------------+ * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)+ * Report bugs and download new versions at http://pugixml.org/+ *+ * This library is distributed under the MIT License. See notice at the end+ * of this file.+ *+ * This work is based on the pugxml parser, which is:+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)+ */++#ifndef HEADER_PUGICONFIG_HPP+#define HEADER_PUGICONFIG_HPP++// Uncomment this to enable wchar_t mode+// #define PUGIXML_WCHAR_MODE++// Uncomment this to disable XPath+// #define PUGIXML_NO_XPATH++// Uncomment this to disable STL+// #define PUGIXML_NO_STL++// Uncomment this to disable exceptions+#define PUGIXML_NO_EXCEPTIONS++// Set this to control attributes for public classes/functions, i.e.:+// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL+// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL+// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall+// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead++// Tune these constants to adjust memory-related behavior+// #define PUGIXML_MEMORY_PAGE_SIZE 32768+// #define PUGIXML_MEMORY_OUTPUT_STACK 10240+// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096++// Uncomment this to switch to header-only version+// #define PUGIXML_HEADER_ONLY+// #include "pugixml.cpp"++// Uncomment this to enable long long support+// #define PUGIXML_HAS_LONG_LONG++#endif++/**+ * Copyright (c) 2006-2014 Arseny Kapoulkine+ *+ * Permission is hereby granted, free of charge, to any person+ * obtaining a copy of this software and associated documentation+ * files (the "Software"), to deal in the Software without+ * restriction, including without limitation the rights to use,+ * copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the+ * Software is furnished to do so, subject to the following+ * conditions:+ *+ * The above copyright notice and this permission notice shall be+ * included in all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+ * OTHER DEALINGS IN THE SOFTWARE.+ */
+ pugixml-1.4/pugixml.cpp view
@@ -0,0 +1,10639 @@+/**+ * pugixml parser - version 1.4+ * --------------------------------------------------------+ * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)+ * Report bugs and download new versions at http://pugixml.org/+ *+ * This library is distributed under the MIT License. See notice at the end+ * of this file.+ *+ * This work is based on the pugxml parser, which is:+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)+ */++#ifndef SOURCE_PUGIXML_CPP+#define SOURCE_PUGIXML_CPP++#include "pugixml.hpp"++#include <stdlib.h>+#include <stdio.h>+#include <string.h>+#include <assert.h>++#ifdef PUGIXML_WCHAR_MODE+# include <wchar.h>+#endif++#ifndef PUGIXML_NO_XPATH+# include <math.h>+# include <float.h>+# ifdef PUGIXML_NO_EXCEPTIONS+# include <setjmp.h>+# endif+#endif++#ifndef PUGIXML_NO_STL+# include <istream>+# include <ostream>+# include <string>+#endif++// For placement new+#include <new>++#ifdef _MSC_VER+# pragma warning(push)+# pragma warning(disable: 4127) // conditional expression is constant+# pragma warning(disable: 4324) // structure was padded due to __declspec(align())+# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable+# pragma warning(disable: 4702) // unreachable code+# pragma warning(disable: 4996) // this function or variable may be unsafe+# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged+#endif++#ifdef __INTEL_COMPILER+# pragma warning(disable: 177) // function was declared but never referenced +# pragma warning(disable: 279) // controlling expression is constant+# pragma warning(disable: 1478 1786) // function was declared "deprecated"+# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type+#endif++#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)+# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away+#endif++#ifdef __BORLANDC__+# pragma option push+# pragma warn -8008 // condition is always false+# pragma warn -8066 // unreachable code+#endif++#ifdef __SNC__+// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug+# pragma diag_suppress=178 // function was declared but never referenced+# pragma diag_suppress=237 // controlling expression is constant+#endif++// Inlining controls+#if defined(_MSC_VER) && _MSC_VER >= 1300+# define PUGI__NO_INLINE __declspec(noinline)+#elif defined(__GNUC__)+# define PUGI__NO_INLINE __attribute__((noinline))+#else+# define PUGI__NO_INLINE +#endif++// Simple static assertion+#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }++// Digital Mars C++ bug workaround for passing char loaded from memory via stack+#ifdef __DMC__+# define PUGI__DMC_VOLATILE volatile+#else+# define PUGI__DMC_VOLATILE+#endif++// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)+#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)+using std::memcpy;+using std::memmove;+#endif++// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features+#if defined(_MSC_VER) && !defined(__S3E__)+# define PUGI__MSVC_CRT_VERSION _MSC_VER+#endif++#ifdef PUGIXML_HEADER_ONLY+# define PUGI__NS_BEGIN namespace pugi { namespace impl {+# define PUGI__NS_END } }+# define PUGI__FN inline+# define PUGI__FN_NO_INLINE inline+#else+# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces+# define PUGI__NS_BEGIN namespace pugi { namespace impl {+# define PUGI__NS_END } }+# else+# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace {+# define PUGI__NS_END } } }+# endif+# define PUGI__FN+# define PUGI__FN_NO_INLINE PUGI__NO_INLINE+#endif++// uintptr_t+#if !defined(_MSC_VER) || _MSC_VER >= 1600+# include <stdint.h>+#else+# ifndef _UINTPTR_T_DEFINED+// No native uintptr_t in MSVC6 and in some WinCE versions+typedef size_t uintptr_t;+#define _UINTPTR_T_DEFINED+# endif+PUGI__NS_BEGIN+ typedef unsigned __int8 uint8_t;+ typedef unsigned __int16 uint16_t;+ typedef unsigned __int32 uint32_t;+PUGI__NS_END+#endif++// Memory allocation+PUGI__NS_BEGIN+ PUGI__FN void* default_allocate(size_t size)+ {+ return malloc(size);+ }++ PUGI__FN void default_deallocate(void* ptr)+ {+ free(ptr);+ }++ template <typename T>+ struct xml_memory_management_function_storage+ {+ static allocation_function allocate;+ static deallocation_function deallocate;+ };++ template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;+ template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;++ typedef xml_memory_management_function_storage<int> xml_memory;+PUGI__NS_END++// String utilities+PUGI__NS_BEGIN+ // Get string length+ PUGI__FN size_t strlength(const char_t* s)+ {+ assert(s);++ #ifdef PUGIXML_WCHAR_MODE+ return wcslen(s);+ #else+ return strlen(s);+ #endif+ }++ // Compare two strings+ PUGI__FN bool strequal(const char_t* src, const char_t* dst)+ {+ assert(src && dst);++ #ifdef PUGIXML_WCHAR_MODE+ return wcscmp(src, dst) == 0;+ #else+ return strcmp(src, dst) == 0;+ #endif+ }++ // Compare lhs with [rhs_begin, rhs_end)+ PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)+ {+ for (size_t i = 0; i < count; ++i)+ if (lhs[i] != rhs[i])+ return false;+ + return lhs[count] == 0;+ }++ // Get length of wide string, even if CRT lacks wide character support+ PUGI__FN size_t strlength_wide(const wchar_t* s)+ {+ assert(s);++ #ifdef PUGIXML_WCHAR_MODE+ return wcslen(s);+ #else+ const wchar_t* end = s;+ while (*end) end++;+ return static_cast<size_t>(end - s);+ #endif+ }++#ifdef PUGIXML_WCHAR_MODE+ // Convert string to wide string, assuming all symbols are ASCII+ PUGI__FN void widen_ascii(wchar_t* dest, const char* source)+ {+ for (const char* i = source; *i; ++i) *dest++ = *i;+ *dest = 0;+ }+#endif+PUGI__NS_END++#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)+// auto_ptr-like buffer holder for exception recovery+PUGI__NS_BEGIN+ struct buffer_holder+ {+ void* data;+ void (*deleter)(void*);++ buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_)+ {+ }++ ~buffer_holder()+ {+ if (data) deleter(data);+ }++ void* release()+ {+ void* result = data;+ data = 0;+ return result;+ }+ };+PUGI__NS_END+#endif++PUGI__NS_BEGIN+ static const size_t xml_memory_page_size =+ #ifdef PUGIXML_MEMORY_PAGE_SIZE+ PUGIXML_MEMORY_PAGE_SIZE+ #else+ 32768+ #endif+ ;++ static const uintptr_t xml_memory_page_alignment = 32;+ static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1);+ static const uintptr_t xml_memory_page_name_allocated_mask = 16;+ static const uintptr_t xml_memory_page_value_allocated_mask = 8;+ static const uintptr_t xml_memory_page_type_mask = 7;++ struct xml_allocator;++ struct xml_memory_page+ {+ static xml_memory_page* construct(void* memory)+ {+ if (!memory) return 0; //$ redundant, left for performance++ xml_memory_page* result = static_cast<xml_memory_page*>(memory);++ result->allocator = 0;+ result->memory = 0;+ result->prev = 0;+ result->next = 0;+ result->busy_size = 0;+ result->freed_size = 0;++ return result;+ }++ xml_allocator* allocator;++ void* memory;++ xml_memory_page* prev;+ xml_memory_page* next;++ size_t busy_size;+ size_t freed_size;++ char data[1];+ };++ struct xml_memory_string_header+ {+ uint16_t page_offset; // offset from page->data+ uint16_t full_size; // 0 if string occupies whole page+ };++ struct xml_allocator+ {+ xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)+ {+ }++ xml_memory_page* allocate_page(size_t data_size)+ {+ size_t size = offsetof(xml_memory_page, data) + data_size;++ // allocate block with some alignment, leaving memory for worst-case padding+ void* memory = xml_memory::allocate(size + xml_memory_page_alignment);+ if (!memory) return 0;++ // align upwards to page boundary+ void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1));++ // prepare page structure+ xml_memory_page* page = xml_memory_page::construct(page_memory);+ assert(page);++ page->memory = memory;+ page->allocator = _root->allocator;++ return page;+ }++ static void deallocate_page(xml_memory_page* page)+ {+ xml_memory::deallocate(page->memory);+ }++ void* allocate_memory_oob(size_t size, xml_memory_page*& out_page);++ void* allocate_memory(size_t size, xml_memory_page*& out_page)+ {+ if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page);++ void* buf = _root->data + _busy_size;++ _busy_size += size;++ out_page = _root;++ return buf;+ }++ void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)+ {+ if (page == _root) page->busy_size = _busy_size;++ assert(ptr >= page->data && ptr < page->data + page->busy_size);+ (void)!ptr;++ page->freed_size += size;+ assert(page->freed_size <= page->busy_size);++ if (page->freed_size == page->busy_size)+ {+ if (page->next == 0)+ {+ assert(_root == page);++ // top page freed, just reset sizes+ page->busy_size = page->freed_size = 0;+ _busy_size = 0;+ }+ else+ {+ assert(_root != page);+ assert(page->prev);++ // remove from the list+ page->prev->next = page->next;+ page->next->prev = page->prev;++ // deallocate+ deallocate_page(page);+ }+ }+ }++ char_t* allocate_string(size_t length)+ {+ // allocate memory for string and header block+ size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);+ + // round size up to pointer alignment boundary+ size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);++ xml_memory_page* page;+ xml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page));++ if (!header) return 0;++ // setup header+ ptrdiff_t page_offset = reinterpret_cast<char*>(header) - page->data;++ assert(page_offset >= 0 && page_offset < (1 << 16));+ header->page_offset = static_cast<uint16_t>(page_offset);++ // full_size == 0 for large strings that occupy the whole page+ assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0));+ header->full_size = static_cast<uint16_t>(full_size < (1 << 16) ? full_size : 0);++ // round-trip through void* to avoid 'cast increases required alignment of target type' warning+ // header is guaranteed a pointer-sized alignment, which should be enough for char_t+ return static_cast<char_t*>(static_cast<void*>(header + 1));+ }++ void deallocate_string(char_t* string)+ {+ // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings+ // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string++ // get header+ xml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1;++ // deallocate+ size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset;+ xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));++ // if full_size == 0 then this string occupies the whole page+ size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size;++ deallocate_memory(header, full_size, page);+ }++ xml_memory_page* _root;+ size_t _busy_size;+ };++ PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)+ {+ const size_t large_allocation_threshold = xml_memory_page_size / 4;++ xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size);+ out_page = page;++ if (!page) return 0;++ if (size <= large_allocation_threshold)+ {+ _root->busy_size = _busy_size;++ // insert page at the end of linked list+ page->prev = _root;+ _root->next = page;+ _root = page;++ _busy_size = size;+ }+ else+ {+ // insert page before the end of linked list, so that it is deleted as soon as possible+ // the last page is not deleted even if it's empty (see deallocate_memory)+ assert(_root->prev);++ page->prev = _root->prev;+ page->next = _root;++ _root->prev->next = page;+ _root->prev = page;+ }++ // allocate inside page+ page->busy_size = size;++ return page->data;+ }+PUGI__NS_END++namespace pugi+{+ /// A 'name=value' XML attribute structure.+ struct xml_attribute_struct+ {+ /// Default ctor+ xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)+ {+ }++ uintptr_t header;++ char_t* name; ///< Pointer to attribute name.+ char_t* value; ///< Pointer to attribute value.++ xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list)+ xml_attribute_struct* next_attribute; ///< Next attribute+ };++ /// An XML document tree node.+ struct xml_node_struct+ {+ /// Default ctor+ /// \param type - node type+ xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)+ {+ }++ uintptr_t header;++ xml_node_struct* parent; ///< Pointer to parent++ char_t* name; ///< Pointer to element name.+ char_t* value; ///< Pointer to any associated string data.++ xml_node_struct* first_child; ///< First child+ + xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list)+ xml_node_struct* next_sibling; ///< Right brother+ + xml_attribute_struct* first_attribute; ///< First attribute+ };+}++PUGI__NS_BEGIN+ struct xml_extra_buffer+ {+ char_t* buffer;+ xml_extra_buffer* next;+ };++ struct xml_document_struct: public xml_node_struct, public xml_allocator+ {+ xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0)+ {+ }++ const char_t* buffer;++ xml_extra_buffer* extra_buffers;+ };++ inline xml_allocator& get_allocator(const xml_node_struct* node)+ {+ assert(node);++ return *reinterpret_cast<xml_memory_page*>(node->header & xml_memory_page_pointer_mask)->allocator;+ }+PUGI__NS_END++// Low-level DOM operations+PUGI__NS_BEGIN+ inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)+ {+ xml_memory_page* page;+ void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page);++ return new (memory) xml_attribute_struct(page);+ }++ inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type)+ {+ xml_memory_page* page;+ void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page);++ return new (memory) xml_node_struct(page, type);+ }++ inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)+ {+ uintptr_t header = a->header;++ if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name);+ if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value);++ alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));+ }++ inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)+ {+ uintptr_t header = n->header;++ if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name);+ if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value);++ for (xml_attribute_struct* attr = n->first_attribute; attr; )+ {+ xml_attribute_struct* next = attr->next_attribute;++ destroy_attribute(attr, alloc);++ attr = next;+ }++ for (xml_node_struct* child = n->first_child; child; )+ {+ xml_node_struct* next = child->next_sibling;++ destroy_node(child, alloc);++ child = next;+ }++ alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));+ }++ PUGI__FN_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)+ {+ xml_node_struct* child = allocate_node(alloc, type);+ if (!child) return 0;++ child->parent = node;++ xml_node_struct* first_child = node->first_child;+ + if (first_child)+ {+ xml_node_struct* last_child = first_child->prev_sibling_c;++ last_child->next_sibling = child;+ child->prev_sibling_c = last_child;+ first_child->prev_sibling_c = child;+ }+ else+ {+ node->first_child = child;+ child->prev_sibling_c = child;+ }+ + return child;+ }++ PUGI__FN_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc)+ {+ xml_attribute_struct* a = allocate_attribute(alloc);+ if (!a) return 0;++ xml_attribute_struct* first_attribute = node->first_attribute;++ if (first_attribute)+ {+ xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c;++ last_attribute->next_attribute = a;+ a->prev_attribute_c = last_attribute;+ first_attribute->prev_attribute_c = a;+ }+ else+ {+ node->first_attribute = a;+ a->prev_attribute_c = a;+ }+ + return a;+ }+PUGI__NS_END++// Helper classes for code generation+PUGI__NS_BEGIN+ struct opt_false+ {+ enum { value = 0 };+ };++ struct opt_true+ {+ enum { value = 1 };+ };+PUGI__NS_END++// Unicode utilities+PUGI__NS_BEGIN+ inline uint16_t endian_swap(uint16_t value)+ {+ return static_cast<uint16_t>(((value & 0xff) << 8) | (value >> 8));+ }++ inline uint32_t endian_swap(uint32_t value)+ {+ return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24);+ }++ struct utf8_counter+ {+ typedef size_t value_type;++ static value_type low(value_type result, uint32_t ch)+ {+ // U+0000..U+007F+ if (ch < 0x80) return result + 1;+ // U+0080..U+07FF+ else if (ch < 0x800) return result + 2;+ // U+0800..U+FFFF+ else return result + 3;+ }++ static value_type high(value_type result, uint32_t)+ {+ // U+10000..U+10FFFF+ return result + 4;+ }+ };++ struct utf8_writer+ {+ typedef uint8_t* value_type;++ static value_type low(value_type result, uint32_t ch)+ {+ // U+0000..U+007F+ if (ch < 0x80)+ {+ *result = static_cast<uint8_t>(ch);+ return result + 1;+ }+ // U+0080..U+07FF+ else if (ch < 0x800)+ {+ result[0] = static_cast<uint8_t>(0xC0 | (ch >> 6));+ result[1] = static_cast<uint8_t>(0x80 | (ch & 0x3F));+ return result + 2;+ }+ // U+0800..U+FFFF+ else+ {+ result[0] = static_cast<uint8_t>(0xE0 | (ch >> 12));+ result[1] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));+ result[2] = static_cast<uint8_t>(0x80 | (ch & 0x3F));+ return result + 3;+ }+ }++ static value_type high(value_type result, uint32_t ch)+ {+ // U+10000..U+10FFFF+ result[0] = static_cast<uint8_t>(0xF0 | (ch >> 18));+ result[1] = static_cast<uint8_t>(0x80 | ((ch >> 12) & 0x3F));+ result[2] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));+ result[3] = static_cast<uint8_t>(0x80 | (ch & 0x3F));+ return result + 4;+ }++ static value_type any(value_type result, uint32_t ch)+ {+ return (ch < 0x10000) ? low(result, ch) : high(result, ch);+ }+ };++ struct utf16_counter+ {+ typedef size_t value_type;++ static value_type low(value_type result, uint32_t)+ {+ return result + 1;+ }++ static value_type high(value_type result, uint32_t)+ {+ return result + 2;+ }+ };++ struct utf16_writer+ {+ typedef uint16_t* value_type;++ static value_type low(value_type result, uint32_t ch)+ {+ *result = static_cast<uint16_t>(ch);++ return result + 1;+ }++ static value_type high(value_type result, uint32_t ch)+ {+ uint32_t msh = static_cast<uint32_t>(ch - 0x10000) >> 10;+ uint32_t lsh = static_cast<uint32_t>(ch - 0x10000) & 0x3ff;++ result[0] = static_cast<uint16_t>(0xD800 + msh);+ result[1] = static_cast<uint16_t>(0xDC00 + lsh);++ return result + 2;+ }++ static value_type any(value_type result, uint32_t ch)+ {+ return (ch < 0x10000) ? low(result, ch) : high(result, ch);+ }+ };++ struct utf32_counter+ {+ typedef size_t value_type;++ static value_type low(value_type result, uint32_t)+ {+ return result + 1;+ }++ static value_type high(value_type result, uint32_t)+ {+ return result + 1;+ }+ };++ struct utf32_writer+ {+ typedef uint32_t* value_type;++ static value_type low(value_type result, uint32_t ch)+ {+ *result = ch;++ return result + 1;+ }++ static value_type high(value_type result, uint32_t ch)+ {+ *result = ch;++ return result + 1;+ }++ static value_type any(value_type result, uint32_t ch)+ {+ *result = ch;++ return result + 1;+ }+ };++ struct latin1_writer+ {+ typedef uint8_t* value_type;++ static value_type low(value_type result, uint32_t ch)+ {+ *result = static_cast<uint8_t>(ch > 255 ? '?' : ch);++ return result + 1;+ }++ static value_type high(value_type result, uint32_t ch)+ {+ (void)ch;++ *result = '?';++ return result + 1;+ }+ };++ template <size_t size> struct wchar_selector;++ template <> struct wchar_selector<2>+ {+ typedef uint16_t type;+ typedef utf16_counter counter;+ typedef utf16_writer writer;+ };++ template <> struct wchar_selector<4>+ {+ typedef uint32_t type;+ typedef utf32_counter counter;+ typedef utf32_writer writer;+ };++ typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;+ typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;++ template <typename Traits, typename opt_swap = opt_false> struct utf_decoder+ {+ static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result)+ {+ const uint8_t utf8_byte_mask = 0x3f;++ while (size)+ {+ uint8_t lead = *data;++ // 0xxxxxxx -> U+0000..U+007F+ if (lead < 0x80)+ {+ result = Traits::low(result, lead);+ data += 1;+ size -= 1;++ // process aligned single-byte (ascii) blocks+ if ((reinterpret_cast<uintptr_t>(data) & 3) == 0)+ {+ // round-trip through void* to silence 'cast increases required alignment of target type' warnings+ while (size >= 4 && (*static_cast<const uint32_t*>(static_cast<const void*>(data)) & 0x80808080) == 0)+ {+ result = Traits::low(result, data[0]);+ result = Traits::low(result, data[1]);+ result = Traits::low(result, data[2]);+ result = Traits::low(result, data[3]);+ data += 4;+ size -= 4;+ }+ }+ }+ // 110xxxxx -> U+0080..U+07FF+ else if (static_cast<unsigned int>(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80)+ {+ result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask));+ data += 2;+ size -= 2;+ }+ // 1110xxxx -> U+0800-U+FFFF+ else if (static_cast<unsigned int>(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80)+ {+ result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask));+ data += 3;+ size -= 3;+ }+ // 11110xxx -> U+10000..U+10FFFF+ else if (static_cast<unsigned int>(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80)+ {+ result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask));+ data += 4;+ size -= 4;+ }+ // 10xxxxxx or 11111xxx -> invalid+ else+ {+ data += 1;+ size -= 1;+ }+ }++ return result;+ }++ static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result)+ {+ const uint16_t* end = data + size;++ while (data < end)+ {+ unsigned int lead = opt_swap::value ? endian_swap(*data) : *data;++ // U+0000..U+D7FF+ if (lead < 0xD800)+ {+ result = Traits::low(result, lead);+ data += 1;+ }+ // U+E000..U+FFFF+ else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000)+ {+ result = Traits::low(result, lead);+ data += 1;+ }+ // surrogate pair lead+ else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && data + 1 < end)+ {+ uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];++ if (static_cast<unsigned int>(next - 0xDC00) < 0x400)+ {+ result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));+ data += 2;+ }+ else+ {+ data += 1;+ }+ }+ else+ {+ data += 1;+ }+ }++ return result;+ }++ static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result)+ {+ const uint32_t* end = data + size;++ while (data < end)+ {+ uint32_t lead = opt_swap::value ? endian_swap(*data) : *data;++ // U+0000..U+FFFF+ if (lead < 0x10000)+ {+ result = Traits::low(result, lead);+ data += 1;+ }+ // U+10000..U+10FFFF+ else+ {+ result = Traits::high(result, lead);+ data += 1;+ }+ }++ return result;+ }++ static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result)+ {+ for (size_t i = 0; i < size; ++i)+ {+ result = Traits::low(result, data[i]);+ }++ return result;+ }++ static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result)+ {+ return decode_utf16_block(data, size, result);+ }++ static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result)+ {+ return decode_utf32_block(data, size, result);+ }++ static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result)+ {+ return decode_wchar_block_impl(reinterpret_cast<const wchar_selector<sizeof(wchar_t)>::type*>(data), size, result);+ }+ };++ template <typename T> PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length)+ {+ for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]);+ }++#ifdef PUGIXML_WCHAR_MODE+ PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)+ {+ for (size_t i = 0; i < length; ++i) result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));+ }+#endif+PUGI__NS_END++PUGI__NS_BEGIN+ enum chartype_t+ {+ ct_parse_pcdata = 1, // \0, &, \r, <+ ct_parse_attr = 2, // \0, &, \r, ', "+ ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab+ ct_space = 8, // \r, \n, space, tab+ ct_parse_cdata = 16, // \0, ], >, \r+ ct_parse_comment = 32, // \0, -, >, \r+ ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .+ ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, :+ };++ static const unsigned char chartype_table[256] =+ {+ 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31+ 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63+ 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95+ 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127++ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128++ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192+ };++ enum chartypex_t+ {+ ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, >+ ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, "+ ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _+ ctx_digit = 8, // 0-9+ ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, .+ };+ + static const unsigned char chartypex_table[256] =+ {+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31+ 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63++ 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95+ 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127++ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128++ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20+ };+ +#ifdef PUGIXML_WCHAR_MODE+ #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct))+#else+ #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(c)] & (ct))+#endif++ #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table)+ #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table)++ PUGI__FN bool is_little_endian()+ {+ unsigned int ui = 1;++ return *reinterpret_cast<unsigned char*>(&ui) == 1;+ }++ PUGI__FN xml_encoding get_wchar_encoding()+ {+ PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);++ if (sizeof(wchar_t) == 2)+ return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;+ else + return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;+ }++ PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)+ {+ // look for BOM in first few bytes+ if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be;+ if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le;+ if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be;+ if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le;+ if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8;++ // look for <, <? or <?xm in various encodings+ if (d0 == 0 && d1 == 0 && d2 == 0 && d3 == 0x3c) return encoding_utf32_be;+ if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le;+ if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be;+ if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le;+ if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d) return encoding_utf8;++ // look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early)+ if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be;+ if (d0 == 0x3c && d1 == 0) return encoding_utf16_le;++ // no known BOM detected, assume utf8+ return encoding_utf8;+ }++ PUGI__FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size)+ {+ // replace wchar encoding with utf implementation+ if (encoding == encoding_wchar) return get_wchar_encoding();++ // replace utf16 encoding with utf16 with specific endianness+ if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;++ // replace utf32 encoding with utf32 with specific endianness+ if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;++ // only do autodetection if no explicit encoding is requested+ if (encoding != encoding_auto) return encoding;++ // skip encoding autodetection if input buffer is too small+ if (size < 4) return encoding_utf8;++ // try to guess encoding (based on XML specification, Appendix F.1)+ const uint8_t* data = static_cast<const uint8_t*>(contents);++ PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3];++ return guess_buffer_encoding(d0, d1, d2, d3);+ }++ PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)+ {+ size_t length = size / sizeof(char_t);++ if (is_mutable)+ {+ out_buffer = static_cast<char_t*>(const_cast<void*>(contents));+ out_length = length;+ }+ else+ {+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ memcpy(buffer, contents, length * sizeof(char_t));+ buffer[length] = 0;++ out_buffer = buffer;+ out_length = length + 1;+ }++ return true;+ }++#ifdef PUGIXML_WCHAR_MODE+ PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re)+ {+ return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) ||+ (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be);+ }++ PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)+ {+ const char_t* data = static_cast<const char_t*>(contents);+ size_t length = size / sizeof(char_t);++ if (is_mutable)+ {+ char_t* buffer = const_cast<char_t*>(data);++ convert_wchar_endian_swap(buffer, data, length);++ out_buffer = buffer;+ out_length = length;+ }+ else+ {+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ convert_wchar_endian_swap(buffer, data, length);+ buffer[length] = 0;++ out_buffer = buffer;+ out_length = length + 1;+ }++ return true;+ }++ PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)+ {+ const uint8_t* data = static_cast<const uint8_t*>(contents);+ size_t data_length = size;++ // first pass: get length in wchar_t units+ size_t length = utf_decoder<wchar_counter>::decode_utf8_block(data, data_length, 0);++ // allocate buffer of suitable length+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ // second pass: convert utf8 input to wchar_t+ wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);+ wchar_writer::value_type oend = utf_decoder<wchar_writer>::decode_utf8_block(data, data_length, obegin);++ assert(oend == obegin + length);+ *oend = 0;++ out_buffer = buffer;+ out_length = length + 1;++ return true;+ }++ template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)+ {+ const uint16_t* data = static_cast<const uint16_t*>(contents);+ size_t data_length = size / sizeof(uint16_t);++ // first pass: get length in wchar_t units+ size_t length = utf_decoder<wchar_counter, opt_swap>::decode_utf16_block(data, data_length, 0);++ // allocate buffer of suitable length+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ // second pass: convert utf16 input to wchar_t+ wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);+ wchar_writer::value_type oend = utf_decoder<wchar_writer, opt_swap>::decode_utf16_block(data, data_length, obegin);++ assert(oend == obegin + length);+ *oend = 0;++ out_buffer = buffer;+ out_length = length + 1;++ return true;+ }++ template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)+ {+ const uint32_t* data = static_cast<const uint32_t*>(contents);+ size_t data_length = size / sizeof(uint32_t);++ // first pass: get length in wchar_t units+ size_t length = utf_decoder<wchar_counter, opt_swap>::decode_utf32_block(data, data_length, 0);++ // allocate buffer of suitable length+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ // second pass: convert utf32 input to wchar_t+ wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);+ wchar_writer::value_type oend = utf_decoder<wchar_writer, opt_swap>::decode_utf32_block(data, data_length, obegin);++ assert(oend == obegin + length);+ *oend = 0;++ out_buffer = buffer;+ out_length = length + 1;++ return true;+ }++ PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)+ {+ const uint8_t* data = static_cast<const uint8_t*>(contents);+ size_t data_length = size;++ // get length in wchar_t units+ size_t length = data_length;++ // allocate buffer of suitable length+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ // convert latin1 input to wchar_t+ wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);+ wchar_writer::value_type oend = utf_decoder<wchar_writer>::decode_latin1_block(data, data_length, obegin);++ assert(oend == obegin + length);+ *oend = 0;++ out_buffer = buffer;+ out_length = length + 1;++ return true;+ }++ PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)+ {+ // get native encoding+ xml_encoding wchar_encoding = get_wchar_encoding();++ // fast path: no conversion required+ if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);++ // only endian-swapping is required+ if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);++ // source encoding is utf8+ if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size);++ // source encoding is utf16+ if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)+ {+ xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;++ return (native_encoding == encoding) ?+ convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :+ convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());+ }++ // source encoding is utf32+ if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)+ {+ xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;++ return (native_encoding == encoding) ?+ convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :+ convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());+ }++ // source encoding is latin1+ if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size);++ assert(!"Invalid encoding");+ return false;+ }+#else+ template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)+ {+ const uint16_t* data = static_cast<const uint16_t*>(contents);+ size_t data_length = size / sizeof(uint16_t);++ // first pass: get length in utf8 units+ size_t length = utf_decoder<utf8_counter, opt_swap>::decode_utf16_block(data, data_length, 0);++ // allocate buffer of suitable length+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ // second pass: convert utf16 input to utf8+ uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);+ uint8_t* oend = utf_decoder<utf8_writer, opt_swap>::decode_utf16_block(data, data_length, obegin);++ assert(oend == obegin + length);+ *oend = 0;++ out_buffer = buffer;+ out_length = length + 1;++ return true;+ }++ template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)+ {+ const uint32_t* data = static_cast<const uint32_t*>(contents);+ size_t data_length = size / sizeof(uint32_t);++ // first pass: get length in utf8 units+ size_t length = utf_decoder<utf8_counter, opt_swap>::decode_utf32_block(data, data_length, 0);++ // allocate buffer of suitable length+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ // second pass: convert utf32 input to utf8+ uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);+ uint8_t* oend = utf_decoder<utf8_writer, opt_swap>::decode_utf32_block(data, data_length, obegin);++ assert(oend == obegin + length);+ *oend = 0;++ out_buffer = buffer;+ out_length = length + 1;++ return true;+ }++ PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size)+ {+ for (size_t i = 0; i < size; ++i)+ if (data[i] > 127)+ return i;++ return size;+ }++ PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)+ {+ const uint8_t* data = static_cast<const uint8_t*>(contents);+ size_t data_length = size;++ // get size of prefix that does not need utf8 conversion+ size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length);+ assert(prefix_length <= data_length);++ const uint8_t* postfix = data + prefix_length;+ size_t postfix_length = data_length - prefix_length;++ // if no conversion is needed, just return the original buffer+ if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);++ // first pass: get length in utf8 units+ size_t length = prefix_length + utf_decoder<utf8_counter>::decode_latin1_block(postfix, postfix_length, 0);++ // allocate buffer of suitable length+ char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!buffer) return false;++ // second pass: convert latin1 input to utf8+ memcpy(buffer, data, prefix_length);++ uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);+ uint8_t* oend = utf_decoder<utf8_writer>::decode_latin1_block(postfix, postfix_length, obegin + prefix_length);++ assert(oend == obegin + length);+ *oend = 0;++ out_buffer = buffer;+ out_length = length + 1;++ return true;+ }++ PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)+ {+ // fast path: no conversion required+ if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);++ // source encoding is utf16+ if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)+ {+ xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;++ return (native_encoding == encoding) ?+ convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :+ convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());+ }++ // source encoding is utf32+ if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)+ {+ xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;++ return (native_encoding == encoding) ?+ convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :+ convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());+ }++ // source encoding is latin1+ if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);++ assert(!"Invalid encoding");+ return false;+ }+#endif++ PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length)+ {+ // get length in utf8 characters+ return utf_decoder<utf8_counter>::decode_wchar_block(str, length, 0);+ }++ PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)+ {+ // convert to utf8+ uint8_t* begin = reinterpret_cast<uint8_t*>(buffer);+ uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(str, length, begin);+ + assert(begin + size == end);+ (void)!end;++ // zero-terminate+ buffer[size] = 0;+ }+ +#ifndef PUGIXML_NO_STL+ PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length)+ {+ // first pass: get length in utf8 characters+ size_t size = as_utf8_begin(str, length);++ // allocate resulting string+ std::string result;+ result.resize(size);++ // second pass: convert to utf8+ if (size > 0) as_utf8_end(&result[0], size, str, length);++ return result;+ }++ PUGI__FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size)+ {+ const uint8_t* data = reinterpret_cast<const uint8_t*>(str);++ // first pass: get length in wchar_t units+ size_t length = utf_decoder<wchar_counter>::decode_utf8_block(data, size, 0);++ // allocate resulting string+ std::basic_string<wchar_t> result;+ result.resize(length);++ // second pass: convert to wchar_t+ if (length > 0)+ {+ wchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]);+ wchar_writer::value_type end = utf_decoder<wchar_writer>::decode_utf8_block(data, size, begin);++ assert(begin + length == end);+ (void)!end;+ }++ return result;+ }+#endif++ inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target)+ {+ assert(target);+ size_t target_length = strlength(target);++ // always reuse document buffer memory if possible+ if (!allocated) return target_length >= length;++ // reuse heap memory if waste is not too great+ const size_t reuse_threshold = 32;++ return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2);+ }++ PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source)+ {+ assert(header);++ size_t source_length = strlength(source);++ if (source_length == 0)+ {+ // empty string and null pointer are equivalent, so just deallocate old memory+ xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;++ if (header & header_mask) alloc->deallocate_string(dest);+ + // mark the string as not allocated+ dest = 0;+ header &= ~header_mask;++ return true;+ }+ else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest))+ {+ // we can reuse old buffer, so just copy the new data (including zero terminator)+ memcpy(dest, source, (source_length + 1) * sizeof(char_t));+ + return true;+ }+ else+ {+ xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;++ // allocate new buffer+ char_t* buf = alloc->allocate_string(source_length + 1);+ if (!buf) return false;++ // copy the string (including zero terminator)+ memcpy(buf, source, (source_length + 1) * sizeof(char_t));++ // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures)+ if (header & header_mask) alloc->deallocate_string(dest);+ + // the string is now allocated, so set the flag+ dest = buf;+ header |= header_mask;++ return true;+ }+ }++ struct gap+ {+ char_t* end;+ size_t size;+ + gap(): end(0), size(0)+ {+ }+ + // Push new gap, move s count bytes further (skipping the gap).+ // Collapse previous gap.+ void push(char_t*& s, size_t count)+ {+ if (end) // there was a gap already; collapse it+ {+ // Move [old_gap_end, new_gap_start) to [old_gap_start, ...)+ assert(s >= end);+ memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));+ }+ + s += count; // end of current gap+ + // "merge" two gaps+ end = s;+ size += count;+ }+ + // Collapse all gaps, return past-the-end pointer+ char_t* flush(char_t* s)+ {+ if (end)+ {+ // Move [old_gap_end, current_pos) to [old_gap_start, ...)+ assert(s >= end);+ memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));++ return s - size;+ }+ else return s;+ }+ };+ + PUGI__FN char_t* strconv_escape(char_t* s, gap& g)+ {+ char_t* stre = s + 1;++ switch (*stre)+ {+ case '#': // &#...+ {+ unsigned int ucsc = 0;++ if (stre[1] == 'x') // &#x... (hex code)+ {+ stre += 2;++ char_t ch = *stre;++ if (ch == ';') return stre;++ for (;;)+ {+ if (static_cast<unsigned int>(ch - '0') <= 9)+ ucsc = 16 * ucsc + (ch - '0');+ else if (static_cast<unsigned int>((ch | ' ') - 'a') <= 5)+ ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10);+ else if (ch == ';')+ break;+ else // cancel+ return stre;++ ch = *++stre;+ }+ + ++stre;+ }+ else // &#... (dec code)+ {+ char_t ch = *++stre;++ if (ch == ';') return stre;++ for (;;)+ {+ if (static_cast<unsigned int>(static_cast<unsigned int>(ch) - '0') <= 9)+ ucsc = 10 * ucsc + (ch - '0');+ else if (ch == ';')+ break;+ else // cancel+ return stre;++ ch = *++stre;+ }+ + ++stre;+ }++ #ifdef PUGIXML_WCHAR_MODE+ s = reinterpret_cast<char_t*>(wchar_writer::any(reinterpret_cast<wchar_writer::value_type>(s), ucsc));+ #else+ s = reinterpret_cast<char_t*>(utf8_writer::any(reinterpret_cast<uint8_t*>(s), ucsc));+ #endif+ + g.push(s, stre - s);+ return stre;+ }++ case 'a': // &a+ {+ ++stre;++ if (*stre == 'm') // &am+ {+ if (*++stre == 'p' && *++stre == ';') // &+ {+ *s++ = '&';+ ++stre;+ + g.push(s, stre - s);+ return stre;+ }+ }+ else if (*stre == 'p') // &ap+ {+ if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // '+ {+ *s++ = '\'';+ ++stre;++ g.push(s, stre - s);+ return stre;+ }+ }+ break;+ }++ case 'g': // &g+ {+ if (*++stre == 't' && *++stre == ';') // >+ {+ *s++ = '>';+ ++stre;+ + g.push(s, stre - s);+ return stre;+ }+ break;+ }++ case 'l': // &l+ {+ if (*++stre == 't' && *++stre == ';') // <+ {+ *s++ = '<';+ ++stre;+ + g.push(s, stre - s);+ return stre;+ }+ break;+ }++ case 'q': // &q+ {+ if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // "+ {+ *s++ = '"';+ ++stre;+ + g.push(s, stre - s);+ return stre;+ }+ break;+ }++ default:+ break;+ }+ + return stre;+ }++ // Utility macro for last character handling+ #define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))++ PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)+ {+ gap g;+ + while (true)+ {+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) ++s;+ + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair+ {+ *s++ = '\n'; // replace first one with 0x0a+ + if (*s == '\n') g.push(s, 1);+ }+ else if (s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')) // comment ends here+ {+ *g.flush(s) = 0;+ + return s + (s[2] == '>' ? 3 : 2);+ }+ else if (*s == 0)+ {+ return 0;+ }+ else ++s;+ }+ }++ PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch)+ {+ gap g;+ + while (true)+ {+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) ++s;+ + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair+ {+ *s++ = '\n'; // replace first one with 0x0a+ + if (*s == '\n') g.push(s, 1);+ }+ else if (s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here+ {+ *g.flush(s) = 0;+ + return s + 1;+ }+ else if (*s == 0)+ {+ return 0;+ }+ else ++s;+ }+ }+ + typedef char_t* (*strconv_pcdata_t)(char_t*);+ + template <typename opt_trim, typename opt_eol, typename opt_escape> struct strconv_pcdata_impl+ {+ static char_t* parse(char_t* s)+ {+ gap g;++ char_t* begin = s;++ while (true)+ {+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s;+ + if (*s == '<') // PCDATA ends here+ {+ char_t* end = g.flush(s);++ if (opt_trim::value)+ while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))+ --end;++ *end = 0;+ + return s + 1;+ }+ else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair+ {+ *s++ = '\n'; // replace first one with 0x0a+ + if (*s == '\n') g.push(s, 1);+ }+ else if (opt_escape::value && *s == '&')+ {+ s = strconv_escape(s, g);+ }+ else if (*s == 0)+ {+ char_t* end = g.flush(s);++ if (opt_trim::value)+ while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))+ --end;++ *end = 0;++ return s;+ }+ else ++s;+ }+ }+ };+ + PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)+ {+ PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800);++ switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim)+ {+ case 0: return strconv_pcdata_impl<opt_false, opt_false, opt_false>::parse;+ case 1: return strconv_pcdata_impl<opt_false, opt_false, opt_true>::parse;+ case 2: return strconv_pcdata_impl<opt_false, opt_true, opt_false>::parse;+ case 3: return strconv_pcdata_impl<opt_false, opt_true, opt_true>::parse;+ case 4: return strconv_pcdata_impl<opt_true, opt_false, opt_false>::parse;+ case 5: return strconv_pcdata_impl<opt_true, opt_false, opt_true>::parse;+ case 6: return strconv_pcdata_impl<opt_true, opt_true, opt_false>::parse;+ case 7: return strconv_pcdata_impl<opt_true, opt_true, opt_true>::parse;+ default: assert(false); return 0; // should not get here+ }+ }++ typedef char_t* (*strconv_attribute_t)(char_t*, char_t);+ + template <typename opt_escape> struct strconv_attribute_impl+ {+ static char_t* parse_wnorm(char_t* s, char_t end_quote)+ {+ gap g;++ // trim leading whitespaces+ if (PUGI__IS_CHARTYPE(*s, ct_space))+ {+ char_t* str = s;+ + do ++str;+ while (PUGI__IS_CHARTYPE(*str, ct_space));+ + g.push(s, str - s);+ }++ while (true)+ {+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s;+ + if (*s == end_quote)+ {+ char_t* str = g.flush(s);+ + do *str-- = 0;+ while (PUGI__IS_CHARTYPE(*str, ct_space));+ + return s + 1;+ }+ else if (PUGI__IS_CHARTYPE(*s, ct_space))+ {+ *s++ = ' ';+ + if (PUGI__IS_CHARTYPE(*s, ct_space))+ {+ char_t* str = s + 1;+ while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str;+ + g.push(s, str - s);+ }+ }+ else if (opt_escape::value && *s == '&')+ {+ s = strconv_escape(s, g);+ }+ else if (!*s)+ {+ return 0;+ }+ else ++s;+ }+ }++ static char_t* parse_wconv(char_t* s, char_t end_quote)+ {+ gap g;++ while (true)+ {+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s;+ + if (*s == end_quote)+ {+ *g.flush(s) = 0;+ + return s + 1;+ }+ else if (PUGI__IS_CHARTYPE(*s, ct_space))+ {+ if (*s == '\r')+ {+ *s++ = ' ';+ + if (*s == '\n') g.push(s, 1);+ }+ else *s++ = ' ';+ }+ else if (opt_escape::value && *s == '&')+ {+ s = strconv_escape(s, g);+ }+ else if (!*s)+ {+ return 0;+ }+ else ++s;+ }+ }++ static char_t* parse_eol(char_t* s, char_t end_quote)+ {+ gap g;++ while (true)+ {+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;+ + if (*s == end_quote)+ {+ *g.flush(s) = 0;+ + return s + 1;+ }+ else if (*s == '\r')+ {+ *s++ = '\n';+ + if (*s == '\n') g.push(s, 1);+ }+ else if (opt_escape::value && *s == '&')+ {+ s = strconv_escape(s, g);+ }+ else if (!*s)+ {+ return 0;+ }+ else ++s;+ }+ }++ static char_t* parse_simple(char_t* s, char_t end_quote)+ {+ gap g;++ while (true)+ {+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;+ + if (*s == end_quote)+ {+ *g.flush(s) = 0;+ + return s + 1;+ }+ else if (opt_escape::value && *s == '&')+ {+ s = strconv_escape(s, g);+ }+ else if (!*s)+ {+ return 0;+ }+ else ++s;+ }+ }+ };++ PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)+ {+ PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);+ + switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes)+ {+ case 0: return strconv_attribute_impl<opt_false>::parse_simple;+ case 1: return strconv_attribute_impl<opt_true>::parse_simple;+ case 2: return strconv_attribute_impl<opt_false>::parse_eol;+ case 3: return strconv_attribute_impl<opt_true>::parse_eol;+ case 4: return strconv_attribute_impl<opt_false>::parse_wconv;+ case 5: return strconv_attribute_impl<opt_true>::parse_wconv;+ case 6: return strconv_attribute_impl<opt_false>::parse_wconv;+ case 7: return strconv_attribute_impl<opt_true>::parse_wconv;+ case 8: return strconv_attribute_impl<opt_false>::parse_wnorm;+ case 9: return strconv_attribute_impl<opt_true>::parse_wnorm;+ case 10: return strconv_attribute_impl<opt_false>::parse_wnorm;+ case 11: return strconv_attribute_impl<opt_true>::parse_wnorm;+ case 12: return strconv_attribute_impl<opt_false>::parse_wnorm;+ case 13: return strconv_attribute_impl<opt_true>::parse_wnorm;+ case 14: return strconv_attribute_impl<opt_false>::parse_wnorm;+ case 15: return strconv_attribute_impl<opt_true>::parse_wnorm;+ default: assert(false); return 0; // should not get here+ }+ }++ inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0)+ {+ xml_parse_result result;+ result.status = status;+ result.offset = offset;++ return result;+ }++ struct xml_parser+ {+ xml_allocator alloc;+ char_t* error_offset;+ xml_parse_status error_status;+ + // Parser utilities.+ #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }+ #define PUGI__OPTSET(OPT) ( optmsk & (OPT) )+ #define PUGI__PUSHNODE(TYPE) { cursor = append_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }+ #define PUGI__POPNODE() { cursor = cursor->parent; }+ #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; }+ #define PUGI__SCANWHILE(X) { while ((X)) ++s; }+ #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; }+ #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast<char_t*>(0)+ #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); }+ + xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)+ {+ }++ // DOCTYPE consists of nested sections of the following possible types:+ // <!-- ... -->, <? ... ?>, "...", '...'+ // <![...]]>+ // <!...>+ // First group can not contain nested groups+ // Second group can contain nested groups of the same type+ // Third group can contain all other groups+ char_t* parse_doctype_primitive(char_t* s)+ {+ if (*s == '"' || *s == '\'')+ {+ // quoted string+ char_t ch = *s++;+ PUGI__SCANFOR(*s == ch);+ if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);++ s++;+ }+ else if (s[0] == '<' && s[1] == '?')+ {+ // <? ... ?>+ s += 2;+ PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype+ if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);++ s += 2;+ }+ else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-')+ {+ s += 4;+ PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype+ if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);++ s += 4;+ }+ else PUGI__THROW_ERROR(status_bad_doctype, s);++ return s;+ }++ char_t* parse_doctype_ignore(char_t* s)+ {+ assert(s[0] == '<' && s[1] == '!' && s[2] == '[');+ s++;++ while (*s)+ {+ if (s[0] == '<' && s[1] == '!' && s[2] == '[')+ {+ // nested ignore section+ s = parse_doctype_ignore(s);+ if (!s) return s;+ }+ else if (s[0] == ']' && s[1] == ']' && s[2] == '>')+ {+ // ignore section end+ s += 3;++ return s;+ }+ else s++;+ }++ PUGI__THROW_ERROR(status_bad_doctype, s);+ }++ char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel)+ {+ assert((s[0] == '<' || s[0] == 0) && s[1] == '!');+ s++;++ while (*s)+ {+ if (s[0] == '<' && s[1] == '!' && s[2] != '-')+ {+ if (s[2] == '[')+ {+ // ignore+ s = parse_doctype_ignore(s);+ if (!s) return s;+ }+ else+ {+ // some control group+ s = parse_doctype_group(s, endch, false);+ if (!s) return s;++ // skip >+ assert(*s == '>');+ s++;+ }+ }+ else if (s[0] == '<' || s[0] == '"' || s[0] == '\'')+ {+ // unknown tag (forbidden), or some primitive group+ s = parse_doctype_primitive(s);+ if (!s) return s;+ }+ else if (*s == '>')+ {+ return s;+ }+ else s++;+ }++ if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);++ return s;+ }++ char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch)+ {+ // parse node contents, starting with exclamation mark+ ++s;++ if (*s == '-') // '<!-...'+ {+ ++s;++ if (*s == '-') // '<!--...'+ {+ ++s;++ if (PUGI__OPTSET(parse_comments))+ {+ PUGI__PUSHNODE(node_comment); // Append a new node on the tree.+ cursor->value = s; // Save the offset.+ }++ if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments))+ {+ s = strconv_comment(s, endch);++ if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value);+ }+ else+ {+ // Scan for terminating '-->'.+ PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>'));+ PUGI__CHECK_ERROR(status_bad_comment, s);++ if (PUGI__OPTSET(parse_comments))+ *s = 0; // Zero-terminate this segment at the first terminating '-'.++ s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'.+ }+ }+ else PUGI__THROW_ERROR(status_bad_comment, s);+ }+ else if (*s == '[')+ {+ // '<![CDATA[...'+ if (*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[')+ {+ ++s;++ if (PUGI__OPTSET(parse_cdata))+ {+ PUGI__PUSHNODE(node_cdata); // Append a new node on the tree.+ cursor->value = s; // Save the offset.++ if (PUGI__OPTSET(parse_eol))+ {+ s = strconv_cdata(s, endch);++ if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value);+ }+ else+ {+ // Scan for terminating ']]>'.+ PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));+ PUGI__CHECK_ERROR(status_bad_cdata, s);++ *s++ = 0; // Zero-terminate this segment.+ }+ }+ else // Flagged for discard, but we still have to scan for the terminator.+ {+ // Scan for terminating ']]>'.+ PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));+ PUGI__CHECK_ERROR(status_bad_cdata, s);++ ++s;+ }++ s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'.+ }+ else PUGI__THROW_ERROR(status_bad_cdata, s);+ }+ else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && ENDSWITH(s[6], 'E'))+ {+ s -= 2;++ if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s);++ char_t* mark = s + 9;++ s = parse_doctype_group(s, endch, true);+ if (!s) return s;++ assert((*s == 0 && endch == '>') || *s == '>');+ if (*s) *s++ = 0;++ if (PUGI__OPTSET(parse_doctype))+ {+ while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark;++ PUGI__PUSHNODE(node_doctype);++ cursor->value = mark;++ PUGI__POPNODE();+ }+ }+ else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s);+ else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s);+ else PUGI__THROW_ERROR(status_unrecognized_tag, s);++ return s;+ }++ char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch)+ {+ // load into registers+ xml_node_struct* cursor = ref_cursor;+ char_t ch = 0;++ // parse node contents, starting with question mark+ ++s;++ // read PI target+ char_t* target = s;++ if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s);++ PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol));+ PUGI__CHECK_ERROR(status_bad_pi, s);++ // determine node type; stricmp / strcasecmp is not portable+ bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s;++ if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi))+ {+ if (declaration)+ {+ // disallow non top-level declarations+ if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s);++ PUGI__PUSHNODE(node_declaration);+ }+ else+ {+ PUGI__PUSHNODE(node_pi);+ }++ cursor->name = target;++ PUGI__ENDSEG();++ // parse value/attributes+ if (ch == '?')+ {+ // empty node+ if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);+ s += (*s == '>');++ PUGI__POPNODE();+ }+ else if (PUGI__IS_CHARTYPE(ch, ct_space))+ {+ PUGI__SKIPWS();++ // scan for tag end+ char_t* value = s;++ PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));+ PUGI__CHECK_ERROR(status_bad_pi, s);++ if (declaration)+ {+ // replace ending ? with / so that 'element' terminates properly+ *s = '/';++ // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES+ s = value;+ }+ else+ {+ // store value and step over >+ cursor->value = value;+ PUGI__POPNODE();++ PUGI__ENDSEG();++ s += (*s == '>');+ }+ }+ else PUGI__THROW_ERROR(status_bad_pi, s);+ }+ else+ {+ // scan for tag end+ PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));+ PUGI__CHECK_ERROR(status_bad_pi, s);++ s += (s[1] == '>' ? 2 : 1);+ }++ // store from registers+ ref_cursor = cursor;++ return s;+ }++ char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch)+ {+ strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk);+ strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk);+ + char_t ch = 0;+ xml_node_struct* cursor = root;+ char_t* mark = s;++ while (*s != 0)+ {+ if (*s == '<')+ {+ ++s;++ LOC_TAG:+ if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...'+ {+ PUGI__PUSHNODE(node_element); // Append a new node to the tree.++ cursor->name = s;++ PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.+ PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.++ if (ch == '>')+ {+ // end of tag+ }+ else if (PUGI__IS_CHARTYPE(ch, ct_space))+ {+ LOC_ATTRIBUTES:+ while (true)+ {+ PUGI__SKIPWS(); // Eat any whitespace.+ + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...+ {+ xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute.+ if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);++ a->name = s; // Save the offset.++ PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.+ PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance++ PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.+ PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance++ if (PUGI__IS_CHARTYPE(ch, ct_space))+ {+ PUGI__SKIPWS(); // Eat any whitespace.+ PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance++ ch = *s;+ ++s;+ }+ + if (ch == '=') // '<... #=...'+ {+ PUGI__SKIPWS(); // Eat any whitespace.++ if (*s == '"' || *s == '\'') // '<... #="...'+ {+ ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.+ ++s; // Step over the quote.+ a->value = s; // Save the offset.++ s = strconv_attribute(s, ch);+ + if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value);++ // After this line the loop continues from the start;+ // Whitespaces, / and > are ok, symbols and EOF are wrong,+ // everything else will be detected+ if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s);+ }+ else PUGI__THROW_ERROR(status_bad_attribute, s);+ }+ else PUGI__THROW_ERROR(status_bad_attribute, s);+ }+ else if (*s == '/')+ {+ ++s;+ + if (*s == '>')+ {+ PUGI__POPNODE();+ s++;+ break;+ }+ else if (*s == 0 && endch == '>')+ {+ PUGI__POPNODE();+ break;+ }+ else PUGI__THROW_ERROR(status_bad_start_element, s);+ }+ else if (*s == '>')+ {+ ++s;++ break;+ }+ else if (*s == 0 && endch == '>')+ {+ break;+ }+ else PUGI__THROW_ERROR(status_bad_start_element, s);+ }++ // !!!+ }+ else if (ch == '/') // '<#.../'+ {+ if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);++ PUGI__POPNODE(); // Pop.++ s += (*s == '>');+ }+ else if (ch == 0)+ {+ // we stepped over null terminator, backtrack & handle closing tag+ --s;+ + if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s);+ }+ else PUGI__THROW_ERROR(status_bad_start_element, s);+ }+ else if (*s == '/')+ {+ ++s;++ char_t* name = cursor->name;+ if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s);+ + while (PUGI__IS_CHARTYPE(*s, ct_symbol))+ {+ if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s);+ }++ if (*name)+ {+ if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s);+ else PUGI__THROW_ERROR(status_end_element_mismatch, s);+ }+ + PUGI__POPNODE(); // Pop.++ PUGI__SKIPWS();++ if (*s == 0)+ {+ if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s);+ }+ else+ {+ if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s);+ ++s;+ }+ }+ else if (*s == '?') // '<?...'+ {+ s = parse_question(s, cursor, optmsk, endch);+ if (!s) return s;++ assert(cursor);+ if ((cursor->header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES;+ }+ else if (*s == '!') // '<!...'+ {+ s = parse_exclamation(s, cursor, optmsk, endch);+ if (!s) return s;+ }+ else if (*s == 0 && endch == '?') PUGI__THROW_ERROR(status_bad_pi, s);+ else PUGI__THROW_ERROR(status_unrecognized_tag, s);+ }+ else+ {+ mark = s; // Save this offset while searching for a terminator.++ PUGI__SKIPWS(); // Eat whitespace if no genuine PCDATA here.++ if (*s == '<' || !*s)+ {+ // We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one+ assert(mark != s);++ if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI__OPTSET(parse_trim_pcdata))+ {+ continue;+ }+ else if (PUGI__OPTSET(parse_ws_pcdata_single))+ {+ if (s[0] != '<' || s[1] != '/' || cursor->first_child) continue;+ }+ }++ if (!PUGI__OPTSET(parse_trim_pcdata))+ s = mark;+ + if (cursor->parent || PUGI__OPTSET(parse_fragment))+ {+ PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.+ cursor->value = s; // Save the offset.++ s = strconv_pcdata(s);+ + PUGI__POPNODE(); // Pop since this is a standalone.+ + if (!*s) break;+ }+ else+ {+ PUGI__SCANFOR(*s == '<'); // '...<'+ if (!*s) break;+ + ++s;+ }++ // We're after '<'+ goto LOC_TAG;+ }+ }++ // check that last tag is closed+ if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s);++ return s;+ }++ #ifdef PUGIXML_WCHAR_MODE+ static char_t* parse_skip_bom(char_t* s)+ {+ unsigned int bom = 0xfeff;+ return (s[0] == static_cast<wchar_t>(bom)) ? s + 1 : s;+ }+ #else+ static char_t* parse_skip_bom(char_t* s)+ {+ return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s;+ }+ #endif++ static bool has_element_node_siblings(xml_node_struct* node)+ {+ while (node)+ {+ xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1);+ if (type == node_element) return true;++ node = node->next_sibling;+ }++ return false;+ }++ static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk)+ {+ // allocator object is a part of document object+ xml_allocator& alloc = *static_cast<xml_allocator*>(xmldoc);++ // early-out for empty documents+ if (length == 0)+ return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);++ // get last child of the root before parsing+ xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c : 0;+ + // create parser on stack+ xml_parser parser(alloc);++ // save last character and make buffer zero-terminated (speeds up parsing)+ char_t endch = buffer[length - 1];+ buffer[length - 1] = 0;+ + // skip BOM to make sure it does not end up as part of parse output+ char_t* buffer_data = parse_skip_bom(buffer);++ // perform actual parsing+ parser.parse_tree(buffer_data, root, optmsk, endch);++ // update allocator state+ alloc = parser.alloc;++ xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);+ assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);++ if (result)+ {+ // since we removed last character, we have to handle the only possible false positive (stray <)+ if (endch == '<')+ return make_parse_result(status_unrecognized_tag, length - 1);++ // check if there are any element nodes parsed+ xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling : root->first_child;++ if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed))+ return make_parse_result(status_no_document_element, length - 1);+ }+ else+ {+ // roll back offset if it occurs on a null terminator in the source buffer+ if (result.offset > 0 && static_cast<size_t>(result.offset) == length - 1 && endch == 0)+ result.offset--;+ }++ return result;+ }+ };++ // Output facilities+ PUGI__FN xml_encoding get_write_native_encoding()+ {+ #ifdef PUGIXML_WCHAR_MODE+ return get_wchar_encoding();+ #else+ return encoding_utf8;+ #endif+ }++ PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding)+ {+ // replace wchar encoding with utf implementation+ if (encoding == encoding_wchar) return get_wchar_encoding();++ // replace utf16 encoding with utf16 with specific endianness+ if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;++ // replace utf32 encoding with utf32 with specific endianness+ if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;++ // only do autodetection if no explicit encoding is requested+ if (encoding != encoding_auto) return encoding;++ // assume utf8 encoding+ return encoding_utf8;+ }++#ifdef PUGIXML_WCHAR_MODE+ PUGI__FN size_t get_valid_length(const char_t* data, size_t length)+ {+ assert(length > 0);++ // discard last character if it's the lead of a surrogate pair + return (sizeof(wchar_t) == 2 && static_cast<unsigned int>(static_cast<uint16_t>(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length;+ }++ PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)+ {+ // only endian-swapping is required+ if (need_endian_swap_utf(encoding, get_wchar_encoding()))+ {+ convert_wchar_endian_swap(r_char, data, length);++ return length * sizeof(char_t);+ }+ + // convert to utf8+ if (encoding == encoding_utf8)+ {+ uint8_t* dest = r_u8;+ uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(data, length, dest);++ return static_cast<size_t>(end - dest);+ }++ // convert to utf16+ if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)+ {+ uint16_t* dest = r_u16;++ // convert to native utf16+ uint16_t* end = utf_decoder<utf16_writer>::decode_wchar_block(data, length, dest);++ // swap if necessary+ xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;++ if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));++ return static_cast<size_t>(end - dest) * sizeof(uint16_t);+ }++ // convert to utf32+ if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)+ {+ uint32_t* dest = r_u32;++ // convert to native utf32+ uint32_t* end = utf_decoder<utf32_writer>::decode_wchar_block(data, length, dest);++ // swap if necessary+ xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;++ if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));++ return static_cast<size_t>(end - dest) * sizeof(uint32_t);+ }++ // convert to latin1+ if (encoding == encoding_latin1)+ {+ uint8_t* dest = r_u8;+ uint8_t* end = utf_decoder<latin1_writer>::decode_wchar_block(data, length, dest);++ return static_cast<size_t>(end - dest);+ }++ assert(!"Invalid encoding");+ return 0;+ }+#else+ PUGI__FN size_t get_valid_length(const char_t* data, size_t length)+ {+ assert(length > 4);++ for (size_t i = 1; i <= 4; ++i)+ {+ uint8_t ch = static_cast<uint8_t>(data[length - i]);++ // either a standalone character or a leading one+ if ((ch & 0xc0) != 0x80) return length - i;+ }++ // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk+ return length;+ }++ PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)+ {+ if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)+ {+ uint16_t* dest = r_u16;++ // convert to native utf16+ uint16_t* end = utf_decoder<utf16_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);++ // swap if necessary+ xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;++ if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));++ return static_cast<size_t>(end - dest) * sizeof(uint16_t);+ }++ if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)+ {+ uint32_t* dest = r_u32;++ // convert to native utf32+ uint32_t* end = utf_decoder<utf32_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);++ // swap if necessary+ xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;++ if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));++ return static_cast<size_t>(end - dest) * sizeof(uint32_t);+ }++ if (encoding == encoding_latin1)+ {+ uint8_t* dest = r_u8;+ uint8_t* end = utf_decoder<latin1_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);++ return static_cast<size_t>(end - dest);+ }++ assert(!"Invalid encoding");+ return 0;+ }+#endif++ class xml_buffered_writer+ {+ xml_buffered_writer(const xml_buffered_writer&);+ xml_buffered_writer& operator=(const xml_buffered_writer&);++ public:+ xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding))+ {+ PUGI__STATIC_ASSERT(bufcapacity >= 8);+ }++ ~xml_buffered_writer()+ {+ flush();+ }++ void flush()+ {+ flush(buffer, bufsize);+ bufsize = 0;+ }++ void flush(const char_t* data, size_t size)+ {+ if (size == 0) return;++ // fast path, just write data+ if (encoding == get_write_native_encoding())+ writer.write(data, size * sizeof(char_t));+ else+ {+ // convert chunk+ size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);+ assert(result <= sizeof(scratch));++ // write data+ writer.write(scratch.data_u8, result);+ }+ }++ void write(const char_t* data, size_t length)+ {+ if (bufsize + length > bufcapacity)+ {+ // flush the remaining buffer contents+ flush();++ // handle large chunks+ if (length > bufcapacity)+ {+ if (encoding == get_write_native_encoding())+ {+ // fast path, can just write data chunk+ writer.write(data, length * sizeof(char_t));+ return;+ }++ // need to convert in suitable chunks+ while (length > bufcapacity)+ {+ // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer+ // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)+ size_t chunk_size = get_valid_length(data, bufcapacity);++ // convert chunk and write+ flush(data, chunk_size);++ // iterate+ data += chunk_size;+ length -= chunk_size;+ }++ // small tail is copied below+ bufsize = 0;+ }+ }++ memcpy(buffer + bufsize, data, length * sizeof(char_t));+ bufsize += length;+ }++ void write(const char_t* data)+ {+ write(data, strlength(data));+ }++ void write(char_t d0)+ {+ if (bufsize + 1 > bufcapacity) flush();++ buffer[bufsize + 0] = d0;+ bufsize += 1;+ }++ void write(char_t d0, char_t d1)+ {+ if (bufsize + 2 > bufcapacity) flush();++ buffer[bufsize + 0] = d0;+ buffer[bufsize + 1] = d1;+ bufsize += 2;+ }++ void write(char_t d0, char_t d1, char_t d2)+ {+ if (bufsize + 3 > bufcapacity) flush();++ buffer[bufsize + 0] = d0;+ buffer[bufsize + 1] = d1;+ buffer[bufsize + 2] = d2;+ bufsize += 3;+ }++ void write(char_t d0, char_t d1, char_t d2, char_t d3)+ {+ if (bufsize + 4 > bufcapacity) flush();++ buffer[bufsize + 0] = d0;+ buffer[bufsize + 1] = d1;+ buffer[bufsize + 2] = d2;+ buffer[bufsize + 3] = d3;+ bufsize += 4;+ }++ void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)+ {+ if (bufsize + 5 > bufcapacity) flush();++ buffer[bufsize + 0] = d0;+ buffer[bufsize + 1] = d1;+ buffer[bufsize + 2] = d2;+ buffer[bufsize + 3] = d3;+ buffer[bufsize + 4] = d4;+ bufsize += 5;+ }++ void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)+ {+ if (bufsize + 6 > bufcapacity) flush();++ buffer[bufsize + 0] = d0;+ buffer[bufsize + 1] = d1;+ buffer[bufsize + 2] = d2;+ buffer[bufsize + 3] = d3;+ buffer[bufsize + 4] = d4;+ buffer[bufsize + 5] = d5;+ bufsize += 6;+ }++ // utf8 maximum expansion: x4 (-> utf32)+ // utf16 maximum expansion: x2 (-> utf32)+ // utf32 maximum expansion: x1+ enum+ {+ bufcapacitybytes =+ #ifdef PUGIXML_MEMORY_OUTPUT_STACK+ PUGIXML_MEMORY_OUTPUT_STACK+ #else+ 10240+ #endif+ ,+ bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4)+ };++ char_t buffer[bufcapacity];++ union+ {+ uint8_t data_u8[4 * bufcapacity];+ uint16_t data_u16[2 * bufcapacity];+ uint32_t data_u32[bufcapacity];+ char_t data_char[bufcapacity];+ } scratch;++ xml_writer& writer;+ size_t bufsize;+ xml_encoding encoding;+ };++ PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type)+ {+ while (*s)+ {+ const char_t* prev = s;+ + // While *s is a usual symbol+ while (!PUGI__IS_CHARTYPEX(*s, type)) ++s;+ + writer.write(prev, static_cast<size_t>(s - prev));++ switch (*s)+ {+ case 0: break;+ case '&':+ writer.write('&', 'a', 'm', 'p', ';');+ ++s;+ break;+ case '<':+ writer.write('&', 'l', 't', ';');+ ++s;+ break;+ case '>':+ writer.write('&', 'g', 't', ';');+ ++s;+ break;+ case '"':+ writer.write('&', 'q', 'u', 'o', 't', ';');+ ++s;+ break;+ default: // s is not a usual symbol+ {+ unsigned int ch = static_cast<unsigned int>(*s++);+ assert(ch < 32);++ writer.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';');+ }+ }+ }+ }++ PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)+ {+ if (flags & format_no_escapes)+ writer.write(s);+ else+ text_output_escaped(writer, s, type);+ }++ PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)+ {+ do+ {+ writer.write('<', '!', '[', 'C', 'D');+ writer.write('A', 'T', 'A', '[');++ const char_t* prev = s;++ // look for ]]> sequence - we can't output it as is since it terminates CDATA+ while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s;++ // skip ]] if we stopped at ]]>, > will go to the next CDATA section+ if (*s) s += 2;++ writer.write(prev, static_cast<size_t>(s - prev));++ writer.write(']', ']', '>');+ }+ while (*s);+ }++ PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)+ {+ const char_t* default_name = PUGIXML_TEXT(":anonymous");++ for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())+ {+ writer.write(' ');+ writer.write(a.name()[0] ? a.name() : default_name);+ writer.write('=', '"');++ text_output(writer, a.value(), ctx_special_attr, flags);++ writer.write('"');+ }+ }++ PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth)+ {+ const char_t* default_name = PUGIXML_TEXT(":anonymous");++ if ((flags & format_indent) != 0 && (flags & format_raw) == 0)+ for (unsigned int i = 0; i < depth; ++i) writer.write(indent);++ switch (node.type())+ {+ case node_document:+ {+ for (xml_node n = node.first_child(); n; n = n.next_sibling())+ node_output(writer, n, indent, flags, depth);+ break;+ }+ + case node_element:+ {+ const char_t* name = node.name()[0] ? node.name() : default_name;++ writer.write('<');+ writer.write(name);++ node_output_attributes(writer, node, flags);++ if (flags & format_raw)+ {+ if (!node.first_child())+ writer.write(' ', '/', '>');+ else+ {+ writer.write('>');++ for (xml_node n = node.first_child(); n; n = n.next_sibling())+ node_output(writer, n, indent, flags, depth + 1);++ writer.write('<', '/');+ writer.write(name);+ writer.write('>');+ }+ }+ else if (!node.first_child())+ writer.write(' ', '/', '>', '\n');+ else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata))+ {+ writer.write('>');++ if (node.first_child().type() == node_pcdata)+ text_output(writer, node.first_child().value(), ctx_special_pcdata, flags);+ else+ text_output_cdata(writer, node.first_child().value());++ writer.write('<', '/');+ writer.write(name);+ writer.write('>', '\n');+ }+ else+ {+ writer.write('>', '\n');+ + for (xml_node n = node.first_child(); n; n = n.next_sibling())+ node_output(writer, n, indent, flags, depth + 1);++ if ((flags & format_indent) != 0 && (flags & format_raw) == 0)+ for (unsigned int i = 0; i < depth; ++i) writer.write(indent);+ + writer.write('<', '/');+ writer.write(name);+ writer.write('>', '\n');+ }++ break;+ }+ + case node_pcdata:+ text_output(writer, node.value(), ctx_special_pcdata, flags);+ if ((flags & format_raw) == 0) writer.write('\n');+ break;++ case node_cdata:+ text_output_cdata(writer, node.value());+ if ((flags & format_raw) == 0) writer.write('\n');+ break;++ case node_comment:+ writer.write('<', '!', '-', '-');+ writer.write(node.value());+ writer.write('-', '-', '>');+ if ((flags & format_raw) == 0) writer.write('\n');+ break;++ case node_pi:+ case node_declaration:+ writer.write('<', '?');+ writer.write(node.name()[0] ? node.name() : default_name);++ if (node.type() == node_declaration)+ {+ node_output_attributes(writer, node, flags);+ }+ else if (node.value()[0])+ {+ writer.write(' ');+ writer.write(node.value());+ }++ writer.write('?', '>');+ if ((flags & format_raw) == 0) writer.write('\n');+ break;++ case node_doctype:+ writer.write('<', '!', 'D', 'O', 'C');+ writer.write('T', 'Y', 'P', 'E');++ if (node.value()[0])+ {+ writer.write(' ');+ writer.write(node.value());+ }++ writer.write('>');+ if ((flags & format_raw) == 0) writer.write('\n');+ break;++ default:+ assert(!"Invalid node type");+ }+ }++ inline bool has_declaration(const xml_node& node)+ {+ for (xml_node child = node.first_child(); child; child = child.next_sibling())+ {+ xml_node_type type = child.type();++ if (type == node_declaration) return true;+ if (type == node_element) return false;+ }++ return false;+ }++ inline bool allow_insert_child(xml_node_type parent, xml_node_type child)+ {+ if (parent != node_document && parent != node_element) return false;+ if (child == node_document || child == node_null) return false;+ if (parent != node_document && (child == node_declaration || child == node_doctype)) return false;++ return true;+ }++ PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip)+ {+ assert(dest.type() == source.type());++ switch (source.type())+ {+ case node_element:+ {+ dest.set_name(source.name());++ for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())+ dest.append_attribute(a.name()).set_value(a.value());++ for (xml_node c = source.first_child(); c; c = c.next_sibling())+ {+ if (c == skip) continue;++ xml_node cc = dest.append_child(c.type());+ assert(cc);++ recursive_copy_skip(cc, c, skip);+ }++ break;+ }++ case node_pcdata:+ case node_cdata:+ case node_comment:+ case node_doctype:+ dest.set_value(source.value());+ break;++ case node_pi:+ dest.set_name(source.name());+ dest.set_value(source.value());+ break;++ case node_declaration:+ {+ dest.set_name(source.name());++ for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())+ dest.append_attribute(a.name()).set_value(a.value());++ break;+ }++ default:+ assert(!"Invalid node type");+ }+ }++ inline bool is_text_node(xml_node_struct* node)+ {+ xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1);++ return type == node_pcdata || type == node_cdata;+ }++ // get value with conversion functions+ PUGI__FN int get_integer_base(const char_t* value)+ {+ const char_t* s = value;++ while (PUGI__IS_CHARTYPE(*s, ct_space))+ s++;++ if (*s == '-')+ s++;++ return (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;+ }++ PUGI__FN int get_value_int(const char_t* value, int def)+ {+ if (!value) return def;++ int base = get_integer_base(value);++ #ifdef PUGIXML_WCHAR_MODE+ return static_cast<int>(wcstol(value, 0, base));+ #else+ return static_cast<int>(strtol(value, 0, base));+ #endif+ }++ PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def)+ {+ if (!value) return def;++ int base = get_integer_base(value);++ #ifdef PUGIXML_WCHAR_MODE+ return static_cast<unsigned int>(wcstoul(value, 0, base));+ #else+ return static_cast<unsigned int>(strtoul(value, 0, base));+ #endif+ }++ PUGI__FN double get_value_double(const char_t* value, double def)+ {+ if (!value) return def;++ #ifdef PUGIXML_WCHAR_MODE+ return wcstod(value, 0);+ #else+ return strtod(value, 0);+ #endif+ }++ PUGI__FN float get_value_float(const char_t* value, float def)+ {+ if (!value) return def;++ #ifdef PUGIXML_WCHAR_MODE+ return static_cast<float>(wcstod(value, 0));+ #else+ return static_cast<float>(strtod(value, 0));+ #endif+ }++ PUGI__FN bool get_value_bool(const char_t* value, bool def)+ {+ if (!value) return def;++ // only look at first char+ char_t first = *value;++ // 1*, t* (true), T* (True), y* (yes), Y* (YES)+ return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y');+ }++#ifdef PUGIXML_HAS_LONG_LONG+ PUGI__FN long long get_value_llong(const char_t* value, long long def)+ {+ if (!value) return def;++ int base = get_integer_base(value);++ #ifdef PUGIXML_WCHAR_MODE+ #ifdef PUGI__MSVC_CRT_VERSION+ return _wcstoi64(value, 0, base);+ #else+ return wcstoll(value, 0, base);+ #endif+ #else+ #ifdef PUGI__MSVC_CRT_VERSION+ return _strtoi64(value, 0, base);+ #else+ return strtoll(value, 0, base);+ #endif+ #endif+ }++ PUGI__FN unsigned long long get_value_ullong(const char_t* value, unsigned long long def)+ {+ if (!value) return def;++ int base = get_integer_base(value);++ #ifdef PUGIXML_WCHAR_MODE+ #ifdef PUGI__MSVC_CRT_VERSION+ return _wcstoui64(value, 0, base);+ #else+ return wcstoull(value, 0, base);+ #endif+ #else+ #ifdef PUGI__MSVC_CRT_VERSION+ return _strtoui64(value, 0, base);+ #else+ return strtoull(value, 0, base);+ #endif+ #endif+ }+#endif++ // set value with conversion functions+ PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128])+ {+ #ifdef PUGIXML_WCHAR_MODE+ char_t wbuf[128];+ impl::widen_ascii(wbuf, buf);++ return strcpy_insitu(dest, header, header_mask, wbuf);+ #else+ return strcpy_insitu(dest, header, header_mask, buf);+ #endif+ }++ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value)+ {+ char buf[128];+ sprintf(buf, "%d", value);+ + return set_value_buffer(dest, header, header_mask, buf);+ }++ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value)+ {+ char buf[128];+ sprintf(buf, "%u", value);++ return set_value_buffer(dest, header, header_mask, buf);+ }++ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value)+ {+ char buf[128];+ sprintf(buf, "%g", value);++ return set_value_buffer(dest, header, header_mask, buf);+ }+ + PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value)+ {+ return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));+ }++#ifdef PUGIXML_HAS_LONG_LONG+ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, long long value)+ {+ char buf[128];+ sprintf(buf, "%lld", value);+ + return set_value_buffer(dest, header, header_mask, buf);+ }++ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned long long value)+ {+ char buf[128];+ sprintf(buf, "%llu", value);+ + return set_value_buffer(dest, header, header_mask, buf);+ }+#endif++ // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick+ PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result)+ {+ #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)+ // there are 64-bit versions of fseek/ftell, let's use them+ typedef __int64 length_type;++ _fseeki64(file, 0, SEEK_END);+ length_type length = _ftelli64(file);+ _fseeki64(file, 0, SEEK_SET);+ #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__)+ // there are 64-bit versions of fseek/ftell, let's use them+ typedef off64_t length_type;++ fseeko64(file, 0, SEEK_END);+ length_type length = ftello64(file);+ fseeko64(file, 0, SEEK_SET);+ #else+ // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway.+ typedef long length_type;++ fseek(file, 0, SEEK_END);+ length_type length = ftell(file);+ fseek(file, 0, SEEK_SET);+ #endif++ // check for I/O errors+ if (length < 0) return status_io_error;+ + // check for overflow+ size_t result = static_cast<size_t>(length);++ if (static_cast<length_type>(result) != length) return status_out_of_memory;++ // finalize+ out_result = result;++ return status_ok;+ }++ PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) + {+ // We only need to zero-terminate if encoding conversion does not do it for us+ #ifdef PUGIXML_WCHAR_MODE+ xml_encoding wchar_encoding = get_wchar_encoding();++ if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding))+ {+ size_t length = size / sizeof(char_t);++ static_cast<char_t*>(buffer)[length] = 0;+ return (length + 1) * sizeof(char_t);+ }+ #else+ if (encoding == encoding_utf8)+ {+ static_cast<char*>(buffer)[size] = 0;+ return size + 1;+ }+ #endif++ return size;+ }++ PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding)+ {+ if (!file) return make_parse_result(status_file_not_found);++ // get file size (can result in I/O errors)+ size_t size = 0;+ xml_parse_status size_status = get_file_size(file, size);++ if (size_status != status_ok)+ {+ fclose(file);+ return make_parse_result(size_status);+ }+ + size_t max_suffix_size = sizeof(char_t);++ // allocate buffer for the whole file+ char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));++ if (!contents)+ {+ fclose(file);+ return make_parse_result(status_out_of_memory);+ }++ // read file in memory+ size_t read_size = fread(contents, 1, size, file);+ fclose(file);++ if (read_size != size)+ {+ xml_memory::deallocate(contents);+ return make_parse_result(status_io_error);+ }++ xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size);+ + return doc.load_buffer_inplace_own(contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding);+ }++#ifndef PUGIXML_NO_STL+ template <typename T> struct xml_stream_chunk+ {+ static xml_stream_chunk* create()+ {+ void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));+ + return new (memory) xml_stream_chunk();+ }++ static void destroy(void* ptr)+ {+ xml_stream_chunk* chunk = static_cast<xml_stream_chunk*>(ptr);++ // free chunk chain+ while (chunk)+ {+ xml_stream_chunk* next = chunk->next;+ xml_memory::deallocate(chunk);+ chunk = next;+ }+ }++ xml_stream_chunk(): next(0), size(0)+ {+ }++ xml_stream_chunk* next;+ size_t size;++ T data[xml_memory_page_size / sizeof(T)];+ };++ template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)+ {+ buffer_holder chunks(0, xml_stream_chunk<T>::destroy);++ // read file to a chunk list+ size_t total = 0;+ xml_stream_chunk<T>* last = 0;++ while (!stream.eof())+ {+ // allocate new chunk+ xml_stream_chunk<T>* chunk = xml_stream_chunk<T>::create();+ if (!chunk) return status_out_of_memory;++ // append chunk to list+ if (last) last = last->next = chunk;+ else chunks.data = last = chunk;++ // read data to chunk+ stream.read(chunk->data, static_cast<std::streamsize>(sizeof(chunk->data) / sizeof(T)));+ chunk->size = static_cast<size_t>(stream.gcount()) * sizeof(T);++ // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors+ if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;++ // guard against huge files (chunk size is small enough to make this overflow check work)+ if (total + chunk->size < total) return status_out_of_memory;+ total += chunk->size;+ }++ size_t max_suffix_size = sizeof(char_t);++ // copy chunk list to a contiguous buffer+ char* buffer = static_cast<char*>(xml_memory::allocate(total + max_suffix_size));+ if (!buffer) return status_out_of_memory;++ char* write = buffer;++ for (xml_stream_chunk<T>* chunk = static_cast<xml_stream_chunk<T>*>(chunks.data); chunk; chunk = chunk->next)+ {+ assert(write + chunk->size <= buffer + total);+ memcpy(write, chunk->data, chunk->size);+ write += chunk->size;+ }++ assert(write == buffer + total);++ // return buffer+ *out_buffer = buffer;+ *out_size = total;++ return status_ok;+ }++ template <typename T> PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)+ {+ // get length of remaining data in stream+ typename std::basic_istream<T>::pos_type pos = stream.tellg();+ stream.seekg(0, std::ios::end);+ std::streamoff length = stream.tellg() - pos;+ stream.seekg(pos);++ if (stream.fail() || pos < 0) return status_io_error;++ // guard against huge files+ size_t read_length = static_cast<size_t>(length);++ if (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory;++ size_t max_suffix_size = sizeof(char_t);++ // read stream data into memory (guard against stream exceptions with buffer holder)+ buffer_holder buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);+ if (!buffer.data) return status_out_of_memory;++ stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length));++ // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors+ if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;++ // return buffer+ size_t actual_length = static_cast<size_t>(stream.gcount());+ assert(actual_length <= read_length);+ + *out_buffer = buffer.release();+ *out_size = actual_length * sizeof(T);++ return status_ok;+ }++ template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding)+ {+ void* buffer = 0;+ size_t size = 0;+ xml_parse_status status = status_ok;++ // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits)+ if (stream.fail()) return make_parse_result(status_io_error);++ // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)+ if (stream.tellg() < 0)+ {+ stream.clear(); // clear error flags that could be set by a failing tellg+ status = load_stream_data_noseek(stream, &buffer, &size);+ }+ else+ status = load_stream_data_seek(stream, &buffer, &size);++ if (status != status_ok) return make_parse_result(status);++ xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size);+ + return doc.load_buffer_inplace_own(buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding);+ }+#endif++#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__))+ PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)+ {+ return _wfopen(path, mode);+ }+#else+ PUGI__FN char* convert_path_heap(const wchar_t* str)+ {+ assert(str);++ // first pass: get length in utf8 characters+ size_t length = strlength_wide(str);+ size_t size = as_utf8_begin(str, length);++ // allocate resulting string+ char* result = static_cast<char*>(xml_memory::allocate(size + 1));+ if (!result) return 0;++ // second pass: convert to utf8+ as_utf8_end(result, size, str, length);++ return result;+ }++ PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)+ {+ // there is no standard function to open wide paths, so our best bet is to try utf8 path+ char* path_utf8 = convert_path_heap(path);+ if (!path_utf8) return 0;++ // convert mode to ASCII (we mirror _wfopen interface)+ char mode_ascii[4] = {0};+ for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]);++ // try to open the utf8 path+ FILE* result = fopen(path_utf8, mode_ascii);++ // free dummy buffer+ xml_memory::deallocate(path_utf8);++ return result;+ }+#endif++ PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)+ {+ if (!file) return false;++ xml_writer_file writer(file);+ doc.save(writer, indent, flags, encoding);++ int result = ferror(file);++ fclose(file);++ return result == 0;+ }++ PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer)+ {+ // check input buffer+ assert(contents || size == 0);++ // get actual encoding+ xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);++ // get private buffer+ char_t* buffer = 0;+ size_t length = 0;++ if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);+ + // delete original buffer if we performed a conversion+ if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);++ // store buffer for offset_debug+ doc->buffer = buffer;++ // parse+ xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options);++ // remember encoding+ res.encoding = buffer_encoding;++ // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself+ if (own || buffer != contents) *out_buffer = buffer;++ return res;+ }+PUGI__NS_END++namespace pugi+{+ PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_)+ {+ }++ PUGI__FN void xml_writer_file::write(const void* data, size_t size)+ {+ size_t result = fwrite(data, 1, size, static_cast<FILE*>(file));+ (void)!result; // unfortunately we can't do proper error handling here+ }++#ifndef PUGIXML_NO_STL+ PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)+ {+ }++ PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream): narrow_stream(0), wide_stream(&stream)+ {+ }++ PUGI__FN void xml_writer_stream::write(const void* data, size_t size)+ {+ if (narrow_stream)+ {+ assert(!wide_stream);+ narrow_stream->write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size));+ }+ else+ {+ assert(wide_stream);+ assert(size % sizeof(wchar_t) == 0);++ wide_stream->write(reinterpret_cast<const wchar_t*>(data), static_cast<std::streamsize>(size / sizeof(wchar_t)));+ }+ }+#endif++ PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0)+ {+ }+ + PUGI__FN xml_tree_walker::~xml_tree_walker()+ {+ }++ PUGI__FN int xml_tree_walker::depth() const+ {+ return _depth;+ }++ PUGI__FN bool xml_tree_walker::begin(xml_node&)+ {+ return true;+ }++ PUGI__FN bool xml_tree_walker::end(xml_node&)+ {+ return true;+ }++ PUGI__FN xml_attribute::xml_attribute(): _attr(0)+ {+ }++ PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr)+ {+ }++ PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***)+ {+ }++ PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const+ {+ return _attr ? unspecified_bool_xml_attribute : 0;+ }++ PUGI__FN bool xml_attribute::operator!() const+ {+ return !_attr;+ }++ PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const+ {+ return (_attr == r._attr);+ }+ + PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const+ {+ return (_attr != r._attr);+ }++ PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const+ {+ return (_attr < r._attr);+ }+ + PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const+ {+ return (_attr > r._attr);+ }+ + PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const+ {+ return (_attr <= r._attr);+ }+ + PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const+ {+ return (_attr >= r._attr);+ }++ PUGI__FN xml_attribute xml_attribute::next_attribute() const+ {+ return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute();+ }++ PUGI__FN xml_attribute xml_attribute::previous_attribute() const+ {+ return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute();+ }++ PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const+ {+ return (_attr && _attr->value) ? _attr->value : def;+ }++ PUGI__FN int xml_attribute::as_int(int def) const+ {+ return impl::get_value_int(_attr ? _attr->value : 0, def);+ }++ PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const+ {+ return impl::get_value_uint(_attr ? _attr->value : 0, def);+ }++ PUGI__FN double xml_attribute::as_double(double def) const+ {+ return impl::get_value_double(_attr ? _attr->value : 0, def);+ }++ PUGI__FN float xml_attribute::as_float(float def) const+ {+ return impl::get_value_float(_attr ? _attr->value : 0, def);+ }++ PUGI__FN bool xml_attribute::as_bool(bool def) const+ {+ return impl::get_value_bool(_attr ? _attr->value : 0, def);+ }++#ifdef PUGIXML_HAS_LONG_LONG+ PUGI__FN long long xml_attribute::as_llong(long long def) const+ {+ return impl::get_value_llong(_attr ? _attr->value : 0, def);+ }++ PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const+ {+ return impl::get_value_ullong(_attr ? _attr->value : 0, def);+ }+#endif++ PUGI__FN bool xml_attribute::empty() const+ {+ return !_attr;+ }++ PUGI__FN const char_t* xml_attribute::name() const+ {+ return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT("");+ }++ PUGI__FN const char_t* xml_attribute::value() const+ {+ return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT("");+ }++ PUGI__FN size_t xml_attribute::hash_value() const+ {+ return static_cast<size_t>(reinterpret_cast<uintptr_t>(_attr) / sizeof(xml_attribute_struct));+ }++ PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const+ {+ return _attr;+ }++ PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs)+ {+ set_value(rhs);+ return *this;+ }+ + PUGI__FN xml_attribute& xml_attribute::operator=(int rhs)+ {+ set_value(rhs);+ return *this;+ }++ PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs)+ {+ set_value(rhs);+ return *this;+ }++ PUGI__FN xml_attribute& xml_attribute::operator=(double rhs)+ {+ set_value(rhs);+ return *this;+ }+ + PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)+ {+ set_value(rhs);+ return *this;+ }++#ifdef PUGIXML_HAS_LONG_LONG+ PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs)+ {+ set_value(rhs);+ return *this;+ }++ PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs)+ {+ set_value(rhs);+ return *this;+ }+#endif++ PUGI__FN bool xml_attribute::set_name(const char_t* rhs)+ {+ if (!_attr) return false;+ + return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs);+ }+ + PUGI__FN bool xml_attribute::set_value(const char_t* rhs)+ {+ if (!_attr) return false;++ return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);+ }++ PUGI__FN bool xml_attribute::set_value(int rhs)+ {+ if (!_attr) return false;++ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);+ }++ PUGI__FN bool xml_attribute::set_value(unsigned int rhs)+ {+ if (!_attr) return false;++ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);+ }++ PUGI__FN bool xml_attribute::set_value(double rhs)+ {+ if (!_attr) return false;++ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);+ }+ + PUGI__FN bool xml_attribute::set_value(bool rhs)+ {+ if (!_attr) return false;++ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);+ }++#ifdef PUGIXML_HAS_LONG_LONG+ PUGI__FN bool xml_attribute::set_value(long long rhs)+ {+ if (!_attr) return false;++ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);+ }++ PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)+ {+ if (!_attr) return false;++ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);+ }+#endif++#ifdef __BORLANDC__+ PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs)+ {+ return (bool)lhs && rhs;+ }++ PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs)+ {+ return (bool)lhs || rhs;+ }+#endif++ PUGI__FN xml_node::xml_node(): _root(0)+ {+ }++ PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p)+ {+ }+ + PUGI__FN static void unspecified_bool_xml_node(xml_node***)+ {+ }++ PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const+ {+ return _root ? unspecified_bool_xml_node : 0;+ }++ PUGI__FN bool xml_node::operator!() const+ {+ return !_root;+ }++ PUGI__FN xml_node::iterator xml_node::begin() const+ {+ return iterator(_root ? _root->first_child : 0, _root);+ }++ PUGI__FN xml_node::iterator xml_node::end() const+ {+ return iterator(0, _root);+ }+ + PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const+ {+ return attribute_iterator(_root ? _root->first_attribute : 0, _root);+ }++ PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const+ {+ return attribute_iterator(0, _root);+ }+ + PUGI__FN xml_object_range<xml_node_iterator> xml_node::children() const+ {+ return xml_object_range<xml_node_iterator>(begin(), end());+ }++ PUGI__FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const+ {+ return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_));+ }++ PUGI__FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const+ {+ return xml_object_range<xml_attribute_iterator>(attributes_begin(), attributes_end());+ }++ PUGI__FN bool xml_node::operator==(const xml_node& r) const+ {+ return (_root == r._root);+ }++ PUGI__FN bool xml_node::operator!=(const xml_node& r) const+ {+ return (_root != r._root);+ }++ PUGI__FN bool xml_node::operator<(const xml_node& r) const+ {+ return (_root < r._root);+ }+ + PUGI__FN bool xml_node::operator>(const xml_node& r) const+ {+ return (_root > r._root);+ }+ + PUGI__FN bool xml_node::operator<=(const xml_node& r) const+ {+ return (_root <= r._root);+ }+ + PUGI__FN bool xml_node::operator>=(const xml_node& r) const+ {+ return (_root >= r._root);+ }++ PUGI__FN bool xml_node::empty() const+ {+ return !_root;+ }+ + PUGI__FN const char_t* xml_node::name() const+ {+ return (_root && _root->name) ? _root->name : PUGIXML_TEXT("");+ }++ PUGI__FN xml_node_type xml_node::type() const+ {+ return _root ? static_cast<xml_node_type>((_root->header & impl::xml_memory_page_type_mask) + 1) : node_null;+ }+ + PUGI__FN const char_t* xml_node::value() const+ {+ return (_root && _root->value) ? _root->value : PUGIXML_TEXT("");+ }+ + PUGI__FN xml_node xml_node::child(const char_t* name_) const+ {+ if (!_root) return xml_node();++ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)+ if (i->name && impl::strequal(name_, i->name)) return xml_node(i);++ return xml_node();+ }++ PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const+ {+ if (!_root) return xml_attribute();++ for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)+ if (i->name && impl::strequal(name_, i->name))+ return xml_attribute(i);+ + return xml_attribute();+ }+ + PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const+ {+ if (!_root) return xml_node();+ + for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)+ if (i->name && impl::strequal(name_, i->name)) return xml_node(i);++ return xml_node();+ }++ PUGI__FN xml_node xml_node::next_sibling() const+ {+ if (!_root) return xml_node();+ + if (_root->next_sibling) return xml_node(_root->next_sibling);+ else return xml_node();+ }++ PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const+ {+ if (!_root) return xml_node();+ + for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)+ if (i->name && impl::strequal(name_, i->name)) return xml_node(i);++ return xml_node();+ }++ PUGI__FN xml_node xml_node::previous_sibling() const+ {+ if (!_root) return xml_node();+ + if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c);+ else return xml_node();+ }++ PUGI__FN xml_node xml_node::parent() const+ {+ return _root ? xml_node(_root->parent) : xml_node();+ }++ PUGI__FN xml_node xml_node::root() const+ {+ if (!_root) return xml_node();++ impl::xml_memory_page* page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);++ return xml_node(static_cast<impl::xml_document_struct*>(page->allocator));+ }++ PUGI__FN xml_text xml_node::text() const+ {+ return xml_text(_root);+ }++ PUGI__FN const char_t* xml_node::child_value() const+ {+ if (!_root) return PUGIXML_TEXT("");+ + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)+ if (i->value && impl::is_text_node(i))+ return i->value;++ return PUGIXML_TEXT("");+ }++ PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const+ {+ return child(name_).child_value();+ }++ PUGI__FN xml_attribute xml_node::first_attribute() const+ {+ return _root ? xml_attribute(_root->first_attribute) : xml_attribute();+ }++ PUGI__FN xml_attribute xml_node::last_attribute() const+ {+ return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute();+ }++ PUGI__FN xml_node xml_node::first_child() const+ {+ return _root ? xml_node(_root->first_child) : xml_node();+ }++ PUGI__FN xml_node xml_node::last_child() const+ {+ return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node();+ }++ PUGI__FN bool xml_node::set_name(const char_t* rhs)+ {+ switch (type())+ {+ case node_pi:+ case node_declaration:+ case node_element:+ return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs);++ default:+ return false;+ }+ }+ + PUGI__FN bool xml_node::set_value(const char_t* rhs)+ {+ switch (type())+ {+ case node_pi:+ case node_cdata:+ case node_pcdata:+ case node_comment:+ case node_doctype:+ return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs);++ default:+ return false;+ }+ }++ PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_)+ {+ if (type() != node_element && type() != node_declaration) return xml_attribute();+ + xml_attribute a(impl::append_attribute_ll(_root, impl::get_allocator(_root)));+ a.set_name(name_);+ + return a;+ }++ PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_)+ {+ if (type() != node_element && type() != node_declaration) return xml_attribute();+ + xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));+ if (!a) return xml_attribute();++ a.set_name(name_);+ + xml_attribute_struct* head = _root->first_attribute;++ if (head)+ {+ a._attr->prev_attribute_c = head->prev_attribute_c;+ head->prev_attribute_c = a._attr;+ }+ else+ a._attr->prev_attribute_c = a._attr;+ + a._attr->next_attribute = head;+ _root->first_attribute = a._attr;+ + return a;+ }++ PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)+ {+ if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();+ + // check that attribute belongs to *this+ xml_attribute_struct* cur = attr._attr;++ while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;++ if (cur != _root->first_attribute) return xml_attribute();++ xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));+ if (!a) return xml_attribute();++ a.set_name(name_);++ if (attr._attr->prev_attribute_c->next_attribute)+ attr._attr->prev_attribute_c->next_attribute = a._attr;+ else+ _root->first_attribute = a._attr;+ + a._attr->prev_attribute_c = attr._attr->prev_attribute_c;+ a._attr->next_attribute = attr._attr;+ attr._attr->prev_attribute_c = a._attr;+ + return a;+ }++ PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)+ {+ if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();+ + // check that attribute belongs to *this+ xml_attribute_struct* cur = attr._attr;++ while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;++ if (cur != _root->first_attribute) return xml_attribute();++ xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));+ if (!a) return xml_attribute();++ a.set_name(name_);++ if (attr._attr->next_attribute)+ attr._attr->next_attribute->prev_attribute_c = a._attr;+ else+ _root->first_attribute->prev_attribute_c = a._attr;+ + a._attr->next_attribute = attr._attr->next_attribute;+ a._attr->prev_attribute_c = attr._attr;+ attr._attr->next_attribute = a._attr;++ return a;+ }++ PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)+ {+ if (!proto) return xml_attribute();++ xml_attribute result = append_attribute(proto.name());+ result.set_value(proto.value());++ return result;+ }++ PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)+ {+ if (!proto) return xml_attribute();++ xml_attribute result = prepend_attribute(proto.name());+ result.set_value(proto.value());++ return result;+ }++ PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)+ {+ if (!proto) return xml_attribute();++ xml_attribute result = insert_attribute_after(proto.name(), attr);+ result.set_value(proto.value());++ return result;+ }++ PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)+ {+ if (!proto) return xml_attribute();++ xml_attribute result = insert_attribute_before(proto.name(), attr);+ result.set_value(proto.value());++ return result;+ }++ PUGI__FN xml_node xml_node::append_child(xml_node_type type_)+ {+ if (!impl::allow_insert_child(this->type(), type_)) return xml_node();+ + xml_node n(impl::append_node(_root, impl::get_allocator(_root), type_));++ if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));++ return n;+ }++ PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)+ {+ if (!impl::allow_insert_child(this->type(), type_)) return xml_node();+ + xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));+ if (!n) return xml_node();++ n._root->parent = _root;++ xml_node_struct* head = _root->first_child;++ if (head)+ {+ n._root->prev_sibling_c = head->prev_sibling_c;+ head->prev_sibling_c = n._root;+ }+ else+ n._root->prev_sibling_c = n._root;+ + n._root->next_sibling = head;+ _root->first_child = n._root;+ + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));++ return n;+ }++ PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node)+ {+ if (!impl::allow_insert_child(this->type(), type_)) return xml_node();+ if (!node._root || node._root->parent != _root) return xml_node();+ + xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));+ if (!n) return xml_node();++ n._root->parent = _root;+ + if (node._root->prev_sibling_c->next_sibling)+ node._root->prev_sibling_c->next_sibling = n._root;+ else+ _root->first_child = n._root;+ + n._root->prev_sibling_c = node._root->prev_sibling_c;+ n._root->next_sibling = node._root;+ node._root->prev_sibling_c = n._root;++ if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));++ return n;+ }++ PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)+ {+ if (!impl::allow_insert_child(this->type(), type_)) return xml_node();+ if (!node._root || node._root->parent != _root) return xml_node();+ + xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));+ if (!n) return xml_node();++ n._root->parent = _root;+ + if (node._root->next_sibling)+ node._root->next_sibling->prev_sibling_c = n._root;+ else+ _root->first_child->prev_sibling_c = n._root;+ + n._root->next_sibling = node._root->next_sibling;+ n._root->prev_sibling_c = node._root;+ node._root->next_sibling = n._root;++ if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));++ return n;+ }++ PUGI__FN xml_node xml_node::append_child(const char_t* name_)+ {+ xml_node result = append_child(node_element);++ result.set_name(name_);++ return result;+ }++ PUGI__FN xml_node xml_node::prepend_child(const char_t* name_)+ {+ xml_node result = prepend_child(node_element);++ result.set_name(name_);++ return result;+ }++ PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)+ {+ xml_node result = insert_child_after(node_element, node);++ result.set_name(name_);++ return result;+ }++ PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)+ {+ xml_node result = insert_child_before(node_element, node);++ result.set_name(name_);++ return result;+ }++ PUGI__FN xml_node xml_node::append_copy(const xml_node& proto)+ {+ xml_node result = append_child(proto.type());++ if (result) impl::recursive_copy_skip(result, proto, result);++ return result;+ }++ PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)+ {+ xml_node result = prepend_child(proto.type());++ if (result) impl::recursive_copy_skip(result, proto, result);++ return result;+ }++ PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)+ {+ xml_node result = insert_child_after(proto.type(), node);++ if (result) impl::recursive_copy_skip(result, proto, result);++ return result;+ }++ PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)+ {+ xml_node result = insert_child_before(proto.type(), node);++ if (result) impl::recursive_copy_skip(result, proto, result);++ return result;+ }++ PUGI__FN bool xml_node::remove_attribute(const char_t* name_)+ {+ return remove_attribute(attribute(name_));+ }++ PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a)+ {+ if (!_root || !a._attr) return false;++ // check that attribute belongs to *this+ xml_attribute_struct* attr = a._attr;++ while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c;++ if (attr != _root->first_attribute) return false;++ if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c;+ else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c;+ + if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute;+ else _root->first_attribute = a._attr->next_attribute;++ impl::destroy_attribute(a._attr, impl::get_allocator(_root));++ return true;+ }++ PUGI__FN bool xml_node::remove_child(const char_t* name_)+ {+ return remove_child(child(name_));+ }++ PUGI__FN bool xml_node::remove_child(const xml_node& n)+ {+ if (!_root || !n._root || n._root->parent != _root) return false;++ if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c;+ else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c;+ + if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling;+ else _root->first_child = n._root->next_sibling;+ + impl::destroy_node(n._root, impl::get_allocator(_root));++ return true;+ }++ PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)+ {+ // append_buffer is only valid for elements/documents+ if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root);++ // get document node+ impl::xml_document_struct* doc = static_cast<impl::xml_document_struct*>(root()._root);+ assert(doc);+ + // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later)+ impl::xml_memory_page* page = 0;+ impl::xml_extra_buffer* extra = static_cast<impl::xml_extra_buffer*>(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page));+ (void)page;++ if (!extra) return impl::make_parse_result(status_out_of_memory);++ // save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level+ char_t* rootname = _root->name;+ _root->name = 0;++ // parse+ char_t* buffer = 0;+ xml_parse_result res = impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &buffer);++ // restore name+ _root->name = rootname;++ // add extra buffer to the list+ extra->buffer = buffer;+ extra->next = doc->extra_buffers;+ doc->extra_buffers = extra;++ return res;+ }++ PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const+ {+ if (!_root) return xml_node();+ + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)+ if (i->name && impl::strequal(name_, i->name))+ {+ for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)+ if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT("")))+ return xml_node(i);+ }++ return xml_node();+ }++ PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const+ {+ if (!_root) return xml_node();+ + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)+ for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)+ if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT("")))+ return xml_node(i);++ return xml_node();+ }++#ifndef PUGIXML_NO_STL+ PUGI__FN string_t xml_node::path(char_t delimiter) const+ {+ xml_node cursor = *this; // Make a copy.+ + string_t result = cursor.name();++ while (cursor.parent())+ {+ cursor = cursor.parent();+ + string_t temp = cursor.name();+ temp += delimiter;+ temp += result;+ result.swap(temp);+ }++ return result;+ }+#endif++ PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const+ {+ xml_node found = *this; // Current search context.++ if (!_root || !path_ || !path_[0]) return found;++ if (path_[0] == delimiter)+ {+ // Absolute path; e.g. '/foo/bar'+ found = found.root();+ ++path_;+ }++ const char_t* path_segment = path_;++ while (*path_segment == delimiter) ++path_segment;++ const char_t* path_segment_end = path_segment;++ while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end;++ if (path_segment == path_segment_end) return found;++ const char_t* next_segment = path_segment_end;++ while (*next_segment == delimiter) ++next_segment;++ if (*path_segment == '.' && path_segment + 1 == path_segment_end)+ return found.first_element_by_path(next_segment, delimiter);+ else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end)+ return found.parent().first_element_by_path(next_segment, delimiter);+ else+ {+ for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling)+ {+ if (j->name && impl::strequalrange(j->name, path_segment, static_cast<size_t>(path_segment_end - path_segment)))+ {+ xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);++ if (subsearch) return subsearch;+ }+ }++ return xml_node();+ }+ }++ PUGI__FN bool xml_node::traverse(xml_tree_walker& walker)+ {+ walker._depth = -1;+ + xml_node arg_begin = *this;+ if (!walker.begin(arg_begin)) return false;++ xml_node cur = first_child();+ + if (cur)+ {+ ++walker._depth;++ do + {+ xml_node arg_for_each = cur;+ if (!walker.for_each(arg_for_each))+ return false;+ + if (cur.first_child())+ {+ ++walker._depth;+ cur = cur.first_child();+ }+ else if (cur.next_sibling())+ cur = cur.next_sibling();+ else+ {+ // Borland C++ workaround+ while (!cur.next_sibling() && cur != *this && !cur.parent().empty())+ {+ --walker._depth;+ cur = cur.parent();+ }+ + if (cur != *this)+ cur = cur.next_sibling();+ }+ }+ while (cur && cur != *this);+ }++ assert(walker._depth == -1);++ xml_node arg_end = *this;+ return walker.end(arg_end);+ }++ PUGI__FN size_t xml_node::hash_value() const+ {+ return static_cast<size_t>(reinterpret_cast<uintptr_t>(_root) / sizeof(xml_node_struct));+ }++ PUGI__FN xml_node_struct* xml_node::internal_object() const+ {+ return _root;+ }++ PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const+ {+ if (!_root) return;++ impl::xml_buffered_writer buffered_writer(writer, encoding);++ impl::node_output(buffered_writer, *this, indent, flags, depth);+ }++#ifndef PUGIXML_NO_STL+ PUGI__FN void xml_node::print(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const+ {+ xml_writer_stream writer(stream);++ print(writer, indent, flags, encoding, depth);+ }++ PUGI__FN void xml_node::print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const+ {+ xml_writer_stream writer(stream);++ print(writer, indent, flags, encoding_wchar, depth);+ }+#endif++ PUGI__FN ptrdiff_t xml_node::offset_debug() const+ {+ xml_node_struct* r = root()._root;++ if (!r) return -1;++ const char_t* buffer = static_cast<impl::xml_document_struct*>(r)->buffer;++ if (!buffer) return -1;++ switch (type())+ {+ case node_document:+ return 0;++ case node_element:+ case node_declaration:+ case node_pi:+ return (_root->header & impl::xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer;++ case node_pcdata:+ case node_cdata:+ case node_comment:+ case node_doctype:+ return (_root->header & impl::xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer;++ default:+ return -1;+ }+ }++#ifdef __BORLANDC__+ PUGI__FN bool operator&&(const xml_node& lhs, bool rhs)+ {+ return (bool)lhs && rhs;+ }++ PUGI__FN bool operator||(const xml_node& lhs, bool rhs)+ {+ return (bool)lhs || rhs;+ }+#endif++ PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root)+ {+ }++ PUGI__FN xml_node_struct* xml_text::_data() const+ {+ if (!_root || impl::is_text_node(_root)) return _root;++ for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling)+ if (impl::is_text_node(node))+ return node;++ return 0;+ }++ PUGI__FN xml_node_struct* xml_text::_data_new()+ {+ xml_node_struct* d = _data();+ if (d) return d;++ return xml_node(_root).append_child(node_pcdata).internal_object();+ }++ PUGI__FN xml_text::xml_text(): _root(0)+ {+ }++ PUGI__FN static void unspecified_bool_xml_text(xml_text***)+ {+ }++ PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const+ {+ return _data() ? unspecified_bool_xml_text : 0;+ }++ PUGI__FN bool xml_text::operator!() const+ {+ return !_data();+ }++ PUGI__FN bool xml_text::empty() const+ {+ return _data() == 0;+ }++ PUGI__FN const char_t* xml_text::get() const+ {+ xml_node_struct* d = _data();++ return (d && d->value) ? d->value : PUGIXML_TEXT("");+ }++ PUGI__FN const char_t* xml_text::as_string(const char_t* def) const+ {+ xml_node_struct* d = _data();++ return (d && d->value) ? d->value : def;+ }++ PUGI__FN int xml_text::as_int(int def) const+ {+ xml_node_struct* d = _data();++ return impl::get_value_int(d ? d->value : 0, def);+ }++ PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const+ {+ xml_node_struct* d = _data();++ return impl::get_value_uint(d ? d->value : 0, def);+ }++ PUGI__FN double xml_text::as_double(double def) const+ {+ xml_node_struct* d = _data();++ return impl::get_value_double(d ? d->value : 0, def);+ }++ PUGI__FN float xml_text::as_float(float def) const+ {+ xml_node_struct* d = _data();++ return impl::get_value_float(d ? d->value : 0, def);+ }++ PUGI__FN bool xml_text::as_bool(bool def) const+ {+ xml_node_struct* d = _data();++ return impl::get_value_bool(d ? d->value : 0, def);+ }++#ifdef PUGIXML_HAS_LONG_LONG+ PUGI__FN long long xml_text::as_llong(long long def) const+ {+ xml_node_struct* d = _data();++ return impl::get_value_llong(d ? d->value : 0, def);+ }++ PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const+ {+ xml_node_struct* d = _data();++ return impl::get_value_ullong(d ? d->value : 0, def);+ }+#endif++ PUGI__FN bool xml_text::set(const char_t* rhs)+ {+ xml_node_struct* dn = _data_new();++ return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;+ }++ PUGI__FN bool xml_text::set(int rhs)+ {+ xml_node_struct* dn = _data_new();++ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;+ }++ PUGI__FN bool xml_text::set(unsigned int rhs)+ {+ xml_node_struct* dn = _data_new();++ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;+ }++ PUGI__FN bool xml_text::set(double rhs)+ {+ xml_node_struct* dn = _data_new();++ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;+ }++ PUGI__FN bool xml_text::set(bool rhs)+ {+ xml_node_struct* dn = _data_new();++ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;+ }++#ifdef PUGIXML_HAS_LONG_LONG+ PUGI__FN bool xml_text::set(long long rhs)+ {+ xml_node_struct* dn = _data_new();++ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;+ }++ PUGI__FN bool xml_text::set(unsigned long long rhs)+ {+ xml_node_struct* dn = _data_new();++ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;+ }+#endif++ PUGI__FN xml_text& xml_text::operator=(const char_t* rhs)+ {+ set(rhs);+ return *this;+ }++ PUGI__FN xml_text& xml_text::operator=(int rhs)+ {+ set(rhs);+ return *this;+ }++ PUGI__FN xml_text& xml_text::operator=(unsigned int rhs)+ {+ set(rhs);+ return *this;+ }++ PUGI__FN xml_text& xml_text::operator=(double rhs)+ {+ set(rhs);+ return *this;+ }++ PUGI__FN xml_text& xml_text::operator=(bool rhs)+ {+ set(rhs);+ return *this;+ }++#ifdef PUGIXML_HAS_LONG_LONG+ PUGI__FN xml_text& xml_text::operator=(long long rhs)+ {+ set(rhs);+ return *this;+ }++ PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs)+ {+ set(rhs);+ return *this;+ }+#endif++ PUGI__FN xml_node xml_text::data() const+ {+ return xml_node(_data());+ }++#ifdef __BORLANDC__+ PUGI__FN bool operator&&(const xml_text& lhs, bool rhs)+ {+ return (bool)lhs && rhs;+ }++ PUGI__FN bool operator||(const xml_text& lhs, bool rhs)+ {+ return (bool)lhs || rhs;+ }+#endif++ PUGI__FN xml_node_iterator::xml_node_iterator()+ {+ }++ PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent())+ {+ }++ PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)+ {+ }++ PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const+ {+ return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;+ }+ + PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const+ {+ return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;+ }++ PUGI__FN xml_node& xml_node_iterator::operator*() const+ {+ assert(_wrap._root);+ return _wrap;+ }++ PUGI__FN xml_node* xml_node_iterator::operator->() const+ {+ assert(_wrap._root);+ return const_cast<xml_node*>(&_wrap); // BCC32 workaround+ }++ PUGI__FN const xml_node_iterator& xml_node_iterator::operator++()+ {+ assert(_wrap._root);+ _wrap._root = _wrap._root->next_sibling;+ return *this;+ }++ PUGI__FN xml_node_iterator xml_node_iterator::operator++(int)+ {+ xml_node_iterator temp = *this;+ ++*this;+ return temp;+ }++ PUGI__FN const xml_node_iterator& xml_node_iterator::operator--()+ {+ _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child();+ return *this;+ }++ PUGI__FN xml_node_iterator xml_node_iterator::operator--(int)+ {+ xml_node_iterator temp = *this;+ --*this;+ return temp;+ }++ PUGI__FN xml_attribute_iterator::xml_attribute_iterator()+ {+ }++ PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent)+ {+ }++ PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)+ {+ }++ PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const+ {+ return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root;+ }+ + PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const+ {+ return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root;+ }++ PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const+ {+ assert(_wrap._attr);+ return _wrap;+ }++ PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const+ {+ assert(_wrap._attr);+ return const_cast<xml_attribute*>(&_wrap); // BCC32 workaround+ }++ PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++()+ {+ assert(_wrap._attr);+ _wrap._attr = _wrap._attr->next_attribute;+ return *this;+ }++ PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int)+ {+ xml_attribute_iterator temp = *this;+ ++*this;+ return temp;+ }++ PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--()+ {+ _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute();+ return *this;+ }++ PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int)+ {+ xml_attribute_iterator temp = *this;+ --*this;+ return temp;+ }++ PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0)+ {+ }++ PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name)+ {+ }++ PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name)+ {+ }++ PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const+ {+ return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;+ }++ PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const+ {+ return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;+ }++ PUGI__FN xml_node& xml_named_node_iterator::operator*() const+ {+ assert(_wrap._root);+ return _wrap;+ }++ PUGI__FN xml_node* xml_named_node_iterator::operator->() const+ {+ assert(_wrap._root);+ return const_cast<xml_node*>(&_wrap); // BCC32 workaround+ }++ PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++()+ {+ assert(_wrap._root);+ _wrap = _wrap.next_sibling(_name);+ return *this;+ }++ PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int)+ {+ xml_named_node_iterator temp = *this;+ ++*this;+ return temp;+ }++ PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--()+ {+ if (_wrap._root)+ _wrap = _wrap.previous_sibling(_name);+ else+ {+ _wrap = _parent.last_child();++ if (!impl::strequal(_wrap.name(), _name))+ _wrap = _wrap.previous_sibling(_name);+ }++ return *this;+ }++ PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int)+ {+ xml_named_node_iterator temp = *this;+ --*this;+ return temp;+ }++ PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto)+ {+ }++ PUGI__FN xml_parse_result::operator bool() const+ {+ return status == status_ok;+ }++ PUGI__FN const char* xml_parse_result::description() const+ {+ switch (status)+ {+ case status_ok: return "No error";++ case status_file_not_found: return "File was not found";+ case status_io_error: return "Error reading from file/stream";+ case status_out_of_memory: return "Could not allocate memory";+ case status_internal_error: return "Internal error occurred";++ case status_unrecognized_tag: return "Could not determine tag type";++ case status_bad_pi: return "Error parsing document declaration/processing instruction";+ case status_bad_comment: return "Error parsing comment";+ case status_bad_cdata: return "Error parsing CDATA section";+ case status_bad_doctype: return "Error parsing document type declaration";+ case status_bad_pcdata: return "Error parsing PCDATA section";+ case status_bad_start_element: return "Error parsing start element tag";+ case status_bad_attribute: return "Error parsing element attribute";+ case status_bad_end_element: return "Error parsing end element tag";+ case status_end_element_mismatch: return "Start-end tags mismatch";++ case status_append_invalid_root: return "Unable to append nodes: root is not an element or document";++ case status_no_document_element: return "No document element found";++ default: return "Unknown error";+ }+ }++ PUGI__FN xml_document::xml_document(): _buffer(0)+ {+ create();+ }++ PUGI__FN xml_document::~xml_document()+ {+ destroy();+ }++ PUGI__FN void xml_document::reset()+ {+ destroy();+ create();+ }++ PUGI__FN void xml_document::reset(const xml_document& proto)+ {+ reset();++ for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling())+ append_copy(cur);+ }++ PUGI__FN void xml_document::create()+ {+ assert(!_root);++ // initialize sentinel page+ PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment <= sizeof(_memory));++ // align upwards to page boundary+ void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1));++ // prepare page structure+ impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory);+ assert(page);++ page->busy_size = impl::xml_memory_page_size;++ // allocate new root+ _root = new (page->data) impl::xml_document_struct(page);+ _root->prev_sibling_c = _root;++ // setup sentinel page+ page->allocator = static_cast<impl::xml_document_struct*>(_root);+ }++ PUGI__FN void xml_document::destroy()+ {+ assert(_root);++ // destroy static storage+ if (_buffer)+ {+ impl::xml_memory::deallocate(_buffer);+ _buffer = 0;+ }++ // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator)+ for (impl::xml_extra_buffer* extra = static_cast<impl::xml_document_struct*>(_root)->extra_buffers; extra; extra = extra->next)+ {+ if (extra->buffer) impl::xml_memory::deallocate(extra->buffer);+ }++ // destroy dynamic storage, leave sentinel page (it's in static memory)+ impl::xml_memory_page* root_page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);+ assert(root_page && !root_page->prev && !root_page->memory);++ for (impl::xml_memory_page* page = root_page->next; page; )+ {+ impl::xml_memory_page* next = page->next;++ impl::xml_allocator::deallocate_page(page);++ page = next;+ }++ _root = 0;+ }++#ifndef PUGIXML_NO_STL+ PUGI__FN xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)+ {+ reset();++ return impl::load_stream_impl(*this, stream, options, encoding);+ }++ PUGI__FN xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)+ {+ reset();++ return impl::load_stream_impl(*this, stream, options, encoding_wchar);+ }+#endif++ PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)+ {+ // Force native encoding (skip autodetection)+ #ifdef PUGIXML_WCHAR_MODE+ xml_encoding encoding = encoding_wchar;+ #else+ xml_encoding encoding = encoding_utf8;+ #endif++ return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding);+ }++ PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding)+ {+ reset();++ FILE* file = fopen(path_, "rb");++ return impl::load_file_impl(*this, file, options, encoding);+ }++ PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)+ {+ reset();++ FILE* file = impl::open_file_wide(path_, L"rb");++ return impl::load_file_impl(*this, file, options, encoding);+ }++ PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)+ {+ reset();++ return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, const_cast<void*>(contents), size, options, encoding, false, false, &_buffer);+ }++ PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding)+ {+ reset();++ return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, false, &_buffer);+ }+ + PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding)+ {+ reset();++ return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, true, &_buffer);+ }++ PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const+ {+ impl::xml_buffered_writer buffered_writer(writer, encoding);++ if ((flags & format_write_bom) && encoding != encoding_latin1)+ {+ // BOM always represents the codepoint U+FEFF, so just write it in native encoding+ #ifdef PUGIXML_WCHAR_MODE+ unsigned int bom = 0xfeff;+ buffered_writer.write(static_cast<wchar_t>(bom));+ #else+ buffered_writer.write('\xef', '\xbb', '\xbf');+ #endif+ }++ if (!(flags & format_no_declaration) && !impl::has_declaration(*this))+ {+ buffered_writer.write(PUGIXML_TEXT("<?xml version=\"1.0\""));+ if (encoding == encoding_latin1) buffered_writer.write(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));+ buffered_writer.write('?', '>');+ if (!(flags & format_raw)) buffered_writer.write('\n');+ }++ impl::node_output(buffered_writer, *this, indent, flags, 0);+ }++#ifndef PUGIXML_NO_STL+ PUGI__FN void xml_document::save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const+ {+ xml_writer_stream writer(stream);++ save(writer, indent, flags, encoding);+ }++ PUGI__FN void xml_document::save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags) const+ {+ xml_writer_stream writer(stream);++ save(writer, indent, flags, encoding_wchar);+ }+#endif++ PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const+ {+ FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb");+ return impl::save_file_impl(*this, file, indent, flags, encoding);+ }++ PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const+ {+ FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb");+ return impl::save_file_impl(*this, file, indent, flags, encoding);+ }++ PUGI__FN xml_node xml_document::document_element() const+ {+ assert(_root);++ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)+ if ((i->header & impl::xml_memory_page_type_mask) + 1 == node_element)+ return xml_node(i);++ return xml_node();+ }++#ifndef PUGIXML_NO_STL+ PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str)+ {+ assert(str);++ return impl::as_utf8_impl(str, impl::strlength_wide(str));+ }++ PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t>& str)+ {+ return impl::as_utf8_impl(str.c_str(), str.size());+ }+ + PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str)+ {+ assert(str);++ return impl::as_wide_impl(str, strlen(str));+ }+ + PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const std::string& str)+ {+ return impl::as_wide_impl(str.c_str(), str.size());+ }+#endif++ PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate)+ {+ impl::xml_memory::allocate = allocate;+ impl::xml_memory::deallocate = deallocate;+ }++ PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function()+ {+ return impl::xml_memory::allocate;+ }++ PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function()+ {+ return impl::xml_memory::deallocate;+ }+}++#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))+namespace std+{+ // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)+ PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&)+ {+ return std::bidirectional_iterator_tag();+ }++ PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&)+ {+ return std::bidirectional_iterator_tag();+ }++ PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)+ {+ return std::bidirectional_iterator_tag();+ }+}+#endif++#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)+namespace std+{+ // Workarounds for (non-standard) iterator category detection+ PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&)+ {+ return std::bidirectional_iterator_tag();+ }++ PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&)+ {+ return std::bidirectional_iterator_tag();+ }++ PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)+ {+ return std::bidirectional_iterator_tag();+ }+}+#endif++#ifndef PUGIXML_NO_XPATH++// STL replacements+PUGI__NS_BEGIN+ struct equal_to+ {+ template <typename T> bool operator()(const T& lhs, const T& rhs) const+ {+ return lhs == rhs;+ }+ };++ struct not_equal_to+ {+ template <typename T> bool operator()(const T& lhs, const T& rhs) const+ {+ return lhs != rhs;+ }+ };++ struct less+ {+ template <typename T> bool operator()(const T& lhs, const T& rhs) const+ {+ return lhs < rhs;+ }+ };++ struct less_equal+ {+ template <typename T> bool operator()(const T& lhs, const T& rhs) const+ {+ return lhs <= rhs;+ }+ };++ template <typename T> void swap(T& lhs, T& rhs)+ {+ T temp = lhs;+ lhs = rhs;+ rhs = temp;+ }++ template <typename I, typename Pred> I min_element(I begin, I end, const Pred& pred)+ {+ I result = begin;++ for (I it = begin + 1; it != end; ++it)+ if (pred(*it, *result))+ result = it;++ return result;+ }++ template <typename I> void reverse(I begin, I end)+ {+ while (end - begin > 1) swap(*begin++, *--end);+ }++ template <typename I> I unique(I begin, I end)+ {+ // fast skip head+ while (end - begin > 1 && *begin != *(begin + 1)) begin++;++ if (begin == end) return begin;++ // last written element+ I write = begin++; ++ // merge unique elements+ while (begin != end)+ {+ if (*begin != *write)+ *++write = *begin++;+ else+ begin++;+ }++ // past-the-end (write points to live element)+ return write + 1;+ }++ template <typename I> void copy_backwards(I begin, I end, I target)+ {+ while (begin != end) *--target = *--end;+ }++ template <typename I, typename Pred, typename T> void insertion_sort(I begin, I end, const Pred& pred, T*)+ {+ assert(begin != end);++ for (I it = begin + 1; it != end; ++it)+ {+ T val = *it;++ if (pred(val, *begin))+ {+ // move to front+ copy_backwards(begin, it, it + 1);+ *begin = val;+ }+ else+ {+ I hole = it;++ // move hole backwards+ while (pred(val, *(hole - 1)))+ {+ *hole = *(hole - 1);+ hole--;+ }++ // fill hole with element+ *hole = val;+ }+ }+ }++ // std variant for elements with ==+ template <typename I, typename Pred> void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend)+ {+ I eqbeg = middle, eqend = middle + 1;++ // expand equal range+ while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg;+ while (eqend != end && *eqend == *eqbeg) ++eqend;++ // process outer elements+ I ltend = eqbeg, gtbeg = eqend;++ for (;;)+ {+ // find the element from the right side that belongs to the left one+ for (; gtbeg != end; ++gtbeg)+ if (!pred(*eqbeg, *gtbeg))+ {+ if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++);+ else break;+ }++ // find the element from the left side that belongs to the right one+ for (; ltend != begin; --ltend)+ if (!pred(*(ltend - 1), *eqbeg))+ {+ if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg);+ else break;+ }++ // scanned all elements+ if (gtbeg == end && ltend == begin)+ {+ *out_eqbeg = eqbeg;+ *out_eqend = eqend;+ return;+ }++ // make room for elements by moving equal area+ if (gtbeg == end)+ {+ if (--ltend != --eqbeg) swap(*ltend, *eqbeg);+ swap(*eqbeg, *--eqend);+ }+ else if (ltend == begin)+ {+ if (eqend != gtbeg) swap(*eqbeg, *eqend);+ ++eqend;+ swap(*gtbeg++, *eqbeg++);+ }+ else swap(*gtbeg++, *--ltend);+ }+ }++ template <typename I, typename Pred> void median3(I first, I middle, I last, const Pred& pred)+ {+ if (pred(*middle, *first)) swap(*middle, *first);+ if (pred(*last, *middle)) swap(*last, *middle);+ if (pred(*middle, *first)) swap(*middle, *first);+ }++ template <typename I, typename Pred> void median(I first, I middle, I last, const Pred& pred)+ {+ if (last - first <= 40)+ {+ // median of three for small chunks+ median3(first, middle, last, pred);+ }+ else+ {+ // median of nine+ size_t step = (last - first + 1) / 8;++ median3(first, first + step, first + 2 * step, pred);+ median3(middle - step, middle, middle + step, pred);+ median3(last - 2 * step, last - step, last, pred);+ median3(first + step, middle, last - step, pred);+ }+ }++ template <typename I, typename Pred> void sort(I begin, I end, const Pred& pred)+ {+ // sort large chunks+ while (end - begin > 32)+ {+ // find median element+ I middle = begin + (end - begin) / 2;+ median(begin, middle, end - 1, pred);++ // partition in three chunks (< = >)+ I eqbeg, eqend;+ partition(begin, middle, end, pred, &eqbeg, &eqend);++ // loop on larger half+ if (eqbeg - begin > end - eqend)+ {+ sort(eqend, end, pred);+ end = eqbeg;+ }+ else+ {+ sort(begin, eqbeg, pred);+ begin = eqend;+ }+ }++ // insertion sort small chunk+ if (begin != end) insertion_sort(begin, end, pred, &*begin);+ }+PUGI__NS_END++// Allocator used for AST and evaluation stacks+PUGI__NS_BEGIN+ struct xpath_memory_block+ { + xpath_memory_block* next;++ char data[+ #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE+ PUGIXML_MEMORY_XPATH_PAGE_SIZE+ #else+ 4096+ #endif+ ];+ };+ + class xpath_allocator+ {+ xpath_memory_block* _root;+ size_t _root_size;++ public:+ #ifdef PUGIXML_NO_EXCEPTIONS+ jmp_buf* error_handler;+ #endif++ xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size)+ {+ #ifdef PUGIXML_NO_EXCEPTIONS+ error_handler = 0;+ #endif+ }+ + void* allocate_nothrow(size_t size)+ {+ const size_t block_capacity = sizeof(_root->data);++ // align size so that we're able to store pointers in subsequent blocks+ size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);++ if (_root_size + size <= block_capacity)+ {+ void* buf = _root->data + _root_size;+ _root_size += size;+ return buf;+ }+ else+ {+ size_t block_data_size = (size > block_capacity) ? size : block_capacity;+ size_t block_size = block_data_size + offsetof(xpath_memory_block, data);++ xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size));+ if (!block) return 0;+ + block->next = _root;+ + _root = block;+ _root_size = size;+ + return block->data;+ }+ }++ void* allocate(size_t size)+ {+ void* result = allocate_nothrow(size);++ if (!result)+ {+ #ifdef PUGIXML_NO_EXCEPTIONS+ assert(error_handler);+ longjmp(*error_handler, 1);+ #else+ throw std::bad_alloc();+ #endif+ }++ return result;+ }++ void* reallocate(void* ptr, size_t old_size, size_t new_size)+ {+ // align size so that we're able to store pointers in subsequent blocks+ old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);+ new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);++ // we can only reallocate the last object+ assert(ptr == 0 || static_cast<char*>(ptr) + old_size == _root->data + _root_size);++ // adjust root size so that we have not allocated the object at all+ bool only_object = (_root_size == old_size);++ if (ptr) _root_size -= old_size;++ // allocate a new version (this will obviously reuse the memory if possible)+ void* result = allocate(new_size);+ assert(result);++ // we have a new block+ if (result != ptr && ptr)+ {+ // copy old data+ assert(new_size >= old_size);+ memcpy(result, ptr, old_size);++ // free the previous page if it had no other objects+ if (only_object)+ {+ assert(_root->data == result);+ assert(_root->next);++ xpath_memory_block* next = _root->next->next;++ if (next)+ {+ // deallocate the whole page, unless it was the first one+ xml_memory::deallocate(_root->next);+ _root->next = next;+ }+ }+ }++ return result;+ }++ void revert(const xpath_allocator& state)+ {+ // free all new pages+ xpath_memory_block* cur = _root;++ while (cur != state._root)+ {+ xpath_memory_block* next = cur->next;++ xml_memory::deallocate(cur);++ cur = next;+ }++ // restore state+ _root = state._root;+ _root_size = state._root_size;+ }++ void release()+ {+ xpath_memory_block* cur = _root;+ assert(cur);++ while (cur->next)+ {+ xpath_memory_block* next = cur->next;++ xml_memory::deallocate(cur);++ cur = next;+ }+ }+ };++ struct xpath_allocator_capture+ {+ xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc)+ {+ }++ ~xpath_allocator_capture()+ {+ _target->revert(_state);+ }++ xpath_allocator* _target;+ xpath_allocator _state;+ };++ struct xpath_stack+ {+ xpath_allocator* result;+ xpath_allocator* temp;+ };++ struct xpath_stack_data+ {+ xpath_memory_block blocks[2];+ xpath_allocator result;+ xpath_allocator temp;+ xpath_stack stack;++ #ifdef PUGIXML_NO_EXCEPTIONS+ jmp_buf error_handler;+ #endif++ xpath_stack_data(): result(blocks + 0), temp(blocks + 1)+ {+ blocks[0].next = blocks[1].next = 0;++ stack.result = &result;+ stack.temp = &temp;++ #ifdef PUGIXML_NO_EXCEPTIONS+ result.error_handler = temp.error_handler = &error_handler;+ #endif+ }++ ~xpath_stack_data()+ {+ result.release();+ temp.release();+ }+ };+PUGI__NS_END++// String class+PUGI__NS_BEGIN+ class xpath_string+ {+ const char_t* _buffer;+ bool _uses_heap;++ static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)+ {+ char_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t)));+ assert(result);++ memcpy(result, string, length * sizeof(char_t));+ result[length] = 0;++ return result;+ }++ static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc)+ {+ return duplicate_string(string, strlength(string), alloc);+ }++ public:+ xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false)+ {+ }++ explicit xpath_string(const char_t* str, xpath_allocator* alloc)+ {+ bool empty_ = (*str == 0);++ _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(str, alloc);+ _uses_heap = !empty_;+ }++ explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap)+ {+ }++ xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc)+ {+ assert(begin <= end);++ bool empty_ = (begin == end);++ _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast<size_t>(end - begin), alloc);+ _uses_heap = !empty_;+ }++ void append(const xpath_string& o, xpath_allocator* alloc)+ {+ // skip empty sources+ if (!*o._buffer) return;++ // fast append for constant empty target and constant source+ if (!*_buffer && !_uses_heap && !o._uses_heap)+ {+ _buffer = o._buffer;+ }+ else+ {+ // need to make heap copy+ size_t target_length = strlength(_buffer);+ size_t source_length = strlength(o._buffer);+ size_t result_length = target_length + source_length;++ // allocate new buffer+ char_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t)));+ assert(result);++ // append first string to the new buffer in case there was no reallocation+ if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t));++ // append second string to the new buffer+ memcpy(result + target_length, o._buffer, source_length * sizeof(char_t));+ result[result_length] = 0;++ // finalize+ _buffer = result;+ _uses_heap = true;+ }+ }++ const char_t* c_str() const+ {+ return _buffer;+ }++ size_t length() const+ {+ return strlength(_buffer);+ }+ + char_t* data(xpath_allocator* alloc)+ {+ // make private heap copy+ if (!_uses_heap)+ {+ _buffer = duplicate_string(_buffer, alloc);+ _uses_heap = true;+ }++ return const_cast<char_t*>(_buffer);+ }++ bool empty() const+ {+ return *_buffer == 0;+ }++ bool operator==(const xpath_string& o) const+ {+ return strequal(_buffer, o._buffer);+ }++ bool operator!=(const xpath_string& o) const+ {+ return !strequal(_buffer, o._buffer);+ }++ bool uses_heap() const+ {+ return _uses_heap;+ }+ };++ PUGI__FN xpath_string xpath_string_const(const char_t* str)+ {+ return xpath_string(str, false);+ }+PUGI__NS_END++PUGI__NS_BEGIN+ PUGI__FN bool starts_with(const char_t* string, const char_t* pattern)+ {+ while (*pattern && *string == *pattern)+ {+ string++;+ pattern++;+ }++ return *pattern == 0;+ }++ PUGI__FN const char_t* find_char(const char_t* s, char_t c)+ {+ #ifdef PUGIXML_WCHAR_MODE+ return wcschr(s, c);+ #else+ return strchr(s, c);+ #endif+ }++ PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p)+ {+ #ifdef PUGIXML_WCHAR_MODE+ // MSVC6 wcsstr bug workaround (if s is empty it always returns 0)+ return (*p == 0) ? s : wcsstr(s, p);+ #else+ return strstr(s, p);+ #endif+ }++ // Converts symbol to lower case, if it is an ASCII one+ PUGI__FN char_t tolower_ascii(char_t ch)+ {+ return static_cast<unsigned int>(ch - 'A') < 26 ? static_cast<char_t>(ch | ' ') : ch;+ }++ PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc)+ {+ if (na.attribute())+ return xpath_string_const(na.attribute().value());+ else+ {+ const xml_node& n = na.node();++ switch (n.type())+ {+ case node_pcdata:+ case node_cdata:+ case node_comment:+ case node_pi:+ return xpath_string_const(n.value());+ + case node_document:+ case node_element:+ {+ xpath_string result;++ xml_node cur = n.first_child();+ + while (cur && cur != n)+ {+ if (cur.type() == node_pcdata || cur.type() == node_cdata)+ result.append(xpath_string_const(cur.value()), alloc);++ if (cur.first_child())+ cur = cur.first_child();+ else if (cur.next_sibling())+ cur = cur.next_sibling();+ else+ {+ while (!cur.next_sibling() && cur != n)+ cur = cur.parent();++ if (cur != n) cur = cur.next_sibling();+ }+ }+ + return result;+ }+ + default:+ return xpath_string();+ }+ }+ }+ + PUGI__FN unsigned int node_height(xml_node n)+ {+ unsigned int result = 0;+ + while (n)+ {+ ++result;+ n = n.parent();+ }+ + return result;+ }+ + PUGI__FN bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh)+ {+ // normalize heights+ for (unsigned int i = rh; i < lh; i++) ln = ln.parent();+ for (unsigned int j = lh; j < rh; j++) rn = rn.parent();+ + // one node is the ancestor of the other+ if (ln == rn) return lh < rh;+ + // find common ancestor+ while (ln.parent() != rn.parent())+ {+ ln = ln.parent();+ rn = rn.parent();+ }++ // there is no common ancestor (the shared parent is null), nodes are from different documents+ if (!ln.parent()) return ln < rn;++ // determine sibling order+ for (; ln; ln = ln.next_sibling())+ if (ln == rn)+ return true;+ + return false;+ }++ PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node)+ {+ while (node && node != parent) node = node.parent();++ return parent && node == parent;+ }++ PUGI__FN const void* document_order(const xpath_node& xnode)+ {+ xml_node_struct* node = xnode.node().internal_object();++ if (node)+ {+ if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name;+ if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value;+ return 0;+ }++ xml_attribute_struct* attr = xnode.attribute().internal_object();++ if (attr)+ {+ if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name;+ if ((attr->header & xml_memory_page_value_allocated_mask) == 0) return attr->value;+ return 0;+ }++ return 0;+ }+ + struct document_order_comparator+ {+ bool operator()(const xpath_node& lhs, const xpath_node& rhs) const+ {+ // optimized document order based check+ const void* lo = document_order(lhs);+ const void* ro = document_order(rhs);++ if (lo && ro) return lo < ro;++ // slow comparison+ xml_node ln = lhs.node(), rn = rhs.node();++ // compare attributes+ if (lhs.attribute() && rhs.attribute())+ {+ // shared parent+ if (lhs.parent() == rhs.parent())+ {+ // determine sibling order+ for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute())+ if (a == rhs.attribute())+ return true;+ + return false;+ }+ + // compare attribute parents+ ln = lhs.parent();+ rn = rhs.parent();+ }+ else if (lhs.attribute())+ {+ // attributes go after the parent element+ if (lhs.parent() == rhs.node()) return false;+ + ln = lhs.parent();+ }+ else if (rhs.attribute())+ {+ // attributes go after the parent element+ if (rhs.parent() == lhs.node()) return true;+ + rn = rhs.parent();+ }++ if (ln == rn) return false;+ + unsigned int lh = node_height(ln);+ unsigned int rh = node_height(rn);+ + return node_is_before(ln, lh, rn, rh);+ }+ };++ struct duplicate_comparator+ {+ bool operator()(const xpath_node& lhs, const xpath_node& rhs) const+ {+ if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true;+ else return rhs.attribute() ? false : lhs.node() < rhs.node();+ }+ };+ + PUGI__FN double gen_nan()+ {+ #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))+ union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1];+ u[0].i = 0x7fc00000;+ return u[0].f;+ #else+ // fallback+ const volatile double zero = 0.0;+ return zero / zero;+ #endif+ }+ + PUGI__FN bool is_nan(double value)+ {+ #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)+ return !!_isnan(value);+ #elif defined(fpclassify) && defined(FP_NAN)+ return fpclassify(value) == FP_NAN;+ #else+ // fallback+ const volatile double v = value;+ return v != v;+ #endif+ }+ + PUGI__FN const char_t* convert_number_to_string_special(double value)+ {+ #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)+ if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0;+ if (_isnan(value)) return PUGIXML_TEXT("NaN");+ return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");+ #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO)+ switch (fpclassify(value))+ {+ case FP_NAN:+ return PUGIXML_TEXT("NaN");++ case FP_INFINITE:+ return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");++ case FP_ZERO:+ return PUGIXML_TEXT("0");++ default:+ return 0;+ }+ #else+ // fallback+ const volatile double v = value;++ if (v == 0) return PUGIXML_TEXT("0");+ if (v != v) return PUGIXML_TEXT("NaN");+ if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");+ return 0;+ #endif+ }+ + PUGI__FN bool convert_number_to_boolean(double value)+ {+ return (value != 0 && !is_nan(value));+ }+ + PUGI__FN void truncate_zeros(char* begin, char* end)+ {+ while (begin != end && end[-1] == '0') end--;++ *end = 0;+ }++ // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent+#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)+ PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent)+ {+ // get base values+ int sign, exponent;+ _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign);++ // truncate redundant zeros+ truncate_zeros(buffer, buffer + strlen(buffer));++ // fill results+ *out_mantissa = buffer;+ *out_exponent = exponent;+ }+#else+ PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent)+ {+ // get a scientific notation value with IEEE DBL_DIG decimals+ sprintf(buffer, "%.*e", DBL_DIG, value);+ assert(strlen(buffer) < buffer_size);+ (void)!buffer_size;++ // get the exponent (possibly negative)+ char* exponent_string = strchr(buffer, 'e');+ assert(exponent_string);++ int exponent = atoi(exponent_string + 1);++ // extract mantissa string: skip sign+ char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer;+ assert(mantissa[0] != '0' && mantissa[1] == '.');++ // divide mantissa by 10 to eliminate integer part+ mantissa[1] = mantissa[0];+ mantissa++;+ exponent++;++ // remove extra mantissa digits and zero-terminate mantissa+ truncate_zeros(mantissa, exponent_string);++ // fill results+ *out_mantissa = mantissa;+ *out_exponent = exponent;+ }+#endif++ PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc)+ {+ // try special number conversion+ const char_t* special = convert_number_to_string_special(value);+ if (special) return xpath_string_const(special);++ // get mantissa + exponent form+ char mantissa_buffer[32];++ char* mantissa;+ int exponent;+ convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent);++ // allocate a buffer of suitable length for the number+ size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4;+ char_t* result = static_cast<char_t*>(alloc->allocate(sizeof(char_t) * result_size));+ assert(result);++ // make the number!+ char_t* s = result;++ // sign+ if (value < 0) *s++ = '-';++ // integer part+ if (exponent <= 0)+ {+ *s++ = '0';+ }+ else+ {+ while (exponent > 0)+ {+ assert(*mantissa == 0 || static_cast<unsigned int>(static_cast<unsigned int>(*mantissa) - '0') <= 9);+ *s++ = *mantissa ? *mantissa++ : '0';+ exponent--;+ }+ }++ // fractional part+ if (*mantissa)+ {+ // decimal point+ *s++ = '.';++ // extra zeroes from negative exponent+ while (exponent < 0)+ {+ *s++ = '0';+ exponent++;+ }++ // extra mantissa digits+ while (*mantissa)+ {+ assert(static_cast<unsigned int>(*mantissa - '0') <= 9);+ *s++ = *mantissa++;+ }+ }++ // zero-terminate+ assert(s < result + result_size);+ *s = 0;++ return xpath_string(result, true);+ }+ + PUGI__FN bool check_string_to_number_format(const char_t* string)+ {+ // parse leading whitespace+ while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;++ // parse sign+ if (*string == '-') ++string;++ if (!*string) return false;++ // if there is no integer part, there should be a decimal part with at least one digit+ if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false;++ // parse integer part+ while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;++ // parse decimal part+ if (*string == '.')+ {+ ++string;++ while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;+ }++ // parse trailing whitespace+ while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;++ return *string == 0;+ }++ PUGI__FN double convert_string_to_number(const char_t* string)+ {+ // check string format+ if (!check_string_to_number_format(string)) return gen_nan();++ // parse string+ #ifdef PUGIXML_WCHAR_MODE+ return wcstod(string, 0);+ #else+ return atof(string);+ #endif+ }++ PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result)+ {+ size_t length = static_cast<size_t>(end - begin);+ char_t* scratch = buffer;++ if (length >= sizeof(buffer) / sizeof(buffer[0]))+ {+ // need to make dummy on-heap copy+ scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!scratch) return false;+ }++ // copy string to zero-terminated buffer and perform conversion+ memcpy(scratch, begin, length * sizeof(char_t));+ scratch[length] = 0;++ *out_result = convert_string_to_number(scratch);++ // free dummy buffer+ if (scratch != buffer) xml_memory::deallocate(scratch);++ return true;+ }+ + PUGI__FN double round_nearest(double value)+ {+ return floor(value + 0.5);+ }++ PUGI__FN double round_nearest_nzero(double value)+ {+ // same as round_nearest, but returns -0 for [-0.5, -0]+ // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0)+ return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5);+ }+ + PUGI__FN const char_t* qualified_name(const xpath_node& node)+ {+ return node.attribute() ? node.attribute().name() : node.node().name();+ }+ + PUGI__FN const char_t* local_name(const xpath_node& node)+ {+ const char_t* name = qualified_name(node);+ const char_t* p = find_char(name, ':');+ + return p ? p + 1 : name;+ }++ struct namespace_uri_predicate+ {+ const char_t* prefix;+ size_t prefix_length;++ namespace_uri_predicate(const char_t* name)+ {+ const char_t* pos = find_char(name, ':');++ prefix = pos ? name : 0;+ prefix_length = pos ? static_cast<size_t>(pos - name) : 0;+ }++ bool operator()(const xml_attribute& a) const+ {+ const char_t* name = a.name();++ if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false;++ return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0;+ }+ };++ PUGI__FN const char_t* namespace_uri(const xml_node& node)+ {+ namespace_uri_predicate pred = node.name();+ + xml_node p = node;+ + while (p)+ {+ xml_attribute a = p.find_attribute(pred);+ + if (a) return a.value();+ + p = p.parent();+ }+ + return PUGIXML_TEXT("");+ }++ PUGI__FN const char_t* namespace_uri(const xml_attribute& attr, const xml_node& parent)+ {+ namespace_uri_predicate pred = attr.name();+ + // Default namespace does not apply to attributes+ if (!pred.prefix) return PUGIXML_TEXT("");+ + xml_node p = parent;+ + while (p)+ {+ xml_attribute a = p.find_attribute(pred);+ + if (a) return a.value();+ + p = p.parent();+ }+ + return PUGIXML_TEXT("");+ }++ PUGI__FN const char_t* namespace_uri(const xpath_node& node)+ {+ return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node());+ }++ PUGI__FN void normalize_space(char_t* buffer)+ {+ char_t* write = buffer;++ for (char_t* it = buffer; *it; )+ {+ char_t ch = *it++;++ if (PUGI__IS_CHARTYPE(ch, ct_space))+ {+ // replace whitespace sequence with single space+ while (PUGI__IS_CHARTYPE(*it, ct_space)) it++;++ // avoid leading spaces+ if (write != buffer) *write++ = ' ';+ }+ else *write++ = ch;+ }++ // remove trailing space+ if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--;++ // zero-terminate+ *write = 0;+ }++ PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to)+ {+ size_t to_length = strlength(to);++ char_t* write = buffer;++ while (*buffer)+ {+ PUGI__DMC_VOLATILE char_t ch = *buffer++;++ const char_t* pos = find_char(from, ch);++ if (!pos)+ *write++ = ch; // do not process+ else if (static_cast<size_t>(pos - from) < to_length)+ *write++ = to[pos - from]; // replace+ }++ // zero-terminate+ *write = 0;+ }++ struct xpath_variable_boolean: xpath_variable+ {+ xpath_variable_boolean(): value(false)+ {+ }++ bool value;+ char_t name[1];+ };++ struct xpath_variable_number: xpath_variable+ {+ xpath_variable_number(): value(0)+ {+ }++ double value;+ char_t name[1];+ };++ struct xpath_variable_string: xpath_variable+ {+ xpath_variable_string(): value(0)+ {+ }++ ~xpath_variable_string()+ {+ if (value) xml_memory::deallocate(value);+ }++ char_t* value;+ char_t name[1];+ };++ struct xpath_variable_node_set: xpath_variable+ {+ xpath_node_set value;+ char_t name[1];+ };++ static const xpath_node_set dummy_node_set;++ PUGI__FN unsigned int hash_string(const char_t* str)+ {+ // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)+ unsigned int result = 0;++ while (*str)+ {+ result += static_cast<unsigned int>(*str++);+ result += result << 10;+ result ^= result >> 6;+ }+ + result += result << 3;+ result ^= result >> 11;+ result += result << 15;+ + return result;+ }++ template <typename T> PUGI__FN T* new_xpath_variable(const char_t* name)+ {+ size_t length = strlength(name);+ if (length == 0) return 0; // empty variable names are invalid++ // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters+ void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t));+ if (!memory) return 0;++ T* result = new (memory) T();++ memcpy(result->name, name, (length + 1) * sizeof(char_t));++ return result;+ }++ PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name)+ {+ switch (type)+ {+ case xpath_type_node_set:+ return new_xpath_variable<xpath_variable_node_set>(name);++ case xpath_type_number:+ return new_xpath_variable<xpath_variable_number>(name);++ case xpath_type_string:+ return new_xpath_variable<xpath_variable_string>(name);++ case xpath_type_boolean:+ return new_xpath_variable<xpath_variable_boolean>(name);++ default:+ return 0;+ }+ }++ template <typename T> PUGI__FN void delete_xpath_variable(T* var)+ {+ var->~T();+ xml_memory::deallocate(var);+ }++ PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var)+ {+ switch (type)+ {+ case xpath_type_node_set:+ delete_xpath_variable(static_cast<xpath_variable_node_set*>(var));+ break;++ case xpath_type_number:+ delete_xpath_variable(static_cast<xpath_variable_number*>(var));+ break;++ case xpath_type_string:+ delete_xpath_variable(static_cast<xpath_variable_string*>(var));+ break;++ case xpath_type_boolean:+ delete_xpath_variable(static_cast<xpath_variable_boolean*>(var));+ break;++ default:+ assert(!"Invalid variable type");+ }+ }++ PUGI__FN xpath_variable* get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end)+ {+ size_t length = static_cast<size_t>(end - begin);+ char_t* scratch = buffer;++ if (length >= sizeof(buffer) / sizeof(buffer[0]))+ {+ // need to make dummy on-heap copy+ scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));+ if (!scratch) return 0;+ }++ // copy string to zero-terminated buffer and perform lookup+ memcpy(scratch, begin, length * sizeof(char_t));+ scratch[length] = 0;++ xpath_variable* result = set->get(scratch);++ // free dummy buffer+ if (scratch != buffer) xml_memory::deallocate(scratch);++ return result;+ }+PUGI__NS_END++// Internal node set class+PUGI__NS_BEGIN+ PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev)+ {+ xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;++ if (type == xpath_node_set::type_unsorted)+ {+ sort(begin, end, document_order_comparator());++ type = xpath_node_set::type_sorted;+ }+ + if (type != order) reverse(begin, end);+ + return order;+ }++ PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type)+ {+ if (begin == end) return xpath_node();++ switch (type)+ {+ case xpath_node_set::type_sorted:+ return *begin;++ case xpath_node_set::type_sorted_reverse:+ return *(end - 1);++ case xpath_node_set::type_unsorted:+ return *min_element(begin, end, document_order_comparator());++ default:+ assert(!"Invalid node set type");+ return xpath_node();+ }+ }++ class xpath_node_set_raw+ {+ xpath_node_set::type_t _type;++ xpath_node* _begin;+ xpath_node* _end;+ xpath_node* _eos;++ public:+ xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0)+ {+ }++ xpath_node* begin() const+ {+ return _begin;+ }++ xpath_node* end() const+ {+ return _end;+ }++ bool empty() const+ {+ return _begin == _end;+ }++ size_t size() const+ {+ return static_cast<size_t>(_end - _begin);+ }++ xpath_node first() const+ {+ return xpath_first(_begin, _end, _type);+ }++ void push_back(const xpath_node& node, xpath_allocator* alloc)+ {+ if (_end == _eos)+ {+ size_t capacity = static_cast<size_t>(_eos - _begin);++ // get new capacity (1.5x rule)+ size_t new_capacity = capacity + capacity / 2 + 1;++ // reallocate the old array or allocate a new one+ xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));+ assert(data);++ // finalize+ _begin = data;+ _end = data + capacity;+ _eos = data + new_capacity;+ }++ *_end++ = node;+ }++ void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc)+ {+ size_t size_ = static_cast<size_t>(_end - _begin);+ size_t capacity = static_cast<size_t>(_eos - _begin);+ size_t count = static_cast<size_t>(end_ - begin_);++ if (size_ + count > capacity)+ {+ // reallocate the old array or allocate a new one+ xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node)));+ assert(data);++ // finalize+ _begin = data;+ _end = data + size_;+ _eos = data + size_ + count;+ }++ memcpy(_end, begin_, count * sizeof(xpath_node));+ _end += count;+ }++ void sort_do()+ {+ _type = xpath_sort(_begin, _end, _type, false);+ }++ void truncate(xpath_node* pos)+ {+ assert(_begin <= pos && pos <= _end);++ _end = pos;+ }++ void remove_duplicates()+ {+ if (_type == xpath_node_set::type_unsorted)+ sort(_begin, _end, duplicate_comparator());+ + _end = unique(_begin, _end);+ }++ xpath_node_set::type_t type() const+ {+ return _type;+ }++ void set_type(xpath_node_set::type_t value)+ {+ _type = value;+ }+ };+PUGI__NS_END++PUGI__NS_BEGIN+ struct xpath_context+ {+ xpath_node n;+ size_t position, size;++ xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_)+ {+ }+ };++ enum lexeme_t+ {+ lex_none = 0,+ lex_equal,+ lex_not_equal,+ lex_less,+ lex_greater,+ lex_less_or_equal,+ lex_greater_or_equal,+ lex_plus,+ lex_minus,+ lex_multiply,+ lex_union,+ lex_var_ref,+ lex_open_brace,+ lex_close_brace,+ lex_quoted_string,+ lex_number,+ lex_slash,+ lex_double_slash,+ lex_open_square_brace,+ lex_close_square_brace,+ lex_string,+ lex_comma,+ lex_axis_attribute,+ lex_dot,+ lex_double_dot,+ lex_double_colon,+ lex_eof+ };++ struct xpath_lexer_string+ {+ const char_t* begin;+ const char_t* end;++ xpath_lexer_string(): begin(0), end(0)+ {+ }++ bool operator==(const char_t* other) const+ {+ size_t length = static_cast<size_t>(end - begin);++ return strequalrange(other, begin, length);+ }+ };++ class xpath_lexer+ {+ const char_t* _cur;+ const char_t* _cur_lexeme_pos;+ xpath_lexer_string _cur_lexeme_contents;++ lexeme_t _cur_lexeme;++ public:+ explicit xpath_lexer(const char_t* query): _cur(query)+ {+ next();+ }+ + const char_t* state() const+ {+ return _cur;+ }+ + void next()+ {+ const char_t* cur = _cur;++ while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur;++ // save lexeme position for error reporting+ _cur_lexeme_pos = cur;++ switch (*cur)+ {+ case 0:+ _cur_lexeme = lex_eof;+ break;+ + case '>':+ if (*(cur+1) == '=')+ {+ cur += 2;+ _cur_lexeme = lex_greater_or_equal;+ }+ else+ {+ cur += 1;+ _cur_lexeme = lex_greater;+ }+ break;++ case '<':+ if (*(cur+1) == '=')+ {+ cur += 2;+ _cur_lexeme = lex_less_or_equal;+ }+ else+ {+ cur += 1;+ _cur_lexeme = lex_less;+ }+ break;++ case '!':+ if (*(cur+1) == '=')+ {+ cur += 2;+ _cur_lexeme = lex_not_equal;+ }+ else+ {+ _cur_lexeme = lex_none;+ }+ break;++ case '=':+ cur += 1;+ _cur_lexeme = lex_equal;++ break;+ + case '+':+ cur += 1;+ _cur_lexeme = lex_plus;++ break;++ case '-':+ cur += 1;+ _cur_lexeme = lex_minus;++ break;++ case '*':+ cur += 1;+ _cur_lexeme = lex_multiply;++ break;++ case '|':+ cur += 1;+ _cur_lexeme = lex_union;++ break;+ + case '$':+ cur += 1;++ if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))+ {+ _cur_lexeme_contents.begin = cur;++ while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;++ if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname+ {+ cur++; // :++ while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;+ }++ _cur_lexeme_contents.end = cur;+ + _cur_lexeme = lex_var_ref;+ }+ else+ {+ _cur_lexeme = lex_none;+ }++ break;++ case '(':+ cur += 1;+ _cur_lexeme = lex_open_brace;++ break;++ case ')':+ cur += 1;+ _cur_lexeme = lex_close_brace;++ break;+ + case '[':+ cur += 1;+ _cur_lexeme = lex_open_square_brace;++ break;++ case ']':+ cur += 1;+ _cur_lexeme = lex_close_square_brace;++ break;++ case ',':+ cur += 1;+ _cur_lexeme = lex_comma;++ break;++ case '/':+ if (*(cur+1) == '/')+ {+ cur += 2;+ _cur_lexeme = lex_double_slash;+ }+ else+ {+ cur += 1;+ _cur_lexeme = lex_slash;+ }+ break;+ + case '.':+ if (*(cur+1) == '.')+ {+ cur += 2;+ _cur_lexeme = lex_double_dot;+ }+ else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit))+ {+ _cur_lexeme_contents.begin = cur; // .++ ++cur;++ while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;++ _cur_lexeme_contents.end = cur;+ + _cur_lexeme = lex_number;+ }+ else+ {+ cur += 1;+ _cur_lexeme = lex_dot;+ }+ break;++ case '@':+ cur += 1;+ _cur_lexeme = lex_axis_attribute;++ break;++ case '"':+ case '\'':+ {+ char_t terminator = *cur;++ ++cur;++ _cur_lexeme_contents.begin = cur;+ while (*cur && *cur != terminator) cur++;+ _cur_lexeme_contents.end = cur;+ + if (!*cur)+ _cur_lexeme = lex_none;+ else+ {+ cur += 1;+ _cur_lexeme = lex_quoted_string;+ }++ break;+ }++ case ':':+ if (*(cur+1) == ':')+ {+ cur += 2;+ _cur_lexeme = lex_double_colon;+ }+ else+ {+ _cur_lexeme = lex_none;+ }+ break;++ default:+ if (PUGI__IS_CHARTYPEX(*cur, ctx_digit))+ {+ _cur_lexeme_contents.begin = cur;++ while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;+ + if (*cur == '.')+ {+ cur++;++ while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;+ }++ _cur_lexeme_contents.end = cur;++ _cur_lexeme = lex_number;+ }+ else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))+ {+ _cur_lexeme_contents.begin = cur;++ while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;++ if (cur[0] == ':')+ {+ if (cur[1] == '*') // namespace test ncname:*+ {+ cur += 2; // :*+ }+ else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname+ {+ cur++; // :++ while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;+ }+ }++ _cur_lexeme_contents.end = cur;+ + _cur_lexeme = lex_string;+ }+ else+ {+ _cur_lexeme = lex_none;+ }+ }++ _cur = cur;+ }++ lexeme_t current() const+ {+ return _cur_lexeme;+ }++ const char_t* current_pos() const+ {+ return _cur_lexeme_pos;+ }++ const xpath_lexer_string& contents() const+ {+ assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string);++ return _cur_lexeme_contents;+ }+ };++ enum ast_type_t+ {+ ast_unknown,+ ast_op_or, // left or right+ ast_op_and, // left and right+ ast_op_equal, // left = right+ ast_op_not_equal, // left != right+ ast_op_less, // left < right+ ast_op_greater, // left > right+ ast_op_less_or_equal, // left <= right+ ast_op_greater_or_equal, // left >= right+ ast_op_add, // left + right+ ast_op_subtract, // left - right+ ast_op_multiply, // left * right+ ast_op_divide, // left / right+ ast_op_mod, // left % right+ ast_op_negate, // left - right+ ast_op_union, // left | right+ ast_predicate, // apply predicate to set; next points to next predicate+ ast_filter, // select * from left where right+ ast_filter_posinv, // select * from left where right; proximity position invariant+ ast_string_constant, // string constant+ ast_number_constant, // number constant+ ast_variable, // variable+ ast_func_last, // last()+ ast_func_position, // position()+ ast_func_count, // count(left)+ ast_func_id, // id(left)+ ast_func_local_name_0, // local-name()+ ast_func_local_name_1, // local-name(left)+ ast_func_namespace_uri_0, // namespace-uri()+ ast_func_namespace_uri_1, // namespace-uri(left)+ ast_func_name_0, // name()+ ast_func_name_1, // name(left)+ ast_func_string_0, // string()+ ast_func_string_1, // string(left)+ ast_func_concat, // concat(left, right, siblings)+ ast_func_starts_with, // starts_with(left, right)+ ast_func_contains, // contains(left, right)+ ast_func_substring_before, // substring-before(left, right)+ ast_func_substring_after, // substring-after(left, right)+ ast_func_substring_2, // substring(left, right)+ ast_func_substring_3, // substring(left, right, third)+ ast_func_string_length_0, // string-length()+ ast_func_string_length_1, // string-length(left)+ ast_func_normalize_space_0, // normalize-space()+ ast_func_normalize_space_1, // normalize-space(left)+ ast_func_translate, // translate(left, right, third)+ ast_func_boolean, // boolean(left)+ ast_func_not, // not(left)+ ast_func_true, // true()+ ast_func_false, // false()+ ast_func_lang, // lang(left)+ ast_func_number_0, // number()+ ast_func_number_1, // number(left)+ ast_func_sum, // sum(left)+ ast_func_floor, // floor(left)+ ast_func_ceiling, // ceiling(left)+ ast_func_round, // round(left)+ ast_step, // process set left with step+ ast_step_root // select root node+ };++ enum axis_t+ {+ axis_ancestor,+ axis_ancestor_or_self,+ axis_attribute,+ axis_child,+ axis_descendant,+ axis_descendant_or_self,+ axis_following,+ axis_following_sibling,+ axis_namespace,+ axis_parent,+ axis_preceding,+ axis_preceding_sibling,+ axis_self+ };+ + enum nodetest_t+ {+ nodetest_none,+ nodetest_name,+ nodetest_type_node,+ nodetest_type_comment,+ nodetest_type_pi,+ nodetest_type_text,+ nodetest_pi,+ nodetest_all,+ nodetest_all_in_namespace+ };++ template <axis_t N> struct axis_to_type+ {+ static const axis_t axis;+ };++ template <axis_t N> const axis_t axis_to_type<N>::axis = N;+ + class xpath_ast_node+ {+ private:+ // node type+ char _type;+ char _rettype;++ // for ast_step / ast_predicate+ char _axis;+ char _test;++ // tree node structure+ xpath_ast_node* _left;+ xpath_ast_node* _right;+ xpath_ast_node* _next;++ union+ {+ // value for ast_string_constant+ const char_t* string;+ // value for ast_number_constant+ double number;+ // variable for ast_variable+ xpath_variable* variable;+ // node test for ast_step (node name/namespace/node type/pi target)+ const char_t* nodetest;+ } _data;++ xpath_ast_node(const xpath_ast_node&);+ xpath_ast_node& operator=(const xpath_ast_node&);++ template <class Comp> static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)+ {+ xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();++ if (lt != xpath_type_node_set && rt != xpath_type_node_set)+ {+ if (lt == xpath_type_boolean || rt == xpath_type_boolean)+ return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));+ else if (lt == xpath_type_number || rt == xpath_type_number)+ return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));+ else if (lt == xpath_type_string || rt == xpath_type_string)+ {+ xpath_allocator_capture cr(stack.result);++ xpath_string ls = lhs->eval_string(c, stack);+ xpath_string rs = rhs->eval_string(c, stack);++ return comp(ls, rs);+ }+ }+ else if (lt == xpath_type_node_set && rt == xpath_type_node_set)+ {+ xpath_allocator_capture cr(stack.result);++ xpath_node_set_raw ls = lhs->eval_node_set(c, stack);+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);++ for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)+ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)+ {+ xpath_allocator_capture cri(stack.result);++ if (comp(string_value(*li, stack.result), string_value(*ri, stack.result)))+ return true;+ }++ return false;+ }+ else+ {+ if (lt == xpath_type_node_set)+ {+ swap(lhs, rhs);+ swap(lt, rt);+ }++ if (lt == xpath_type_boolean)+ return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));+ else if (lt == xpath_type_number)+ {+ xpath_allocator_capture cr(stack.result);++ double l = lhs->eval_number(c, stack);+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);++ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)+ {+ xpath_allocator_capture cri(stack.result);++ if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))+ return true;+ }++ return false;+ }+ else if (lt == xpath_type_string)+ {+ xpath_allocator_capture cr(stack.result);++ xpath_string l = lhs->eval_string(c, stack);+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);++ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)+ {+ xpath_allocator_capture cri(stack.result);++ if (comp(l, string_value(*ri, stack.result)))+ return true;+ }++ return false;+ }+ }++ assert(!"Wrong types");+ return false;+ }++ template <class Comp> static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)+ {+ xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();++ if (lt != xpath_type_node_set && rt != xpath_type_node_set)+ return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));+ else if (lt == xpath_type_node_set && rt == xpath_type_node_set)+ {+ xpath_allocator_capture cr(stack.result);++ xpath_node_set_raw ls = lhs->eval_node_set(c, stack);+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);++ for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)+ {+ xpath_allocator_capture cri(stack.result);++ double l = convert_string_to_number(string_value(*li, stack.result).c_str());++ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)+ {+ xpath_allocator_capture crii(stack.result);++ if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))+ return true;+ }+ }++ return false;+ }+ else if (lt != xpath_type_node_set && rt == xpath_type_node_set)+ {+ xpath_allocator_capture cr(stack.result);++ double l = lhs->eval_number(c, stack);+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);++ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)+ {+ xpath_allocator_capture cri(stack.result);++ if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))+ return true;+ }++ return false;+ }+ else if (lt == xpath_type_node_set && rt != xpath_type_node_set)+ {+ xpath_allocator_capture cr(stack.result);++ xpath_node_set_raw ls = lhs->eval_node_set(c, stack);+ double r = rhs->eval_number(c, stack);++ for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)+ {+ xpath_allocator_capture cri(stack.result);++ if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r))+ return true;+ }++ return false;+ }+ else+ {+ assert(!"Wrong types");+ return false;+ }+ }++ void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)+ {+ assert(ns.size() >= first);++ size_t i = 1;+ size_t size = ns.size() - first;+ + xpath_node* last = ns.begin() + first;+ + // remove_if... or well, sort of+ for (xpath_node* it = last; it != ns.end(); ++it, ++i)+ {+ xpath_context c(*it, i, size);+ + if (expr->rettype() == xpath_type_number)+ {+ if (expr->eval_number(c, stack) == i)+ *last++ = *it;+ }+ else if (expr->eval_boolean(c, stack))+ *last++ = *it;+ }+ + ns.truncate(last);+ }++ void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack)+ {+ if (ns.size() == first) return;+ + for (xpath_ast_node* pred = _right; pred; pred = pred->_next)+ {+ apply_predicate(ns, first, pred->_left, stack);+ }+ }++ void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc)+ {+ if (!a) return;++ const char_t* name = a.name();++ // There are no attribute nodes corresponding to attributes that declare namespaces+ // That is, "xmlns:..." or "xmlns"+ if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return;+ + switch (_test)+ {+ case nodetest_name:+ if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc);+ break;+ + case nodetest_type_node:+ case nodetest_all:+ ns.push_back(xpath_node(a, parent), alloc);+ break;+ + case nodetest_all_in_namespace:+ if (starts_with(name, _data.nodetest))+ ns.push_back(xpath_node(a, parent), alloc);+ break;+ + default:+ ;+ }+ }+ + void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc)+ {+ if (!n) return;++ switch (_test)+ {+ case nodetest_name:+ if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc);+ break;+ + case nodetest_type_node:+ ns.push_back(n, alloc);+ break;+ + case nodetest_type_comment:+ if (n.type() == node_comment)+ ns.push_back(n, alloc);+ break;+ + case nodetest_type_text:+ if (n.type() == node_pcdata || n.type() == node_cdata)+ ns.push_back(n, alloc);+ break;+ + case nodetest_type_pi:+ if (n.type() == node_pi)+ ns.push_back(n, alloc);+ break;+ + case nodetest_pi:+ if (n.type() == node_pi && strequal(n.name(), _data.nodetest))+ ns.push_back(n, alloc);+ break;+ + case nodetest_all:+ if (n.type() == node_element)+ ns.push_back(n, alloc);+ break;+ + case nodetest_all_in_namespace:+ if (n.type() == node_element && starts_with(n.name(), _data.nodetest))+ ns.push_back(n, alloc);+ break;++ default:+ assert(!"Unknown axis");+ } + }++ template <class T> void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T)+ {+ const axis_t axis = T::axis;++ switch (axis)+ {+ case axis_attribute:+ {+ for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute())+ step_push(ns, a, n, alloc);+ + break;+ }+ + case axis_child:+ {+ for (xml_node c = n.first_child(); c; c = c.next_sibling())+ step_push(ns, c, alloc);+ + break;+ }+ + case axis_descendant:+ case axis_descendant_or_self:+ {+ if (axis == axis_descendant_or_self)+ step_push(ns, n, alloc);+ + xml_node cur = n.first_child();+ + while (cur && cur != n)+ {+ step_push(ns, cur, alloc);+ + if (cur.first_child())+ cur = cur.first_child();+ else if (cur.next_sibling())+ cur = cur.next_sibling();+ else+ {+ while (!cur.next_sibling() && cur != n)+ cur = cur.parent();+ + if (cur != n) cur = cur.next_sibling();+ }+ }+ + break;+ }+ + case axis_following_sibling:+ {+ for (xml_node c = n.next_sibling(); c; c = c.next_sibling())+ step_push(ns, c, alloc);+ + break;+ }+ + case axis_preceding_sibling:+ {+ for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling())+ step_push(ns, c, alloc);+ + break;+ }+ + case axis_following:+ {+ xml_node cur = n;++ // exit from this node so that we don't include descendants+ while (cur && !cur.next_sibling()) cur = cur.parent();+ cur = cur.next_sibling();++ for (;;)+ {+ step_push(ns, cur, alloc);++ if (cur.first_child())+ cur = cur.first_child();+ else if (cur.next_sibling())+ cur = cur.next_sibling();+ else+ {+ while (cur && !cur.next_sibling()) cur = cur.parent();+ cur = cur.next_sibling();++ if (!cur) break;+ }+ }++ break;+ }++ case axis_preceding:+ {+ xml_node cur = n;++ while (cur && !cur.previous_sibling()) cur = cur.parent();+ cur = cur.previous_sibling();++ for (;;)+ {+ if (cur.last_child())+ cur = cur.last_child();+ else+ {+ // leaf node, can't be ancestor+ step_push(ns, cur, alloc);++ if (cur.previous_sibling())+ cur = cur.previous_sibling();+ else+ {+ do + {+ cur = cur.parent();+ if (!cur) break;++ if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc);+ }+ while (!cur.previous_sibling());++ cur = cur.previous_sibling();++ if (!cur) break;+ }+ }+ }++ break;+ }+ + case axis_ancestor:+ case axis_ancestor_or_self:+ {+ if (axis == axis_ancestor_or_self)+ step_push(ns, n, alloc);++ xml_node cur = n.parent();+ + while (cur)+ {+ step_push(ns, cur, alloc);+ + cur = cur.parent();+ }+ + break;+ }++ case axis_self:+ {+ step_push(ns, n, alloc);++ break;+ }++ case axis_parent:+ {+ if (n.parent()) step_push(ns, n.parent(), alloc);++ break;+ }+ + default:+ assert(!"Unimplemented axis");+ }+ }+ + template <class T> void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, T v)+ {+ const axis_t axis = T::axis;++ switch (axis)+ {+ case axis_ancestor:+ case axis_ancestor_or_self:+ {+ if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test+ step_push(ns, a, p, alloc);++ xml_node cur = p;+ + while (cur)+ {+ step_push(ns, cur, alloc);+ + cur = cur.parent();+ }+ + break;+ }++ case axis_descendant_or_self:+ case axis_self:+ {+ if (_test == nodetest_type_node) // reject attributes based on principal node type test+ step_push(ns, a, p, alloc);++ break;+ }++ case axis_following:+ {+ xml_node cur = p;+ + for (;;)+ {+ if (cur.first_child())+ cur = cur.first_child();+ else if (cur.next_sibling())+ cur = cur.next_sibling();+ else+ {+ while (cur && !cur.next_sibling()) cur = cur.parent();+ cur = cur.next_sibling();+ + if (!cur) break;+ }++ step_push(ns, cur, alloc);+ }++ break;+ }++ case axis_parent:+ {+ step_push(ns, p, alloc);++ break;+ }++ case axis_preceding:+ {+ // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding+ step_fill(ns, p, alloc, v);+ break;+ }+ + default:+ assert(!"Unimplemented axis");+ }+ }+ + template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v)+ {+ const axis_t axis = T::axis;+ bool attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);++ xpath_node_set_raw ns;+ ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);++ if (_left)+ {+ xpath_node_set_raw s = _left->eval_node_set(c, stack);++ // self axis preserves the original order+ if (axis == axis_self) ns.set_type(s.type());++ for (const xpath_node* it = s.begin(); it != s.end(); ++it)+ {+ size_t size = ns.size();++ // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes+ if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);+ + if (it->node())+ step_fill(ns, it->node(), stack.result, v);+ else if (attributes)+ step_fill(ns, it->attribute(), it->parent(), stack.result, v);+ + apply_predicates(ns, size, stack);+ }+ }+ else+ {+ if (c.n.node())+ step_fill(ns, c.n.node(), stack.result, v);+ else if (attributes)+ step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v);+ + apply_predicates(ns, 0, stack);+ }++ // child, attribute and self axes always generate unique set of nodes+ // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice+ if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted)+ ns.remove_duplicates();++ return ns;+ }+ + public:+ xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value):+ _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)+ {+ assert(type == ast_string_constant);+ _data.string = value;+ }++ xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value):+ _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)+ {+ assert(type == ast_number_constant);+ _data.number = value;+ }+ + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value):+ _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)+ {+ assert(type == ast_variable);+ _data.variable = value;+ }+ + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0):+ _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0)+ {+ }++ xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents):+ _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)+ {+ _data.nodetest = contents;+ }++ void set_next(xpath_ast_node* value)+ {+ _next = value;+ }++ void set_right(xpath_ast_node* value)+ {+ _right = value;+ }++ bool eval_boolean(const xpath_context& c, const xpath_stack& stack)+ {+ switch (_type)+ {+ case ast_op_or:+ return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack);+ + case ast_op_and:+ return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack);+ + case ast_op_equal:+ return compare_eq(_left, _right, c, stack, equal_to());++ case ast_op_not_equal:+ return compare_eq(_left, _right, c, stack, not_equal_to());+ + case ast_op_less:+ return compare_rel(_left, _right, c, stack, less());+ + case ast_op_greater:+ return compare_rel(_right, _left, c, stack, less());++ case ast_op_less_or_equal:+ return compare_rel(_left, _right, c, stack, less_equal());+ + case ast_op_greater_or_equal:+ return compare_rel(_right, _left, c, stack, less_equal());++ case ast_func_starts_with:+ {+ xpath_allocator_capture cr(stack.result);++ xpath_string lr = _left->eval_string(c, stack);+ xpath_string rr = _right->eval_string(c, stack);++ return starts_with(lr.c_str(), rr.c_str());+ }++ case ast_func_contains:+ {+ xpath_allocator_capture cr(stack.result);++ xpath_string lr = _left->eval_string(c, stack);+ xpath_string rr = _right->eval_string(c, stack);++ return find_substring(lr.c_str(), rr.c_str()) != 0;+ }++ case ast_func_boolean:+ return _left->eval_boolean(c, stack);+ + case ast_func_not:+ return !_left->eval_boolean(c, stack);+ + case ast_func_true:+ return true;+ + case ast_func_false:+ return false;++ case ast_func_lang:+ {+ if (c.n.attribute()) return false;+ + xpath_allocator_capture cr(stack.result);++ xpath_string lang = _left->eval_string(c, stack);+ + for (xml_node n = c.n.node(); n; n = n.parent())+ {+ xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang"));+ + if (a)+ {+ const char_t* value = a.value();+ + // strnicmp / strncasecmp is not portable+ for (const char_t* lit = lang.c_str(); *lit; ++lit)+ {+ if (tolower_ascii(*lit) != tolower_ascii(*value)) return false;+ ++value;+ }+ + return *value == 0 || *value == '-';+ }+ }+ + return false;+ }++ case ast_variable:+ {+ assert(_rettype == _data.variable->type());++ if (_rettype == xpath_type_boolean)+ return _data.variable->get_boolean();++ // fallthrough to type conversion+ }++ default:+ {+ switch (_rettype)+ {+ case xpath_type_number:+ return convert_number_to_boolean(eval_number(c, stack));+ + case xpath_type_string:+ {+ xpath_allocator_capture cr(stack.result);++ return !eval_string(c, stack).empty();+ }+ + case xpath_type_node_set: + {+ xpath_allocator_capture cr(stack.result);++ return !eval_node_set(c, stack).empty();+ }++ default:+ assert(!"Wrong expression for return type boolean");+ return false;+ }+ }+ }+ }++ double eval_number(const xpath_context& c, const xpath_stack& stack)+ {+ switch (_type)+ {+ case ast_op_add:+ return _left->eval_number(c, stack) + _right->eval_number(c, stack);+ + case ast_op_subtract:+ return _left->eval_number(c, stack) - _right->eval_number(c, stack);++ case ast_op_multiply:+ return _left->eval_number(c, stack) * _right->eval_number(c, stack);++ case ast_op_divide:+ return _left->eval_number(c, stack) / _right->eval_number(c, stack);++ case ast_op_mod:+ return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack));++ case ast_op_negate:+ return -_left->eval_number(c, stack);++ case ast_number_constant:+ return _data.number;++ case ast_func_last:+ return static_cast<double>(c.size);+ + case ast_func_position:+ return static_cast<double>(c.position);++ case ast_func_count:+ {+ xpath_allocator_capture cr(stack.result);++ return static_cast<double>(_left->eval_node_set(c, stack).size());+ }+ + case ast_func_string_length_0:+ {+ xpath_allocator_capture cr(stack.result);++ return static_cast<double>(string_value(c.n, stack.result).length());+ }+ + case ast_func_string_length_1:+ {+ xpath_allocator_capture cr(stack.result);++ return static_cast<double>(_left->eval_string(c, stack).length());+ }+ + case ast_func_number_0:+ {+ xpath_allocator_capture cr(stack.result);++ return convert_string_to_number(string_value(c.n, stack.result).c_str());+ }+ + case ast_func_number_1:+ return _left->eval_number(c, stack);++ case ast_func_sum:+ {+ xpath_allocator_capture cr(stack.result);++ double r = 0;+ + xpath_node_set_raw ns = _left->eval_node_set(c, stack);+ + for (const xpath_node* it = ns.begin(); it != ns.end(); ++it)+ {+ xpath_allocator_capture cri(stack.result);++ r += convert_string_to_number(string_value(*it, stack.result).c_str());+ }+ + return r;+ }++ case ast_func_floor:+ {+ double r = _left->eval_number(c, stack);+ + return r == r ? floor(r) : r;+ }++ case ast_func_ceiling:+ {+ double r = _left->eval_number(c, stack);+ + return r == r ? ceil(r) : r;+ }++ case ast_func_round:+ return round_nearest_nzero(_left->eval_number(c, stack));+ + case ast_variable:+ {+ assert(_rettype == _data.variable->type());++ if (_rettype == xpath_type_number)+ return _data.variable->get_number();++ // fallthrough to type conversion+ }++ default:+ {+ switch (_rettype)+ {+ case xpath_type_boolean:+ return eval_boolean(c, stack) ? 1 : 0;+ + case xpath_type_string:+ {+ xpath_allocator_capture cr(stack.result);++ return convert_string_to_number(eval_string(c, stack).c_str());+ }+ + case xpath_type_node_set:+ {+ xpath_allocator_capture cr(stack.result);++ return convert_string_to_number(eval_string(c, stack).c_str());+ }+ + default:+ assert(!"Wrong expression for return type number");+ return 0;+ }+ + }+ }+ }+ + xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack)+ {+ assert(_type == ast_func_concat);++ xpath_allocator_capture ct(stack.temp);++ // count the string number+ size_t count = 1;+ for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++;++ // gather all strings+ xpath_string static_buffer[4];+ xpath_string* buffer = static_buffer;++ // allocate on-heap for large concats+ if (count > sizeof(static_buffer) / sizeof(static_buffer[0]))+ {+ buffer = static_cast<xpath_string*>(stack.temp->allocate(count * sizeof(xpath_string)));+ assert(buffer);+ }++ // evaluate all strings to temporary stack+ xpath_stack swapped_stack = {stack.temp, stack.result};++ buffer[0] = _left->eval_string(c, swapped_stack);++ size_t pos = 1;+ for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack);+ assert(pos == count);++ // get total length+ size_t length = 0;+ for (size_t i = 0; i < count; ++i) length += buffer[i].length();++ // create final string+ char_t* result = static_cast<char_t*>(stack.result->allocate((length + 1) * sizeof(char_t)));+ assert(result);++ char_t* ri = result;++ for (size_t j = 0; j < count; ++j)+ for (const char_t* bi = buffer[j].c_str(); *bi; ++bi)+ *ri++ = *bi;++ *ri = 0;++ return xpath_string(result, true);+ }++ xpath_string eval_string(const xpath_context& c, const xpath_stack& stack)+ {+ switch (_type)+ {+ case ast_string_constant:+ return xpath_string_const(_data.string);+ + case ast_func_local_name_0:+ {+ xpath_node na = c.n;+ + return xpath_string_const(local_name(na));+ }++ case ast_func_local_name_1:+ {+ xpath_allocator_capture cr(stack.result);++ xpath_node_set_raw ns = _left->eval_node_set(c, stack);+ xpath_node na = ns.first();+ + return xpath_string_const(local_name(na));+ }++ case ast_func_name_0:+ {+ xpath_node na = c.n;+ + return xpath_string_const(qualified_name(na));+ }++ case ast_func_name_1:+ {+ xpath_allocator_capture cr(stack.result);++ xpath_node_set_raw ns = _left->eval_node_set(c, stack);+ xpath_node na = ns.first();+ + return xpath_string_const(qualified_name(na));+ }++ case ast_func_namespace_uri_0:+ {+ xpath_node na = c.n;+ + return xpath_string_const(namespace_uri(na));+ }++ case ast_func_namespace_uri_1:+ {+ xpath_allocator_capture cr(stack.result);++ xpath_node_set_raw ns = _left->eval_node_set(c, stack);+ xpath_node na = ns.first();+ + return xpath_string_const(namespace_uri(na));+ }++ case ast_func_string_0:+ return string_value(c.n, stack.result);++ case ast_func_string_1:+ return _left->eval_string(c, stack);++ case ast_func_concat:+ return eval_string_concat(c, stack);++ case ast_func_substring_before:+ {+ xpath_allocator_capture cr(stack.temp);++ xpath_stack swapped_stack = {stack.temp, stack.result};++ xpath_string s = _left->eval_string(c, swapped_stack);+ xpath_string p = _right->eval_string(c, swapped_stack);++ const char_t* pos = find_substring(s.c_str(), p.c_str());+ + return pos ? xpath_string(s.c_str(), pos, stack.result) : xpath_string();+ }+ + case ast_func_substring_after:+ {+ xpath_allocator_capture cr(stack.temp);++ xpath_stack swapped_stack = {stack.temp, stack.result};++ xpath_string s = _left->eval_string(c, swapped_stack);+ xpath_string p = _right->eval_string(c, swapped_stack);+ + const char_t* pos = find_substring(s.c_str(), p.c_str());+ if (!pos) return xpath_string();++ const char_t* result = pos + p.length();++ return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result);+ }++ case ast_func_substring_2:+ {+ xpath_allocator_capture cr(stack.temp);++ xpath_stack swapped_stack = {stack.temp, stack.result};++ xpath_string s = _left->eval_string(c, swapped_stack);+ size_t s_length = s.length();++ double first = round_nearest(_right->eval_number(c, stack));+ + if (is_nan(first)) return xpath_string(); // NaN+ else if (first >= s_length + 1) return xpath_string();+ + size_t pos = first < 1 ? 1 : static_cast<size_t>(first);+ assert(1 <= pos && pos <= s_length + 1);++ const char_t* rbegin = s.c_str() + (pos - 1);+ + return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_const(rbegin);+ }+ + case ast_func_substring_3:+ {+ xpath_allocator_capture cr(stack.temp);++ xpath_stack swapped_stack = {stack.temp, stack.result};++ xpath_string s = _left->eval_string(c, swapped_stack);+ size_t s_length = s.length();++ double first = round_nearest(_right->eval_number(c, stack));+ double last = first + round_nearest(_right->_next->eval_number(c, stack));+ + if (is_nan(first) || is_nan(last)) return xpath_string();+ else if (first >= s_length + 1) return xpath_string();+ else if (first >= last) return xpath_string();+ else if (last < 1) return xpath_string();+ + size_t pos = first < 1 ? 1 : static_cast<size_t>(first);+ size_t end = last >= s_length + 1 ? s_length + 1 : static_cast<size_t>(last);++ assert(1 <= pos && pos <= end && end <= s_length + 1);+ const char_t* rbegin = s.c_str() + (pos - 1);+ const char_t* rend = s.c_str() + (end - 1);++ return (end == s_length + 1 && !s.uses_heap()) ? xpath_string_const(rbegin) : xpath_string(rbegin, rend, stack.result);+ }++ case ast_func_normalize_space_0:+ {+ xpath_string s = string_value(c.n, stack.result);++ normalize_space(s.data(stack.result));++ return s;+ }++ case ast_func_normalize_space_1:+ {+ xpath_string s = _left->eval_string(c, stack);++ normalize_space(s.data(stack.result));+ + return s;+ }++ case ast_func_translate:+ {+ xpath_allocator_capture cr(stack.temp);++ xpath_stack swapped_stack = {stack.temp, stack.result};++ xpath_string s = _left->eval_string(c, stack);+ xpath_string from = _right->eval_string(c, swapped_stack);+ xpath_string to = _right->_next->eval_string(c, swapped_stack);++ translate(s.data(stack.result), from.c_str(), to.c_str());++ return s;+ }++ case ast_variable:+ {+ assert(_rettype == _data.variable->type());++ if (_rettype == xpath_type_string)+ return xpath_string_const(_data.variable->get_string());++ // fallthrough to type conversion+ }++ default:+ {+ switch (_rettype)+ {+ case xpath_type_boolean:+ return xpath_string_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));+ + case xpath_type_number:+ return convert_number_to_string(eval_number(c, stack), stack.result);+ + case xpath_type_node_set:+ {+ xpath_allocator_capture cr(stack.temp);++ xpath_stack swapped_stack = {stack.temp, stack.result};++ xpath_node_set_raw ns = eval_node_set(c, swapped_stack);+ return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);+ }+ + default:+ assert(!"Wrong expression for return type string");+ return xpath_string();+ }+ }+ }+ }++ xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack)+ {+ switch (_type)+ {+ case ast_op_union:+ {+ xpath_allocator_capture cr(stack.temp);++ xpath_stack swapped_stack = {stack.temp, stack.result};++ xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack);+ xpath_node_set_raw rs = _right->eval_node_set(c, stack);+ + // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother+ rs.set_type(xpath_node_set::type_unsorted);++ rs.append(ls.begin(), ls.end(), stack.result);+ rs.remove_duplicates();+ + return rs;+ }++ case ast_filter:+ case ast_filter_posinv:+ {+ xpath_node_set_raw set = _left->eval_node_set(c, stack);++ // either expression is a number or it contains position() call; sort by document order+ if (_type == ast_filter) set.sort_do();++ apply_predicate(set, 0, _right, stack);+ + return set;+ }+ + case ast_func_id:+ return xpath_node_set_raw();+ + case ast_step:+ {+ switch (_axis)+ {+ case axis_ancestor:+ return step_do(c, stack, axis_to_type<axis_ancestor>());+ + case axis_ancestor_or_self:+ return step_do(c, stack, axis_to_type<axis_ancestor_or_self>());++ case axis_attribute:+ return step_do(c, stack, axis_to_type<axis_attribute>());++ case axis_child:+ return step_do(c, stack, axis_to_type<axis_child>());+ + case axis_descendant:+ return step_do(c, stack, axis_to_type<axis_descendant>());++ case axis_descendant_or_self:+ return step_do(c, stack, axis_to_type<axis_descendant_or_self>());++ case axis_following:+ return step_do(c, stack, axis_to_type<axis_following>());+ + case axis_following_sibling:+ return step_do(c, stack, axis_to_type<axis_following_sibling>());+ + case axis_namespace:+ // namespaced axis is not supported+ return xpath_node_set_raw();+ + case axis_parent:+ return step_do(c, stack, axis_to_type<axis_parent>());+ + case axis_preceding:+ return step_do(c, stack, axis_to_type<axis_preceding>());++ case axis_preceding_sibling:+ return step_do(c, stack, axis_to_type<axis_preceding_sibling>());+ + case axis_self:+ return step_do(c, stack, axis_to_type<axis_self>());++ default:+ assert(!"Unknown axis");+ return xpath_node_set_raw();+ }+ }++ case ast_step_root:+ {+ assert(!_right); // root step can't have any predicates++ xpath_node_set_raw ns;++ ns.set_type(xpath_node_set::type_sorted);++ if (c.n.node()) ns.push_back(c.n.node().root(), stack.result);+ else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result);++ return ns;+ }++ case ast_variable:+ {+ assert(_rettype == _data.variable->type());++ if (_rettype == xpath_type_node_set)+ {+ const xpath_node_set& s = _data.variable->get_node_set();++ xpath_node_set_raw ns;++ ns.set_type(s.type());+ ns.append(s.begin(), s.end(), stack.result);++ return ns;+ }++ // fallthrough to type conversion+ }++ default:+ assert(!"Wrong expression for return type node set");+ return xpath_node_set_raw();+ }+ }+ + bool is_posinv()+ {+ switch (_type)+ {+ case ast_func_position:+ return false;++ case ast_string_constant:+ case ast_number_constant:+ case ast_variable:+ return true;++ case ast_step:+ case ast_step_root:+ return true;++ case ast_predicate:+ case ast_filter:+ case ast_filter_posinv:+ return true;++ default:+ if (_left && !_left->is_posinv()) return false;+ + for (xpath_ast_node* n = _right; n; n = n->_next)+ if (!n->is_posinv()) return false;+ + return true;+ }+ }++ xpath_value_type rettype() const+ {+ return static_cast<xpath_value_type>(_rettype);+ }+ };++ struct xpath_parser+ {+ xpath_allocator* _alloc;+ xpath_lexer _lexer;++ const char_t* _query;+ xpath_variable_set* _variables;++ xpath_parse_result* _result;++ char_t _scratch[32];++ #ifdef PUGIXML_NO_EXCEPTIONS+ jmp_buf _error_handler;+ #endif++ void throw_error(const char* message)+ {+ _result->error = message;+ _result->offset = _lexer.current_pos() - _query;++ #ifdef PUGIXML_NO_EXCEPTIONS+ longjmp(_error_handler, 1);+ #else+ throw xpath_exception(*_result);+ #endif+ }++ void throw_error_oom()+ {+ #ifdef PUGIXML_NO_EXCEPTIONS+ throw_error("Out of memory");+ #else+ throw std::bad_alloc();+ #endif+ }++ void* alloc_node()+ {+ void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node));++ if (!result) throw_error_oom();++ return result;+ }++ const char_t* alloc_string(const xpath_lexer_string& value)+ {+ if (value.begin)+ {+ size_t length = static_cast<size_t>(value.end - value.begin);++ char_t* c = static_cast<char_t*>(_alloc->allocate_nothrow((length + 1) * sizeof(char_t)));+ if (!c) throw_error_oom();+ assert(c); // workaround for clang static analysis++ memcpy(c, value.begin, length * sizeof(char_t));+ c[length] = 0;++ return c;+ }+ else return 0;+ }++ xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2])+ {+ assert(argc <= 1);++ if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");++ return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]);+ }++ xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2])+ {+ switch (name.begin[0])+ {+ case 'b':+ if (name == PUGIXML_TEXT("boolean") && argc == 1)+ return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]);+ + break;+ + case 'c':+ if (name == PUGIXML_TEXT("count") && argc == 1)+ {+ if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");+ return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]);+ }+ else if (name == PUGIXML_TEXT("contains") && argc == 2)+ return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]);+ else if (name == PUGIXML_TEXT("concat") && argc >= 2)+ return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]);+ else if (name == PUGIXML_TEXT("ceiling") && argc == 1)+ return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]);+ + break;+ + case 'f':+ if (name == PUGIXML_TEXT("false") && argc == 0)+ return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean);+ else if (name == PUGIXML_TEXT("floor") && argc == 1)+ return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]);+ + break;+ + case 'i':+ if (name == PUGIXML_TEXT("id") && argc == 1)+ return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]);+ + break;+ + case 'l':+ if (name == PUGIXML_TEXT("last") && argc == 0)+ return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number);+ else if (name == PUGIXML_TEXT("lang") && argc == 1)+ return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]);+ else if (name == PUGIXML_TEXT("local-name") && argc <= 1)+ return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args);+ + break;+ + case 'n':+ if (name == PUGIXML_TEXT("name") && argc <= 1)+ return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args);+ else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1)+ return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args);+ else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1)+ return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]);+ else if (name == PUGIXML_TEXT("not") && argc == 1)+ return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]);+ else if (name == PUGIXML_TEXT("number") && argc <= 1)+ return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]);+ + break;+ + case 'p':+ if (name == PUGIXML_TEXT("position") && argc == 0)+ return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number);+ + break;+ + case 'r':+ if (name == PUGIXML_TEXT("round") && argc == 1)+ return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]);++ break;+ + case 's':+ if (name == PUGIXML_TEXT("string") && argc <= 1)+ return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);+ else if (name == PUGIXML_TEXT("string-length") && argc <= 1)+ return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]);+ else if (name == PUGIXML_TEXT("starts-with") && argc == 2)+ return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);+ else if (name == PUGIXML_TEXT("substring-before") && argc == 2)+ return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]);+ else if (name == PUGIXML_TEXT("substring-after") && argc == 2)+ return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]);+ else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3))+ return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]);+ else if (name == PUGIXML_TEXT("sum") && argc == 1)+ {+ if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");+ return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]);+ }++ break;+ + case 't':+ if (name == PUGIXML_TEXT("translate") && argc == 3)+ return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]);+ else if (name == PUGIXML_TEXT("true") && argc == 0)+ return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean);+ + break;++ default:+ break;+ }++ throw_error("Unrecognized function or wrong parameter count");++ return 0;+ }++ axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified)+ {+ specified = true;++ switch (name.begin[0])+ {+ case 'a':+ if (name == PUGIXML_TEXT("ancestor"))+ return axis_ancestor;+ else if (name == PUGIXML_TEXT("ancestor-or-self"))+ return axis_ancestor_or_self;+ else if (name == PUGIXML_TEXT("attribute"))+ return axis_attribute;+ + break;+ + case 'c':+ if (name == PUGIXML_TEXT("child"))+ return axis_child;+ + break;+ + case 'd':+ if (name == PUGIXML_TEXT("descendant"))+ return axis_descendant;+ else if (name == PUGIXML_TEXT("descendant-or-self"))+ return axis_descendant_or_self;+ + break;+ + case 'f':+ if (name == PUGIXML_TEXT("following"))+ return axis_following;+ else if (name == PUGIXML_TEXT("following-sibling"))+ return axis_following_sibling;+ + break;+ + case 'n':+ if (name == PUGIXML_TEXT("namespace"))+ return axis_namespace;+ + break;+ + case 'p':+ if (name == PUGIXML_TEXT("parent"))+ return axis_parent;+ else if (name == PUGIXML_TEXT("preceding"))+ return axis_preceding;+ else if (name == PUGIXML_TEXT("preceding-sibling"))+ return axis_preceding_sibling;+ + break;+ + case 's':+ if (name == PUGIXML_TEXT("self"))+ return axis_self;+ + break;++ default:+ break;+ }++ specified = false;+ return axis_child;+ }++ nodetest_t parse_node_test_type(const xpath_lexer_string& name)+ {+ switch (name.begin[0])+ {+ case 'c':+ if (name == PUGIXML_TEXT("comment"))+ return nodetest_type_comment;++ break;++ case 'n':+ if (name == PUGIXML_TEXT("node"))+ return nodetest_type_node;++ break;++ case 'p':+ if (name == PUGIXML_TEXT("processing-instruction"))+ return nodetest_type_pi;++ break;++ case 't':+ if (name == PUGIXML_TEXT("text"))+ return nodetest_type_text;++ break;+ + default:+ break;+ }++ return nodetest_none;+ }++ // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall+ xpath_ast_node* parse_primary_expression()+ {+ switch (_lexer.current())+ {+ case lex_var_ref:+ {+ xpath_lexer_string name = _lexer.contents();++ if (!_variables)+ throw_error("Unknown variable: variable set is not provided");++ xpath_variable* var = get_variable_scratch(_scratch, _variables, name.begin, name.end);++ if (!var)+ throw_error("Unknown variable: variable set does not contain the given name");++ _lexer.next();++ return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var);+ }++ case lex_open_brace:+ {+ _lexer.next();++ xpath_ast_node* n = parse_expression();++ if (_lexer.current() != lex_close_brace)+ throw_error("Unmatched braces");++ _lexer.next();++ return n;+ }++ case lex_quoted_string:+ {+ const char_t* value = alloc_string(_lexer.contents());++ xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value);+ _lexer.next();++ return n;+ }++ case lex_number:+ {+ double value = 0;++ if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value))+ throw_error_oom();++ xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value);+ _lexer.next();++ return n;+ }++ case lex_string:+ {+ xpath_ast_node* args[2] = {0};+ size_t argc = 0;+ + xpath_lexer_string function = _lexer.contents();+ _lexer.next();+ + xpath_ast_node* last_arg = 0;+ + if (_lexer.current() != lex_open_brace)+ throw_error("Unrecognized function call");+ _lexer.next();++ if (_lexer.current() != lex_close_brace)+ args[argc++] = parse_expression();++ while (_lexer.current() != lex_close_brace)+ {+ if (_lexer.current() != lex_comma)+ throw_error("No comma between function arguments");+ _lexer.next();+ + xpath_ast_node* n = parse_expression();+ + if (argc < 2) args[argc] = n;+ else last_arg->set_next(n);++ argc++;+ last_arg = n;+ }+ + _lexer.next();++ return parse_function(function, argc, args);+ }++ default:+ throw_error("Unrecognizable primary expression");++ return 0;+ }+ }+ + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate+ // Predicate ::= '[' PredicateExpr ']'+ // PredicateExpr ::= Expr+ xpath_ast_node* parse_filter_expression()+ {+ xpath_ast_node* n = parse_primary_expression();++ while (_lexer.current() == lex_open_square_brace)+ {+ _lexer.next();++ xpath_ast_node* expr = parse_expression();++ if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set");++ bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv();++ n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr);++ if (_lexer.current() != lex_close_square_brace)+ throw_error("Unmatched square brace");+ + _lexer.next();+ }+ + return n;+ }+ + // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep+ // AxisSpecifier ::= AxisName '::' | '@'?+ // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')'+ // NameTest ::= '*' | NCName ':' '*' | QName+ // AbbreviatedStep ::= '.' | '..'+ xpath_ast_node* parse_step(xpath_ast_node* set)+ {+ if (set && set->rettype() != xpath_type_node_set)+ throw_error("Step has to be applied to node set");++ bool axis_specified = false;+ axis_t axis = axis_child; // implied child axis++ if (_lexer.current() == lex_axis_attribute)+ {+ axis = axis_attribute;+ axis_specified = true;+ + _lexer.next();+ }+ else if (_lexer.current() == lex_dot)+ {+ _lexer.next();+ + return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0);+ }+ else if (_lexer.current() == lex_double_dot)+ {+ _lexer.next();+ + return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0);+ }+ + nodetest_t nt_type = nodetest_none;+ xpath_lexer_string nt_name;+ + if (_lexer.current() == lex_string)+ {+ // node name test+ nt_name = _lexer.contents();+ _lexer.next();++ // was it an axis name?+ if (_lexer.current() == lex_double_colon)+ {+ // parse axis name+ if (axis_specified) throw_error("Two axis specifiers in one step");++ axis = parse_axis_name(nt_name, axis_specified);++ if (!axis_specified) throw_error("Unknown axis");++ // read actual node test+ _lexer.next();++ if (_lexer.current() == lex_multiply)+ {+ nt_type = nodetest_all;+ nt_name = xpath_lexer_string();+ _lexer.next();+ }+ else if (_lexer.current() == lex_string)+ {+ nt_name = _lexer.contents();+ _lexer.next();+ }+ else throw_error("Unrecognized node test");+ }+ + if (nt_type == nodetest_none)+ {+ // node type test or processing-instruction+ if (_lexer.current() == lex_open_brace)+ {+ _lexer.next();+ + if (_lexer.current() == lex_close_brace)+ {+ _lexer.next();++ nt_type = parse_node_test_type(nt_name);++ if (nt_type == nodetest_none) throw_error("Unrecognized node type");+ + nt_name = xpath_lexer_string();+ }+ else if (nt_name == PUGIXML_TEXT("processing-instruction"))+ {+ if (_lexer.current() != lex_quoted_string)+ throw_error("Only literals are allowed as arguments to processing-instruction()");+ + nt_type = nodetest_pi;+ nt_name = _lexer.contents();+ _lexer.next();+ + if (_lexer.current() != lex_close_brace)+ throw_error("Unmatched brace near processing-instruction()");+ _lexer.next();+ }+ else+ throw_error("Unmatched brace near node type test");++ }+ // QName or NCName:*+ else+ {+ if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:*+ {+ nt_name.end--; // erase *+ + nt_type = nodetest_all_in_namespace;+ }+ else nt_type = nodetest_name;+ }+ }+ }+ else if (_lexer.current() == lex_multiply)+ {+ nt_type = nodetest_all;+ _lexer.next();+ }+ else throw_error("Unrecognized node test");+ + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name));+ + xpath_ast_node* last = 0;+ + while (_lexer.current() == lex_open_square_brace)+ {+ _lexer.next();+ + xpath_ast_node* expr = parse_expression();++ xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr);+ + if (_lexer.current() != lex_close_square_brace)+ throw_error("Unmatched square brace");+ _lexer.next();+ + if (last) last->set_next(pred);+ else n->set_right(pred);+ + last = pred;+ }+ + return n;+ }+ + // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step+ xpath_ast_node* parse_relative_location_path(xpath_ast_node* set)+ {+ xpath_ast_node* n = parse_step(set);+ + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)+ {+ lexeme_t l = _lexer.current();+ _lexer.next();++ if (l == lex_double_slash)+ n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);+ + n = parse_step(n);+ }+ + return n;+ }+ + // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath+ // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath+ xpath_ast_node* parse_location_path()+ {+ if (_lexer.current() == lex_slash)+ {+ _lexer.next();+ + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set);++ // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path+ lexeme_t l = _lexer.current();++ if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply)+ return parse_relative_location_path(n);+ else+ return n;+ }+ else if (_lexer.current() == lex_double_slash)+ {+ _lexer.next();+ + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set);+ n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);+ + return parse_relative_location_path(n);+ }++ // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1+ return parse_relative_location_path(0);+ }+ + // PathExpr ::= LocationPath+ // | FilterExpr+ // | FilterExpr '/' RelativeLocationPath+ // | FilterExpr '//' RelativeLocationPath+ // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr+ // UnaryExpr ::= UnionExpr | '-' UnaryExpr+ xpath_ast_node* parse_path_or_unary_expression()+ {+ // Clarification.+ // PathExpr begins with either LocationPath or FilterExpr.+ // FilterExpr begins with PrimaryExpr+ // PrimaryExpr begins with '$' in case of it being a variable reference,+ // '(' in case of it being an expression, string literal, number constant or+ // function call.++ if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || + _lexer.current() == lex_quoted_string || _lexer.current() == lex_number ||+ _lexer.current() == lex_string)+ {+ if (_lexer.current() == lex_string)+ {+ // This is either a function call, or not - if not, we shall proceed with location path+ const char_t* state = _lexer.state();+ + while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state;+ + if (*state != '(') return parse_location_path();++ // This looks like a function call; however this still can be a node-test. Check it.+ if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path();+ }+ + xpath_ast_node* n = parse_filter_expression();++ if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)+ {+ lexeme_t l = _lexer.current();+ _lexer.next();+ + if (l == lex_double_slash)+ {+ if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set");++ n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);+ }+ + // select from location path+ return parse_relative_location_path(n);+ }++ return n;+ }+ else if (_lexer.current() == lex_minus)+ {+ _lexer.next();++ // precedence 7+ - only parses union expressions+ xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7);++ return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr);+ }+ else+ return parse_location_path();+ }++ struct binary_op_t+ {+ ast_type_t asttype;+ xpath_value_type rettype;+ int precedence;++ binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0)+ {+ }++ binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_)+ {+ }++ static binary_op_t parse(xpath_lexer& lexer)+ {+ switch (lexer.current())+ {+ case lex_string:+ if (lexer.contents() == PUGIXML_TEXT("or"))+ return binary_op_t(ast_op_or, xpath_type_boolean, 1);+ else if (lexer.contents() == PUGIXML_TEXT("and"))+ return binary_op_t(ast_op_and, xpath_type_boolean, 2);+ else if (lexer.contents() == PUGIXML_TEXT("div"))+ return binary_op_t(ast_op_divide, xpath_type_number, 6);+ else if (lexer.contents() == PUGIXML_TEXT("mod"))+ return binary_op_t(ast_op_mod, xpath_type_number, 6);+ else+ return binary_op_t();++ case lex_equal:+ return binary_op_t(ast_op_equal, xpath_type_boolean, 3);++ case lex_not_equal:+ return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3);++ case lex_less:+ return binary_op_t(ast_op_less, xpath_type_boolean, 4);++ case lex_greater:+ return binary_op_t(ast_op_greater, xpath_type_boolean, 4);++ case lex_less_or_equal:+ return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4);++ case lex_greater_or_equal:+ return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4);++ case lex_plus:+ return binary_op_t(ast_op_add, xpath_type_number, 5);++ case lex_minus:+ return binary_op_t(ast_op_subtract, xpath_type_number, 5);++ case lex_multiply:+ return binary_op_t(ast_op_multiply, xpath_type_number, 6);++ case lex_union:+ return binary_op_t(ast_op_union, xpath_type_node_set, 7);++ default:+ return binary_op_t();+ }+ }+ };++ xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit)+ {+ binary_op_t op = binary_op_t::parse(_lexer);++ while (op.asttype != ast_unknown && op.precedence >= limit)+ {+ _lexer.next();++ xpath_ast_node* rhs = parse_path_or_unary_expression();++ binary_op_t nextop = binary_op_t::parse(_lexer);++ while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence)+ {+ rhs = parse_expression_rec(rhs, nextop.precedence);++ nextop = binary_op_t::parse(_lexer);+ }++ if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set))+ throw_error("Union operator has to be applied to node sets");++ lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs);++ op = binary_op_t::parse(_lexer);+ }++ return lhs;+ }++ // Expr ::= OrExpr+ // OrExpr ::= AndExpr | OrExpr 'or' AndExpr+ // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr+ // EqualityExpr ::= RelationalExpr+ // | EqualityExpr '=' RelationalExpr+ // | EqualityExpr '!=' RelationalExpr+ // RelationalExpr ::= AdditiveExpr+ // | RelationalExpr '<' AdditiveExpr+ // | RelationalExpr '>' AdditiveExpr+ // | RelationalExpr '<=' AdditiveExpr+ // | RelationalExpr '>=' AdditiveExpr+ // AdditiveExpr ::= MultiplicativeExpr+ // | AdditiveExpr '+' MultiplicativeExpr+ // | AdditiveExpr '-' MultiplicativeExpr+ // MultiplicativeExpr ::= UnaryExpr+ // | MultiplicativeExpr '*' UnaryExpr+ // | MultiplicativeExpr 'div' UnaryExpr+ // | MultiplicativeExpr 'mod' UnaryExpr+ xpath_ast_node* parse_expression()+ {+ return parse_expression_rec(parse_path_or_unary_expression(), 0);+ }++ xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result)+ {+ }++ xpath_ast_node* parse()+ {+ xpath_ast_node* result = parse_expression();+ + if (_lexer.current() != lex_eof)+ {+ // there are still unparsed tokens left, error+ throw_error("Incorrect query");+ }+ + return result;+ }++ static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result)+ {+ xpath_parser parser(query, variables, alloc, result);++ #ifdef PUGIXML_NO_EXCEPTIONS+ int error = setjmp(parser._error_handler);++ return (error == 0) ? parser.parse() : 0;+ #else+ return parser.parse();+ #endif+ }+ };++ struct xpath_query_impl+ {+ static xpath_query_impl* create()+ {+ void* memory = xml_memory::allocate(sizeof(xpath_query_impl));++ return new (memory) xpath_query_impl();+ }++ static void destroy(void* ptr)+ {+ if (!ptr) return;+ + // free all allocated pages+ static_cast<xpath_query_impl*>(ptr)->alloc.release();++ // free allocator memory (with the first page)+ xml_memory::deallocate(ptr);+ }++ xpath_query_impl(): root(0), alloc(&block)+ {+ block.next = 0;+ }++ xpath_ast_node* root;+ xpath_allocator alloc;+ xpath_memory_block block;+ };++ PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd)+ {+ if (!impl) return xpath_string();++ #ifdef PUGIXML_NO_EXCEPTIONS+ if (setjmp(sd.error_handler)) return xpath_string();+ #endif++ xpath_context c(n, 1, 1);++ return impl->root->eval_string(c, sd.stack);+ }+PUGI__NS_END++namespace pugi+{+#ifndef PUGIXML_NO_EXCEPTIONS+ PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_)+ {+ assert(_result.error);+ }+ + PUGI__FN const char* xpath_exception::what() const throw()+ {+ return _result.error;+ }++ PUGI__FN const xpath_parse_result& xpath_exception::result() const+ {+ return _result;+ }+#endif+ + PUGI__FN xpath_node::xpath_node()+ {+ }+ + PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_)+ {+ }+ + PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_)+ {+ }++ PUGI__FN xml_node xpath_node::node() const+ {+ return _attribute ? xml_node() : _node;+ }+ + PUGI__FN xml_attribute xpath_node::attribute() const+ {+ return _attribute;+ }+ + PUGI__FN xml_node xpath_node::parent() const+ {+ return _attribute ? _node : _node.parent();+ }++ PUGI__FN static void unspecified_bool_xpath_node(xpath_node***)+ {+ }++ PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const+ {+ return (_node || _attribute) ? unspecified_bool_xpath_node : 0;+ }+ + PUGI__FN bool xpath_node::operator!() const+ {+ return !(_node || _attribute);+ }++ PUGI__FN bool xpath_node::operator==(const xpath_node& n) const+ {+ return _node == n._node && _attribute == n._attribute;+ }+ + PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const+ {+ return _node != n._node || _attribute != n._attribute;+ }++#ifdef __BORLANDC__+ PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs)+ {+ return (bool)lhs && rhs;+ }++ PUGI__FN bool operator||(const xpath_node& lhs, bool rhs)+ {+ return (bool)lhs || rhs;+ }+#endif++ PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_)+ {+ assert(begin_ <= end_);++ size_t size_ = static_cast<size_t>(end_ - begin_);++ if (size_ <= 1)+ {+ // deallocate old buffer+ if (_begin != &_storage) impl::xml_memory::deallocate(_begin);++ // use internal buffer+ if (begin_ != end_) _storage = *begin_;++ _begin = &_storage;+ _end = &_storage + size_;+ }+ else+ {+ // make heap copy+ xpath_node* storage = static_cast<xpath_node*>(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));++ if (!storage)+ {+ #ifdef PUGIXML_NO_EXCEPTIONS+ return;+ #else+ throw std::bad_alloc();+ #endif+ }++ memcpy(storage, begin_, size_ * sizeof(xpath_node));+ + // deallocate old buffer+ if (_begin != &_storage) impl::xml_memory::deallocate(_begin);++ // finalize+ _begin = storage;+ _end = storage + size_;+ }+ }++ PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage)+ {+ }++ PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage)+ {+ _assign(begin_, end_);+ }++ PUGI__FN xpath_node_set::~xpath_node_set()+ {+ if (_begin != &_storage) impl::xml_memory::deallocate(_begin);+ }+ + PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage)+ {+ _assign(ns._begin, ns._end);+ }+ + PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns)+ {+ if (this == &ns) return *this;+ + _type = ns._type;+ _assign(ns._begin, ns._end);++ return *this;+ }++ PUGI__FN xpath_node_set::type_t xpath_node_set::type() const+ {+ return _type;+ }+ + PUGI__FN size_t xpath_node_set::size() const+ {+ return _end - _begin;+ }+ + PUGI__FN bool xpath_node_set::empty() const+ {+ return _begin == _end;+ }+ + PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const+ {+ assert(index < size());+ return _begin[index];+ }++ PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const+ {+ return _begin;+ }+ + PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const+ {+ return _end;+ }+ + PUGI__FN void xpath_node_set::sort(bool reverse)+ {+ _type = impl::xpath_sort(_begin, _end, _type, reverse);+ }++ PUGI__FN xpath_node xpath_node_set::first() const+ {+ return impl::xpath_first(_begin, _end, _type);+ }++ PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0)+ {+ }++ PUGI__FN xpath_parse_result::operator bool() const+ {+ return error == 0;+ }++ PUGI__FN const char* xpath_parse_result::description() const+ {+ return error ? error : "No error";+ }++ PUGI__FN xpath_variable::xpath_variable(): _type(xpath_type_none), _next(0)+ {+ }++ PUGI__FN const char_t* xpath_variable::name() const+ {+ switch (_type)+ {+ case xpath_type_node_set:+ return static_cast<const impl::xpath_variable_node_set*>(this)->name;++ case xpath_type_number:+ return static_cast<const impl::xpath_variable_number*>(this)->name;++ case xpath_type_string:+ return static_cast<const impl::xpath_variable_string*>(this)->name;++ case xpath_type_boolean:+ return static_cast<const impl::xpath_variable_boolean*>(this)->name;++ default:+ assert(!"Invalid variable type");+ return 0;+ }+ }++ PUGI__FN xpath_value_type xpath_variable::type() const+ {+ return _type;+ }++ PUGI__FN bool xpath_variable::get_boolean() const+ {+ return (_type == xpath_type_boolean) ? static_cast<const impl::xpath_variable_boolean*>(this)->value : false;+ }++ PUGI__FN double xpath_variable::get_number() const+ {+ return (_type == xpath_type_number) ? static_cast<const impl::xpath_variable_number*>(this)->value : impl::gen_nan();+ }++ PUGI__FN const char_t* xpath_variable::get_string() const+ {+ const char_t* value = (_type == xpath_type_string) ? static_cast<const impl::xpath_variable_string*>(this)->value : 0;+ return value ? value : PUGIXML_TEXT("");+ }++ PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const+ {+ return (_type == xpath_type_node_set) ? static_cast<const impl::xpath_variable_node_set*>(this)->value : impl::dummy_node_set;+ }++ PUGI__FN bool xpath_variable::set(bool value)+ {+ if (_type != xpath_type_boolean) return false;++ static_cast<impl::xpath_variable_boolean*>(this)->value = value;+ return true;+ }++ PUGI__FN bool xpath_variable::set(double value)+ {+ if (_type != xpath_type_number) return false;++ static_cast<impl::xpath_variable_number*>(this)->value = value;+ return true;+ }++ PUGI__FN bool xpath_variable::set(const char_t* value)+ {+ if (_type != xpath_type_string) return false;++ impl::xpath_variable_string* var = static_cast<impl::xpath_variable_string*>(this);++ // duplicate string+ size_t size = (impl::strlength(value) + 1) * sizeof(char_t);++ char_t* copy = static_cast<char_t*>(impl::xml_memory::allocate(size));+ if (!copy) return false;++ memcpy(copy, value, size);++ // replace old string+ if (var->value) impl::xml_memory::deallocate(var->value);+ var->value = copy;++ return true;+ }++ PUGI__FN bool xpath_variable::set(const xpath_node_set& value)+ {+ if (_type != xpath_type_node_set) return false;++ static_cast<impl::xpath_variable_node_set*>(this)->value = value;+ return true;+ }++ PUGI__FN xpath_variable_set::xpath_variable_set()+ {+ for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0;+ }++ PUGI__FN xpath_variable_set::~xpath_variable_set()+ {+ for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)+ {+ xpath_variable* var = _data[i];++ while (var)+ {+ xpath_variable* next = var->_next;++ impl::delete_xpath_variable(var->_type, var);++ var = next;+ }+ }+ }++ PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const+ {+ const size_t hash_size = sizeof(_data) / sizeof(_data[0]);+ size_t hash = impl::hash_string(name) % hash_size;++ // look for existing variable+ for (xpath_variable* var = _data[hash]; var; var = var->_next)+ if (impl::strequal(var->name(), name))+ return var;++ return 0;+ }++ PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)+ {+ const size_t hash_size = sizeof(_data) / sizeof(_data[0]);+ size_t hash = impl::hash_string(name) % hash_size;++ // look for existing variable+ for (xpath_variable* var = _data[hash]; var; var = var->_next)+ if (impl::strequal(var->name(), name))+ return var->type() == type ? var : 0;++ // add new variable+ xpath_variable* result = impl::new_xpath_variable(type, name);++ if (result)+ {+ result->_type = type;+ result->_next = _data[hash];++ _data[hash] = result;+ }++ return result;+ }++ PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value)+ {+ xpath_variable* var = add(name, xpath_type_boolean);+ return var ? var->set(value) : false;+ }++ PUGI__FN bool xpath_variable_set::set(const char_t* name, double value)+ {+ xpath_variable* var = add(name, xpath_type_number);+ return var ? var->set(value) : false;+ }++ PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value)+ {+ xpath_variable* var = add(name, xpath_type_string);+ return var ? var->set(value) : false;+ }++ PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value)+ {+ xpath_variable* var = add(name, xpath_type_node_set);+ return var ? var->set(value) : false;+ }++ PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name)+ {+ return find(name);+ }++ PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const+ {+ return find(name);+ }++ PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0)+ {+ impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create();++ if (!qimpl)+ {+ #ifdef PUGIXML_NO_EXCEPTIONS+ _result.error = "Out of memory";+ #else+ throw std::bad_alloc();+ #endif+ }+ else+ {+ impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy);++ qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);++ if (qimpl->root)+ {+ _impl = static_cast<impl::xpath_query_impl*>(impl_holder.release());+ _result.error = 0;+ }+ }+ }++ PUGI__FN xpath_query::~xpath_query()+ {+ impl::xpath_query_impl::destroy(_impl);+ }++ PUGI__FN xpath_value_type xpath_query::return_type() const+ {+ if (!_impl) return xpath_type_none;++ return static_cast<impl::xpath_query_impl*>(_impl)->root->rettype();+ }++ PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const+ {+ if (!_impl) return false;+ + impl::xpath_context c(n, 1, 1);+ impl::xpath_stack_data sd;++ #ifdef PUGIXML_NO_EXCEPTIONS+ if (setjmp(sd.error_handler)) return false;+ #endif+ + return static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack);+ }+ + PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const+ {+ if (!_impl) return impl::gen_nan();+ + impl::xpath_context c(n, 1, 1);+ impl::xpath_stack_data sd;++ #ifdef PUGIXML_NO_EXCEPTIONS+ if (setjmp(sd.error_handler)) return impl::gen_nan();+ #endif++ return static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack);+ }++#ifndef PUGIXML_NO_STL+ PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const+ {+ impl::xpath_stack_data sd;++ return impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd).c_str();+ }+#endif++ PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const+ {+ impl::xpath_stack_data sd;++ impl::xpath_string r = impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd);++ size_t full_size = r.length() + 1;+ + if (capacity > 0)+ {+ size_t size = (full_size < capacity) ? full_size : capacity;+ assert(size > 0);++ memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t));+ buffer[size - 1] = 0;+ }+ + return full_size;+ }++ PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const+ {+ if (!_impl) return xpath_node_set();++ impl::xpath_ast_node* root = static_cast<impl::xpath_query_impl*>(_impl)->root;++ if (root->rettype() != xpath_type_node_set)+ {+ #ifdef PUGIXML_NO_EXCEPTIONS+ return xpath_node_set();+ #else+ xpath_parse_result res;+ res.error = "Expression does not evaluate to node set";++ throw xpath_exception(res);+ #endif+ }+ + impl::xpath_context c(n, 1, 1);+ impl::xpath_stack_data sd;++ #ifdef PUGIXML_NO_EXCEPTIONS+ if (setjmp(sd.error_handler)) return xpath_node_set();+ #endif++ impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack);++ return xpath_node_set(r.begin(), r.end(), r.type());+ }++ PUGI__FN const xpath_parse_result& xpath_query::result() const+ {+ return _result;+ }++ PUGI__FN static void unspecified_bool_xpath_query(xpath_query***)+ {+ }++ PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const+ {+ return _impl ? unspecified_bool_xpath_query : 0;+ }++ PUGI__FN bool xpath_query::operator!() const+ {+ return !_impl;+ }++ PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const+ {+ xpath_query q(query, variables);+ return select_single_node(q);+ }++ PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const+ {+ xpath_node_set s = query.evaluate_node_set(*this);+ return s.empty() ? xpath_node() : s.first();+ }++ PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const+ {+ xpath_query q(query, variables);+ return select_nodes(q);+ }++ PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const+ {+ return query.evaluate_node_set(*this);+ }+}++#endif++#ifdef __BORLANDC__+# pragma option pop+#endif++// Intel C++ does not properly keep warning state for function templates,+// so popping warning state at the end of translation unit leads to warnings in the middle.+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)+# pragma warning(pop)+#endif++// Undefine all local macros (makes sure we're not leaking macros in header-only mode)+#undef PUGI__NO_INLINE+#undef PUGI__STATIC_ASSERT+#undef PUGI__DMC_VOLATILE+#undef PUGI__MSVC_CRT_VERSION+#undef PUGI__NS_BEGIN+#undef PUGI__NS_END+#undef PUGI__FN+#undef PUGI__FN_NO_INLINE+#undef PUGI__IS_CHARTYPE_IMPL+#undef PUGI__IS_CHARTYPE+#undef PUGI__IS_CHARTYPEX+#undef PUGI__SKIPWS+#undef PUGI__OPTSET+#undef PUGI__PUSHNODE+#undef PUGI__POPNODE+#undef PUGI__SCANFOR+#undef PUGI__SCANWHILE+#undef PUGI__ENDSEG+#undef PUGI__THROW_ERROR+#undef PUGI__CHECK_ERROR++#endif++/**+ * Copyright (c) 2006-2014 Arseny Kapoulkine+ *+ * Permission is hereby granted, free of charge, to any person+ * obtaining a copy of this software and associated documentation+ * files (the "Software"), to deal in the Software without+ * restriction, including without limitation the rights to use,+ * copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the+ * Software is furnished to do so, subject to the following+ * conditions:+ *+ * The above copyright notice and this permission notice shall be+ * included in all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+ * OTHER DEALINGS IN THE SOFTWARE.+ */
+ pugixml-1.4/pugixml.hpp view
@@ -0,0 +1,1332 @@+/**+ * pugixml parser - version 1.4+ * --------------------------------------------------------+ * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)+ * Report bugs and download new versions at http://pugixml.org/+ *+ * This library is distributed under the MIT License. See notice at the end+ * of this file.+ *+ * This work is based on the pugxml parser, which is:+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)+ */++#ifndef PUGIXML_VERSION+// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons+# define PUGIXML_VERSION 140+#endif++// Include user configuration file (this can define various configuration macros)+#include "pugiconfig.hpp"++#ifndef HEADER_PUGIXML_HPP+#define HEADER_PUGIXML_HPP++// Include stddef.h for size_t and ptrdiff_t+#include <stddef.h>++// Include exception header for XPath+#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS)+# include <exception>+#endif++// Include STL headers+#ifndef PUGIXML_NO_STL+# include <iterator>+# include <iosfwd>+# include <string>+#endif++// Macro for deprecated features+#ifndef PUGIXML_DEPRECATED+# if defined(__GNUC__)+# define PUGIXML_DEPRECATED __attribute__((deprecated))+# elif defined(_MSC_VER) && _MSC_VER >= 1300+# define PUGIXML_DEPRECATED __declspec(deprecated)+# else+# define PUGIXML_DEPRECATED+# endif+#endif++// If no API is defined, assume default+#ifndef PUGIXML_API+# define PUGIXML_API+#endif++// If no API for classes is defined, assume default+#ifndef PUGIXML_CLASS+# define PUGIXML_CLASS PUGIXML_API+#endif++// If no API for functions is defined, assume default+#ifndef PUGIXML_FUNCTION+# define PUGIXML_FUNCTION PUGIXML_API+#endif++// If the platform is known to have long long support, enable long long functions+#ifndef PUGIXML_HAS_LONG_LONG+# if defined(__cplusplus) && __cplusplus >= 201103+# define PUGIXML_HAS_LONG_LONG+# elif defined(_MSC_VER) && _MSC_VER >= 1400+# define PUGIXML_HAS_LONG_LONG+# endif+#endif++// Character interface macros+#ifdef PUGIXML_WCHAR_MODE+# define PUGIXML_TEXT(t) L ## t+# define PUGIXML_CHAR wchar_t+#else+# define PUGIXML_TEXT(t) t+# define PUGIXML_CHAR char+#endif++namespace pugi+{+ // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE+ typedef PUGIXML_CHAR char_t;++#ifndef PUGIXML_NO_STL+ // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE+ typedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > string_t;+#endif+}++// The PugiXML namespace+namespace pugi+{+ // Tree node types+ enum xml_node_type+ {+ node_null, // Empty (null) node handle+ node_document, // A document tree's absolute root+ node_element, // Element tag, i.e. '<node/>'+ node_pcdata, // Plain character data, i.e. 'text'+ node_cdata, // Character data, i.e. '<![CDATA[text]]>'+ node_comment, // Comment tag, i.e. '<!-- text -->'+ node_pi, // Processing instruction, i.e. '<?name?>'+ node_declaration, // Document declaration, i.e. '<?xml version="1.0"?>'+ node_doctype // Document type declaration, i.e. '<!DOCTYPE doc>'+ };++ // Parsing options++ // Minimal parsing mode (equivalent to turning all other flags off).+ // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed.+ const unsigned int parse_minimal = 0x0000;++ // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default.+ const unsigned int parse_pi = 0x0001;++ // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default.+ const unsigned int parse_comments = 0x0002;++ // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default.+ const unsigned int parse_cdata = 0x0004;++ // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree.+ // This flag is off by default; turning it on usually results in slower parsing and more memory consumption.+ const unsigned int parse_ws_pcdata = 0x0008;++ // This flag determines if character and entity references are expanded during parsing. This flag is on by default.+ const unsigned int parse_escapes = 0x0010;++ // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default.+ const unsigned int parse_eol = 0x0020;+ + // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default.+ const unsigned int parse_wconv_attribute = 0x0040;++ // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default.+ const unsigned int parse_wnorm_attribute = 0x0080;+ + // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default.+ const unsigned int parse_declaration = 0x0100;++ // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default.+ const unsigned int parse_doctype = 0x0200;++ // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only+ // of whitespace is added to the DOM tree.+ // This flag is off by default; turning it on may result in slower parsing and more memory consumption.+ const unsigned int parse_ws_pcdata_single = 0x0400;++ // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.+ const unsigned int parse_trim_pcdata = 0x0800;++ // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document+ // is a valid document. This flag is off by default.+ const unsigned int parse_fragment = 0x1000;++ // The default parsing mode.+ // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,+ // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.+ const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol;++ // The full parsing mode.+ // Nodes of all types are added to the DOM tree, character/reference entities are expanded,+ // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.+ const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype;++ // These flags determine the encoding of input data for XML document+ enum xml_encoding+ {+ encoding_auto, // Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found+ encoding_utf8, // UTF8 encoding+ encoding_utf16_le, // Little-endian UTF16+ encoding_utf16_be, // Big-endian UTF16+ encoding_utf16, // UTF16 with native endianness+ encoding_utf32_le, // Little-endian UTF32+ encoding_utf32_be, // Big-endian UTF32+ encoding_utf32, // UTF32 with native endianness+ encoding_wchar, // The same encoding wchar_t has (either UTF16 or UTF32)+ encoding_latin1+ };++ // Formatting flags+ + // Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.+ const unsigned int format_indent = 0x01;+ + // Write encoding-specific BOM to the output stream. This flag is off by default.+ const unsigned int format_write_bom = 0x02;++ // Use raw output mode (no indentation and no line breaks are written). This flag is off by default.+ const unsigned int format_raw = 0x04;+ + // Omit default XML declaration even if there is no declaration in the document. This flag is off by default.+ const unsigned int format_no_declaration = 0x08;++ // Don't escape attribute values and PCDATA contents. This flag is off by default.+ const unsigned int format_no_escapes = 0x10;++ // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.+ const unsigned int format_save_file_text = 0x20;++ // The default set of formatting flags.+ // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.+ const unsigned int format_default = format_indent;+ + // Forward declarations+ struct xml_attribute_struct;+ struct xml_node_struct;++ class xml_node_iterator;+ class xml_attribute_iterator;+ class xml_named_node_iterator;++ class xml_tree_walker;++ struct xml_parse_result;++ class xml_node;++ class xml_text;+ + #ifndef PUGIXML_NO_XPATH+ class xpath_node;+ class xpath_node_set;+ class xpath_query;+ class xpath_variable_set;+ #endif++ // Range-based for loop support+ template <typename It> class xml_object_range+ {+ public:+ typedef It const_iterator;+ typedef It iterator;++ xml_object_range(It b, It e): _begin(b), _end(e)+ {+ }++ It begin() const { return _begin; }+ It end() const { return _end; }++ private:+ It _begin, _end;+ };++ // Writer interface for node printing (see xml_node::print)+ class PUGIXML_CLASS xml_writer+ {+ public:+ virtual ~xml_writer() {}++ // Write memory chunk into stream/file/whatever+ virtual void write(const void* data, size_t size) = 0;+ };++ // xml_writer implementation for FILE*+ class PUGIXML_CLASS xml_writer_file: public xml_writer+ {+ public:+ // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio+ xml_writer_file(void* file);++ virtual void write(const void* data, size_t size);++ private:+ void* file;+ };++ #ifndef PUGIXML_NO_STL+ // xml_writer implementation for streams+ class PUGIXML_CLASS xml_writer_stream: public xml_writer+ {+ public:+ // Construct writer from an output stream object+ xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream);+ xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream);++ virtual void write(const void* data, size_t size);++ private:+ std::basic_ostream<char, std::char_traits<char> >* narrow_stream;+ std::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream;+ };+ #endif++ // A light-weight handle for manipulating attributes in DOM tree+ class PUGIXML_CLASS xml_attribute+ {+ friend class xml_attribute_iterator;+ friend class xml_node;++ private:+ xml_attribute_struct* _attr;+ + typedef void (*unspecified_bool_type)(xml_attribute***);++ public:+ // Default constructor. Constructs an empty attribute.+ xml_attribute();+ + // Constructs attribute from internal pointer+ explicit xml_attribute(xml_attribute_struct* attr);++ // Safe bool conversion operator+ operator unspecified_bool_type() const;++ // Borland C++ workaround+ bool operator!() const;++ // Comparison operators (compares wrapped attribute pointers)+ bool operator==(const xml_attribute& r) const;+ bool operator!=(const xml_attribute& r) const;+ bool operator<(const xml_attribute& r) const;+ bool operator>(const xml_attribute& r) const;+ bool operator<=(const xml_attribute& r) const;+ bool operator>=(const xml_attribute& r) const;++ // Check if attribute is empty+ bool empty() const;++ // Get attribute name/value, or "" if attribute is empty+ const char_t* name() const;+ const char_t* value() const;++ // Get attribute value, or the default value if attribute is empty+ const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;++ // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty+ int as_int(int def = 0) const;+ unsigned int as_uint(unsigned int def = 0) const;+ double as_double(double def = 0) const;+ float as_float(float def = 0) const;++ #ifdef PUGIXML_HAS_LONG_LONG+ long long as_llong(long long def = 0) const;+ unsigned long long as_ullong(unsigned long long def = 0) const;+ #endif++ // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty+ bool as_bool(bool def = false) const;++ // Set attribute name/value (returns false if attribute is empty or there is not enough memory)+ bool set_name(const char_t* rhs);+ bool set_value(const char_t* rhs);++ // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")+ bool set_value(int rhs);+ bool set_value(unsigned int rhs);+ bool set_value(double rhs);+ bool set_value(bool rhs);++ #ifdef PUGIXML_HAS_LONG_LONG+ bool set_value(long long rhs);+ bool set_value(unsigned long long rhs);+ #endif++ // Set attribute value (equivalent to set_value without error checking)+ xml_attribute& operator=(const char_t* rhs);+ xml_attribute& operator=(int rhs);+ xml_attribute& operator=(unsigned int rhs);+ xml_attribute& operator=(double rhs);+ xml_attribute& operator=(bool rhs);++ #ifdef PUGIXML_HAS_LONG_LONG+ xml_attribute& operator=(long long rhs);+ xml_attribute& operator=(unsigned long long rhs);+ #endif++ // Get next/previous attribute in the attribute list of the parent node+ xml_attribute next_attribute() const;+ xml_attribute previous_attribute() const;++ // Get hash value (unique for handles to the same object)+ size_t hash_value() const;++ // Get internal pointer+ xml_attribute_struct* internal_object() const;+ };++#ifdef __BORLANDC__+ // Borland C++ workaround+ bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs);+ bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs);+#endif++ // A light-weight handle for manipulating nodes in DOM tree+ class PUGIXML_CLASS xml_node+ {+ friend class xml_attribute_iterator;+ friend class xml_node_iterator;+ friend class xml_named_node_iterator;++ protected:+ xml_node_struct* _root;++ typedef void (*unspecified_bool_type)(xml_node***);++ public:+ // Default constructor. Constructs an empty node.+ xml_node();++ // Constructs node from internal pointer+ explicit xml_node(xml_node_struct* p);++ // Safe bool conversion operator+ operator unspecified_bool_type() const;++ // Borland C++ workaround+ bool operator!() const;+ + // Comparison operators (compares wrapped node pointers)+ bool operator==(const xml_node& r) const;+ bool operator!=(const xml_node& r) const;+ bool operator<(const xml_node& r) const;+ bool operator>(const xml_node& r) const;+ bool operator<=(const xml_node& r) const;+ bool operator>=(const xml_node& r) const;++ // Check if node is empty.+ bool empty() const;++ // Get node type+ xml_node_type type() const;++ // Get node name, or "" if node is empty or it has no name+ const char_t* name() const;++ // Get node value, or "" if node is empty or it has no value+ // Note: For <node>text</node> node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes.+ const char_t* value() const;+ + // Get attribute list+ xml_attribute first_attribute() const;+ xml_attribute last_attribute() const;++ // Get children list+ xml_node first_child() const;+ xml_node last_child() const;++ // Get next/previous sibling in the children list of the parent node+ xml_node next_sibling() const;+ xml_node previous_sibling() const;+ + // Get parent node+ xml_node parent() const;++ // Get root of DOM tree this node belongs to+ xml_node root() const;++ // Get text object for the current node+ xml_text text() const;++ // Get child, attribute or next/previous sibling with the specified name+ xml_node child(const char_t* name) const;+ xml_attribute attribute(const char_t* name) const;+ xml_node next_sibling(const char_t* name) const;+ xml_node previous_sibling(const char_t* name) const;++ // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA+ const char_t* child_value() const;++ // Get child value of child with specified name. Equivalent to child(name).child_value().+ const char_t* child_value(const char_t* name) const;++ // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)+ bool set_name(const char_t* rhs);+ bool set_value(const char_t* rhs);+ + // Add attribute with specified name. Returns added attribute, or empty attribute on errors.+ xml_attribute append_attribute(const char_t* name);+ xml_attribute prepend_attribute(const char_t* name);+ xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr);+ xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr);++ // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors.+ xml_attribute append_copy(const xml_attribute& proto);+ xml_attribute prepend_copy(const xml_attribute& proto);+ xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);+ xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);++ // Add child node with specified type. Returns added node, or empty node on errors.+ xml_node append_child(xml_node_type type = node_element);+ xml_node prepend_child(xml_node_type type = node_element);+ xml_node insert_child_after(xml_node_type type, const xml_node& node);+ xml_node insert_child_before(xml_node_type type, const xml_node& node);++ // Add child element with specified name. Returns added node, or empty node on errors.+ xml_node append_child(const char_t* name);+ xml_node prepend_child(const char_t* name);+ xml_node insert_child_after(const char_t* name, const xml_node& node);+ xml_node insert_child_before(const char_t* name, const xml_node& node);++ // Add a copy of the specified node as a child. Returns added node, or empty node on errors.+ xml_node append_copy(const xml_node& proto);+ xml_node prepend_copy(const xml_node& proto);+ xml_node insert_copy_after(const xml_node& proto, const xml_node& node);+ xml_node insert_copy_before(const xml_node& proto, const xml_node& node);++ // Remove specified attribute+ bool remove_attribute(const xml_attribute& a);+ bool remove_attribute(const char_t* name);++ // Remove specified child+ bool remove_child(const xml_node& n);+ bool remove_child(const char_t* name);++ // Parses buffer as an XML document fragment and appends all nodes as children of the current node.+ // Copies/converts the buffer, so it may be deleted or changed after the function returns.+ // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory.+ xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);++ // Find attribute using predicate. Returns first attribute for which predicate returned true.+ template <typename Predicate> xml_attribute find_attribute(Predicate pred) const+ {+ if (!_root) return xml_attribute();+ + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())+ if (pred(attrib))+ return attrib;+ + return xml_attribute();+ }++ // Find child node using predicate. Returns first child for which predicate returned true.+ template <typename Predicate> xml_node find_child(Predicate pred) const+ {+ if (!_root) return xml_node();+ + for (xml_node node = first_child(); node; node = node.next_sibling())+ if (pred(node))+ return node;+ + return xml_node();+ }++ // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true.+ template <typename Predicate> xml_node find_node(Predicate pred) const+ {+ if (!_root) return xml_node();++ xml_node cur = first_child();+ + while (cur._root && cur._root != _root)+ {+ if (pred(cur)) return cur;++ if (cur.first_child()) cur = cur.first_child();+ else if (cur.next_sibling()) cur = cur.next_sibling();+ else+ {+ while (!cur.next_sibling() && cur._root != _root) cur = cur.parent();++ if (cur._root != _root) cur = cur.next_sibling();+ }+ }++ return xml_node();+ }++ // Find child node by attribute name/value+ xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const;+ xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const;++ #ifndef PUGIXML_NO_STL+ // Get the absolute node path from root as a text string.+ string_t path(char_t delimiter = '/') const;+ #endif++ // Search for a node by path consisting of node names and . or .. elements.+ xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const;++ // Recursively traverse subtree with xml_tree_walker+ bool traverse(xml_tree_walker& walker);+ + #ifndef PUGIXML_NO_XPATH+ // Select single node by evaluating XPath query. Returns first node from the resulting node set.+ xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;+ xpath_node select_single_node(const xpath_query& query) const;++ // Select node set by evaluating XPath query+ xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;+ xpath_node_set select_nodes(const xpath_query& query) const;+ #endif+ + // Print subtree using a writer object+ void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;++ #ifndef PUGIXML_NO_STL+ // Print subtree to stream+ void print(std::basic_ostream<char, std::char_traits<char> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;+ void print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const;+ #endif++ // Child nodes iterators+ typedef xml_node_iterator iterator;++ iterator begin() const;+ iterator end() const;++ // Attribute iterators+ typedef xml_attribute_iterator attribute_iterator;++ attribute_iterator attributes_begin() const;+ attribute_iterator attributes_end() const;++ // Range-based for support+ xml_object_range<xml_node_iterator> children() const;+ xml_object_range<xml_named_node_iterator> children(const char_t* name) const;+ xml_object_range<xml_attribute_iterator> attributes() const;++ // Get node offset in parsed file/string (in char_t units) for debugging purposes+ ptrdiff_t offset_debug() const;++ // Get hash value (unique for handles to the same object)+ size_t hash_value() const;++ // Get internal pointer+ xml_node_struct* internal_object() const;+ };++#ifdef __BORLANDC__+ // Borland C++ workaround+ bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs);+ bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs);+#endif++ // A helper for working with text inside PCDATA nodes+ class PUGIXML_CLASS xml_text+ {+ friend class xml_node;++ xml_node_struct* _root;++ typedef void (*unspecified_bool_type)(xml_text***);++ explicit xml_text(xml_node_struct* root);++ xml_node_struct* _data_new();+ xml_node_struct* _data() const;++ public:+ // Default constructor. Constructs an empty object.+ xml_text();++ // Safe bool conversion operator+ operator unspecified_bool_type() const;++ // Borland C++ workaround+ bool operator!() const;++ // Check if text object is empty+ bool empty() const;++ // Get text, or "" if object is empty+ const char_t* get() const;++ // Get text, or the default value if object is empty+ const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;++ // Get text as a number, or the default value if conversion did not succeed or object is empty+ int as_int(int def = 0) const;+ unsigned int as_uint(unsigned int def = 0) const;+ double as_double(double def = 0) const;+ float as_float(float def = 0) const;++ #ifdef PUGIXML_HAS_LONG_LONG+ long long as_llong(long long def = 0) const;+ unsigned long long as_ullong(unsigned long long def = 0) const;+ #endif++ // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty+ bool as_bool(bool def = false) const;++ // Set text (returns false if object is empty or there is not enough memory)+ bool set(const char_t* rhs);++ // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")+ bool set(int rhs);+ bool set(unsigned int rhs);+ bool set(double rhs);+ bool set(bool rhs);++ #ifdef PUGIXML_HAS_LONG_LONG+ bool set(long long rhs);+ bool set(unsigned long long rhs);+ #endif++ // Set text (equivalent to set without error checking)+ xml_text& operator=(const char_t* rhs);+ xml_text& operator=(int rhs);+ xml_text& operator=(unsigned int rhs);+ xml_text& operator=(double rhs);+ xml_text& operator=(bool rhs);++ #ifdef PUGIXML_HAS_LONG_LONG+ xml_text& operator=(long long rhs);+ xml_text& operator=(unsigned long long rhs);+ #endif++ // Get the data node (node_pcdata or node_cdata) for this object+ xml_node data() const;+ };++#ifdef __BORLANDC__+ // Borland C++ workaround+ bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs);+ bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs);+#endif++ // Child node iterator (a bidirectional iterator over a collection of xml_node)+ class PUGIXML_CLASS xml_node_iterator+ {+ friend class xml_node;++ private:+ mutable xml_node _wrap;+ xml_node _parent;++ xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent);++ public:+ // Iterator traits+ typedef ptrdiff_t difference_type;+ typedef xml_node value_type;+ typedef xml_node* pointer;+ typedef xml_node& reference;++ #ifndef PUGIXML_NO_STL+ typedef std::bidirectional_iterator_tag iterator_category;+ #endif++ // Default constructor+ xml_node_iterator();++ // Construct an iterator which points to the specified node+ xml_node_iterator(const xml_node& node);++ // Iterator operators+ bool operator==(const xml_node_iterator& rhs) const;+ bool operator!=(const xml_node_iterator& rhs) const;++ xml_node& operator*() const;+ xml_node* operator->() const;++ const xml_node_iterator& operator++();+ xml_node_iterator operator++(int);++ const xml_node_iterator& operator--();+ xml_node_iterator operator--(int);+ };++ // Attribute iterator (a bidirectional iterator over a collection of xml_attribute)+ class PUGIXML_CLASS xml_attribute_iterator+ {+ friend class xml_node;++ private:+ mutable xml_attribute _wrap;+ xml_node _parent;++ xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent);++ public:+ // Iterator traits+ typedef ptrdiff_t difference_type;+ typedef xml_attribute value_type;+ typedef xml_attribute* pointer;+ typedef xml_attribute& reference;++ #ifndef PUGIXML_NO_STL+ typedef std::bidirectional_iterator_tag iterator_category;+ #endif++ // Default constructor+ xml_attribute_iterator();++ // Construct an iterator which points to the specified attribute+ xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent);++ // Iterator operators+ bool operator==(const xml_attribute_iterator& rhs) const;+ bool operator!=(const xml_attribute_iterator& rhs) const;++ xml_attribute& operator*() const;+ xml_attribute* operator->() const;++ const xml_attribute_iterator& operator++();+ xml_attribute_iterator operator++(int);++ const xml_attribute_iterator& operator--();+ xml_attribute_iterator operator--(int);+ };++ // Named node range helper+ class PUGIXML_CLASS xml_named_node_iterator+ {+ friend class xml_node;++ public:+ // Iterator traits+ typedef ptrdiff_t difference_type;+ typedef xml_node value_type;+ typedef xml_node* pointer;+ typedef xml_node& reference;++ #ifndef PUGIXML_NO_STL+ typedef std::bidirectional_iterator_tag iterator_category;+ #endif++ // Default constructor+ xml_named_node_iterator();++ // Construct an iterator which points to the specified node+ xml_named_node_iterator(const xml_node& node, const char_t* name);++ // Iterator operators+ bool operator==(const xml_named_node_iterator& rhs) const;+ bool operator!=(const xml_named_node_iterator& rhs) const;++ xml_node& operator*() const;+ xml_node* operator->() const;++ const xml_named_node_iterator& operator++();+ xml_named_node_iterator operator++(int);++ const xml_named_node_iterator& operator--();+ xml_named_node_iterator operator--(int);++ private:+ mutable xml_node _wrap;+ xml_node _parent;+ const char_t* _name;++ xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name);+ };++ // Abstract tree walker class (see xml_node::traverse)+ class PUGIXML_CLASS xml_tree_walker+ {+ friend class xml_node;++ private:+ int _depth;+ + protected:+ // Get current traversal depth+ int depth() const;+ + public:+ xml_tree_walker();+ virtual ~xml_tree_walker();++ // Callback that is called when traversal begins+ virtual bool begin(xml_node& node);++ // Callback that is called for each node traversed+ virtual bool for_each(xml_node& node) = 0;++ // Callback that is called when traversal ends+ virtual bool end(xml_node& node);+ };++ // Parsing status, returned as part of xml_parse_result object+ enum xml_parse_status+ {+ status_ok = 0, // No error++ status_file_not_found, // File was not found during load_file()+ status_io_error, // Error reading from file/stream+ status_out_of_memory, // Could not allocate memory+ status_internal_error, // Internal error occurred++ status_unrecognized_tag, // Parser could not determine tag type++ status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction+ status_bad_comment, // Parsing error occurred while parsing comment+ status_bad_cdata, // Parsing error occurred while parsing CDATA section+ status_bad_doctype, // Parsing error occurred while parsing document type declaration+ status_bad_pcdata, // Parsing error occurred while parsing PCDATA section+ status_bad_start_element, // Parsing error occurred while parsing start element tag+ status_bad_attribute, // Parsing error occurred while parsing element attribute+ status_bad_end_element, // Parsing error occurred while parsing end element tag+ status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)++ status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer)++ status_no_document_element // Parsing resulted in a document without element nodes+ };++ // Parsing result+ struct PUGIXML_CLASS xml_parse_result+ {+ // Parsing status (see xml_parse_status)+ xml_parse_status status;++ // Last parsed offset (in char_t units from start of input data)+ ptrdiff_t offset;++ // Source document encoding+ xml_encoding encoding;++ // Default constructor, initializes object to failed state+ xml_parse_result();++ // Cast to bool operator+ operator bool() const;++ // Get error description+ const char* description() const;+ };++ // Document class (DOM tree root)+ class PUGIXML_CLASS xml_document: public xml_node+ {+ private:+ char_t* _buffer;++ char _memory[192];+ + // Non-copyable semantics+ xml_document(const xml_document&);+ const xml_document& operator=(const xml_document&);++ void create();+ void destroy();++ public:+ // Default constructor, makes empty document+ xml_document();++ // Destructor, invalidates all node/attribute handles to this document+ ~xml_document();++ // Removes all nodes, leaving the empty document+ void reset();++ // Removes all nodes, then copies the entire contents of the specified document+ void reset(const xml_document& proto);++ #ifndef PUGIXML_NO_STL+ // Load document from stream.+ xml_parse_result load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);+ xml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default);+ #endif++ // Load document from zero-terminated string. No encoding conversions are applied.+ xml_parse_result load(const char_t* contents, unsigned int options = parse_default);++ // Load document from file+ xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);+ xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);++ // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns.+ xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);++ // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).+ // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed.+ xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);++ // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).+ // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore).+ xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);++ // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details).+ void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;++ #ifndef PUGIXML_NO_STL+ // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details).+ void save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;+ void save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const;+ #endif++ // Save XML to file+ bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;+ bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;++ // Get document element+ xml_node document_element() const;+ };++#ifndef PUGIXML_NO_XPATH+ // XPath query return type+ enum xpath_value_type+ {+ xpath_type_none, // Unknown type (query failed to compile)+ xpath_type_node_set, // Node set (xpath_node_set)+ xpath_type_number, // Number+ xpath_type_string, // String+ xpath_type_boolean // Boolean+ };++ // XPath parsing result+ struct PUGIXML_CLASS xpath_parse_result+ {+ // Error message (0 if no error)+ const char* error;++ // Last parsed offset (in char_t units from string start)+ ptrdiff_t offset;++ // Default constructor, initializes object to failed state+ xpath_parse_result();++ // Cast to bool operator+ operator bool() const;++ // Get error description+ const char* description() const;+ };++ // A single XPath variable+ class PUGIXML_CLASS xpath_variable+ {+ friend class xpath_variable_set;++ protected:+ xpath_value_type _type;+ xpath_variable* _next;++ xpath_variable();++ // Non-copyable semantics+ xpath_variable(const xpath_variable&);+ xpath_variable& operator=(const xpath_variable&);+ + public:+ // Get variable name+ const char_t* name() const;++ // Get variable type+ xpath_value_type type() const;++ // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error+ bool get_boolean() const;+ double get_number() const;+ const char_t* get_string() const;+ const xpath_node_set& get_node_set() const;++ // Set variable value; no type conversion is performed, false is returned on type mismatch error+ bool set(bool value);+ bool set(double value);+ bool set(const char_t* value);+ bool set(const xpath_node_set& value);+ };++ // A set of XPath variables+ class PUGIXML_CLASS xpath_variable_set+ {+ private:+ xpath_variable* _data[64];++ // Non-copyable semantics+ xpath_variable_set(const xpath_variable_set&);+ xpath_variable_set& operator=(const xpath_variable_set&);++ xpath_variable* find(const char_t* name) const;++ public:+ // Default constructor/destructor+ xpath_variable_set();+ ~xpath_variable_set();++ // Add a new variable or get the existing one, if the types match+ xpath_variable* add(const char_t* name, xpath_value_type type);++ // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch+ bool set(const char_t* name, bool value);+ bool set(const char_t* name, double value);+ bool set(const char_t* name, const char_t* value);+ bool set(const char_t* name, const xpath_node_set& value);++ // Get existing variable by name+ xpath_variable* get(const char_t* name);+ const xpath_variable* get(const char_t* name) const;+ };++ // A compiled XPath query object+ class PUGIXML_CLASS xpath_query+ {+ private:+ void* _impl;+ xpath_parse_result _result;++ typedef void (*unspecified_bool_type)(xpath_query***);++ // Non-copyable semantics+ xpath_query(const xpath_query&);+ xpath_query& operator=(const xpath_query&);++ public:+ // Construct a compiled object from XPath expression.+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors.+ explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);++ // Destructor+ ~xpath_query();++ // Get query expression return type+ xpath_value_type return_type() const;+ + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary.+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.+ bool evaluate_boolean(const xpath_node& n) const;+ + // Evaluate expression as double value in the specified context; performs type conversion if necessary.+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.+ double evaluate_number(const xpath_node& n) const;+ + #ifndef PUGIXML_NO_STL+ // Evaluate expression as string value in the specified context; performs type conversion if necessary.+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.+ string_t evaluate_string(const xpath_node& n) const;+ #endif+ + // Evaluate expression as string value in the specified context; performs type conversion if necessary.+ // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero).+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.+ // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead.+ size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const;++ // Evaluate expression as node set in the specified context.+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.+ // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.+ xpath_node_set evaluate_node_set(const xpath_node& n) const;++ // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)+ const xpath_parse_result& result() const;++ // Safe bool conversion operator+ operator unspecified_bool_type() const;++ // Borland C++ workaround+ bool operator!() const;+ };+ + #ifndef PUGIXML_NO_EXCEPTIONS+ // XPath exception class+ class PUGIXML_CLASS xpath_exception: public std::exception+ {+ private:+ xpath_parse_result _result;++ public:+ // Construct exception from parse result+ explicit xpath_exception(const xpath_parse_result& result);++ // Get error message+ virtual const char* what() const throw();++ // Get parse result+ const xpath_parse_result& result() const;+ };+ #endif+ + // XPath node class (either xml_node or xml_attribute)+ class PUGIXML_CLASS xpath_node+ {+ private:+ xml_node _node;+ xml_attribute _attribute;+ + typedef void (*unspecified_bool_type)(xpath_node***);++ public:+ // Default constructor; constructs empty XPath node+ xpath_node();+ + // Construct XPath node from XML node/attribute+ xpath_node(const xml_node& node);+ xpath_node(const xml_attribute& attribute, const xml_node& parent);++ // Get node/attribute, if any+ xml_node node() const;+ xml_attribute attribute() const;+ + // Get parent of contained node/attribute+ xml_node parent() const;++ // Safe bool conversion operator+ operator unspecified_bool_type() const;+ + // Borland C++ workaround+ bool operator!() const;++ // Comparison operators+ bool operator==(const xpath_node& n) const;+ bool operator!=(const xpath_node& n) const;+ };++#ifdef __BORLANDC__+ // Borland C++ workaround+ bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs);+ bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs);+#endif++ // A fixed-size collection of XPath nodes+ class PUGIXML_CLASS xpath_node_set+ {+ public:+ // Collection type+ enum type_t+ {+ type_unsorted, // Not ordered+ type_sorted, // Sorted by document order (ascending)+ type_sorted_reverse // Sorted by document order (descending)+ };+ + // Constant iterator type+ typedef const xpath_node* const_iterator;+ + // Default constructor. Constructs empty set.+ xpath_node_set();++ // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful+ xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted);++ // Destructor+ ~xpath_node_set();+ + // Copy constructor/assignment operator+ xpath_node_set(const xpath_node_set& ns);+ xpath_node_set& operator=(const xpath_node_set& ns);++ // Get collection type+ type_t type() const;+ + // Get collection size+ size_t size() const;++ // Indexing operator+ const xpath_node& operator[](size_t index) const;+ + // Collection iterators+ const_iterator begin() const;+ const_iterator end() const;++ // Sort the collection in ascending/descending order by document order+ void sort(bool reverse = false);+ + // Get first node in the collection by document order+ xpath_node first() const;+ + // Check if collection is empty+ bool empty() const;+ + private:+ type_t _type;+ + xpath_node _storage;+ + xpath_node* _begin;+ xpath_node* _end;++ void _assign(const_iterator begin, const_iterator end);+ };+#endif++#ifndef PUGIXML_NO_STL+ // Convert wide string to UTF8+ std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str);+ std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str);+ + // Convert UTF8 to wide string+ std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str);+ std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& str);+#endif++ // Memory allocation function interface; returns pointer to allocated memory or NULL on failure+ typedef void* (*allocation_function)(size_t size);+ + // Memory deallocation function interface+ typedef void (*deallocation_function)(void* ptr);++ // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions.+ void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate);+ + // Get current memory management functions+ allocation_function PUGIXML_FUNCTION get_memory_allocation_function();+ deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function();+}++#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))+namespace std+{+ // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)+ std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);+ std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);+ std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);+}+#endif++#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)+namespace std+{+ // Workarounds for (non-standard) iterator category detection+ std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);+ std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);+ std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);+}+#endif++#endif++/**+ * Copyright (c) 2006-2014 Arseny Kapoulkine+ *+ * Permission is hereby granted, free of charge, to any person+ * obtaining a copy of this software and associated documentation+ * files (the "Software"), to deal in the Software without+ * restriction, including without limitation the rights to use,+ * copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the+ * Software is furnished to do so, subject to the following+ * conditions:+ *+ * The above copyright notice and this permission notice shall be+ * included in all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+ * OTHER DEALINGS IN THE SOFTWARE.+ */
+ pugixml.cabal view
@@ -0,0 +1,50 @@+name: pugixml+version: 0.1.0+synopsis: pugixml binding.+description: pugixml binding.+license: MIT+license-file: LICENSE+author: HirotomoMoriwaki<philopon.dependence@gmail.com>+maintainer: HirotomoMoriwaki<philopon.dependence@gmail.com>+Homepage: https://github.com/philopon/pugixml-hs+Bug-reports: https://github.com/philopon/pugixml-hs/issues+copyright: (c) 2014 Hirotomo Moriwaki+category: Text, XML+build-type: Simple+extra-source-files: pugixml-1.4/pugiconfig.hpp+ pugixml-1.4/pugixml.hpp+cabal-version: >=1.10++library+ exposed-modules: Text.XML.Pugi+ Text.XML.Pugi.Const+ other-modules: Text.XML.Pugi.Foreign.Const+ Text.XML.Pugi.Foreign.Node+ Text.XML.Pugi.Foreign.Document+ Text.XML.Pugi.Foreign.XPath+ Text.XML.Pugi.Foreign.XPath.Node+ Text.XML.Pugi.Foreign.Types+ Text.XML.Pugi.Foreign.Attr+ -- other-modules: + -- other-extensions: + build-depends: base >=4.6 && <4.8+ , bytestring >=0.10 && <0.11+ , data-default-class >=0.0 && <0.1+ , template-haskell >=2.8 && <2.10+ -- hs-source-dirs: + c-sources: pugixml-1.4/pugixml.cpp cbit/wrapper.cc+ include-dirs: pugixml-1.4+ ghc-options: -Wall -O2+ extra-libraries: stdc+++ default-language: Haskell2010++test-suite test+ main-is: test.hs+ build-depends: base >=4.6 && <4.8+ , bytestring >=0.10 && <0.11+ , pugixml+ , tasty >=0.10 && <0.11+ , tasty-hunit >=0.9 && <0.10+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ default-language: Haskell2010
+ test/test.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedStrings #-}++import Control.Applicative++import Test.Tasty+import Test.Tasty.HUnit++import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L++import Text.XML.Pugi+import Data.Maybe++main :: IO ()+main = defaultMain tests++testAStr :: S.ByteString+testAStr = "<?xml version=\"1.0\" lang=\"ja\"?><foo><bar baz=\"qux\">quux</bar><bar baz=\"bar\">hoge</bar><piyo><![CDATA[<piyopiyo>]]></piyo></foo>"++testA :: Document+testA = either undefined id $ parse def { parseFlags = parseFull } testAStr++tests :: TestTree+tests = testGroup "Tests"+ [ parsePretty+ ]++parsePretty :: TestTree+parsePretty = testGroup "Parse/Pretty"+ [ testCase "testA" $ pp testAStr @?= L.fromStrict testAStr+ ]+ where+ pp s = either undefined (pretty def {prettyFlags = formatRaw} ) $+ parse def { parseFlags = parseFull } s+