summaryrefslogtreecommitdiff
path: root/libxerces-c/xercesc/internal/ReaderMgr.cpp
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-12-04 11:33:20 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-12-05 10:17:24 +0300
commit6af6f698437f9538f8d94d22ca9cba350d5e8d71 (patch)
treeb6e0019fba4c440ec69b18dba67a4691d168f49b /libxerces-c/xercesc/internal/ReaderMgr.cpp
parent51f5110bb4d2d63149ec5bb93c62c8c0499495ae (diff)
Prepare for fixing CVE-2018-1311
Diffstat (limited to 'libxerces-c/xercesc/internal/ReaderMgr.cpp')
-rw-r--r--[l---------]libxerces-c/xercesc/internal/ReaderMgr.cpp1124
1 files changed, 1123 insertions, 1 deletions
diff --git a/libxerces-c/xercesc/internal/ReaderMgr.cpp b/libxerces-c/xercesc/internal/ReaderMgr.cpp
index 6604eb1..d14483e 120000..100644
--- a/libxerces-c/xercesc/internal/ReaderMgr.cpp
+++ b/libxerces-c/xercesc/internal/ReaderMgr.cpp
@@ -1 +1,1123 @@
-../../../upstream/src/xercesc/internal/ReaderMgr.cpp \ No newline at end of file
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * $Id$
+ */
+
+// ---------------------------------------------------------------------------
+// Includes
+// ---------------------------------------------------------------------------
+#include <xercesc/util/BinMemInputStream.hpp>
+#include <xercesc/util/Janitor.hpp>
+#include <xercesc/util/PlatformUtils.hpp>
+#include <xercesc/util/RuntimeException.hpp>
+#include <xercesc/util/UnexpectedEOFException.hpp>
+#include <xercesc/util/XMLURL.hpp>
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xercesc/util/XMLUni.hpp>
+#include <xercesc/util/XMLUri.hpp>
+#include <xercesc/sax/InputSource.hpp>
+#include <xercesc/framework/LocalFileInputSource.hpp>
+#include <xercesc/framework/URLInputSource.hpp>
+#include <xercesc/framework/XMLBuffer.hpp>
+#include <xercesc/framework/XMLDocumentHandler.hpp>
+#include <xercesc/framework/XMLEntityDecl.hpp>
+#include <xercesc/framework/XMLEntityHandler.hpp>
+#include <xercesc/internal/EndOfEntityException.hpp>
+#include <xercesc/internal/ReaderMgr.hpp>
+#include <xercesc/util/OutOfMemoryException.hpp>
+#include <xercesc/util/XMLResourceIdentifier.hpp>
+
+XERCES_CPP_NAMESPACE_BEGIN
+
+// ---------------------------------------------------------------------------
+// ReaderMgr: Constructors and Destructor
+// ---------------------------------------------------------------------------
+ReaderMgr::ReaderMgr(MemoryManager* const manager) :
+
+ fCurEntity(0)
+ , fCurReader(0)
+ , fEntityHandler(0)
+ , fEntityStack(0)
+ , fNextReaderNum(1)
+ , fReaderStack(0)
+ , fThrowEOE(false)
+ , fXMLVersion(XMLReader::XMLV1_0)
+ , fStandardUriConformant(false)
+ , fMemoryManager(manager)
+{
+}
+
+ReaderMgr::~ReaderMgr()
+{
+ //
+ // Clean up the reader and entity stacks. Note that we don't own the
+ // entities, so we don't delete the current entity (and the entity stack
+ // does not own its elements either, so deleting it will not delete the
+ // entities it still references!)
+ //
+ delete fCurReader;
+ delete fReaderStack;
+ delete fEntityStack;
+}
+
+
+// ---------------------------------------------------------------------------
+// ReaderMgr: Getter methods
+// ---------------------------------------------------------------------------
+bool ReaderMgr::isEmpty() const
+{
+ return fReaderStack->empty();
+}
+
+
+// ---------------------------------------------------------------------------
+// ReaderMgr: Scanning APIs
+// ---------------------------------------------------------------------------
+XMLCh ReaderMgr::getNextChar()
+{
+ XMLCh chRet;
+ if (fCurReader->getNextChar(chRet))
+ return chRet;
+
+ //
+ // Didn't get anything back so this reader is hosed. So lets move to
+ // the next reader on the stack. If this fails, it will be because
+ // its the end of the original file, and we just return zero.
+ //
+ // If its the end of an entity and fThrowEOE is set, it will throw out
+ // of here. Otherwise, it will take us down to the next reader and
+ // we'll have more chars.
+ //
+ if (!popReader())
+ return XMLCh(0);
+
+ // Else try again and return the new character
+ fCurReader->getNextChar(chRet);
+ return chRet;
+}
+
+
+void ReaderMgr::getSpaces(XMLBuffer& toFill)
+{
+ // Reset the buffer before we start
+ toFill.reset();
+
+ //
+ // Get all the spaces from the current reader. If it returns true,
+ // it hit a non-space and we are done. Else we have to pop a reader
+ // and keep going.
+ //
+ while (!fCurReader->getSpaces(toFill))
+ {
+ // We wore that one out, so lets pop a reader and try again
+ if (!popReader())
+ break;
+ }
+}
+
+
+void ReaderMgr::getUpToCharOrWS(XMLBuffer& toFill, const XMLCh toCheck)
+{
+ // Reset the target buffer before we start
+ toFill.reset();
+
+ //
+ // Ok, enter a loop where we ask the current reader to get chars until
+ // it meets the criteria. It returns false if it came back due to eating
+ // up all of its data. Else it returned because something matched, and
+ // we are done.
+ //
+ while (!fCurReader->getUpToCharOrWS(toFill, toCheck))
+ {
+ // We ate that one up, lets try to pop another. If not, break out
+ if (!popReader())
+ break;
+ }
+}
+
+
+XMLCh ReaderMgr::peekNextChar()
+{
+ XMLCh chRet;
+ if (fCurReader->peekNextChar(chRet))
+ return chRet;
+
+ //
+ // Didn't get anything back so this reader is hosed. So lets move to
+ // the next reader on the stack. If this fails, it will be because
+ // its the end of the original file, and we just return zero.
+ //
+ if (!popReader())
+ return XMLCh(0);
+
+ // Else peek again and return the character
+ fCurReader->peekNextChar(chRet);
+ return chRet;
+}
+
+
+bool ReaderMgr::skippedChar(const XMLCh toCheck)
+{
+ while (true)
+ {
+ // If we get it, then just return true now
+ if (fCurReader->skippedChar(toCheck))
+ return true;
+
+ //
+ // Check to see if we hit end of input on this reader. If so, then
+ // lets pop and try again. Else, we failed. If we cannot pop another
+ // then we failed.
+ //
+ if (!fCurReader->getNoMoreFlag())
+ break;
+
+ if (!popReader())
+ break;
+ }
+ return false;
+}
+
+
+bool ReaderMgr::skippedSpace()
+{
+ while (true)
+ {
+ // If we get it, then just return true now
+ if (fCurReader->skippedSpace())
+ return true;
+
+ //
+ // Check to see if we hit end of input on this reader. If so, then
+ // lets pop and try again. Else, we failed. If we cannot pop another
+ // then we failed.
+ //
+ if (!fCurReader->getNoMoreFlag())
+ break;
+
+ if (!popReader())
+ break;
+ }
+ return false;
+}
+
+
+bool ReaderMgr::skipIfQuote(XMLCh& chGotten)
+{
+ while (true)
+ {
+ // If we get it, then just return true now
+ if (fCurReader->skipIfQuote(chGotten))
+ return true;
+
+ //
+ // Check to see if we hit end of input on this reader. If so, then
+ // lets pop and try again. Else, we failed. If we cannot pop another
+ // then we failed.
+ //
+ if (!fCurReader->getNoMoreFlag())
+ break;
+
+ if (!popReader())
+ break;
+ }
+ return false;
+}
+
+void ReaderMgr::skipPastSpaces(bool& skippedSomething, bool inDecl /* = false */)
+{
+ // we rely on the fact that fCurReader->skipSpaces will NOT reset the flag to false, but only
+ // set it to true if a space is found
+ skippedSomething = false;
+ //
+ // Skip all the spaces in the current reader. If it returned because
+ // it hit a non-space, break out. Else we have to pop another entity
+ // and keep going.
+ //
+ while (!fCurReader->skipSpaces(skippedSomething, inDecl))
+ {
+ // Try to pop another entity. If we can't then we are done
+ if (!popReader())
+ break;
+ }
+}
+
+void ReaderMgr::skipPastSpaces()
+{
+ // we are not using it, so we don't care to initialize it
+ bool tmpFlag;
+ //
+ // Skip all the spaces in the current reader. If it returned because
+ // it hit a non-space, break out. Else we have to pop another entity
+ // and keep going.
+ //
+ while (!fCurReader->skipSpaces(tmpFlag, false))
+ {
+ // Try to pop another entity. If we can't then we are done
+ if (!popReader())
+ break;
+ }
+}
+
+void ReaderMgr::skipQuotedString(const XMLCh quoteCh)
+{
+ XMLCh nextCh;
+ // If we get an end of file char, then return
+ while ((nextCh = getNextChar())!=0)
+ {
+ // If we get the quote char, then break out
+ if (nextCh == quoteCh)
+ break;
+ }
+}
+
+
+XMLCh ReaderMgr::skipUntilIn(const XMLCh* const listToSkip)
+{
+ XMLCh nextCh;
+ // If we get an end of file char, then return
+ while ((nextCh = peekNextChar())!=0)
+ {
+ if (XMLString::indexOf(listToSkip, nextCh) != -1)
+ break;
+
+ // Its one of ours so eat it
+ getNextChar();
+ }
+ return nextCh;
+}
+
+
+XMLCh ReaderMgr::skipUntilInOrWS(const XMLCh* const listToSkip)
+{
+ XMLCh nextCh;
+ // If we get an end of file char, then return
+ while ((nextCh = peekNextChar())!=0)
+ {
+ if (fCurReader->isWhitespace(nextCh))
+ break;
+
+ if (XMLString::indexOf(listToSkip, nextCh) != -1)
+ break;
+
+ // Its one of ours, so eat it
+ getNextChar();
+ }
+ return nextCh;
+}
+
+
+
+// ---------------------------------------------------------------------------
+// ReaderMgr: Control methods
+// ---------------------------------------------------------------------------
+
+//
+// If the reader stack is empty, then there is only the original main XML
+// entity left. If its empty, then we have no more input.
+//
+bool ReaderMgr::atEOF() const
+{
+ return fReaderStack->empty() && fCurReader->getNoMoreFlag();
+}
+
+
+//
+// This method is called in the case of errors to clean up the stack when
+// entities have been incorrectly left on the stack due to syntax errors.
+// It just cleans back the stack, and sends no entity events.
+//
+void ReaderMgr::cleanStackBackTo(const XMLSize_t readerNum)
+{
+ //
+ // Just start popping readers until we find the one with the indicated
+ // reader number.
+ //
+ while (true)
+ {
+ if (fCurReader->getReaderNum() == readerNum)
+ break;
+
+ if (fReaderStack->empty())
+ ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::RdrMgr_ReaderIdNotFound, fMemoryManager);
+
+ delete fCurReader;
+ fCurReader = fReaderStack->pop();
+ fCurEntity = fEntityStack->pop();
+ }
+}
+
+
+XMLReader* ReaderMgr::createReader( const InputSource& src
+ , const bool
+ , const XMLReader::RefFrom refFrom
+ , const XMLReader::Types type
+ , const XMLReader::Sources source
+ , const bool calcSrcOfs
+ , XMLSize_t lowWaterMark)
+{
+ //
+ // Ask the input source to create us an input stream. The particular
+ // type of input source will know what kind to create.
+ //
+ BinInputStream* newStream = src.makeStream();
+ if (!newStream)
+ return 0;
+
+ Janitor<BinInputStream> streamJanitor(newStream);
+
+ //
+ // Create a new reader and return it. If the source has an encoding that
+ // it wants to force, then we call the constructor that does that.
+ // Otherwise, we just call the one that provides the provisional encoding
+ // to be possibly updated later by the encoding="" setting.
+ //
+ XMLReader* retVal = 0;
+
+ // XMLReader ctor invokes refreshRawBuffer() which calls
+ // newStream->readBytes().
+ // This readBytes() may throw exception, which neither
+ // refresRawBuffer(), nor XMLReader ctor catches.
+ // We need to handle this exception to avoid leak on newStream.
+
+ try {
+ const XMLCh* encoding = src.getEncoding();
+ if(encoding == 0)
+ encoding = newStream->getEncoding();
+ if (encoding)
+ {
+ retVal = new (fMemoryManager) XMLReader
+ (
+ src.getPublicId()
+ , src.getSystemId()
+ , newStream
+ , encoding
+ , refFrom
+ , type
+ , source
+ , false
+ , calcSrcOfs
+ , lowWaterMark
+ , fXMLVersion
+ , fMemoryManager
+ );
+ }
+ else
+ {
+ retVal = new (fMemoryManager) XMLReader
+ (
+ src.getPublicId()
+ , src.getSystemId()
+ , newStream
+ , refFrom
+ , type
+ , source
+ , false
+ , calcSrcOfs
+ , lowWaterMark
+ , fXMLVersion
+ , fMemoryManager
+ );
+ }
+ }
+ catch(const OutOfMemoryException&)
+ {
+ streamJanitor.release();
+
+ throw;
+ }
+
+ assert(retVal);
+
+ streamJanitor.release();
+
+ // Set the next available reader number on this reader
+ retVal->setReaderNum(fNextReaderNum++);
+ return retVal;
+}
+
+
+XMLReader* ReaderMgr::createReader( const XMLCh* const sysId
+ , const XMLCh* const pubId
+ , const bool xmlDecl
+ , const XMLReader::RefFrom refFrom
+ , const XMLReader::Types type
+ , const XMLReader::Sources source
+ , InputSource*& srcToFill
+ , const bool calcSrcOfs
+ , XMLSize_t lowWaterMark
+ , const bool disableDefaultEntityResolution)
+{
+ //Normalize sysId
+ XMLBuffer normalizedSysId(1023, fMemoryManager);
+ if(sysId)
+ XMLString::removeChar(sysId, 0xFFFF, normalizedSysId);
+ const XMLCh* normalizedURI = normalizedSysId.getRawBuffer();
+
+ // Create a buffer for expanding the system id
+ XMLBuffer expSysId(1023, fMemoryManager);
+
+ //
+ // Allow the entity handler to expand the system id if they choose
+ // to do so.
+ //
+ if (fEntityHandler)
+ {
+ if (!fEntityHandler->expandSystemId(normalizedURI, expSysId))
+ expSysId.set(normalizedURI);
+ }
+ else
+ {
+ expSysId.set(normalizedURI);
+ }
+
+ // Call the entity resolver interface to get an input source
+ srcToFill = 0;
+ if (fEntityHandler)
+ {
+ LastExtEntityInfo lastInfo;
+ getLastExtEntityInfo(lastInfo);
+ XMLResourceIdentifier resourceIdentifier(XMLResourceIdentifier::ExternalEntity,
+ expSysId.getRawBuffer(), XMLUni::fgZeroLenString, pubId, lastInfo.systemId,
+ this);
+ srcToFill = fEntityHandler->resolveEntity(&resourceIdentifier);
+ }
+
+ //
+ // If they didn't create a source via the entity resolver, then we
+ // have to create one on our own.
+ //
+ if (!srcToFill)
+ {
+ if (disableDefaultEntityResolution)
+ return 0;
+
+ LastExtEntityInfo lastInfo;
+ getLastExtEntityInfo(lastInfo);
+
+// Keep this #if 0 block as it was exposing a threading problem on AIX.
+// Got rid of the problem by changing XMLURL to not throw malformedurl
+// exceptions.
+#if 0
+ try
+ {
+ XMLURL urlTmp(lastInfo.systemId, expSysId.getRawBuffer(), fMemoryManager);
+ if (urlTmp.isRelative())
+ {
+ ThrowXMLwithMemMgr
+ (
+ MalformedURLException
+ , XMLExcepts::URL_NoProtocolPresent
+ , fMemoryManager
+ );
+ }
+ else {
+ if (fStandardUriConformant && urlTmp.hasInvalidChar())
+ ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
+ srcToFill = new (fMemoryManager) URLInputSource(urlTmp, fMemoryManager);
+ }
+ }
+
+ catch(const MalformedURLException& e)
+ {
+ // Its not a URL, so lets assume its a local file name if non-standard uri is allowed
+ if (!fStandardUriConformant)
+ srcToFill = new (fMemoryManager) LocalFileInputSource
+ (
+ lastInfo.systemId
+ , expSysId.getRawBuffer()
+ , fMemoryManager
+ );
+ else
+ throw e;
+ }
+#else
+ XMLURL urlTmp(fMemoryManager);
+ if ((!urlTmp.setURL(lastInfo.systemId, expSysId.getRawBuffer(), urlTmp)) ||
+ (urlTmp.isRelative()))
+ {
+ if (!fStandardUriConformant)
+ {
+ XMLBuffer resolvedSysId(1023, fMemoryManager);
+ XMLUri::normalizeURI(expSysId.getRawBuffer(), resolvedSysId);
+
+ srcToFill = new (fMemoryManager) LocalFileInputSource
+ (
+ lastInfo.systemId
+ , resolvedSysId.getRawBuffer()
+ , fMemoryManager
+ );
+ }
+ else
+ ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
+ }
+ else
+ {
+ if (fStandardUriConformant && urlTmp.hasInvalidChar())
+ ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
+ srcToFill = new (fMemoryManager) URLInputSource(urlTmp, fMemoryManager);
+ }
+#endif
+ }
+
+ // Put a janitor on the input source
+ Janitor<InputSource> janSrc(srcToFill);
+
+ //
+ // Now call the other version with the input source that we have, and
+ // return the resulting reader.
+ //
+ XMLReader* retVal = createReader
+ (
+ *srcToFill
+ , xmlDecl
+ , refFrom
+ , type
+ , source
+ , calcSrcOfs
+ , lowWaterMark
+ );
+
+ // Either way, we can release the input source now
+ janSrc.orphan();
+
+ // If it failed for any reason, then return zero.
+ if (!retVal)
+ return 0;
+
+ // Give this reader the next available reader number and return it
+ retVal->setReaderNum(fNextReaderNum++);
+ return retVal;
+}
+
+
+XMLReader* ReaderMgr::createReader( const XMLCh* const baseURI
+ , const XMLCh* const sysId
+ , const XMLCh* const pubId
+ , const bool xmlDecl
+ , const XMLReader::RefFrom refFrom
+ , const XMLReader::Types type
+ , const XMLReader::Sources source
+ , InputSource*& srcToFill
+ , const bool calcSrcOfs
+ , XMLSize_t lowWaterMark
+ , const bool disableDefaultEntityResolution)
+{
+ //Normalize sysId
+ XMLBuffer normalizedSysId(1023, fMemoryManager);
+ XMLString::removeChar(sysId, 0xFFFF, normalizedSysId);
+ const XMLCh* normalizedURI = normalizedSysId.getRawBuffer();
+
+ // Create a buffer for expanding the system id
+ XMLBuffer expSysId(1023, fMemoryManager);
+
+ //
+ // Allow the entity handler to expand the system id if they choose
+ // to do so.
+ //
+ if (fEntityHandler)
+ {
+ if (!fEntityHandler->expandSystemId(normalizedURI, expSysId))
+ expSysId.set(normalizedURI);
+ }
+ else
+ {
+ expSysId.set(normalizedURI);
+ }
+
+ // Call the entity resolver interface to get an input source
+ srcToFill = 0;
+ if (fEntityHandler)
+ {
+ XMLResourceIdentifier resourceIdentifier(XMLResourceIdentifier::ExternalEntity,
+ expSysId.getRawBuffer(), XMLUni::fgZeroLenString, pubId, baseURI,
+ this);
+ srcToFill = fEntityHandler->resolveEntity(&resourceIdentifier);
+ }
+
+ //
+ // If they didn't create a source via the entity resolver, then we
+ // have to create one on our own.
+ //
+ if (!srcToFill)
+ {
+ if (disableDefaultEntityResolution)
+ return 0;
+
+ LastExtEntityInfo lastInfo;
+
+ const XMLCh* baseuri=baseURI;
+ if(!baseuri || !*baseuri)
+ {
+ getLastExtEntityInfo(lastInfo);
+ baseuri = lastInfo.systemId;
+ }
+
+ XMLURL urlTmp(fMemoryManager);
+ if ((!urlTmp.setURL(baseuri, expSysId.getRawBuffer(), urlTmp)) ||
+ (urlTmp.isRelative()))
+ {
+ if (!fStandardUriConformant)
+ {
+ XMLBuffer resolvedSysId(1023, fMemoryManager);
+ XMLUri::normalizeURI(expSysId.getRawBuffer(), resolvedSysId);
+
+ srcToFill = new (fMemoryManager) LocalFileInputSource
+ (
+ baseuri
+ , resolvedSysId.getRawBuffer()
+ , fMemoryManager
+ );
+ }
+ else
+ ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
+ }
+ else
+ {
+ if (fStandardUriConformant && urlTmp.hasInvalidChar())
+ ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
+ srcToFill = new (fMemoryManager) URLInputSource(urlTmp, fMemoryManager);
+ }
+ }
+
+ // Put a janitor on the input source
+ Janitor<InputSource> janSrc(srcToFill);
+
+ //
+ // Now call the other version with the input source that we have, and
+ // return the resulting reader.
+ //
+ XMLReader* retVal = createReader
+ (
+ *srcToFill
+ , xmlDecl
+ , refFrom
+ , type
+ , source
+ , calcSrcOfs
+ , lowWaterMark
+ );
+
+ // Either way, we can release the input source now
+ janSrc.orphan();
+
+ // If it failed for any reason, then return zero.
+ if (!retVal)
+ return 0;
+
+ // Give this reader the next available reader number and return it
+ retVal->setReaderNum(fNextReaderNum++);
+ return retVal;
+}
+
+
+XMLReader*
+ReaderMgr::createIntEntReader( const XMLCh* const sysId
+ , const XMLReader::RefFrom refFrom
+ , const XMLReader::Types type
+ , const XMLCh* const dataBuf
+ , const XMLSize_t dataLen
+ , const bool copyBuf
+ , const bool calcSrcOfs
+ , XMLSize_t lowWaterMark)
+{
+ //
+ // This one is easy, we just create an input stream for the data and
+ // provide a few extra goodies.
+ //
+ // NOTE: We use a special encoding string that will be recognized
+ // as a 'do nothing' transcoder for the already internalized XMLCh
+ // data that makes up an internal entity.
+ //
+ BinMemInputStream* newStream = new (fMemoryManager) BinMemInputStream
+ (
+ (const XMLByte*)dataBuf
+ , dataLen * sizeof(XMLCh)
+ , copyBuf ? BinMemInputStream::BufOpt_Copy
+ : BinMemInputStream::BufOpt_Reference
+ , fMemoryManager
+ );
+ if (!newStream)
+ return 0;
+
+ XMLReader* retVal = new (fMemoryManager) XMLReader
+ (
+ sysId
+ , 0
+ , newStream
+ , XMLRecognizer::XERCES_XMLCH
+ , refFrom
+ , type
+ , XMLReader::Source_Internal
+ , false
+ , calcSrcOfs
+ , lowWaterMark
+ , fXMLVersion
+ , fMemoryManager
+ );
+
+ // If it failed for any reason, then return zero.
+ if (!retVal) {
+ delete newStream;
+ return 0;
+ }
+
+ // Set the reader number to the next available number
+ retVal->setReaderNum(fNextReaderNum++);
+ return retVal;
+}
+
+
+const XMLCh* ReaderMgr::getCurrentEncodingStr() const
+{
+ const XMLEntityDecl* theEntity;
+ const XMLReader* theReader = getLastExtEntity(theEntity);
+
+ return theReader->getEncodingStr();
+}
+
+
+const XMLEntityDecl* ReaderMgr::getCurrentEntity() const
+{
+ return fCurEntity;
+}
+
+
+XMLEntityDecl* ReaderMgr::getCurrentEntity()
+{
+ return fCurEntity;
+}
+
+
+XMLSize_t ReaderMgr::getReaderDepth() const
+{
+ // If the stack doesn't exist, its obviously zero
+ if (!fEntityStack)
+ return 0;
+
+ //
+ // The return is the stack size, plus one if there is a current
+ // reader. So if there is no current reader and none on the stack,
+ // its zero, else its some non-zero value.
+ //
+ XMLSize_t retVal = fEntityStack->size();
+ if (fCurReader)
+ retVal++;
+ return retVal;
+}
+
+void ReaderMgr::getLastExtEntityInfo(LastExtEntityInfo& lastInfo) const
+{
+ //
+ // If the reader stack never got created or we've not managed to open any
+ // main entity yet, then we can't give this information.
+ //
+ if (!fReaderStack || !fCurReader)
+ {
+ lastInfo.systemId = XMLUni::fgZeroLenString;
+ lastInfo.publicId = XMLUni::fgZeroLenString;
+ lastInfo.lineNumber = 0;
+ lastInfo.colNumber = 0;
+ return;
+ }
+
+ // We have at least one entity so get the data
+ const XMLEntityDecl* theEntity;
+ const XMLReader* theReader = getLastExtEntity(theEntity);
+
+ // Fill in the info structure with the reader we found
+ lastInfo.systemId = theReader->getSystemId();
+ lastInfo.publicId = theReader->getPublicId();
+ lastInfo.lineNumber = theReader->getLineNumber();
+ lastInfo.colNumber = theReader->getColumnNumber();
+}
+
+
+bool ReaderMgr::isScanningPERefOutOfLiteral() const
+{
+ // If the current reader is not for an entity, then definitely not
+ if (!fCurEntity)
+ return false;
+
+ //
+ // If this is a PE entity, and its not being expanded in a literal
+ // then its true.
+ //
+ if ((fCurReader->getType() == XMLReader::Type_PE)
+ && (fCurReader->getRefFrom() == XMLReader::RefFrom_NonLiteral))
+ {
+ return true;
+ }
+ return false;
+}
+
+
+bool ReaderMgr::pushReader( XMLReader* const reader
+ , XMLEntityDecl* const entity)
+{
+ //
+ // First, if an entity was passed, we have to confirm that this entity
+ // is not already on the entity stack. If so, then this is a recursive
+ // entity expansion, so we issue an error and refuse to put the reader
+ // on the stack.
+ //
+ // If there is no entity passed, then its not an entity being pushed, so
+ // nothing to do. If there is no entity stack yet, then of coures it
+ // cannot already be there.
+ //
+ if (entity && fEntityStack)
+ {
+ const XMLSize_t count = fEntityStack->size();
+ const XMLCh* const theName = entity->getName();
+ for (XMLSize_t index = 0; index < count; index++)
+ {
+ const XMLEntityDecl* curDecl = fEntityStack->elementAt(index);
+ if (curDecl)
+ {
+ if (XMLString::equals(theName, curDecl->getName()))
+ {
+ // Oops, already there so delete reader and return
+ delete reader;
+ return false;
+ }
+ }
+ }
+ }
+
+ //
+ // Fault in the reader stack. Give it an initial capacity of 16, and
+ // tell it it does own its elements.
+ //
+ if (!fReaderStack)
+ fReaderStack = new (fMemoryManager) RefStackOf<XMLReader>(16, true, fMemoryManager);
+
+ // And the entity stack, which does not own its elements
+ if (!fEntityStack)
+ fEntityStack = new (fMemoryManager) RefStackOf<XMLEntityDecl>(16, false, fMemoryManager);
+
+ //
+ // Push the current reader and entity onto their respective stacks.
+ // Note that the the current entity can be null if the current reader
+ // is not for an entity.
+ //
+ if (fCurReader)
+ {
+ fReaderStack->push(fCurReader);
+ fEntityStack->push(fCurEntity);
+ }
+
+ //
+ // Make the passed reader and entity the current top of stack. The
+ // passed entity can (and often is) null.
+ //
+ fCurReader = reader;
+ fCurEntity = entity;
+
+ return true;
+}
+
+
+void ReaderMgr::reset()
+{
+ // Reset all of the flags
+ fThrowEOE = false;
+
+ // Delete the current reader and flush the reader stack
+ delete fCurReader;
+ fCurReader = 0;
+ if (fReaderStack)
+ fReaderStack->removeAllElements();
+
+ //
+ // And do the same for the entity stack, but don't delete the current
+ // entity (if any) since we don't own them.
+ //
+ fCurEntity = 0;
+ if (fEntityStack)
+ fEntityStack->removeAllElements();
+}
+
+
+// ---------------------------------------------------------------------------
+// ReaderMgr: Implement the SAX Locator interface
+// ---------------------------------------------------------------------------
+const XMLCh* ReaderMgr::getPublicId() const
+{
+ if (!fReaderStack && !fCurReader)
+ return XMLUni::fgZeroLenString;
+
+ const XMLEntityDecl* theEntity;
+ return getLastExtEntity(theEntity)->getPublicId();
+}
+
+const XMLCh* ReaderMgr::getSystemId() const
+{
+ if (!fReaderStack && !fCurReader)
+ return XMLUni::fgZeroLenString;
+
+ const XMLEntityDecl* theEntity;
+ return getLastExtEntity(theEntity)->getSystemId();
+}
+
+XMLFileLoc ReaderMgr::getColumnNumber() const
+{
+ if (!fReaderStack && !fCurReader)
+ return 0;
+
+ const XMLEntityDecl* theEntity;
+ return getLastExtEntity(theEntity)->getColumnNumber();
+}
+
+XMLFileLoc ReaderMgr::getLineNumber() const
+{
+ if (!fReaderStack && !fCurReader)
+ return 0;
+
+ const XMLEntityDecl* theEntity;
+ return getLastExtEntity(theEntity)->getLineNumber();
+}
+
+
+
+// ---------------------------------------------------------------------------
+// ReaderMgr: Private helper methods
+// ---------------------------------------------------------------------------
+const XMLReader*
+ReaderMgr::getLastExtEntity(const XMLEntityDecl*& itsEntity) const
+{
+ //
+ // Scan down the reader stack until we find a reader for an entity that
+ // is external. First check that there is anything in the stack at all,
+ // in which case the current reader is the main file and that's the one
+ // that we want.
+ //
+ const XMLReader* theReader = fCurReader;
+
+ //
+ // If there is a current entity and it is not an external entity, then
+ // search the stack; else, keep the reader that we've got since its
+ // either an external entity reader or the main file reader.
+ //
+ const XMLEntityDecl* curEntity = fCurEntity;
+ if (curEntity && !curEntity->isExternal())
+ {
+ XMLSize_t index = fReaderStack->size();
+ if (index)
+ {
+ while (true)
+ {
+ // Move down to the previous element and get a pointer to it
+ index--;
+ curEntity = fEntityStack->elementAt(index);
+
+ //
+ // If its null or its an external entity, then this reader
+ // is what we want, so break out with that one.
+ //
+ if (!curEntity)
+ {
+ theReader = fReaderStack->elementAt(index);
+ break;
+ }
+ else if (curEntity->isExternal())
+ {
+ theReader = fReaderStack->elementAt(index);
+ break;
+ }
+
+ // We hit the end, so leave the main file reader as the one
+ if (!index)
+ break;
+ }
+ }
+ }
+
+ itsEntity = curEntity;
+ return theReader;
+}
+
+
+bool ReaderMgr::popReader()
+{
+ //
+ // We didn't get any more, so try to pop off a reader. If the reader
+ // stack is empty, then we are at the end, so return false.
+ //
+ if (fReaderStack->empty())
+ return false;
+
+ //
+ // Remember the current entity, before we pop off a new one. We might
+ // need this to throw the end of entity exception at the end.
+ //
+ XMLEntityDecl* prevEntity = fCurEntity;
+ const bool prevReaderThrowAtEnd = fCurReader->getThrowAtEnd();
+ const XMLSize_t readerNum = fCurReader->getReaderNum();
+
+ //
+ // Delete the current reader and pop a new reader and entity off
+ // the stacks.
+ //
+ delete fCurReader;
+ fCurReader = fReaderStack->pop();
+ fCurEntity = fEntityStack->pop();
+
+ //
+ // If there was a previous entity, and either the fThrowEOE flag is set
+ // or reader was marked as such, then throw an end of entity.
+ //
+ if (prevEntity && (fThrowEOE || prevReaderThrowAtEnd))
+ throw EndOfEntityException(prevEntity, readerNum);
+
+ while (true)
+ {
+ //
+ // They don't want us to throw, so lets just return with a new
+ // reader. Here we have to do a loop because we might have multiple
+ // readers on these stack that are empty (i.e. the last char in them
+ // was the ';' at the end of the entity ref that caused the next
+ // entity to be pushed.
+ //
+ // So we loop until we find a non-empty reader, or hit the main
+ // file entity. If we find one with some chars available, then break
+ // out and take that one.
+ //
+ if (fCurReader->charsLeftInBuffer())
+ break;
+
+ fCurReader->refreshCharBuffer();
+ if (fCurReader->charsLeftInBuffer())
+ break;
+
+ //
+ // The current one is hosed. So, if the reader stack is empty we
+ // are dead meat and can give up now.
+ //
+ if (fReaderStack->empty())
+ return false;
+
+ // Else pop again and try it one more time
+ delete fCurReader;
+ fCurReader = fReaderStack->pop();
+ fCurEntity = fEntityStack->pop();
+ }
+ return true;
+}
+
+XERCES_CPP_NAMESPACE_END