source: icXML/icXML-devel/src/xercesc/validators/DTD/DTDScanner.cpp @ 2722

Last change on this file since 2722 was 2722, checked in by cameron, 6 years ago

Original Xerces files with import mods for icxercesc

File size: 127.4 KB
Line 
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18/*
19 * $Id: DTDScanner.cpp 833045 2009-11-05 13:21:27Z borisk $
20 */
21
22
23// ---------------------------------------------------------------------------
24//  Includes
25// ---------------------------------------------------------------------------
26#include <xercesc/util/BinMemInputStream.hpp>
27#include <xercesc/util/FlagJanitor.hpp>
28#include <xercesc/util/Janitor.hpp>
29#include <xercesc/util/XMLUniDefs.hpp>
30#include <xercesc/util/ValueStackOf.hpp>
31#include <xercesc/util/UnexpectedEOFException.hpp>
32#include <xercesc/util/OutOfMemoryException.hpp>
33#include <xercesc/sax/InputSource.hpp>
34#include <xercesc/framework/XMLDocumentHandler.hpp>
35#include <xercesc/framework/XMLEntityHandler.hpp>
36#include <icxercesc/framework/XMLValidator.hpp>
37#include <xercesc/internal/EndOfEntityException.hpp>
38#include <icxercesc/internal/XMLScanner.hpp>
39#include <xercesc/validators/common/ContentSpecNode.hpp>
40#include <icxercesc/validators/common/MixedContentModel.hpp>
41#include <xercesc/validators/DTD/DTDEntityDecl.hpp>
42#include <xercesc/validators/DTD/DocTypeHandler.hpp>
43#include <xercesc/validators/DTD/DTDScanner.hpp>
44
45XERCES_CPP_NAMESPACE_BEGIN
46
47// ---------------------------------------------------------------------------
48//  Local methods
49// ---------------------------------------------------------------------------
50//
51//  This method automates the grunt work of looking at a char and see if its
52//  a repetition suffix. If so, it creates a new correct rep node and wraps
53//  the pass node in it. Otherwise, it returns the previous node.
54//
55static ContentSpecNode* makeRepNode(const XMLCh testCh,
56                                    ContentSpecNode* const prevNode,
57                                    MemoryManager* const manager)
58{
59    if (testCh == chQuestion)
60    {
61        return new (manager) ContentSpecNode
62        (
63            ContentSpecNode::ZeroOrOne
64            , prevNode
65            , 0
66            , true
67            , true
68            , manager
69        );
70    }
71     else if (testCh == chPlus)
72    {
73        return new (manager) ContentSpecNode
74        (
75            ContentSpecNode::OneOrMore
76            , prevNode
77            , 0
78            , true
79            , true
80            , manager
81        );
82    }
83     else if (testCh == chAsterisk)
84    {
85        return new (manager) ContentSpecNode
86        (
87            ContentSpecNode::ZeroOrMore
88            , prevNode
89            , 0
90            , true
91            , true
92            , manager
93        );
94    }
95
96    // Just return the incoming node
97    return prevNode;
98}
99
100// ---------------------------------------------------------------------------
101//  DTDValidator: Constructors and Destructor
102// ---------------------------------------------------------------------------
103DTDScanner::DTDScanner( DTDGrammar*           dtdGrammar
104                      , DocTypeHandler* const docTypeHandler
105                      , MemoryManager* const  grammarPoolMemoryManager
106                      , MemoryManager* const  manager) :
107    fMemoryManager(manager)
108    , fGrammarPoolMemoryManager(grammarPoolMemoryManager)
109    , fDocTypeHandler(docTypeHandler)
110    , fDumAttDef(0)
111    , fDumElemDecl(0)
112    , fDumEntityDecl(0)
113    , fInternalSubset(false)
114    , fNextAttrId(1)
115    , fDTDGrammar(dtdGrammar)
116    , fBufMgr(0)
117    , fReaderMgr(0)
118    , fScanner(0)
119    , fPEntityDeclPool(0)
120    , fEmptyNamespaceId(0)
121    , fDocTypeReaderId(0)
122{
123    fPEntityDeclPool = new (fMemoryManager) NameIdPool<DTDEntityDecl>(109, 128, fMemoryManager);
124}
125
126DTDScanner::~DTDScanner()
127{
128    delete fDumAttDef;
129    delete fDumElemDecl;
130    delete fDumEntityDecl;
131    delete fPEntityDeclPool;
132}
133
134// -----------------------------------------------------------------------
135//  Setter methods
136// -----------------------------------------------------------------------
137void DTDScanner::setScannerInfo(XMLScanner* const      owningScanner
138                            , ReaderMgr* const      readerMgr
139                            , XMLBufferMgr* const   bufMgr)
140{
141    // We don't own any of these, we just reference them
142    fScanner = owningScanner;
143    fReaderMgr = readerMgr;
144    fBufMgr = bufMgr;
145
146    if (fScanner->getDoNamespaces())
147        fEmptyNamespaceId = fScanner->getEmptyNamespaceId();
148    else
149        fEmptyNamespaceId = 0;
150
151    fDocTypeReaderId = fReaderMgr->getCurrentReaderNum();
152}
153
154
155// ---------------------------------------------------------------------------
156//  DTDScanner: Private scanning methods
157// ---------------------------------------------------------------------------
158bool DTDScanner::checkForPERef(   const bool    inLiteral
159                                , const bool    inMarkup)
160{
161    bool gotSpace = false;
162
163    //
164    //  See if we have any spaces up front. If so, then skip them and set
165    //  the gotSpaces flag.
166    //
167    if (fReaderMgr->skippedSpace())
168    {
169        fReaderMgr->skipPastSpaces();
170        gotSpace = true;
171    }
172
173    // If the next char is a percent, then expand the PERef
174    if (!fReaderMgr->skippedChar(chPercent))
175       return gotSpace;
176
177    while (true)
178    {
179       if (!expandPERef(false, inLiteral, inMarkup, false))
180          fScanner->emitError(XMLErrs::ExpectedEntityRefName);
181       // And skip any more spaces in the expanded value
182       if (fReaderMgr->skippedSpace())
183       {
184          fReaderMgr->skipPastSpaces();
185          gotSpace = true;
186       }
187       if (!fReaderMgr->skippedChar(chPercent))
188          break;
189    }
190    return gotSpace;
191}
192
193
194bool DTDScanner::expandPERef( const   bool    scanExternal
195                                , const bool    inLiteral
196                                , const bool    inMarkup
197                                , const bool    throwEndOfExt)
198{
199    fScanner->setHasNoDTD(false);
200    XMLBufBid bbName(fBufMgr);
201
202    //
203    //  If we are in the internal subset and in markup, then this is
204    //  an error but we go ahead and do it anyway.
205    //
206    if (fInternalSubset && inMarkup)
207        fScanner->emitError(XMLErrs::PERefInMarkupInIntSubset);
208
209    if (!fReaderMgr->getName(bbName.getBuffer()))
210    {
211        fScanner->emitError(XMLErrs::ExpectedPEName);
212
213        // Skip the semicolon if that's what we ended up on
214        fReaderMgr->skippedChar(chSemiColon);
215        return false;
216    }
217
218    // If no terminating semicolon, emit an error but try to keep going
219    if (!fReaderMgr->skippedChar(chSemiColon))
220        fScanner->emitError(XMLErrs::UnterminatedEntityRef, bbName.getRawBuffer());
221
222    //
223    //  Look it up in the PE decl pool and see if it exists. If not, just
224    //  emit an error and continue.
225    //
226    XMLEntityDecl* decl = fPEntityDeclPool->getByKey(bbName.getRawBuffer());
227    if (!decl)
228    {
229        // XML 1.0 Section 4.1
230        if (fScanner->getStandalone()) {
231            // no need to check fScanner->fHasNoDTD which is for sure false
232            // since we are in expandPERef already
233            fScanner->emitError(XMLErrs::EntityNotFound, bbName.getRawBuffer());
234        }
235        else {
236            if (fScanner->getValidationScheme() == XMLScanner::Val_Always)
237                fScanner->getValidator()->emitError(XMLValid::VC_EntityNotFound, bbName.getRawBuffer());
238        }
239
240        return false;
241    }
242
243    //
244    // XML 1.0 Section 2.9
245    //  If we are a standalone document, then it has to have been declared
246    //  in the internal subset. Keep going though.
247    //
248    if (fScanner->getValidationScheme() == XMLScanner::Val_Always && fScanner->getStandalone() && !decl->getDeclaredInIntSubset())
249        fScanner->getValidator()->emitError(XMLValid::VC_IllegalRefInStandalone, bbName.getRawBuffer());
250
251    //
252    //  Okee dokee, we found it. So create either a memory stream with
253    //  the entity value contents, or a file stream if its an external
254    //  entity.
255    //
256    if (decl->isExternal())
257    {
258        // And now create a reader to read this entity
259        InputSource* srcUsed;
260        XMLReader* reader = fReaderMgr->createReader
261        (
262            decl->getBaseURI()
263            , decl->getSystemId()
264            , decl->getPublicId()
265            , false
266            , inLiteral ? XMLReader::RefFrom_Literal : XMLReader::RefFrom_NonLiteral
267            , XMLReader::Type_PE
268            , XMLReader::Source_External
269            , srcUsed
270            , fScanner->getCalculateSrcOfs()
271            , fScanner->getLowWaterMark()
272            , fScanner->getDisableDefaultEntityResolution()
273        );
274
275        // Put a janitor on the source so its cleaned up on exit
276        Janitor<InputSource> janSrc(srcUsed);
277
278        // If the creation failed then throw an exception
279        if (!reader)
280            ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::Gen_CouldNotOpenExtEntity, srcUsed ? srcUsed->getSystemId() : decl->getSystemId(), fMemoryManager);
281
282        // Set the 'throw at end' flag, to the one we were given
283        reader->setThrowAtEnd(throwEndOfExt);
284
285        //
286        //  Push the reader. If its a recursive expansion, then emit an error
287        //  and return an failure.
288        //
289        if (!fReaderMgr->pushReader(reader, decl))
290        {
291            fScanner->emitError(XMLErrs::RecursiveEntity, decl->getName());
292            return false;
293        }
294
295        //
296        //  If the caller wants us to scan the external entity, then lets
297        //  do that now.
298        //
299        if (scanExternal)
300        {
301            XMLEntityHandler* entHandler = fScanner->getEntityHandler();
302
303            // If we have an entity handler, tell it we are starting this entity
304            if (entHandler)
305                entHandler->startInputSource(*srcUsed);
306
307            //
308            //  Scan the external entity now. The parameter tells it that
309            //  it is not in an include section. Get the current reader
310            //  level so we can catch partial markup errors and be sure
311            //  to get back to here if we get an exception out of the
312            //  ext subset scan.
313            //
314            const XMLSize_t readerNum = fReaderMgr->getCurrentReaderNum();
315            try
316            {
317                scanExtSubsetDecl(false, false);
318            }
319            catch(const OutOfMemoryException&)
320            {
321                throw;
322            }
323            catch(...)
324            {
325                // Pop the reader back to the original level
326                fReaderMgr->cleanStackBackTo(readerNum);
327
328                // End the input source, even though its not happy
329                if (entHandler)
330                    entHandler->endInputSource(*srcUsed);
331                throw;
332            }
333
334            // If we have an entity handler, tell it we are ending this entity
335            if (entHandler)
336                entHandler->endInputSource(*srcUsed);
337        }
338        else {
339            // If it starts with the XML string, then parse a text decl
340            if (fScanner->checkXMLDecl(true))
341                scanTextDecl();
342        }
343    }
344     else
345    {
346        // Create a reader over a memory stream over the entity value
347        XMLReader* valueReader = fReaderMgr->createIntEntReader
348        (
349            decl->getName()
350            , inLiteral ? XMLReader::RefFrom_Literal : XMLReader::RefFrom_NonLiteral
351            , XMLReader::Type_PE
352            , decl->getValue()
353            , decl->getValueLen()
354            , false
355        );
356
357        //
358        //  Trt to push the entity reader onto the reader manager stack,
359        //  where it will become the subsequent input. If it fails, that
360        //  means the entity is recursive, so issue an error. The reader
361        //  will have just been discarded, but we just keep going.
362        //
363        if (!fReaderMgr->pushReader(valueReader, decl))
364            fScanner->emitError(XMLErrs::RecursiveEntity, decl->getName());
365    }
366
367    return true;
368}
369
370
371bool DTDScanner::getQuotedString(XMLBuffer& toFill)
372{
373    // Reset the target buffer
374    toFill.reset();
375
376    // Get the next char which must be a single or double quote
377    XMLCh quoteCh;
378    if (!fReaderMgr->skipIfQuote(quoteCh))
379        return false;
380
381        XMLCh nextCh;
382    // Get another char and see if it matches the starting quote char
383    while ((nextCh=fReaderMgr->getNextChar())!=quoteCh)
384    {
385        //
386        //  We should never get either an end of file null char here. If we
387        //  do, just fail. It will be handled more gracefully in the higher
388        //  level code that called us.
389        //
390        if (!nextCh)
391            return false;
392
393        // Else add it to the buffer
394        toFill.append(nextCh);
395    }
396    return true;
397}
398
399
400XMLAttDef*
401DTDScanner::scanAttDef(DTDElementDecl& parentElem, XMLBuffer& bufToUse)
402{
403    // Check for PE ref or optional whitespace
404    checkForPERef(false, true);
405
406    // Get the name of the attribute
407    if (!fReaderMgr->getName(bufToUse))
408    {
409        fScanner->emitError(XMLErrs::ExpectedAttrName);
410        return 0;
411    }
412
413    //
414    //  Look up this attribute in the parent element's attribute list. If
415    //  it already exists, then use the dummy.
416    //
417    DTDAttDef* decl = parentElem.getAttDef(bufToUse.getRawBuffer());
418    if (decl)
419    {
420        // It already exists, so put out a warning
421        fScanner->emitError
422        (
423            XMLErrs::AttListAlreadyExists
424            , bufToUse.getRawBuffer()
425            , parentElem.getFullName()
426        );
427
428        // Use the dummy decl to parse into and set its name to the name we got
429        if (!fDumAttDef)
430        {
431            fDumAttDef = new (fMemoryManager) DTDAttDef(fMemoryManager);
432            fDumAttDef->setId(fNextAttrId++);
433        }
434        fDumAttDef->setName(bufToUse.getRawBuffer());
435        decl = fDumAttDef;
436    }
437     else
438    {
439        //
440        //  It does not already exist so create a new one, give it the next
441        //  available unique id, and add it
442        //
443        decl = new (fGrammarPoolMemoryManager) DTDAttDef
444        (
445            bufToUse.getRawBuffer()
446            , XMLAttDef::CData
447            , XMLAttDef::Implied
448            , fGrammarPoolMemoryManager
449        );
450        decl->setId(fNextAttrId++);
451        decl->setExternalAttDeclaration(isReadingExternalEntity());
452        parentElem.addAttDef(decl);
453    }
454
455    // Set a flag to indicate whether we are doing a dummy parse
456    const bool isIgnored = (decl == fDumAttDef);
457
458    // Space is required here, so check for PE ref, and require space
459    if (!checkForPERef(false, true))
460        fScanner->emitError(XMLErrs::ExpectedWhitespace);
461
462    //
463    //  Next has to be one of the attribute type strings. This tells us what
464    //  is to follow.
465    //
466    if (fReaderMgr->skippedString(XMLUni::fgCDATAString))
467    {
468        decl->setType(XMLAttDef::CData);
469    }
470     else if (fReaderMgr->skippedString(XMLUni::fgIDString))
471    {
472        if (!fReaderMgr->skippedString(XMLUni::fgRefString))
473            decl->setType(XMLAttDef::ID);
474        else if (!fReaderMgr->skippedChar(chLatin_S))
475            decl->setType(XMLAttDef::IDRef);
476        else
477            decl->setType(XMLAttDef::IDRefs);
478    }
479     else if (fReaderMgr->skippedString(XMLUni::fgEntitString))
480    {
481        if (fReaderMgr->skippedChar(chLatin_Y))
482        {
483            decl->setType(XMLAttDef::Entity);
484        }
485         else if (fReaderMgr->skippedString(XMLUni::fgIESString))
486        {
487            decl->setType(XMLAttDef::Entities);
488        }
489         else
490        {
491            fScanner->emitError
492            (
493                XMLErrs::ExpectedAttributeType
494                , decl->getFullName()
495                , parentElem.getFullName()
496            );
497            return 0;
498        }
499    }
500     else if (fReaderMgr->skippedString(XMLUni::fgNmTokenString))
501    {
502        if (fReaderMgr->skippedChar(chLatin_S))
503            decl->setType(XMLAttDef::NmTokens);
504        else
505            decl->setType(XMLAttDef::NmToken);
506    }
507     else if (fReaderMgr->skippedString(XMLUni::fgNotationString))
508    {
509        // Check for PE ref and require space
510        if (!checkForPERef(false, true))
511            fScanner->emitError(XMLErrs::ExpectedWhitespace);
512
513        decl->setType(XMLAttDef::Notation);
514        if (!scanEnumeration(*decl, bufToUse, true))
515            return 0;
516
517        // Set the value as the enumeration for this decl
518        decl->setEnumeration(bufToUse.getRawBuffer());
519    }
520     else if (fReaderMgr->skippedChar(chOpenParen))
521    {
522        decl->setType(XMLAttDef::Enumeration);
523        if (!scanEnumeration(*decl, bufToUse, false))
524            return 0;
525
526        // Set the value as the enumeration for this decl
527        decl->setEnumeration(bufToUse.getRawBuffer());
528    }
529     else
530    {
531        fScanner->emitError
532        (
533            XMLErrs::ExpectedAttributeType
534            , decl->getFullName()
535            , parentElem.getFullName()
536        );
537        return 0;
538    }
539
540    // Space is required here, so check for PE ref, and require space
541    if (!checkForPERef(false, true))
542        fScanner->emitError(XMLErrs::ExpectedWhitespace);
543
544    // And then scan for the optional default value declaration
545    scanDefaultDecl(*decl);
546
547    // If validating, then do a couple of validation constraints
548    if (fScanner->getValidationScheme() == XMLScanner::Val_Always)
549    {
550        if (decl->getType() == XMLAttDef::ID)
551        {
552            if ((decl->getDefaultType() != XMLAttDef::Implied)
553            &&  (decl->getDefaultType() != XMLAttDef::Required))
554            {
555                fScanner->getValidator()->emitError(XMLValid::BadIDAttrDefType, decl->getFullName());
556            }
557        }
558
559        // if attdef is xml:space, check correct enumeration (default|preserve)
560        const XMLCh fgXMLSpace[] = { chLatin_x, chLatin_m, chLatin_l, chColon, chLatin_s, chLatin_p, chLatin_a, chLatin_c, chLatin_e, chNull };
561
562        if (XMLString::equals(decl->getFullName(),fgXMLSpace)) {
563            const XMLCh fgPreserve[] = { chLatin_p, chLatin_r, chLatin_e, chLatin_s, chLatin_e, chLatin_r, chLatin_v, chLatin_e, chNull };
564            const XMLCh fgDefault[] = { chLatin_d, chLatin_e, chLatin_f, chLatin_a, chLatin_u, chLatin_l, chLatin_t, chNull };
565            bool ok = false;
566            if (decl->getType() == XMLAttDef::Enumeration) {
567                BaseRefVectorOf<XMLCh>* enumVector = XMLString::tokenizeString(decl->getEnumeration(), fMemoryManager);
568                XMLSize_t size = enumVector->size();
569                ok = (size == 1 &&
570                     (XMLString::equals(enumVector->elementAt(0), fgDefault) ||
571                      XMLString::equals(enumVector->elementAt(0), fgPreserve))) ||
572                     (size == 2 &&
573                     (XMLString::equals(enumVector->elementAt(0), fgDefault) &&
574                      XMLString::equals(enumVector->elementAt(1), fgPreserve))) ||
575                     (size == 2 &&
576                     (XMLString::equals(enumVector->elementAt(1), fgDefault) &&
577                      XMLString::equals(enumVector->elementAt(0), fgPreserve)));
578                delete enumVector;
579            }
580            if (!ok)
581                fScanner->getValidator()->emitError(XMLValid::IllegalXMLSpace);
582        }
583    }
584
585    // If we have a doc type handler, tell it about this attdef.
586    if (fDocTypeHandler)
587        fDocTypeHandler->attDef(parentElem, *decl, isIgnored);
588    return decl;
589}
590
591
592void DTDScanner::scanAttListDecl()
593{
594    // Space is required here, so check for a PE ref
595    if (!checkForPERef(false, true))
596    {
597        fScanner->emitError(XMLErrs::ExpectedWhitespace);
598        fReaderMgr->skipPastChar(chCloseAngle);
599        return;
600    }
601
602    //
603    //  Next should be the name of the element it belongs to, so get a buffer
604    //  and get the name into it.
605    //
606    XMLBufBid bbName(fBufMgr);
607    if (!fReaderMgr->getName(bbName.getBuffer()))
608    {
609        fScanner->emitError(XMLErrs::ExpectedElementName);
610        fReaderMgr->skipPastChar(chCloseAngle);
611        return;
612    }
613
614    //
615    //  Find this element's declaration. If it has not been declared yet,
616    //  we will force one into the list, but not mark it as declared.
617    //
618    DTDElementDecl* elemDecl = (DTDElementDecl*) fDTDGrammar->getElemDecl(fEmptyNamespaceId, 0, bbName.getRawBuffer(), Grammar::TOP_LEVEL_SCOPE);
619    if (!elemDecl)
620    {
621        //
622        //  Lets fault in a declaration and add it to the pool. We mark
623        //  it having been created because of an attlist. Later, if its
624        //  declared, this will be updated.
625        //
626        elemDecl = new (fGrammarPoolMemoryManager) DTDElementDecl
627        (
628            bbName.getRawBuffer()
629            , fEmptyNamespaceId
630            , DTDElementDecl::Any
631            , fGrammarPoolMemoryManager
632        );
633        elemDecl->setCreateReason(XMLElementDecl::AttList);
634        elemDecl->setExternalElemDeclaration(isReadingExternalEntity());
635        fDTDGrammar->putElemDecl((XMLElementDecl*) elemDecl);
636    }
637
638    // If we have a doc type handler, tell it the att list is starting
639    if (fDocTypeHandler)
640        fDocTypeHandler->startAttList(*elemDecl);
641
642    //
643    //  Now we loop until we are done with all of the attributes in this
644    //  list. We need a buffer to use for local processing.
645    //
646    XMLBufBid   bbTmp(fBufMgr);
647    XMLBuffer&  tmpBuf = bbTmp.getBuffer();
648    bool        seenAnId = false;
649    while (true)
650    {
651        // Get the next char out and see what it tells us to do
652        const XMLCh nextCh = fReaderMgr->peekNextChar();
653
654        // Watch for EOF
655        if (!nextCh)
656            ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
657
658        if (nextCh == chCloseAngle)
659        {
660            // We are done with this attribute list
661            fReaderMgr->getNextChar();
662            break;
663        }
664         else if (fReaderMgr->getCurrentReader()->isWhitespace(nextCh))
665        {
666            //
667            //  If advanced callbacks are enabled and we have a doc
668            //  type handler, then gather up the white space and call
669            //  back on the doctype handler. Otherwise, just skip
670            //  whitespace.
671            //
672            if (fDocTypeHandler)
673            {
674                fReaderMgr->getSpaces(tmpBuf);
675                fDocTypeHandler->doctypeWhitespace
676                (
677                    tmpBuf.getRawBuffer()
678                    , tmpBuf.getLen()
679                );
680            }
681             else
682            {
683                fReaderMgr->skipPastSpaces();
684            }
685        }
686         else if (nextCh == chPercent)
687        {
688            // Eat the percent and expand the ref
689            fReaderMgr->getNextChar();
690            expandPERef(false, false, true);
691        }
692         else
693        {
694            //
695            //  It must be an attribute name, so scan it. We let
696            //  it use our local buffer for its name scanning.
697            //
698            XMLAttDef* attDef = scanAttDef(*elemDecl, tmpBuf);
699
700            if (!attDef)
701            {
702                fReaderMgr->skipPastChar(chCloseAngle);
703                break;
704            }
705
706            //
707            //  If we are validating and its an ID type, then we have to
708            //  make sure that we have not seen an id attribute yet. Set
709            //  the flag to say that we've seen one now also.
710            //
711            if (fScanner->getValidationScheme() == XMLScanner::Val_Always)
712            {
713                if (attDef->getType() == XMLAttDef::ID)
714                {
715                    if (seenAnId)
716                        fScanner->getValidator()->emitError(XMLValid::MultipleIdAttrs, elemDecl->getFullName());
717                    seenAnId = true;
718                }
719            }
720        }
721    }
722
723    // If we have a doc type handler, tell it the att list is ending
724    if (fDocTypeHandler)
725        fDocTypeHandler->endAttList(*elemDecl);
726}
727
728
729//
730//  This method is called to scan the value of an attribute in content. This
731//  involves some normalization and replacement of general entity and
732//  character references.
733//
734//  End of entity's must be dealt with here. During DTD scan, they can come
735//  from external entities. During content, they can come from any entity.
736//  We just eat the end of entity and continue with our scan until we come
737//  to the closing quote. If an unterminated value causes us to go through
738//  subsequent entities, that will cause errors back in the calling code,
739//  but there's little we can do about it here.
740//
741bool DTDScanner::scanAttValue(const   XMLCh* const        attrName
742                                ,       XMLBuffer&          toFill
743                                , const XMLAttDef::AttTypes type)
744{
745    enum States
746    {
747        InWhitespace
748        , InContent
749    };
750
751    // Reset the target buffer
752    toFill.reset();
753
754    // Get the next char which must be a single or double quote
755    XMLCh quoteCh;
756    if (!fReaderMgr->skipIfQuote(quoteCh))
757        return false;
758
759    //
760    //  We have to get the current reader because we have to ignore closing
761    //  quotes until we hit the same reader again.
762    //
763    const XMLSize_t curReader = fReaderMgr->getCurrentReaderNum();
764
765    //
766    //  Loop until we get the attribute value. Note that we use a double
767    //  loop here to avoid the setup/teardown overhead of the exception
768    //  handler on every round.
769    //
770    XMLCh   nextCh;
771    XMLCh   secondCh = 0;
772    States  curState = InContent;
773    bool    firstNonWS = false;
774    bool    gotLeadingSurrogate = false;
775    bool    escaped;
776    while (true)
777    {
778    try
779    {
780        while(true)
781        {
782            nextCh = fReaderMgr->getNextChar();
783
784            if (!nextCh)
785                ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
786
787            // Check for our ending quote in the same entity
788            if (nextCh == quoteCh)
789            {
790                if (curReader == fReaderMgr->getCurrentReaderNum())
791                    return true;
792
793                // Watch for spillover into a previous entity
794                if (curReader > fReaderMgr->getCurrentReaderNum())
795                {
796                    fScanner->emitError(XMLErrs::PartialMarkupInEntity);
797                    return false;
798                }
799            }
800
801            //
802            //  Check for an entity ref now, before we let it affect our
803            //  whitespace normalization logic below. We ignore the empty flag
804            //  in this one.
805            //
806            escaped = false;
807            if (nextCh == chAmpersand)
808            {
809                if (scanEntityRef(nextCh, secondCh, escaped) != EntityExp_Returned)
810                {
811                    gotLeadingSurrogate = false;
812                    continue;
813                }
814            }
815            else if ((nextCh >= 0xD800) && (nextCh <= 0xDBFF))
816            {
817                // Check for correct surrogate pairs
818                if (gotLeadingSurrogate)
819                    fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
820                else
821                    gotLeadingSurrogate = true;
822            }
823             else
824            {
825                if (gotLeadingSurrogate)
826                {
827                    if ((nextCh < 0xDC00) || (nextCh > 0xDFFF))
828                        fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
829                }
830                // Its got to at least be a valid XML character
831                else if (!fReaderMgr->getCurrentReader()->isXMLChar(nextCh))
832                {
833                    XMLCh tmpBuf[9];
834                    XMLString::binToText
835                    (
836                        nextCh
837                        , tmpBuf
838                        , 8
839                        , 16
840                        , fMemoryManager
841                    );
842                    fScanner->emitError
843                    (
844                        XMLErrs::InvalidCharacterInAttrValue
845                        , attrName
846                        , tmpBuf
847                    );
848                }
849
850                gotLeadingSurrogate = false;
851            }
852
853            //
854            //  If its not escaped, then make sure its not a < character, which
855            //  is not allowed in attribute values.
856            //
857            if (!escaped && (nextCh == chOpenAngle))
858                fScanner->emitError(XMLErrs::BracketInAttrValue, attrName);
859
860            //
861            //  If the attribute is a CDATA type we do simple replacement of
862            //  tabs and new lines with spaces, if the character is not escaped
863            //  by way of a char ref.
864            //
865            //  Otherwise, we do the standard non-CDATA normalization of
866            //  compressing whitespace to single spaces and getting rid of
867            //  leading and trailing whitespace.
868            //
869            if (type == XMLAttDef::CData)
870            {
871                if (!escaped)
872                {
873                    if ((nextCh == 0x09) || (nextCh == 0x0A) || (nextCh == 0x0D))
874                        nextCh = chSpace;
875                }
876            }
877             else
878            {
879                if (curState == InWhitespace)
880                {
881                    if (!fReaderMgr->getCurrentReader()->isWhitespace(nextCh))
882                    {
883                        if (firstNonWS)
884                            toFill.append(chSpace);
885                        curState = InContent;
886                        firstNonWS = true;
887                    }
888                     else
889                    {
890                        continue;
891                    }
892                }
893                 else if (curState == InContent)
894                {
895                    if (fReaderMgr->getCurrentReader()->isWhitespace(nextCh))
896                    {
897                        curState = InWhitespace;
898                        continue;
899                    }
900                    firstNonWS = true;
901                }
902            }
903
904            // Else add it to the buffer
905            toFill.append(nextCh);
906
907            if (secondCh)
908            {
909                toFill.append(secondCh);
910                secondCh=0;
911            }
912        }
913    }
914
915    catch(const EndOfEntityException&)
916    {
917        // Just eat it and continue.
918        gotLeadingSurrogate = false;
919        escaped = false;
920    }
921    }
922    return true;
923}
924
925
926bool DTDScanner::scanCharRef(XMLCh& first, XMLCh& second)
927{
928    bool gotOne = false;
929    unsigned int value = 0;
930
931    //
932    //  Set the radix. Its supposed to be a lower case x if hex. But, in
933    //  order to recover well, we check for an upper and put out an error
934    //  for that.
935    //
936    unsigned int radix = 10;
937
938    if (fReaderMgr->skippedChar(chLatin_x))
939    {
940        radix = 16;
941    }
942     else if (fReaderMgr->skippedChar(chLatin_X))
943    {
944        fScanner->emitError(XMLErrs::HexRadixMustBeLowerCase);
945        radix = 16;
946    }
947
948    while (true)
949    {
950        const XMLCh nextCh = fReaderMgr->peekNextChar();
951
952        // Watch for EOF
953        if (!nextCh)
954            ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
955
956        // Break out on the terminating semicolon
957        if (nextCh == chSemiColon)
958        {
959            fReaderMgr->getNextChar();
960            break;
961        }
962
963        //
964        //  Convert this char to a binary value, or bail out if its not
965        //  one.
966        //
967        unsigned int nextVal;
968        if ((nextCh >= chDigit_0) && (nextCh <= chDigit_9))
969            nextVal = (unsigned int)(nextCh - chDigit_0);
970        else if ((nextCh >= chLatin_A) && (nextCh <= chLatin_F))
971            nextVal= (unsigned int)(10 + (nextCh - chLatin_A));
972        else if ((nextCh >= chLatin_a) && (nextCh <= chLatin_f))
973            nextVal = (unsigned int)(10 + (nextCh - chLatin_a));
974        else
975        {
976            //
977            //  If we got at least a sigit, then do an unterminated ref
978            //  error. Else, do an expected a numerical ref thing.
979            //
980            if (gotOne)
981                fScanner->emitError(XMLErrs::UnterminatedCharRef);
982            else
983                fScanner->emitError(XMLErrs::ExpectedNumericalCharRef);
984
985            return false;
986        }
987
988        //
989        //  Make sure its valid for the radix. If not, then just eat the
990        //  digit and go on after issueing an error. Else, update the
991        //  running value with this new digit.
992        //
993        if (nextVal >= radix)
994        {
995            XMLCh tmpStr[2];
996            tmpStr[0] = nextCh;
997            tmpStr[1] = chNull;
998            fScanner->emitError(XMLErrs::BadDigitForRadix, tmpStr);
999        }
1000         else
1001        {
1002            value = (value * radix) + nextVal;
1003        }
1004
1005        // Indicate that we got at least one good digit
1006        gotOne = true;
1007
1008        // Eat the char we just processed
1009        fReaderMgr->getNextChar();
1010    }
1011
1012    // Return the char (or chars)
1013    // And check if the character expanded is valid or not
1014    if (value >= 0x10000 && value <= 0x10FFFF)
1015    {
1016        value -= 0x10000;
1017        first  = XMLCh((value >> 10) + 0xD800);
1018        second = XMLCh((value & 0x3FF) + 0xDC00);
1019    }
1020    else if (value <= 0xFFFD)
1021    {
1022        first  = XMLCh(value);
1023        second = 0;
1024        if (!fReaderMgr->getCurrentReader()->isXMLChar(first) && !fReaderMgr->getCurrentReader()->isControlChar(first)) {
1025            // Character reference was not in the valid range
1026            fScanner->emitError(XMLErrs::InvalidCharacterRef);
1027            return false;
1028        }
1029    }
1030    else {
1031        // Character reference was not in the valid range
1032        fScanner->emitError(XMLErrs::InvalidCharacterRef);
1033        return false;
1034    }
1035
1036    return true;
1037}
1038
1039
1040ContentSpecNode*
1041DTDScanner::scanChildren(const DTDElementDecl& elemDecl, XMLBuffer& bufToUse)
1042{
1043    // Check for a PE ref here, but don't require spaces
1044    checkForPERef(false, true);
1045
1046    ValueStackOf<XMLSize_t>* arrNestedDecl=NULL;
1047    //
1048    //  We know that the caller just saw an opening parenthesis, so we need
1049    //  to parse until we hit the end of it; if we find several parenthesis,
1050    //  store them in an array to be processed later.
1051    //
1052    //  We have to check for one up front, since it could be something like
1053    //  (((a)*)) etc...
1054    //
1055    ContentSpecNode* curNode = 0;
1056    while(fReaderMgr->skippedChar(chOpenParen))
1057    {
1058        // to check entity nesting
1059        const XMLSize_t curReader = fReaderMgr->getCurrentReaderNum();
1060        if(arrNestedDecl==NULL)
1061            arrNestedDecl=new (fMemoryManager) ValueStackOf<XMLSize_t>(5, fMemoryManager);
1062        arrNestedDecl->push(curReader);
1063
1064        // Check for a PE ref here, but don't require spaces
1065        checkForPERef(false, true);
1066    }
1067
1068    // We must find a leaf node here, either standalone or nested in the parenthesis
1069    if (!fReaderMgr->getName(bufToUse))
1070    {
1071        fScanner->emitError(XMLErrs::ExpectedElementName);
1072        return 0;
1073    }
1074
1075    //
1076    //  Create a leaf node for it. If we can find the element id for
1077    //  this element, then use it. Else, we have to fault in an element
1078    //  decl, marked as created because of being in a content model.
1079    //
1080    XMLElementDecl* decl = fDTDGrammar->getElemDecl(fEmptyNamespaceId, 0, bufToUse.getRawBuffer(), Grammar::TOP_LEVEL_SCOPE);
1081    if (!decl)
1082    {
1083        decl = new (fGrammarPoolMemoryManager) DTDElementDecl
1084        (
1085            bufToUse.getRawBuffer()
1086            , fEmptyNamespaceId
1087            , DTDElementDecl::Any
1088            , fGrammarPoolMemoryManager
1089        );
1090        decl->setCreateReason(XMLElementDecl::InContentModel);
1091        decl->setExternalElemDeclaration(isReadingExternalEntity());
1092        fDTDGrammar->putElemDecl(decl);
1093    }
1094    curNode = new (fGrammarPoolMemoryManager) ContentSpecNode
1095    (
1096        decl->getElementName()
1097        , fGrammarPoolMemoryManager
1098    );
1099
1100    // Check for a PE ref here, but don't require spaces
1101    const bool gotSpaces = checkForPERef(false, true);
1102
1103    // Check for a repetition character after the leaf
1104    XMLCh repCh = fReaderMgr->peekNextChar();
1105    ContentSpecNode* tmpNode = makeRepNode(repCh, curNode, fGrammarPoolMemoryManager);
1106    if (tmpNode != curNode)
1107    {
1108        if (gotSpaces)
1109        {
1110            if (fScanner->emitErrorWillThrowException(XMLErrs::UnexpectedWhitespace))
1111            {
1112                delete tmpNode;
1113            }
1114            fScanner->emitError(XMLErrs::UnexpectedWhitespace);
1115        }
1116        fReaderMgr->getNextChar();
1117        curNode = tmpNode;
1118    }
1119
1120    while(arrNestedDecl==NULL || !arrNestedDecl->empty())
1121    {
1122        // Check for a PE ref here, but don't require spaces
1123        checkForPERef(false, true);
1124
1125        //
1126        //  Ok, the next character tells us what kind of content this particular
1127        //  model this particular parentesized section is. Its either a choice if
1128        //  we see ',', a sequence if we see '|', or a single leaf node if we see
1129        //  a closing paren.
1130        //
1131        const XMLCh opCh = fReaderMgr->peekNextChar();
1132
1133        if ((opCh != chComma)
1134        &&  (opCh != chPipe)
1135        &&  (opCh != chCloseParen))
1136        {
1137            // Not a legal char, so delete our node and return failure
1138            delete curNode;
1139            fScanner->emitError(XMLErrs::ExpectedSeqChoiceLeaf);
1140            return 0;
1141        }
1142
1143        //
1144        //  Create the head node of the correct type. We need this to remember
1145        //  the top of the local tree. If it was a single subexpr, then just
1146        //  set the head node to the current node. For the others, we'll build
1147        //  the tree off the second child as we move across.
1148        //
1149        ContentSpecNode* headNode = 0;
1150        ContentSpecNode::NodeTypes curType = ContentSpecNode::UnknownType;
1151        if (opCh == chComma)
1152        {
1153            curType = ContentSpecNode::Sequence;
1154            headNode = new (fGrammarPoolMemoryManager) ContentSpecNode
1155            (
1156                curType
1157                , curNode
1158                , 0
1159                , true
1160                , true
1161                , fGrammarPoolMemoryManager
1162            );
1163            curNode = headNode;
1164        }
1165         else if (opCh == chPipe)
1166        {
1167            curType = ContentSpecNode::Choice;
1168            headNode = new (fGrammarPoolMemoryManager) ContentSpecNode
1169            (
1170                curType
1171                , curNode
1172                , 0
1173                , true
1174                , true
1175                , fGrammarPoolMemoryManager
1176            );
1177            curNode = headNode;
1178        }
1179         else
1180        {
1181            headNode = curNode;
1182            fReaderMgr->getNextChar();
1183        }
1184
1185        //
1186        //  If it was a sequence or choice, we just loop until we get to the
1187        //  end of our section, adding each new leaf or sub expression to the
1188        //  right child of the current node, and making that new node the current
1189        //  node.
1190        //
1191        if ((opCh == chComma) || (opCh == chPipe))
1192        {
1193            ContentSpecNode* lastNode = 0;
1194            while (true)
1195            {
1196                //
1197                //  The next thing must either be another | or , character followed
1198                //  by another leaf or subexpression, or a closing parenthesis, or a
1199                //  PE ref.
1200                //
1201                if (fReaderMgr->lookingAtChar(chPercent))
1202                {
1203                    checkForPERef(false, true);
1204                }
1205                 else if (fReaderMgr->skippedSpace())
1206                {
1207                    // Just skip whitespace
1208                    fReaderMgr->skipPastSpaces();
1209                }
1210                 else if (fReaderMgr->skippedChar(chCloseParen))
1211                {
1212                    //
1213                    //  We've hit the end of this section, so break out. But, we
1214                    //  need to see if we left a partial sequence of choice node
1215                    //  without a second node. If so, we have to undo that and
1216                    //  put its left child into the right node of the previous
1217                    //  node.
1218                    //
1219                    if ((curNode->getType() == ContentSpecNode::Choice)
1220                    ||  (curNode->getType() == ContentSpecNode::Sequence))
1221                    {
1222                        if (!curNode->getSecond())
1223                        {
1224                            ContentSpecNode* saveFirst = curNode->orphanFirst();
1225                            lastNode->setSecond(saveFirst);
1226                            curNode = lastNode;
1227                        }
1228                    }
1229                    break;
1230                }
1231                 else if (fReaderMgr->skippedChar(opCh))
1232                {
1233                    // Check for a PE ref here, but don't require spaces
1234                    checkForPERef(false, true);
1235
1236                    if (fReaderMgr->skippedChar(chOpenParen))
1237                    {
1238                        const XMLSize_t curReader = fReaderMgr->getCurrentReaderNum();
1239
1240                        // Recurse to handle this new guy
1241                        ContentSpecNode* subNode;
1242                        try {
1243                            subNode = scanChildren(elemDecl, bufToUse);
1244                        }
1245                        catch (const XMLErrs::Codes)
1246                        {
1247                            delete headNode;
1248                            throw;
1249                        }
1250
1251                        // If it failed, we are done, clean up here and return failure
1252                        if (!subNode)
1253                        {
1254                            delete headNode;
1255                            return 0;
1256                        }
1257
1258                        if (curReader != fReaderMgr->getCurrentReaderNum() && fScanner->getValidationScheme() == XMLScanner::Val_Always)
1259                            fScanner->getValidator()->emitError(XMLValid::PartialMarkupInPE);
1260
1261                        // Else patch it in and make it the new current
1262                        ContentSpecNode* newCur = new (fGrammarPoolMemoryManager) ContentSpecNode
1263                        (
1264                            curType
1265                            , subNode
1266                            , 0
1267                            , true
1268                            , true
1269                            , fGrammarPoolMemoryManager
1270                        );
1271                        curNode->setSecond(newCur);
1272                        lastNode = curNode;
1273                        curNode = newCur;
1274                    }
1275                     else
1276                    {
1277                        //
1278                        //  Got to be a leaf node, so get a name. If we cannot get
1279                        //  one, then clean up and get outa here.
1280                        //
1281                        if (!fReaderMgr->getName(bufToUse))
1282                        {
1283                            delete headNode;
1284                            fScanner->emitError(XMLErrs::ExpectedElementName);
1285                            return 0;
1286                        }
1287
1288                        //
1289                        //  Create a leaf node for it. If we can find the element
1290                        //  id for this element, then use it. Else, we have to
1291                        //  fault in an element decl, marked as created because
1292                        //  of being in a content model.
1293                        //
1294                        XMLElementDecl* decl = fDTDGrammar->getElemDecl(fEmptyNamespaceId, 0, bufToUse.getRawBuffer(), Grammar::TOP_LEVEL_SCOPE);
1295                        if (!decl)
1296                        {
1297                            decl = new (fGrammarPoolMemoryManager) DTDElementDecl
1298                            (
1299                                bufToUse.getRawBuffer()
1300                                , fEmptyNamespaceId
1301                                , DTDElementDecl::Any
1302                                , fGrammarPoolMemoryManager
1303                            );
1304                            decl->setCreateReason(XMLElementDecl::InContentModel);
1305                            decl->setExternalElemDeclaration(isReadingExternalEntity());
1306                            fDTDGrammar->putElemDecl(decl);
1307                        }
1308
1309                        ContentSpecNode* tmpLeaf = new (fGrammarPoolMemoryManager) ContentSpecNode
1310                        (
1311                            decl->getElementName()
1312                            , fGrammarPoolMemoryManager
1313                        );
1314
1315                        // Check for a repetition character after the leaf
1316                        const XMLCh repCh = fReaderMgr->peekNextChar();
1317                        ContentSpecNode* tmpLeaf2 = makeRepNode(repCh, tmpLeaf, fGrammarPoolMemoryManager);
1318                        if (tmpLeaf != tmpLeaf2)
1319                            fReaderMgr->getNextChar();
1320
1321                        //
1322                        //  Create a new sequence or choice node, with the leaf
1323                        //  (or rep surrounding it) we just got as its first node.
1324                        //  Make the new node the second node of the current node,
1325                        //  and then make it the current node.
1326                        //
1327                        ContentSpecNode* newCur = new (fGrammarPoolMemoryManager) ContentSpecNode
1328                        (
1329                            curType
1330                            , tmpLeaf2
1331                            , 0
1332                            , true
1333                            , true
1334                            , fGrammarPoolMemoryManager
1335                        );
1336                        curNode->setSecond(newCur);
1337                        lastNode = curNode;
1338                        curNode = newCur;
1339                    }
1340                }
1341                 else
1342                {
1343                    // Cannot be valid
1344                    delete headNode;  // emitError may do a throw so need to clean-up first
1345                    if (opCh == chComma)
1346                    {
1347                        fScanner->emitError(XMLErrs::ExpectedChoiceOrCloseParen);
1348                    }
1349                     else
1350                    {
1351                        fScanner->emitError
1352                        (
1353                            XMLErrs::ExpectedSeqOrCloseParen
1354                            , elemDecl.getFullName()
1355                        );
1356                    }               
1357                    return 0;
1358                }
1359            }
1360        }
1361
1362        //
1363        //  We saw the terminating parenthesis so lets check for any repetition
1364        //  character, and create a node for that, making the head node the child
1365        //  of it.
1366        //
1367        const XMLCh repCh = fReaderMgr->peekNextChar();
1368        curNode = makeRepNode(repCh, headNode, fGrammarPoolMemoryManager);
1369        if (curNode != headNode)
1370            fReaderMgr->getNextChar();
1371
1372        // prepare for recursion
1373        if(arrNestedDecl==NULL)
1374            break;
1375        else
1376        {
1377            // If that failed, no need to go further, return failure
1378            if (!curNode)
1379                return 0;
1380
1381            const XMLSize_t curReader = arrNestedDecl->pop();
1382            if (curReader != fReaderMgr->getCurrentReaderNum() && fScanner->getValidationScheme() == XMLScanner::Val_Always)
1383                fScanner->getValidator()->emitError(XMLValid::PartialMarkupInPE);
1384
1385            if(arrNestedDecl->empty())
1386            {
1387                delete arrNestedDecl;
1388                arrNestedDecl=NULL;
1389            }
1390        }
1391    }
1392
1393    return curNode;
1394}
1395
1396
1397//
1398//  We get here after the '<!--' part of the comment. We scan past the
1399//  terminating '-->' It will calls the appropriate handler with the comment
1400//  text, if one is provided. A comment can be in either the document or
1401//  the DTD, so the fInDocument flag is used to know which handler to send
1402//  it to.
1403//
1404void DTDScanner::scanComment()
1405{
1406    enum States
1407    {
1408        InText
1409        , OneDash
1410        , TwoDashes
1411    };
1412
1413    // Get a buffer for this
1414    XMLBufBid bbComment(fBufMgr);
1415
1416    //
1417    //  Get the comment text into a temp buffer. Be sure to use temp buffer
1418    //  two here, since its to be used for stuff that is potentially longer
1419    //  than just a name.
1420    //
1421    bool   gotLeadingSurrogate = false;
1422    States curState = InText;
1423    while (true)
1424    {
1425        // Get the next character
1426        const XMLCh nextCh = fReaderMgr->getNextChar();
1427
1428        //  Watch for an end of file
1429        if (!nextCh)
1430        {
1431            fScanner->emitError(XMLErrs::UnterminatedComment);
1432            ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
1433        }
1434
1435        // Check for correct surrogate pairs
1436        if ((nextCh >= 0xD800) && (nextCh <= 0xDBFF))
1437        {
1438            if (gotLeadingSurrogate)
1439                fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
1440            else
1441                gotLeadingSurrogate = true;
1442        }
1443        else
1444        {
1445            if (gotLeadingSurrogate)
1446            {
1447                if ((nextCh < 0xDC00) || (nextCh > 0xDFFF))
1448                    fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
1449            }
1450            // Its got to at least be a valid XML character
1451            else if (!fReaderMgr->getCurrentReader()->isXMLChar(nextCh)) {
1452
1453                XMLCh tmpBuf[9];
1454                XMLString::binToText
1455                (
1456                    nextCh
1457                    , tmpBuf
1458                    , 8
1459                    , 16
1460                    , fMemoryManager
1461                );
1462                fScanner->emitError(XMLErrs::InvalidCharacter, tmpBuf);
1463            }
1464
1465            gotLeadingSurrogate = false;
1466        }
1467
1468        if (curState == InText)
1469        {
1470            // If its a dash, go to OneDash state. Otherwise take as text
1471            if (nextCh == chDash)
1472                curState = OneDash;
1473            else
1474                bbComment.append(nextCh);
1475        }
1476        else if (curState == OneDash)
1477        {
1478            //
1479            //  If its another dash, then we change to the two dashes states.
1480            //  Otherwise, we have to put in the deficit dash and the new
1481            //  character and go back to InText.
1482            //
1483            if (nextCh == chDash)
1484            {
1485                curState = TwoDashes;
1486            }
1487            else
1488            {
1489                bbComment.append(chDash);
1490                bbComment.append(nextCh);
1491                curState = InText;
1492            }
1493        }
1494        else if (curState == TwoDashes)
1495        {
1496            // The next character must be the closing bracket
1497            if (nextCh != chCloseAngle)
1498            {
1499                fScanner->emitError(XMLErrs::IllegalSequenceInComment);
1500                fReaderMgr->skipPastChar(chCloseAngle);
1501                return;
1502            }
1503            break;
1504        }
1505    }
1506
1507    // If there is a doc type handler, then pass on the comment stuff
1508    if (fDocTypeHandler)
1509        fDocTypeHandler->doctypeComment(bbComment.getRawBuffer());
1510}
1511
1512
1513bool DTDScanner::scanContentSpec(DTDElementDecl& toFill)
1514{
1515    //
1516    //  Check for for a couple of the predefined content type strings. If
1517    //  its not one of these, its got to be a parenthesized reg ex type
1518    //  expression.
1519    //
1520    if (fReaderMgr->skippedString(XMLUni::fgEmptyString))
1521    {
1522        toFill.setModelType(DTDElementDecl::Empty);
1523        return true;
1524    }
1525
1526    if (fReaderMgr->skippedString(XMLUni::fgAnyString))
1527    {
1528        toFill.setModelType(DTDElementDecl::Any);
1529        return true;
1530    }
1531
1532    // Its got to be a parenthesized regular expression
1533    if (!fReaderMgr->skippedChar(chOpenParen))
1534    {
1535        fScanner->emitError
1536        (
1537            XMLErrs::ExpectedContentSpecExpr
1538            , toFill.getFullName()
1539        );
1540        return false;
1541    }
1542
1543    // Get the current reader id, so we can test for partial markup
1544    const XMLSize_t curReader = fReaderMgr->getCurrentReaderNum();
1545
1546    // We could have a PE ref here, but don't require space
1547    checkForPERef(false, true);
1548
1549    //
1550    //  Now we look for a PCDATA string. If its PCDATA, then it must be a
1551    //  MIXED model. Otherwise, it must be a regular list of children in
1552    //  a regular expression perhaps.
1553    //
1554    bool status;
1555    if (fReaderMgr->skippedString(XMLUni::fgPCDATAString))
1556    {
1557        // Set the model to mixed
1558        toFill.setModelType(DTDElementDecl::Mixed_Simple);
1559        status = scanMixed(toFill);
1560
1561        //
1562        //  If we are validating we have to check that there are no multiple
1563        //  uses of any child elements.
1564        //
1565        if (fScanner->getValidationScheme() == XMLScanner::Val_Always)
1566        {
1567            if (((const MixedContentModel*)toFill.getContentModel())->hasDups())
1568                fScanner->getValidator()->emitError(XMLValid::RepElemInMixed);
1569        }
1570    }
1571     else
1572    {
1573        //
1574        //  We have to do a recursive scan of the content model. Create a
1575        //  buffer for it to use, for efficiency. It returns the top ofthe
1576        //  content spec node tree, which we set if successful.
1577        //
1578        toFill.setModelType(DTDElementDecl::Children);
1579        XMLBufBid bbTmp(fBufMgr);
1580        ContentSpecNode* resNode = scanChildren(toFill, bbTmp.getBuffer());
1581        status = (resNode != 0);
1582        if (status)
1583            toFill.setContentSpec(resNode);
1584    }
1585
1586    // Make sure we are on the same reader as where we started
1587    if (curReader != fReaderMgr->getCurrentReaderNum() && fScanner->getValidationScheme() == XMLScanner::Val_Always)
1588        fScanner->getValidator()->emitError(XMLValid::PartialMarkupInPE);
1589
1590    return status;
1591}
1592
1593
1594void DTDScanner::scanDefaultDecl(DTDAttDef& toFill)
1595{
1596    if (fReaderMgr->skippedString(XMLUni::fgRequiredString))
1597    {
1598        toFill.setDefaultType(XMLAttDef::Required);
1599        return;
1600    }
1601
1602    if (fReaderMgr->skippedString(XMLUni::fgImpliedString))
1603    {
1604        toFill.setDefaultType(XMLAttDef::Implied);
1605        return;
1606    }
1607
1608    if (fReaderMgr->skippedString(XMLUni::fgFixedString))
1609    {
1610        //
1611        //  There must be space before the fixed value. If there is not, then
1612        //  emit an error but keep going.
1613        //
1614        if (!fReaderMgr->skippedSpace())
1615            fScanner->emitError(XMLErrs::ExpectedWhitespace);
1616        else
1617            fReaderMgr->skipPastSpaces();
1618        toFill.setDefaultType(XMLAttDef::Fixed);
1619    }
1620     else
1621    {
1622        toFill.setDefaultType(XMLAttDef::Default);
1623    }
1624
1625    //
1626    //  If we got here, its fixed or default, so we need to get a value.
1627    //  If we don't, then emit an error but just set the default value to
1628    //  an empty string and try to keep going.
1629    //
1630    // Check for PE ref or optional whitespace
1631    checkForPERef(false, true);
1632
1633    XMLBufBid bbValue(fBufMgr);
1634    if (!scanAttValue(toFill.getFullName(), bbValue.getBuffer(), toFill.getType()))
1635        fScanner->emitError(XMLErrs::ExpectedDefAttrDecl);
1636
1637    toFill.setValue(bbValue.getRawBuffer());
1638}
1639
1640
1641//
1642//  This is called after seeing '<!ELEMENT' which indicates that an element
1643//  markup is starting. This guy scans the rest of it and adds it to the
1644//  element decl pool if it has not already been declared.
1645//
1646void DTDScanner::scanElementDecl()
1647{
1648    //
1649    //  Space is legal (required actually) here so check for a PE ref. If
1650    //  we don't get our whitespace, then issue and error, but try to keep
1651    //  going.
1652    //
1653    if (!checkForPERef(false, true))
1654        fScanner->emitError(XMLErrs::ExpectedWhitespace);
1655
1656    // Get a buffer for the element name and scan in the name
1657    XMLBufBid bbName(fBufMgr);
1658    if (!fReaderMgr->getName(bbName.getBuffer()))
1659    {
1660        fScanner->emitError(XMLErrs::ExpectedElementName);
1661        fReaderMgr->skipPastChar(chCloseAngle);
1662        return;
1663    }
1664
1665    // Look this guy up in the element decl pool
1666    DTDElementDecl* decl = (DTDElementDecl*) fDTDGrammar->getElemDecl(fEmptyNamespaceId, 0, bbName.getRawBuffer(), Grammar::TOP_LEVEL_SCOPE);
1667
1668    //
1669    //  If it does not exist, then we need to create it. If it does and
1670    //  its marked as declared, then that's an error, but we still need to
1671    //  scan over the content model so use the dummy declaration that the
1672    //  parsing code can fill in.
1673    //
1674    if (decl)
1675    {
1676        if (decl->isDeclared())
1677        {
1678            if (fScanner->getValidationScheme() == XMLScanner::Val_Always)
1679                fScanner->getValidator()->emitError(XMLValid::ElementAlreadyExists, bbName.getRawBuffer());
1680
1681            if (!fDumElemDecl)
1682                fDumElemDecl = new (fMemoryManager) DTDElementDecl
1683                (
1684                    bbName.getRawBuffer()
1685                    , fEmptyNamespaceId
1686                    , DTDElementDecl::Any
1687                    , fMemoryManager
1688                );
1689            else
1690                fDumElemDecl->setElementName(bbName.getRawBuffer(),fEmptyNamespaceId);
1691        }
1692    }
1693     else
1694    {
1695        //
1696        //  Create the new empty declaration to fill in and put it into
1697        //  the decl pool.
1698        //
1699        decl = new (fGrammarPoolMemoryManager) DTDElementDecl
1700        (
1701            bbName.getRawBuffer()
1702            , fEmptyNamespaceId
1703            , DTDElementDecl::Any
1704            , fGrammarPoolMemoryManager
1705        );
1706        fDTDGrammar->putElemDecl(decl);
1707    }
1708
1709    // Set a flag for whether we will ignore this one
1710    const bool isIgnored = (decl == fDumElemDecl);
1711
1712    // Mark this one if being externally declared
1713    decl->setExternalElemDeclaration(isReadingExternalEntity());
1714
1715    // Mark this one as being declared
1716    decl->setCreateReason(XMLElementDecl::Declared);
1717
1718    // Another check for a PE ref, with at least required whitespace
1719    if (!checkForPERef(false, true))
1720        fScanner->emitError(XMLErrs::ExpectedWhitespace);
1721
1722    // And now scan the content model for this guy.
1723    if (!scanContentSpec(*decl))
1724    {
1725        fReaderMgr->skipPastChar(chCloseAngle);
1726        return;
1727    }
1728
1729    // Another check for a PE ref, but we don't require whitespace here
1730    checkForPERef(false, true);
1731
1732    // And we should have the ending angle bracket
1733    if (!fReaderMgr->skippedChar(chCloseAngle))
1734    {
1735        fScanner->emitError(XMLErrs::UnterminatedElementDecl, bbName.getRawBuffer());
1736        fReaderMgr->skipPastChar(chCloseAngle);
1737    }
1738
1739    //
1740    //  If we have a DTD handler tell it about the new element decl. We
1741    //  tell it if its one that can be ignored, cause its an override of a
1742    //  previously existing decl. If it is being ignored, only call back
1743    //  if advanced callbacks are enabled.
1744    //
1745    if (fDocTypeHandler)
1746        fDocTypeHandler->elementDecl(*decl, isIgnored);
1747}
1748
1749
1750//
1751//  This method will process a general or parameter entity reference. The
1752//  entity name and entity text will be stored in the entity pool. The value
1753//  of the entity will be scanned for any other parameter entity or char
1754//  references which will be expanded. So the stored value can only have
1755//  general entity references when done.
1756//
1757void DTDScanner::scanEntityDecl()
1758{
1759    //
1760    //  Space is required here, but we cannot check for a PE Ref since
1761    //  there could be a legal (no-ref) percent sign here. Since any
1762    //  entity that ended here would be illegal, we just skip spaces
1763    //  and then check for a percent.
1764    //
1765    if (!fReaderMgr->lookingAtSpace())
1766        fScanner->emitError(XMLErrs::ExpectedWhitespace);
1767    else
1768        fReaderMgr->skipPastSpaces();
1769    bool isPEDecl = fReaderMgr->skippedChar(chPercent);
1770
1771    //
1772    //  If a PE decl, then check if it is followed by a space; if it is so,
1773    //  eat the percent and check for spaces or a PE ref on the other side of it.
1774    //  Otherwise, it has to be an entity reference for a general entity.
1775    //
1776    if (isPEDecl)
1777    {
1778        if(!fReaderMgr->getCurrentReader()->isWhitespace(fReaderMgr->peekNextChar()))
1779        {
1780            isPEDecl=false;
1781            while (true)
1782            {
1783               if (!expandPERef(false, false, true, false))
1784                  fScanner->emitError(XMLErrs::ExpectedEntityRefName);
1785               // And skip any more spaces in the expanded value
1786               if (fReaderMgr->skippedSpace())
1787                  fReaderMgr->skipPastSpaces();
1788               if (!fReaderMgr->skippedChar(chPercent))
1789                  break;
1790            }
1791        }
1792        else if (!checkForPERef(false, true))
1793            fScanner->emitError(XMLErrs::ExpectedWhitespace);
1794    }
1795
1796    //
1797    //  Now lets get a name, which should be the name of the entity. We
1798    //  have to get a buffer for this.
1799    //
1800    XMLBufBid bbName(fBufMgr);
1801    if (!fReaderMgr->getName(bbName.getBuffer()))
1802    {
1803        fScanner->emitError(XMLErrs::ExpectedPEName);
1804        fReaderMgr->skipPastChar(chCloseAngle);
1805        return;
1806    }
1807
1808    // If namespaces are enabled, then no colons allowed
1809    if (fScanner->getDoNamespaces())
1810    {
1811        if (XMLString::indexOf(bbName.getRawBuffer(), chColon) != -1)
1812            fScanner->emitError(XMLErrs::ColonNotLegalWithNS);
1813    }
1814
1815    //
1816    //  See if this entity already exists. If so, then the existing one
1817    //  takes precendence. So we use the local dummy decl to parse into
1818    //  and just ignore the results.
1819    //
1820    DTDEntityDecl* entityDecl;
1821    if (isPEDecl)
1822        entityDecl = fPEntityDeclPool->getByKey(bbName.getRawBuffer());
1823    else
1824        entityDecl = fDTDGrammar->getEntityDecl(bbName.getRawBuffer());
1825
1826    if (entityDecl)
1827    {
1828        if (!fDumEntityDecl)
1829            fDumEntityDecl = new (fMemoryManager) DTDEntityDecl(fMemoryManager);
1830        fDumEntityDecl->setName(bbName.getRawBuffer());
1831        entityDecl = fDumEntityDecl;
1832    }
1833     else
1834    {
1835        // Its not in existence already, then create an entity decl for it
1836        entityDecl = new (fGrammarPoolMemoryManager) DTDEntityDecl(bbName.getRawBuffer(), false, fGrammarPoolMemoryManager);
1837
1838        //
1839        //  Set the declaration location. The parameter indicates whether its
1840        //  declared in the content/internal subset, so we know whether or not
1841        //  its in the external subset.
1842        //
1843        entityDecl->setDeclaredInIntSubset(fInternalSubset);
1844
1845        // Add it to the appropriate entity decl pool
1846        if (isPEDecl)
1847            fPEntityDeclPool->put(entityDecl);
1848         else
1849            fDTDGrammar->putEntityDecl(entityDecl);
1850    }
1851
1852    // Set a flag that indicates whether we are ignoring this one
1853    const bool isIgnored = (entityDecl == fDumEntityDecl);
1854
1855    // Set the PE flag on it
1856    entityDecl->setIsParameter(isPEDecl);
1857
1858    //
1859    //  Space is legal (required actually) here so check for a PE ref. If
1860    //  we don't get our whitespace, then issue an error, but try to keep
1861    //  going.
1862    //
1863    if (!checkForPERef(false, true))
1864        fScanner->emitError(XMLErrs::ExpectedWhitespace);
1865
1866    // save the hasNoDTD status for Entity Constraint Checking
1867    bool hasNoDTD = fScanner->getHasNoDTD();
1868    if (hasNoDTD && isPEDecl)
1869        fScanner->setHasNoDTD(false);
1870
1871    // According to the type call the value scanning method
1872    if (!scanEntityDef(*entityDecl, isPEDecl))
1873    {
1874        fReaderMgr->skipPastChar(chCloseAngle);
1875        fScanner->setHasNoDTD(true);
1876        fScanner->emitError(XMLErrs::ExpectedEntityValue);
1877        return;
1878    }
1879    if (hasNoDTD)
1880        fScanner->setHasNoDTD(true);
1881
1882    // Space is legal (but not required) here so check for a PE ref
1883    checkForPERef(false, true);
1884
1885    // And then we have to have the closing angle bracket
1886    if (!fReaderMgr->skippedChar(chCloseAngle))
1887    {
1888        fScanner->emitError(XMLErrs::UnterminatedEntityDecl, entityDecl->getName());
1889        fReaderMgr->skipPastChar(chCloseAngle);
1890    }
1891
1892    //
1893    //  If we have a doc type handler, then call it. But only call it for
1894    //  ignored elements if advanced callbacks are enabled.
1895    //
1896    if (fDocTypeHandler)
1897        fDocTypeHandler->entityDecl(*entityDecl, isPEDecl, isIgnored);
1898}
1899
1900
1901//
1902//  This method will scan a general/character entity ref. It will either
1903//  expand a char ref and return the value directly, or it will expand
1904//  a general entity and a reader for it onto the reader stack.
1905//
1906//  The return value indicates whether the value was returned directly or
1907//  pushed as a reader or it failed.
1908//
1909//  The escaped flag tells the caller whether the returnd parameter resulted
1910//  from a character reference, which escapes the character in some cases. It
1911//  only makes any difference if the return indicates the value was returned
1912//  directly.
1913//
1914//  NOTE: This is only called when scanning attribute values, so we always
1915//  expand general entities.
1916//
1917DTDScanner::EntityExpRes
1918DTDScanner::scanEntityRef(XMLCh& firstCh, XMLCh& secondCh, bool& escaped)
1919{
1920    // Assume no escape and no second char
1921    escaped = false;
1922    secondCh = 0;
1923
1924    // We have to insure its all done in a single entity
1925    const XMLSize_t curReader = fReaderMgr->getCurrentReaderNum();
1926
1927    //
1928    //  If the next char is a pound, then its a character reference and we
1929    //  need to expand it always.
1930    //
1931    if (fReaderMgr->skippedChar(chPound))
1932    {
1933        //
1934        //  Its a character reference, so scan it and get back the numeric
1935        //  value it represents. If it fails, just return immediately.
1936        //
1937        if (!scanCharRef(firstCh, secondCh))
1938            return EntityExp_Failed;
1939
1940        if (curReader != fReaderMgr->getCurrentReaderNum())
1941            fScanner->emitError(XMLErrs::PartialMarkupInEntity);
1942
1943        // Its now escaped since it was a char ref
1944        escaped = true;
1945        return EntityExp_Returned;
1946    }
1947
1948    // Get the name of the general entity
1949    XMLBufBid bbName(fBufMgr);
1950    if (!fReaderMgr->getName(bbName.getBuffer()))
1951    {
1952        fScanner->emitError(XMLErrs::ExpectedEntityRefName);
1953        return EntityExp_Failed;
1954    }
1955
1956    //
1957    //  Next char must be a semi-colon. But if its not, just emit
1958    //  an error and try to continue.
1959    //
1960    if (!fReaderMgr->skippedChar(chSemiColon))
1961        fScanner->emitError(XMLErrs::UnterminatedEntityRef, bbName.getRawBuffer());
1962
1963    // Make sure it was all in one entity reader
1964    if (curReader != fReaderMgr->getCurrentReaderNum())
1965        fScanner->emitError(XMLErrs::PartialMarkupInEntity);
1966
1967    // Look it up the name the general entity pool
1968    XMLEntityDecl* decl = fDTDGrammar->getEntityDecl(bbName.getRawBuffer());
1969
1970    // If it does not exist, then obviously an error
1971    if (!decl)
1972    {
1973        // XML 1.0 Section 4.1
1974        if (fScanner->getStandalone() || fScanner->getHasNoDTD()) {
1975            fScanner->emitError(XMLErrs::EntityNotFound, bbName.getRawBuffer());
1976        }
1977        else {
1978            if (fScanner->getValidationScheme() == XMLScanner::Val_Always)
1979                fScanner->getValidator()->emitError(XMLValid::VC_EntityNotFound, bbName.getRawBuffer());
1980        }
1981
1982        return EntityExp_Failed;
1983    }
1984
1985
1986    //
1987    // XML 1.0 Section 4.1
1988    //  If we are a standalone document, then it has to have been declared
1989    //  in the internal subset.
1990    //
1991    if (fScanner->getStandalone() && !decl->getDeclaredInIntSubset())
1992        fScanner->emitError(XMLErrs::IllegalRefInStandalone, bbName.getRawBuffer());
1993
1994    //
1995    //  If its a special char reference, then its escaped and we can return
1996    //  it directly.
1997    //
1998    if (decl->getIsSpecialChar())
1999    {
2000        firstCh = decl->getValue()[0];
2001        escaped = true;
2002        return EntityExp_Returned;
2003    }
2004
2005    if (decl->isExternal())
2006    {
2007        // If its unparsed, then its not valid here
2008        // XML 1.0 Section 4.4.4 the appearance of a reference to an unparsed entity is forbidden.
2009        if (decl->isUnparsed())
2010        {
2011            fScanner->emitError(XMLErrs::NoUnparsedEntityRefs, bbName.getRawBuffer());
2012            return EntityExp_Failed;
2013        }
2014
2015        // We are in an attribute value, so not valid.
2016        // XML 1.0 Section 4.4.4 a reference to an external entity in an attribute value is forbidden.
2017        fScanner->emitError(XMLErrs::NoExtRefsInAttValue);
2018
2019        // And now create a reader to read this entity
2020        InputSource* srcUsed;
2021        XMLReader* reader = fReaderMgr->createReader
2022        (
2023            decl->getBaseURI()
2024            , decl->getSystemId()
2025            , decl->getPublicId()
2026            , false
2027            , XMLReader::RefFrom_NonLiteral
2028            , XMLReader::Type_General
2029            , XMLReader::Source_External
2030            , srcUsed
2031            , fScanner->getCalculateSrcOfs()
2032            , fScanner->getLowWaterMark()
2033            , fScanner->getDisableDefaultEntityResolution()
2034        );
2035
2036        // Put a janitor on the source so it gets cleaned up on exit
2037        Janitor<InputSource> janSrc(srcUsed);
2038
2039        //
2040        //  If the creation failed then throw an exception
2041        //
2042        if (!reader)
2043            ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::Gen_CouldNotOpenExtEntity, srcUsed ? srcUsed->getSystemId() : decl->getSystemId(), fMemoryManager);
2044
2045        //
2046        //  Push the reader. If its a recursive expansion, then emit an error
2047        //  and return an failure.
2048        //
2049        if (!fReaderMgr->pushReader(reader, decl))
2050        {
2051            fScanner->emitError(XMLErrs::RecursiveEntity, decl->getName());
2052            return EntityExp_Failed;
2053        }
2054
2055        // If it starts with the XML string, then parse a text decl
2056        if (fScanner->checkXMLDecl(true))
2057            scanTextDecl();
2058    }
2059     else
2060    {
2061        //
2062        //  Create a reader over a memory stream over the entity value
2063        //  We force it to assume UTF-16 by passing in an encoding
2064        //  string. This way it won't both trying to predecode the
2065        //  first line, looking for an XML/TextDecl.
2066        //
2067        XMLReader* valueReader = fReaderMgr->createIntEntReader
2068        (
2069            decl->getName()
2070            , XMLReader::RefFrom_NonLiteral
2071            , XMLReader::Type_General
2072            , decl->getValue()
2073            , decl->getValueLen()
2074            , false
2075        );
2076
2077        //
2078        //  Trt to push the entity reader onto the reader manager stack,
2079        //  where it will become the subsequent input. If it fails, that
2080        //  means the entity is recursive, so issue an error. The reader
2081        //  will have just been discarded, but we just keep going.
2082        //
2083        if (!fReaderMgr->pushReader(valueReader, decl))
2084            fScanner->emitError(XMLErrs::RecursiveEntity, decl->getName());
2085    }
2086
2087    return EntityExp_Pushed;
2088}
2089
2090
2091//
2092//  This method will scan a quoted literal of an entity value. It has to
2093//  deal with replacement of PE references; however, since this is a DTD
2094//  scanner, all such entity literals are in entity decls and therefore
2095//  general entities are not expanded.
2096//
2097bool DTDScanner::scanEntityLiteral(XMLBuffer& toFill)
2098{
2099    toFill.reset();
2100
2101    // Get the next char which must be a single or double quote
2102    XMLCh quoteCh;
2103    if (!fReaderMgr->skipIfQuote(quoteCh))
2104        return false;
2105
2106    // Get a buffer for pulling in entity names when we see GE refs
2107    XMLBufBid bbName(fBufMgr);
2108    XMLBuffer& nameBuf = bbName.getBuffer();
2109
2110    // Remember the current reader
2111    const XMLSize_t orgReader = fReaderMgr->getCurrentReaderNum();
2112
2113    //
2114    //  Loop until we see the ending quote character, handling any references
2115    //  in the process.
2116    //
2117    XMLCh   nextCh;
2118    XMLCh   secondCh = 0;
2119    bool    gotLeadingSurrogate = false;
2120    while (true)
2121    {
2122        nextCh = fReaderMgr->getNextChar();
2123
2124        //
2125        //  Watch specifically for EOF and issue a more meaningful error
2126        //  if that occurs (since an unterminated quoted char can cause
2127        //  this easily.)
2128        //
2129        if (!nextCh)
2130        {
2131            fScanner->emitError(XMLErrs::UnterminatedEntityLiteral);
2132            ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
2133        }
2134
2135        //
2136        //  Break out on our terminating quote char when we are back in the
2137        //  same reader. Otherwise, we might trigger on a nested quote char
2138        //  in an expanded entity.
2139        //
2140        if ((nextCh == quoteCh)
2141        &&  (fReaderMgr->getCurrentReaderNum() == orgReader))
2142        {
2143            break;
2144        }
2145
2146        if (nextCh == chPercent)
2147        {
2148            //
2149            //  Put the PE's value on the reader stack and then jump back
2150            //  to the top to start processing it. The parameter indicates
2151            //  that it should not scan the reference's content as an external
2152            //  subset.
2153            //
2154            expandPERef(false, true, true);
2155            continue;
2156        }
2157
2158        //
2159        //  Ok, now that all the other special stuff is checked, we can
2160        //  look for a general entity. In here, we cannot have a naked &
2161        //  and will only expand numerical char refs or the intrinsic char
2162        //  refs. Others will be left alone.
2163        //
2164        if (nextCh == chAmpersand)
2165        {
2166            //
2167            //  Here, we only expand numeric char refs, but not any general
2168            //  entities. However, the stupid XML spec requires that we check
2169            //  and make sure it does refer to a general entity if its not
2170            //  a char ref (i.e. no naked '&' chars.)
2171            //
2172            if (fReaderMgr->skippedChar(chPound))
2173            {
2174                // If it failed, then just jump back to the top and try to pick up
2175                if (!scanCharRef(nextCh, secondCh))
2176                {
2177                    gotLeadingSurrogate = false;
2178                    continue;
2179                }
2180            }
2181             else
2182            {
2183                if (!fReaderMgr->getName(nameBuf))
2184                {
2185                    fScanner->emitError(XMLErrs::ExpectedEntityRefName);
2186                }
2187                 else
2188                {
2189                    //
2190                    //  Since we are not expanding any of this, we have to
2191                    //  put the amp and name into the target buffer as data.
2192                    //
2193                    toFill.append(chAmpersand);
2194                    toFill.append(nameBuf.getRawBuffer());
2195
2196                    // Make sure we skipped a trailing semicolon
2197                    if (!fReaderMgr->skippedChar(chSemiColon))
2198                    {
2199                        fScanner->emitError
2200                        (
2201                            XMLErrs::UnterminatedEntityRef
2202                            , nameBuf.getRawBuffer()
2203                        );
2204                    }
2205
2206                    // And make the new character the semicolon
2207                    nextCh = chSemiColon;
2208                }
2209
2210                // Either way here we reset the surrogate flag
2211                gotLeadingSurrogate = false;
2212            }
2213        }
2214        else if ((nextCh >= 0xD800) && (nextCh <= 0xDBFF))
2215        {
2216            if (gotLeadingSurrogate)
2217                fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
2218            else
2219                gotLeadingSurrogate = true;
2220        }
2221         else
2222        {
2223            if (gotLeadingSurrogate)
2224            {
2225                if ((nextCh < 0xDC00) || (nextCh > 0xDFFF))
2226                    fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
2227            }
2228             else if (!fReaderMgr->getCurrentReader()->isXMLChar(nextCh))
2229            {
2230                XMLCh tmpBuf[9];
2231                XMLString::binToText
2232                (
2233                    nextCh
2234                    , tmpBuf
2235                    , 8
2236                    , 16
2237                    , fMemoryManager
2238                );
2239                fScanner->emitError(XMLErrs::InvalidCharacter, tmpBuf);
2240                fReaderMgr->skipPastChar(quoteCh);
2241                return false;
2242            }
2243            gotLeadingSurrogate = false;
2244        }
2245
2246        // Looks ok, so add it to the literal
2247        toFill.append(nextCh);
2248
2249        if (secondCh)
2250        {
2251            toFill.append(secondCh);
2252            secondCh=0;
2253        }
2254    }
2255
2256    //
2257    //  If we got here and did not get back to the original reader level,
2258    //  then we propogated some entity out of the literal, so issue an
2259    //  error, but don't fail.
2260    //
2261    if (fReaderMgr->getCurrentReaderNum() != orgReader && fScanner->getValidationScheme() == XMLScanner::Val_Always)
2262        fScanner->getValidator()->emitError(XMLValid::PartialMarkupInPE);
2263
2264    return true;
2265}
2266
2267
2268//
2269//  This method is called after the entity name has been scanned, and any
2270//  PE referenced following the name is handled. The passed decl will be
2271//  filled in with the info scanned.
2272//
2273bool DTDScanner::scanEntityDef(DTDEntityDecl& decl, const bool isPEDecl)
2274{
2275    // Its got to be an entity literal
2276    if (fReaderMgr->lookingAtChar(chSingleQuote)
2277    ||  fReaderMgr->lookingAtChar(chDoubleQuote))
2278    {
2279        // Get a buffer for the literal
2280        XMLBufBid bbValue(fBufMgr);
2281
2282        if (!scanEntityLiteral(bbValue.getBuffer()))
2283            return false;
2284
2285        // Set it on the entity decl
2286        decl.setValue(bbValue.getRawBuffer());
2287        return true;
2288    }
2289
2290    //
2291    //  Its got to be an external entity, so there must be an external id.
2292    //  Get buffers for them and scan an external id into them.
2293    //
2294    XMLBufBid bbPubId(fBufMgr);
2295    XMLBufBid bbSysId(fBufMgr);
2296    if (!scanId(bbPubId.getBuffer(), bbSysId.getBuffer(), IDType_External))
2297        return false;
2298
2299    decl.setIsExternal(true);
2300    ReaderMgr::LastExtEntityInfo lastInfo;
2301    fReaderMgr->getLastExtEntityInfo(lastInfo);
2302
2303    // Fill in the id fields of the decl with the info we got
2304    const XMLCh* publicId = bbPubId.getRawBuffer();
2305    const XMLCh* systemId = bbSysId.getRawBuffer();
2306    decl.setPublicId((publicId && *publicId) ? publicId : 0);
2307    decl.setSystemId((systemId && *systemId) ? systemId : 0);
2308    decl.setBaseURI((lastInfo.systemId && *lastInfo.systemId) ? lastInfo.systemId : 0);
2309
2310    // If its a PE decl, we are done
2311    bool gotSpaces = checkForPERef(false, true);
2312    if (isPEDecl)
2313    {
2314        //
2315        //  Check for a common error here. NDATA is not allowed for PEs
2316        //  so check for the NDATA string. If found give a nice meaningful
2317        //  error and continue parsing to eat the NDATA text.
2318        //
2319        if (gotSpaces)
2320        {
2321            if (fReaderMgr->skippedString(XMLUni::fgNDATAString))
2322                fScanner->emitError(XMLErrs::NDATANotValidForPE);
2323        }
2324         else
2325        {
2326            return true;
2327        }
2328    }
2329
2330    // If looking at close angle now, we are done
2331    if (fReaderMgr->lookingAtChar(chCloseAngle))
2332        return true;
2333
2334    // Else we had to have seem the whitespace
2335    if (!gotSpaces)
2336        fScanner->emitError(XMLErrs::ExpectedWhitespace);
2337
2338    // We now have to see a notation data string
2339    if (!fReaderMgr->skippedString(XMLUni::fgNDATAString))
2340        fScanner->emitError(XMLErrs::ExpectedNDATA);
2341
2342    // Space is required here, but try to go on if not
2343    if (!checkForPERef(false, true))
2344        fScanner->emitError(XMLErrs::ExpectedWhitespace);
2345
2346    // Get a name
2347    XMLBufBid bbName(fBufMgr);
2348    if (!fReaderMgr->getName(bbName.getBuffer()))
2349    {
2350        fScanner->emitError(XMLErrs::ExpectedNotationName);
2351        return false;
2352    }
2353
2354    // Set the decl's notation name
2355    decl.setNotationName(bbName.getRawBuffer());
2356
2357    return true;
2358}
2359
2360
2361//
2362//  This method is called after an attribute decl name or a notation decl has
2363//  been scanned and then an opening parenthesis was see, indicating the list
2364//  of values. It scans the enumeration values and creates a single string
2365//  which has a single space between each value.
2366//
2367//  The terminating close paren ends this scan.
2368//
2369bool DTDScanner::scanEnumeration( const   DTDAttDef&  attDef
2370                                    ,       XMLBuffer&  toFill
2371                                    , const bool        notation)
2372{
2373    // Reset the passed buffer
2374    toFill.reset();
2375
2376    // Check for PE ref but don't require space
2377    checkForPERef(false, true);
2378
2379    // If this is a notation, we need an opening paren
2380    if (notation)
2381    {
2382        if (!fReaderMgr->skippedChar(chOpenParen))
2383            fScanner->emitError(XMLErrs::ExpectedOpenParen);
2384    }
2385
2386    // We need a local buffer to use as well
2387    XMLBufBid bbTmp(fBufMgr);
2388
2389    while (true)
2390    {
2391        // Space is allowed here for either type so check for PE ref
2392        checkForPERef(false, true);
2393
2394        // And then get either a name or a name token
2395        bool success;
2396        if (notation)
2397            success = fReaderMgr->getName(bbTmp.getBuffer());
2398        else
2399            success = fReaderMgr->getNameToken(bbTmp.getBuffer());
2400
2401        if (!success)
2402        {
2403            fScanner->emitError
2404            (
2405                XMLErrs::ExpectedEnumValue
2406                , attDef.getFullName()
2407            );
2408            return false;
2409        }
2410
2411        // Append this value to the target value
2412        toFill.append(bbTmp.getRawBuffer(), bbTmp.getLen());
2413
2414        // Space is allowed here for either type so check for PE ref
2415        checkForPERef(false, true);
2416
2417        // Check for the terminating paren
2418        if (fReaderMgr->skippedChar(chCloseParen))
2419            break;
2420
2421        // And append a space separator
2422        toFill.append(chSpace);
2423
2424        // Check for the pipe character separator
2425        if (!fReaderMgr->skippedChar(chPipe))
2426        {
2427            fScanner->emitError(XMLErrs::ExpectedEnumSepOrParen);
2428            return false;
2429        }
2430    }
2431    return true;
2432}
2433
2434
2435bool DTDScanner::scanEq()
2436{
2437    fReaderMgr->skipPastSpaces();
2438    if (fReaderMgr->skippedChar(chEqual))
2439    {
2440        fReaderMgr->skipPastSpaces();
2441        return true;
2442    }
2443    return false;
2444}
2445
2446
2447//
2448//  This method is called when an external entity reference is seen in the
2449//  DTD or an external DTD subset is encountered, and their contents pushed
2450//  onto the reader stack. This method will scan that contents.
2451//
2452void DTDScanner::scanExtSubsetDecl(const bool inIncludeSect, const bool isDTD)
2453{
2454    // Indicate we are in the external subset now
2455    FlagJanitor<bool> janContentFlag(&fInternalSubset, false);
2456
2457
2458    bool bAcceptDecl = !inIncludeSect;
2459
2460    // Get a buffer for whitespace
2461    XMLBufBid bbSpace(fBufMgr);
2462
2463    //
2464    //  If we have a doc type handler and we are not being called recursively
2465    //  to handle an include section, tell it the ext subset starts
2466    //
2467    if (fDocTypeHandler && isDTD && !inIncludeSect)
2468        fDocTypeHandler->startExtSubset();
2469
2470    //
2471    //  We have to play a trick here if the current entity we are parsing
2472    //  is a PE. Because the spooling code will put out a whitespace before
2473    //  and after an expanded PE if its being scanned outside the context of
2474    //  a literal entity, this will confuse this external subset code.
2475    //
2476    //  So, we see if that is what is happening and, if so, eat the single
2477    //  space, a check for the <?xml string. If we find it, we parse that
2478    //  markup right now and put the space back.
2479    //
2480    if (fReaderMgr->isScanningPERefOutOfLiteral())
2481    {
2482        if (fReaderMgr->skippedSpace())
2483        {
2484            if (fScanner->checkXMLDecl(true))
2485            {
2486                scanTextDecl();
2487                bAcceptDecl = false;
2488
2489                // <TBD> Figure out how to do this
2490                // fReaderMgr->unGet(chSpace);
2491            }
2492        }
2493    }
2494
2495    // Get the current reader number
2496    const XMLSize_t orgReader = fReaderMgr->getCurrentReaderNum();
2497
2498    //
2499    //  Loop until we hit the end of the external subset entity. Note that
2500    //  we use a double loop here in order to avoid the overhead of doing
2501    //  the exception setup/teardown work on every loop.
2502    //
2503    bool inMarkup = false;
2504    bool inCharData = false;
2505    while (true)
2506    {
2507        bool bDoBreak=false;    // workaround for Borland bug with 'break' in 'catch'
2508        try
2509        {
2510            while (true)
2511            {
2512                const XMLCh nextCh = fReaderMgr->peekNextChar();
2513
2514                if (!nextCh)
2515                {
2516                    return; // nothing left
2517                }
2518                else if (nextCh == chOpenAngle)
2519                {
2520                    // Get the reader we started this on
2521                    // XML 1.0 P28a Well-formedness constraint: PE Between Declarations
2522                    const XMLSize_t orgReader = fReaderMgr->getCurrentReaderNum();
2523                    bool wasInPE = (fReaderMgr->getCurrentReader()->getType() == XMLReader::Type_PE);
2524
2525                    //
2526                    //  Now scan the markup. Set the flag so that we will know that
2527                    //  we were in markup if an end of entity exception occurs.
2528                    //
2529                    fReaderMgr->getNextChar();
2530                    inMarkup = true;
2531                    scanMarkupDecl(bAcceptDecl);
2532                    inMarkup = false;
2533
2534                    //
2535                    //  And see if we got back to the same level. If not, then its
2536                    //  a partial markup error.
2537                    //
2538                    if (fReaderMgr->getCurrentReaderNum() != orgReader){
2539                        if (wasInPE)
2540                            fScanner->emitError(XMLErrs::PEBetweenDecl);
2541                        else if (fScanner->getValidationScheme() == XMLScanner::Val_Always)
2542                            fScanner->getValidator()->emitError(XMLValid::PartialMarkupInPE);
2543                    }
2544
2545                }
2546                else if (fReaderMgr->getCurrentReader()->isWhitespace(nextCh))
2547                {
2548                    //
2549                    //  If we have a doc type handler, and advanced callbacks are
2550                    //  enabled, then gather up whitespace and call back. Otherwise
2551                    //  just skip whitespaces.
2552                    //
2553                    if (fDocTypeHandler)
2554                    {
2555                        inCharData = true;
2556                        fReaderMgr->getSpaces(bbSpace.getBuffer());
2557                        inCharData = false;
2558
2559                        fDocTypeHandler->doctypeWhitespace
2560                        (
2561                            bbSpace.getRawBuffer()
2562                            , bbSpace.getLen()
2563                        );
2564                    }
2565                    else
2566                    {
2567                        //
2568                        //  If we hit an end of entity in the middle of white
2569                        //  space, that's fine. We'll just come back in here
2570                        //  again on the next round and skip some more.
2571                        //
2572                        fReaderMgr->skipPastSpaces();
2573                    }
2574                }
2575                else if (nextCh == chPercent)
2576                {
2577                    //
2578                    //  Expand (and scan if external) the reference value. Tell
2579                    //  it to throw an end of entity exception at the end of the
2580                    //  entity.
2581                    //
2582                    fReaderMgr->getNextChar();
2583                    expandPERef(true, false, false, true);
2584                }
2585                else if (inIncludeSect && (nextCh == chCloseSquare))
2586                {
2587                    //
2588                    //  Its the end of a conditional include section. So scan it and
2589                    //  decrement the include depth counter.
2590                    //
2591                    fReaderMgr->getNextChar();
2592                    if (!fReaderMgr->skippedChar(chCloseSquare))
2593                    {
2594                        fScanner->emitError(XMLErrs::ExpectedEndOfConditional);
2595                        fReaderMgr->skipPastChar(chCloseAngle);
2596                    }
2597                    else if (!fReaderMgr->skippedChar(chCloseAngle))
2598                    {
2599                        fScanner->emitError(XMLErrs::ExpectedEndOfConditional);
2600                        fReaderMgr->skipPastChar(chCloseAngle);
2601                    }
2602                    return;
2603                }
2604                else
2605                {
2606                    fReaderMgr->getNextChar();
2607                    if (!fReaderMgr->getCurrentReader()->isXMLChar(nextCh))
2608                    {
2609                        XMLCh tmpBuf[9];
2610                        XMLString::binToText
2611                        (
2612                            nextCh
2613                            , tmpBuf
2614                            , 8
2615                            , 16
2616                            , fMemoryManager
2617                        );
2618                        fScanner->emitError(XMLErrs::InvalidCharacter, tmpBuf);
2619                    }
2620                    else
2621                    {
2622                        fScanner->emitError(XMLErrs::InvalidDocumentStructure);
2623                    }
2624
2625                    // Try to get realigned
2626                    static const XMLCh toSkip[] =
2627                    {
2628                        chPercent, chCloseSquare, chOpenAngle, chNull
2629                    };
2630                    fReaderMgr->skipUntilInOrWS(toSkip);
2631                }
2632                bAcceptDecl = false;
2633            }
2634        }
2635        catch(const EndOfEntityException& toCatch)
2636        {
2637            //
2638            //  If the external entity ended while we were in markup, then that's
2639            //  a partial markup error.
2640            //
2641            if (inMarkup)
2642            {
2643                fScanner->emitError(XMLErrs::PartialMarkupInEntity);
2644                inMarkup = false;
2645            }
2646
2647            // If we were in char data, then send what we got
2648            if (inCharData)
2649            {
2650                // Send what we got, then rethrow
2651                if (fDocTypeHandler)
2652                {
2653                    fDocTypeHandler->doctypeWhitespace
2654                    (
2655                        bbSpace.getRawBuffer()
2656                        , bbSpace.getLen()
2657                    );
2658                }
2659                inCharData = false;
2660            }
2661
2662            //
2663            //  If the entity that just ended was the entity that we started
2664            //  on, then this is the end of the external subset.
2665            //
2666            if (orgReader == toCatch.getReaderNum())
2667                bDoBreak=true;
2668        }
2669        if(bDoBreak)
2670            break;
2671    }
2672
2673    // If we have a doc type handler, tell it the ext subset ends
2674    if (fDocTypeHandler && isDTD && !inIncludeSect)
2675        fDocTypeHandler->endExtSubset();
2676}
2677
2678
2679//
2680//  This method will scan for an id, either public or external.
2681//
2682//
2683// [75] ExternalID ::= 'SYSTEM' S SystemLiteral
2684//                     | 'PUBLIC' S PubidLiteral S SystemLiteral
2685// [83] PublicID ::= 'PUBLIC' S PubidLiteral
2686//
2687bool DTDScanner::scanId(          XMLBuffer&  pubIdToFill
2688                            ,       XMLBuffer&  sysIdToFill
2689                            , const IDTypes     whatKind)
2690{
2691    // Clean out both return buffers
2692    pubIdToFill.reset();
2693    sysIdToFill.reset();
2694
2695    //
2696    //  Check first for the system id first. If we find it, and system id
2697    //  is one of the legal values, then lets try to scan it.
2698    //
2699    // 'SYSTEM' S SystemLiteral
2700    if (fReaderMgr->skippedString(XMLUni::fgSysIDString))
2701    {
2702        // If they were looking for a public id, then we failed
2703        if (whatKind == IDType_Public)
2704        {
2705            fScanner->emitError(XMLErrs::ExpectedPublicId);
2706            return false;
2707        }
2708
2709        // We must skip spaces
2710        bool skippedSomething;
2711        fReaderMgr->skipPastSpaces(skippedSomething);
2712        if (!skippedSomething)
2713        {
2714            fScanner->emitError(XMLErrs::ExpectedWhitespace);
2715            return false;
2716        }
2717
2718        // Get the system literal value
2719        return scanSystemLiteral(sysIdToFill);
2720    }
2721
2722    // Now scan for public id
2723    // 'PUBLIC' S PubidLiteral S SystemLiteral
2724    //  or
2725    // 'PUBLIC' S PubidLiteral
2726
2727    // If we don't have any public id string => Error
2728    if (!fReaderMgr->skippedString(XMLUni::fgPubIDString)) {
2729        fScanner->emitError(XMLErrs::ExpectedSystemOrPublicId);
2730        return false;
2731    }
2732
2733    //
2734    //  So following this we must have whitespace, a public literal, whitespace,
2735    //  and a system literal.
2736    //
2737    bool skippedSomething;
2738    fReaderMgr->skipPastSpaces(skippedSomething);
2739    if (!skippedSomething)
2740    {
2741        fScanner->emitError(XMLErrs::ExpectedWhitespace);
2742
2743        //
2744        //  Just in case, if they just forgot the whitespace but the next char
2745        //  is a single or double quote, then keep going.
2746        //
2747        const XMLCh chPeek = fReaderMgr->peekNextChar();
2748        if ((chPeek != chDoubleQuote) && (chPeek != chSingleQuote))
2749            return false;
2750    }
2751
2752    if (!scanPublicLiteral(pubIdToFill))
2753        return false;
2754
2755    // If they wanted a public id, then this is all
2756    if (whatKind == IDType_Public)
2757        return true;
2758
2759    // check if there is any space follows
2760    bool hasSpace;
2761    fReaderMgr->skipPastSpaces(hasSpace);
2762
2763    //
2764    //  In order to recover best here we need to see if
2765    //  the next thing is a quote or not
2766    //
2767    const XMLCh chPeek = fReaderMgr->peekNextChar();
2768    const bool bIsQuote =  ((chPeek == chDoubleQuote)
2769                         || (chPeek == chSingleQuote));
2770
2771    if (!hasSpace)
2772    {
2773        if (whatKind == IDType_External)
2774        {
2775            //
2776            //  If its an external Id, then we need to see the system id.
2777            //  So, emit the error. But, if the next char is a quote, don't
2778            //  give up since its probably going to work. The user just
2779            //  missed the separating space. Otherwise, fail.
2780            //
2781            fScanner->emitError(XMLErrs::ExpectedWhitespace);
2782            if (!bIsQuote)
2783                return false;
2784        }
2785         else
2786        {
2787            //
2788            //  We can legally return here. But, if the next char is a quote,
2789            //  then that's probably not what was desired, since its probably
2790            //  just that space was forgotten and there really is a system
2791            //  id to follow.
2792            //
2793            //  So treat it like missing whitespace if so and keep going.
2794            //  Else, just return success.
2795            //
2796            if (bIsQuote)
2797                fScanner->emitError(XMLErrs::ExpectedWhitespace);
2798             else
2799                return true;
2800        }
2801    }
2802
2803    if (bIsQuote) {
2804        // there is a quote coming, scan the system literal
2805        if (!scanSystemLiteral(sysIdToFill))
2806            return false;
2807    }
2808    else {
2809        // no quote, if expecting exteral id, this is an error
2810        if (whatKind == IDType_External)
2811            fScanner->emitError(XMLErrs::ExpectedQuotedString);
2812    }
2813
2814    return true;
2815}
2816
2817
2818//
2819//  This method will scan the contents of an ignored section. It assumes that
2820//  we already are in the body, i.e. we've seen <![IGNORE[ at this point. So
2821//  we have to just scan until we see a matching ]]> closing markup.
2822//
2823void DTDScanner::scanIgnoredSection()
2824{
2825    //
2826    //  Depth starts at one because we are already in one section and want
2827    //  to parse until we hit its end.
2828    //
2829    unsigned long depth = 1;
2830    bool gotLeadingSurrogate = false;
2831    while (true)
2832    {
2833        const XMLCh nextCh = fReaderMgr->getNextChar();
2834
2835        if (!nextCh)
2836            ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
2837
2838        if (nextCh == chOpenAngle)
2839        {
2840            if (fReaderMgr->skippedChar(chBang)
2841            &&  fReaderMgr->skippedChar(chOpenSquare))
2842            {
2843                depth++;
2844            }
2845        }
2846         else if (nextCh == chCloseSquare)
2847        {
2848            if (fReaderMgr->skippedChar(chCloseSquare))
2849            {
2850                while (fReaderMgr->skippedChar(chCloseSquare))
2851                {
2852                    // Do nothing, just skip them
2853                }
2854
2855                if (fReaderMgr->skippedChar(chCloseAngle))
2856                {
2857                    depth--;
2858                    if (!depth)
2859                        break;
2860                }
2861            }
2862        }
2863        // Deal with surrogate pairs
2864        else if ((nextCh >= 0xD800) && (nextCh <= 0xDBFF))
2865        {
2866            //  Its a leading surrogate. If we already got one, then
2867            //  issue an error, else set leading flag to make sure that
2868            //  we look for a trailing next time.
2869            if (gotLeadingSurrogate)
2870                fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
2871            else
2872                gotLeadingSurrogate = true;
2873        }
2874        else
2875        {
2876            //  If its a trailing surrogate, make sure that we are
2877            //  prepared for that. Else, its just a regular char so make
2878            //  sure that we were not expected a trailing surrogate.
2879            if ((nextCh >= 0xDC00) && (nextCh <= 0xDFFF))
2880            {
2881                // Its trailing, so make sure we were expecting it
2882                if (!gotLeadingSurrogate)
2883                    fScanner->emitError(XMLErrs::Unexpected2ndSurrogateChar);
2884            }
2885            else
2886            {
2887                //  Its just a char, so make sure we were not expecting a
2888                //  trailing surrogate.
2889                if (gotLeadingSurrogate)
2890                    fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
2891
2892                // Its got to at least be a valid XML character
2893                else if (!fReaderMgr->getCurrentReader()->isXMLChar(nextCh))
2894                {
2895                    XMLCh tmpBuf[9];
2896                    XMLString::binToText
2897                    (
2898                        nextCh
2899                        , tmpBuf
2900                        , 8
2901                        , 16
2902                        , fMemoryManager
2903                    );
2904                    fScanner->emitError(XMLErrs::InvalidCharacter, tmpBuf);
2905                }
2906            }
2907            gotLeadingSurrogate = false;
2908        }
2909    }
2910}
2911
2912
2913//
2914//  This method scans the entire internal subset. All we can have here is
2915//  decl markup, and PE references. The expanded PE references must contain
2916//  whole markup, so we don't have to worry about their content at this
2917//  level. We just scan them, expand them, push them, and parse their content
2918//  right there, via the expandERef() method.
2919//
2920bool DTDScanner::scanInternalSubset()
2921{
2922    // Indicate we are in the internal subset now
2923    FlagJanitor<bool> janContentFlag(&fInternalSubset, true);
2924
2925    // If we have a doc type handler, tell it the internal subset starts
2926    if (fDocTypeHandler)
2927        fDocTypeHandler->startIntSubset();
2928
2929    // Get a buffer for whitespace
2930    XMLBufBid bbSpace(fBufMgr);
2931
2932    bool noErrors = true;
2933    while (true)
2934    {
2935        const XMLCh nextCh = fReaderMgr->peekNextChar();
2936
2937        //
2938        //  If we get an end of file marker, just unget it and return a
2939        //  failure status. The caller will then see the end of file and
2940        //  faill out correctly.
2941        //
2942        if (!nextCh)
2943            return false;
2944
2945        // Watch for the end of internal subset marker
2946        if (nextCh == chCloseSquare)
2947        {
2948            fReaderMgr->getNextChar();
2949            break;
2950        }
2951
2952        if (nextCh == chPercent)
2953        {
2954            //
2955            //  Expand (and scan if external) the reference value. Tell
2956            //  it to set the reader to cause an end of entity exception
2957            //  when this reader dies, which is what the scanExtSubset
2958            //  method wants (who is called to scan this.)
2959            //
2960            fReaderMgr->getNextChar();
2961            expandPERef(true, false, false, true);
2962        }
2963         else if (nextCh == chOpenAngle)
2964        {
2965            // Remember this reader before we start the scan, for checking
2966            // XML 1.0 P28a Well-formedness constraint: PE Between Declarations
2967            const XMLSize_t orgReader = fReaderMgr->getCurrentReaderNum();
2968            bool wasInPE = (fReaderMgr->getCurrentReader()->getType() == XMLReader::Type_PE);
2969
2970            // And scan this markup
2971            fReaderMgr->getNextChar();
2972            scanMarkupDecl(false);
2973
2974            // If we did not get back to entry level, then partial markup
2975            if (fReaderMgr->getCurrentReaderNum() != orgReader) {
2976                if (wasInPE)
2977                    fScanner->emitError(XMLErrs::PEBetweenDecl);
2978                else if (fScanner->getValidationScheme() == XMLScanner::Val_Always)
2979                    fScanner->getValidator()->emitError(XMLValid::PartialMarkupInPE);
2980            }
2981        }
2982         else if (fReaderMgr->getCurrentReader()->isWhitespace(nextCh))
2983        {
2984            //
2985            //  IF we are doing advanced callbacks and have a doc type
2986            //  handler, then get the whitespace and call the doc type
2987            //  handler with it. Otherwise, just skip whitespace.
2988            //
2989            if (fDocTypeHandler)
2990            {
2991                fReaderMgr->getSpaces(bbSpace.getBuffer());
2992                fDocTypeHandler->doctypeWhitespace
2993                (
2994                    bbSpace.getRawBuffer()
2995                    , bbSpace.getLen()
2996                );
2997            }
2998             else
2999            {
3000                fReaderMgr->skipPastSpaces();
3001            }
3002        }
3003         else
3004        {
3005            // Not valid, so emit an error
3006            XMLCh tmpBuf[9];
3007            XMLString::binToText
3008            (
3009                fReaderMgr->getNextChar()
3010                , tmpBuf
3011                , 8
3012                , 16
3013                , fMemoryManager
3014            );
3015            fScanner->emitError
3016            (
3017                XMLErrs::InvalidCharacterInIntSubset
3018                , tmpBuf
3019            );
3020
3021            //
3022            //  If an '>', then probably an abnormally terminated
3023            //  internal subset so just return.
3024            //
3025            if (nextCh == chCloseAngle)
3026            {
3027                noErrors = false;
3028                break;
3029            }
3030
3031            //
3032            //  Otherwise, try to sync back up by scanning forward for
3033            //  a reasonable start character.
3034            //
3035            static const XMLCh toSkip[] =
3036            {
3037                chPercent, chCloseSquare, chOpenAngle, chNull
3038            };
3039            fReaderMgr->skipUntilInOrWS(toSkip);
3040        }
3041    }
3042
3043    // If we have a doc type handler, tell it the internal subset ends
3044    if (fDocTypeHandler)
3045        fDocTypeHandler->endIntSubset();
3046
3047    return noErrors;
3048}
3049
3050
3051//
3052//  This method is called once we see a < in the input of an int/ext subset,
3053//  which indicates the start of some sort of markup.
3054//
3055void DTDScanner::scanMarkupDecl(const bool parseTextDecl)
3056{
3057    //
3058    //  We only have two valid first characters here. One is a ! which opens
3059    //  some markup decl. The other is a ?, which could begin either a PI
3060    //  or a text decl. If parseTextDecl is false, we cannot accept a text
3061    //  decl.
3062    //
3063    const XMLCh nextCh = fReaderMgr->getNextChar();
3064
3065    if (nextCh == chBang)
3066    {
3067        if (fReaderMgr->skippedChar(chDash))
3068        {
3069            if (fReaderMgr->skippedChar(chDash))
3070            {
3071                scanComment();
3072            }
3073             else
3074            {
3075                fScanner->emitError(XMLErrs::CommentsMustStartWith);
3076                fReaderMgr->skipPastChar(chCloseAngle);
3077            }
3078        }
3079         else if (fReaderMgr->skippedChar(chOpenSquare))
3080        {
3081            //
3082            //  Its a conditional section. This is only valid in the external
3083            //  subset, so issue an error if we aren't there.
3084            //
3085            if (fInternalSubset)
3086            {
3087                fScanner->emitError(XMLErrs::ConditionalSectInIntSubset);
3088                fReaderMgr->skipPastChar(chCloseAngle);
3089                return;
3090            }
3091
3092            // A PE ref can happen here, but space is not required
3093            checkForPERef(false, true);
3094
3095            if (fReaderMgr->skippedString(XMLUni::fgIncludeString))
3096            {
3097                checkForPERef(false, true);
3098
3099                // Check for the following open square bracket
3100                if (!fReaderMgr->skippedChar(chOpenSquare))
3101                    fScanner->emitError(XMLErrs::ExpectedINCLUDEBracket);
3102
3103                // Get the reader we started this on
3104                const XMLSize_t orgReader = fReaderMgr->getCurrentReaderNum();
3105
3106                checkForPERef(false, true);
3107
3108                //
3109                //  Recurse back to the ext subset call again, telling it its
3110                //  in an include section.
3111                //
3112                scanExtSubsetDecl(true, false);
3113
3114                //
3115                //  And see if we got back to the same level. If not, then its
3116                //  a partial markup error.
3117                //
3118                if (fReaderMgr->getCurrentReaderNum() != orgReader && fScanner->getValidationScheme() == XMLScanner::Val_Always)
3119                    fScanner->getValidator()->emitError(XMLValid::PartialMarkupInPE);
3120
3121            }
3122             else if (fReaderMgr->skippedString(XMLUni::fgIgnoreString))
3123            {
3124                checkForPERef(false, true);
3125
3126                // Check for the following open square bracket
3127                if (!fReaderMgr->skippedChar(chOpenSquare))
3128                    fScanner->emitError(XMLErrs::ExpectedINCLUDEBracket);
3129
3130                // Get the reader we started this on
3131                const XMLSize_t orgReader = fReaderMgr->getCurrentReaderNum();
3132
3133                // And scan over the ignored part
3134                scanIgnoredSection();
3135
3136                //
3137                //  And see if we got back to the same level. If not, then its
3138                //  a partial markup error.
3139                //
3140                if (fReaderMgr->getCurrentReaderNum() != orgReader && fScanner->getValidationScheme() == XMLScanner::Val_Always)
3141                    fScanner->getValidator()->emitError(XMLValid::PartialMarkupInPE);
3142
3143            }
3144             else
3145            {
3146                fScanner->emitError(XMLErrs::ExpectedIncOrIgn);
3147                fReaderMgr->skipPastChar(chCloseAngle);
3148            }
3149        }
3150         else if (fReaderMgr->skippedString(XMLUni::fgAttListString))
3151        {
3152            scanAttListDecl();
3153        }
3154         else if (fReaderMgr->skippedString(XMLUni::fgElemString))
3155        {
3156            scanElementDecl();
3157        }
3158         else if (fReaderMgr->skippedString(XMLUni::fgEntityString))
3159        {
3160            scanEntityDecl();
3161        }
3162         else if (fReaderMgr->skippedString(XMLUni::fgNotationString))
3163        {
3164            scanNotationDecl();
3165        }
3166         else
3167        {
3168            fScanner->emitError(XMLErrs::ExpectedMarkupDecl);
3169            fReaderMgr->skipPastChar(chCloseAngle);
3170        }
3171    }
3172     else if (nextCh == chQuestion)
3173    {
3174        // It could be a PI or the XML declaration. Check for Decl
3175        if (fScanner->checkXMLDecl(false))
3176        {
3177            // If we are not accepting text decls, its an error
3178            if (parseTextDecl)
3179            {
3180                scanTextDecl();
3181            }
3182             else
3183            {
3184                // Emit the error and skip past this markup
3185                fScanner->emitError(XMLErrs::TextDeclNotLegalHere);
3186                fReaderMgr->skipPastChar(chCloseAngle);
3187            }
3188        }
3189         else
3190        {
3191            // It has to be a PI
3192            scanPI();
3193        }
3194    }
3195     else
3196    {
3197        // Can't be valid so emit error and try to skip past end of this decl
3198        fScanner->emitError(XMLErrs::ExpectedMarkupDecl);
3199        fReaderMgr->skipPastChar(chCloseAngle);
3200    }
3201}
3202
3203
3204//
3205//  This method is called for a mixed model element's content mode. We've
3206//  already scanned past the '(PCDATA' part by the time we get here. So
3207//  everything else is element names separated by | characters until we
3208//  hit the end. The passed element decl's content model is filled in with
3209//  the information found.
3210//
3211bool DTDScanner::scanMixed(DTDElementDecl& toFill)
3212{
3213    //
3214    //  The terminating star is only required if there is something more
3215    //  than (PCDATA).
3216    //
3217    bool starRequired = false;
3218
3219    // Get a buffer to be used below to get element names
3220    XMLBufBid bbName(fBufMgr);
3221    XMLBuffer& nameBuf = bbName.getBuffer();
3222
3223    //
3224    //  Create an initial content spec node. Its just a leaf node with a
3225    //  PCDATA element id. This current node pointer will be pushed down the
3226    //  tree as we go.
3227    //
3228    ContentSpecNode* curNode = new (fGrammarPoolMemoryManager) ContentSpecNode
3229    (
3230        new (fGrammarPoolMemoryManager) QName
3231        (
3232            XMLUni::fgZeroLenString
3233            , XMLUni::fgZeroLenString
3234            , XMLElementDecl::fgPCDataElemId
3235            , fGrammarPoolMemoryManager
3236        )
3237        , false
3238        , fGrammarPoolMemoryManager
3239    );
3240
3241    //
3242    //  Set the initial leaf as the temporary head. If we hit the first choice
3243    //  node, it will be set up here. When done, this is the node that's set
3244    //  as the content spec for the element.
3245    //
3246    ContentSpecNode* headNode = curNode;
3247
3248    // Remember the original node so we can sense the first choice node
3249    ContentSpecNode* orgNode = curNode;
3250
3251    //
3252    //  We just loop around, getting the | character at the top and then
3253    //  looking for the next element name. We keep up with the last node
3254    //  and add each new one to its right node.
3255    //
3256    while (true)
3257    {
3258        //
3259        //  First of all we check for some grunt work details of skipping
3260        //  whitespace, expand PE refs, and catching invalid reps.
3261        //
3262        if (fReaderMgr->lookingAtChar(chPercent))
3263        {
3264            // Expand it and continue
3265            checkForPERef(false, true);
3266        }
3267         else if (fReaderMgr->skippedChar(chAsterisk))
3268        {
3269            //
3270            //  Tell them they can't have reps in mixed model, but eat
3271            //  it and keep going if we are allowed to.
3272            //
3273            if (fScanner->emitErrorWillThrowException(XMLErrs::NoRepInMixed))
3274            {
3275                delete headNode;
3276            }
3277            fScanner->emitError(XMLErrs::NoRepInMixed);
3278        }
3279         else if (fReaderMgr->skippedSpace())
3280        {
3281            // Spaces are ok at this point, just eat them and continue
3282            fReaderMgr->skipPastSpaces();
3283        }
3284         else
3285        {
3286            if (!fReaderMgr->skippedChar(chPipe))
3287            {
3288                // Has to be the closing paren now.
3289                if (!fReaderMgr->skippedChar(chCloseParen))
3290                {
3291                    delete headNode;
3292                    fScanner->emitError(XMLErrs::UnterminatedContentModel, toFill.getElementName()->getLocalPart());                     
3293                    return false;
3294                }
3295
3296                bool starSkipped = true;
3297                if (!fReaderMgr->skippedChar(chAsterisk)) {
3298
3299                    starSkipped = false;
3300
3301                    if (starRequired)
3302                    {
3303                        if (fScanner->emitErrorWillThrowException(XMLErrs::ExpectedAsterisk))
3304                        {
3305                            delete headNode;
3306                        }
3307                        fScanner->emitError(XMLErrs::ExpectedAsterisk);
3308                    }
3309                }
3310
3311                //
3312                //  Create a zero or more node and make the original head
3313                //  node its first child.
3314                //
3315                if (starRequired || starSkipped) {
3316                    headNode = new (fGrammarPoolMemoryManager) ContentSpecNode
3317                    (
3318                        ContentSpecNode::ZeroOrMore
3319                        , headNode
3320                        , 0
3321                        , true
3322                        , true
3323                        , fGrammarPoolMemoryManager
3324                    );
3325                }
3326
3327                // Store the head node as the content spec of the element.
3328                toFill.setContentSpec(headNode);
3329                break;
3330            }
3331
3332            // Its more than just a PCDATA, so an ending star will be required now
3333            starRequired = true;
3334
3335            // Space is legal here so check for a PE ref, but don't require space
3336            checkForPERef(false, true);
3337
3338            // Get a name token
3339            if (!fReaderMgr->getName(nameBuf))
3340            {
3341                delete headNode;
3342                fScanner->emitError(XMLErrs::ExpectedElementName);
3343                return false;
3344            }
3345
3346            //
3347            //  Create a leaf node for it. If we can find the element id for
3348            //  this element, then use it. Else, we have to fault in an element
3349            //  decl, marked as created because of being in a content model.
3350            //
3351            XMLElementDecl* decl = fDTDGrammar->getElemDecl(fEmptyNamespaceId, 0, nameBuf.getRawBuffer(), Grammar::TOP_LEVEL_SCOPE);
3352            if (!decl)
3353            {
3354                decl = new (fGrammarPoolMemoryManager) DTDElementDecl
3355                (
3356                    nameBuf.getRawBuffer()
3357                    , fEmptyNamespaceId
3358                    , DTDElementDecl::Any
3359                    , fGrammarPoolMemoryManager
3360                );
3361                decl->setCreateReason(XMLElementDecl::InContentModel);
3362                decl->setExternalElemDeclaration(isReadingExternalEntity());
3363                fDTDGrammar->putElemDecl(decl);
3364            }
3365
3366            //
3367            //  If the current node is the original node, this is the first choice
3368            //  node, so create an initial choice node with the current node and
3369            //  the new element id. Store this as the head node.
3370            //
3371            //  Otherwise, we have to steal the right node of the previous choice
3372            //  and weave in another choice node there, which has the old choice
3373            //  as its left and the new leaf as its right.
3374            //
3375            if (curNode == orgNode)
3376            {
3377                curNode = new (fGrammarPoolMemoryManager) ContentSpecNode
3378                (
3379                    ContentSpecNode::Choice
3380                    , curNode
3381                    , new (fGrammarPoolMemoryManager) ContentSpecNode
3382                      (
3383                          decl->getElementName()
3384                          , fGrammarPoolMemoryManager
3385                      )
3386                    , true
3387                    , true
3388                    , fGrammarPoolMemoryManager
3389                );
3390
3391                // Remember the top node
3392                headNode = curNode;
3393            }
3394             else
3395            {
3396                ContentSpecNode* oldRight = curNode->orphanSecond();
3397                curNode->setSecond
3398                (
3399                    new (fGrammarPoolMemoryManager) ContentSpecNode
3400                    (
3401                        ContentSpecNode::Choice
3402                        , oldRight
3403                        , new (fGrammarPoolMemoryManager) ContentSpecNode
3404                          (
3405                              decl->getElementName()
3406                              , fGrammarPoolMemoryManager
3407                          )
3408                        , true
3409                        , true
3410                        , fGrammarPoolMemoryManager
3411                    )
3412                );
3413
3414                // Make the new right node the current node
3415                curNode = curNode->getSecond();
3416            }
3417        }
3418    }
3419
3420    return true;
3421}
3422
3423
3424//
3425//  This method is called when we see a '<!NOTATION' string while scanning
3426//  markup decl. It parses out the notation and its id and stores a new
3427//  notation decl object in the notation decl pool.
3428//
3429void DTDScanner::scanNotationDecl()
3430{
3431    // Space is required here so check for a PE ref, and require space
3432    if (!checkForPERef(false, true))
3433    {
3434        fScanner->emitError(XMLErrs::ExpectedWhitespace);
3435        fReaderMgr->skipPastChar(chCloseAngle);
3436        return;
3437    }
3438
3439    //
3440    //  And now we get a name, which is the name of the notation. Get a
3441    //  buffer for the name.
3442    //
3443    XMLBufBid bbName(fBufMgr);
3444    if (!fReaderMgr->getName(bbName.getBuffer()))
3445    {
3446        fScanner->emitError(XMLErrs::ExpectedNotationName);
3447        fReaderMgr->skipPastChar(chCloseAngle);
3448        return;
3449    }
3450
3451    // If namespaces are enabled, then no colons allowed
3452    if (fScanner->getDoNamespaces())
3453    {
3454        if (XMLString::indexOf(bbName.getRawBuffer(), chColon) != -1)
3455            fScanner->emitError(XMLErrs::ColonNotLegalWithNS);
3456    }
3457
3458    // Space is required here so check for a PE ref, and require space
3459    if (!checkForPERef(false, true))
3460    {
3461        fScanner->emitError(XMLErrs::ExpectedWhitespace);
3462        fReaderMgr->skipPastChar(chCloseAngle);
3463        return;
3464    }
3465
3466    //
3467    //  And scan an external or public id. We need buffers to use for both
3468    //  of these.
3469    //
3470    XMLBufBid bbPubId(fBufMgr);
3471    XMLBufBid bbSysId(fBufMgr);
3472    if (!scanId(bbPubId.getBuffer(), bbSysId.getBuffer(), IDType_Either))
3473    {
3474        fReaderMgr->skipPastChar(chCloseAngle);
3475        return;
3476    }
3477
3478    // We can have an optional space or PE ref here
3479    checkForPERef(false, true);
3480
3481    //
3482    //  See if it already exists. If so, add it to the notatino decl pool.
3483    //  Otherwise, if advanced callbacks are on, create a temp one and
3484    //  call out for that one.
3485    //
3486    XMLNotationDecl* decl = fDTDGrammar->getNotationDecl(bbName.getRawBuffer());
3487    bool isIgnoring = (decl != 0);
3488    if (isIgnoring)
3489    {
3490        fScanner->emitError(XMLErrs::NotationAlreadyExists, bbName.getRawBuffer());
3491    }
3492     else
3493    {
3494        // Fill in a new notation declaration and add it to the pool
3495        const XMLCh* publicId = bbPubId.getRawBuffer();
3496        const XMLCh* systemId = bbSysId.getRawBuffer();
3497        ReaderMgr::LastExtEntityInfo lastInfo;
3498        fReaderMgr->getLastExtEntityInfo(lastInfo);
3499
3500        decl = new (fGrammarPoolMemoryManager) XMLNotationDecl
3501        (
3502            bbName.getRawBuffer()
3503            , (publicId && *publicId) ? publicId : 0
3504            , (systemId && *systemId) ? systemId : 0
3505            , (lastInfo.systemId && *lastInfo.systemId) ? lastInfo.systemId : 0
3506            , fGrammarPoolMemoryManager
3507        );
3508        fDTDGrammar->putNotationDecl(decl);
3509    }
3510
3511    //
3512    //  If we have a document type handler, then tell it about this. If we
3513    //  are ignoring it, only call out if advanced callbacks are enabled.
3514    //
3515    if (fDocTypeHandler)
3516    {
3517        fDocTypeHandler->notationDecl
3518        (
3519            *decl
3520            , isIgnoring
3521        );
3522    }
3523
3524    // And one more optional space or PE ref
3525    checkForPERef(false, true);
3526
3527    // And skip the terminating bracket
3528    if (!fReaderMgr->skippedChar(chCloseAngle))
3529        fScanner->emitError(XMLErrs::UnterminatedNotationDecl);
3530}
3531
3532
3533//
3534//  Scans a PI and calls the appropriate callbacks. A PI can happen in either
3535//  the document or the DTD, so it calls the appropriate handler according
3536//  to the fInDocument flag.
3537//
3538//  At entry we have just scanned the <? part, and need to now start on the
3539//  PI target name.
3540//
3541void DTDScanner::scanPI()
3542{
3543    const XMLCh* namePtr = 0;
3544    const XMLCh* targetPtr = 0;
3545
3546    //
3547    //  If there are any spaces here, then warn about it. If we aren't in
3548    //  'first error' mode, then we'll come back and can easily pick up
3549    //  again by just skipping them.
3550    //
3551    if (fReaderMgr->lookingAtSpace())
3552    {
3553        fScanner->emitError(XMLErrs::PINameExpected);
3554        fReaderMgr->skipPastSpaces();
3555    }
3556
3557    // Get a buffer for the PI name and scan it in
3558    XMLBufBid bbName(fBufMgr);
3559    if (!fReaderMgr->getName(bbName.getBuffer()))
3560    {
3561        fScanner->emitError(XMLErrs::PINameExpected);
3562        fReaderMgr->skipPastChar(chCloseAngle);
3563        return;
3564    }
3565
3566    // Point the name pointer at the raw data
3567    namePtr = bbName.getRawBuffer();
3568
3569    // See if it issome form of 'xml' and emit a warning
3570    //if (!XMLString::compareIString(namePtr, XMLUni::fgXMLString))
3571    if (bbName.getLen() == 3 &&
3572        (((namePtr[0] == chLatin_x) || (namePtr[0] == chLatin_X)) &&
3573         ((namePtr[1] == chLatin_m) || (namePtr[1] == chLatin_M)) &&
3574         ((namePtr[2] == chLatin_l) || (namePtr[2] == chLatin_L))))       
3575        fScanner->emitError(XMLErrs::NoPIStartsWithXML);
3576
3577    // If namespaces are enabled, then no colons allowed
3578    if (fScanner->getDoNamespaces())
3579    {
3580        if (XMLString::indexOf(namePtr, chColon) != -1)
3581            fScanner->emitError(XMLErrs::ColonNotLegalWithNS);
3582    }
3583
3584    //
3585    //  If we don't hit a space next, then the PI has no target. If we do
3586    //  then get out the target. Get a buffer for it as well
3587    //
3588    XMLBufBid bbTarget(fBufMgr);
3589    if (fReaderMgr->skippedSpace())
3590    {
3591        // Skip any leading spaces
3592        fReaderMgr->skipPastSpaces();
3593
3594        bool gotLeadingSurrogate = false;
3595
3596        // It does have a target, so lets move on to deal with that.
3597        while (1)
3598        {
3599            const XMLCh nextCh = fReaderMgr->getNextChar();
3600
3601            // Watch for an end of file, which is always bad here
3602            if (!nextCh)
3603            {
3604                fScanner->emitError(XMLErrs::UnterminatedPI);
3605                ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
3606            }
3607
3608            // Watch for potential terminating character
3609            if (nextCh == chQuestion)
3610            {
3611                // It must be followed by '>' to be a termination of the target
3612                if (fReaderMgr->skippedChar(chCloseAngle))
3613                    break;
3614            }
3615
3616            // Check for correct surrogate pairs
3617            if ((nextCh >= 0xD800) && (nextCh <= 0xDBFF))
3618            {
3619                if (gotLeadingSurrogate)
3620                    fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
3621                else
3622                    gotLeadingSurrogate = true;
3623            }
3624             else
3625            {
3626                if (gotLeadingSurrogate)
3627                {
3628                    if ((nextCh < 0xDC00) || (nextCh > 0xDFFF))
3629                        fScanner->emitError(XMLErrs::Expected2ndSurrogateChar);
3630                }
3631                // Its got to at least be a valid XML character
3632                else if (!fReaderMgr->getCurrentReader()->isXMLChar(nextCh)) {
3633
3634                    XMLCh tmpBuf[9];
3635                    XMLString::binToText
3636                    (
3637                        nextCh
3638                        , tmpBuf
3639                        , 8
3640                        , 16
3641                        , fMemoryManager
3642                    );
3643                    fScanner->emitError(XMLErrs::InvalidCharacter, tmpBuf);
3644                }
3645
3646                gotLeadingSurrogate = false;
3647            }
3648            bbTarget.append(nextCh);
3649        }
3650    }
3651     else
3652    {
3653        // No target, but make sure its terminated ok
3654        if (!fReaderMgr->skippedChar(chQuestion))
3655        {
3656            fScanner->emitError(XMLErrs::UnterminatedPI);
3657            fReaderMgr->skipPastChar(chCloseAngle);
3658            return;
3659        }
3660
3661        if (!fReaderMgr->skippedChar(chCloseAngle))
3662        {
3663            fScanner->emitError(XMLErrs::UnterminatedPI);
3664            fReaderMgr->skipPastChar(chCloseAngle);
3665            return;
3666        }
3667    }
3668
3669    // Point the target pointer at the raw data
3670    targetPtr = bbTarget.getRawBuffer();
3671
3672    //
3673    //  If we have a handler, then call it.
3674    //
3675    if (fDocTypeHandler)
3676    {
3677        fDocTypeHandler->doctypePI
3678        (
3679            namePtr
3680            , targetPtr
3681        );
3682    }
3683}
3684
3685
3686//
3687//  This method scans a public literal. It must be quoted and all of its
3688//  characters must be valid public id characters. The quotes are discarded
3689//  and the results are returned.
3690//
3691bool DTDScanner::scanPublicLiteral(XMLBuffer& toFill)
3692{
3693    toFill.reset();
3694
3695    // Get the next char which must be a single or double quote
3696    XMLCh quoteCh;
3697    if (!fReaderMgr->skipIfQuote(quoteCh)) {
3698        fScanner->emitError(XMLErrs::ExpectedQuotedString);
3699        return false;
3700    }
3701
3702    while (true)
3703    {
3704        const XMLCh nextCh = fReaderMgr->getNextChar();
3705
3706        // Watch for EOF
3707        if (!nextCh)
3708            ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
3709
3710        if (nextCh == quoteCh)
3711            break;
3712
3713        //
3714        //  If its not a valid public id char, then report it but keep going
3715        //  since that's the best recovery scheme.
3716        //
3717        if (!fReaderMgr->getCurrentReader()->isPublicIdChar(nextCh))
3718        {
3719            XMLCh tmpBuf[9];
3720            XMLString::binToText
3721            (
3722                nextCh
3723                , tmpBuf
3724                , 8
3725                , 16
3726                , fMemoryManager
3727            );
3728            fScanner->emitError(XMLErrs::InvalidPublicIdChar, tmpBuf);
3729        }
3730
3731        toFill.append(nextCh);
3732    }
3733    return true;
3734}
3735
3736
3737//
3738//  This method handles scanning in a quoted system literal. It expects to
3739//  start on the open quote and returns after eating the ending quote. There
3740//  are not really any restrictions on the contents of system literals.
3741//
3742bool DTDScanner::scanSystemLiteral(XMLBuffer& toFill)
3743{
3744    toFill.reset();
3745
3746    // Get the next char which must be a single or double quote
3747    XMLCh quoteCh;
3748    if (!fReaderMgr->skipIfQuote(quoteCh)) {
3749        fScanner->emitError(XMLErrs::ExpectedQuotedString);
3750        return false;
3751    }
3752
3753        XMLCh nextCh;
3754    // Break out on terminating quote
3755    while ((nextCh=fReaderMgr->getNextChar())!=quoteCh)
3756    {
3757        // Watch for EOF
3758        if (!nextCh)
3759            ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
3760        toFill.append(nextCh);
3761    }
3762    return true;
3763}
3764
3765
3766
3767//
3768//  This method is called to scan a text decl line, which can be the first
3769//  line in an external entity or external subset.
3770//
3771//  On entry the <? has been scanned, and next should be 'xml' followed by
3772//  some whitespace, version string, etc...
3773//    [77] TextDecl::= '<?xml' VersionInfo? EncodingDecl S? '?>'
3774//
3775void DTDScanner::scanTextDecl()
3776{
3777    // Skip any subsequent whitespace before the version string
3778    fReaderMgr->skipPastSpaces();
3779
3780    // Next should be the version string
3781    XMLBufBid bbVersion(fBufMgr);
3782    if (fReaderMgr->skippedString(XMLUni::fgVersionString))
3783    {
3784        if (!scanEq())
3785        {
3786            fScanner->emitError(XMLErrs::ExpectedEqSign);
3787            fReaderMgr->skipPastChar(chCloseAngle);
3788            return;
3789        }
3790
3791        //
3792        //  Followed by a single or double quoted version. Get a buffer for
3793        //  the string.
3794        //
3795        if (!getQuotedString(bbVersion.getBuffer()))
3796        {
3797            fScanner->emitError(XMLErrs::BadXMLVersion);
3798            fReaderMgr->skipPastChar(chCloseAngle);
3799            return;
3800        }
3801
3802        // If its not our supported version, issue an error but continue
3803        if (XMLString::equals(bbVersion.getRawBuffer(), XMLUni::fgVersion1_1)) {
3804            if (fScanner->getXMLVersion() != XMLReader::XMLV1_1)
3805                    fScanner->emitError(XMLErrs::UnsupportedXMLVersion, bbVersion.getRawBuffer());
3806        }
3807        else if (!XMLString::equals(bbVersion.getRawBuffer(), XMLUni::fgVersion1_0))
3808            fScanner->emitError(XMLErrs::UnsupportedXMLVersion, bbVersion.getRawBuffer());
3809    }
3810
3811    // Ok, now we must have an encoding string
3812    XMLBufBid bbEncoding(fBufMgr);
3813    fReaderMgr->skipPastSpaces();
3814    bool gotEncoding = false;
3815    if (fReaderMgr->skippedString(XMLUni::fgEncodingString))
3816    {
3817        // There must be a equal sign next
3818        if (!scanEq())
3819        {
3820            fScanner->emitError(XMLErrs::ExpectedEqSign);
3821            fReaderMgr->skipPastChar(chCloseAngle);
3822            return;
3823        }
3824
3825        // Followed by a single or double quoted version string
3826        getQuotedString(bbEncoding.getBuffer());
3827        if (bbEncoding.isEmpty() || !XMLString::isValidEncName(bbEncoding.getRawBuffer()))
3828        {
3829            fScanner->emitError(XMLErrs::BadXMLEncoding, bbEncoding.getRawBuffer());
3830            fReaderMgr->skipPastChar(chCloseAngle);
3831            return;
3832        }
3833
3834        // Indicate that we got an encoding
3835        gotEncoding = true;
3836    }
3837
3838    //
3839    // Encoding declarations are required in the external entity
3840    // if there is a text declaration present
3841    //
3842    if (!gotEncoding)
3843    {
3844      fScanner->emitError(XMLErrs::EncodingRequired);
3845      fReaderMgr->skipPastChar(chCloseAngle);
3846      return;
3847
3848    }
3849
3850    fReaderMgr->skipPastSpaces();
3851    if (!fReaderMgr->skippedChar(chQuestion))
3852    {
3853        fScanner->emitError(XMLErrs::UnterminatedXMLDecl);
3854        fReaderMgr->skipPastChar(chCloseAngle);
3855    }
3856     else if (!fReaderMgr->skippedChar(chCloseAngle))
3857    {
3858        fScanner->emitError(XMLErrs::UnterminatedXMLDecl);
3859        fReaderMgr->skipPastChar(chCloseAngle);
3860    }
3861
3862    //
3863    //  If we have a document type handler and advanced callbacks are on,
3864    //  then call the TextDecl callback
3865    //
3866    if (fDocTypeHandler)
3867    {
3868        fDocTypeHandler->TextDecl
3869        (
3870            bbVersion.getRawBuffer()
3871            , bbEncoding.getRawBuffer()
3872        );
3873    }
3874
3875    //
3876    //  If we got an encoding string, then we have to call back on the reader
3877    //  to tell it what the encoding is.
3878    //
3879    if (!bbEncoding.isEmpty())
3880    {
3881        if (!fReaderMgr->getCurrentReader()->setEncoding(bbEncoding.getRawBuffer()))
3882            fScanner->emitError(XMLErrs::ContradictoryEncoding, bbEncoding.getRawBuffer());
3883    }
3884}
3885
3886XERCES_CPP_NAMESPACE_END
Note: See TracBrowser for help on using the repository browser.