source: icXML/icXML-devel/src/icxmlc/XMLParserImpl.c @ 2774

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

Various fixes

File size: 38.7 KB
Line 
1/*
2 *  Copyright © 2012 International Characters.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 *  icXML is a trademark of International Characters.
5 */
6
7/*
8 * @author Nigel Medforth, nigelm -at- interational-characters.com
9 * @version $Id: XMLParserImpl.c 224 2012-12-12 03:31:56Z nigelm $
10 *
11 */
12
13#include <icxmlc/XMLParserImpl.hpp>
14#include <icxercesc/util/TransService.hpp>
15
16XERCES_CPP_NAMESPACE_BEGIN
17
18#define XML_PARSER_IMPL(retType) \
19        template<class XMLScannerType> \
20        retType \
21        XMLParserImpl<XMLScannerType>
22
23#define XML_PARSER_IMPL_WITH_DOCUMENT_STATE_PARAMS(retType) \
24        template<class XMLScannerType> \
25        template<XMLParser::DocumentStateType docStateType> \
26        retType \
27        XMLParserImpl<XMLScannerType>
28
29// ---------------------------------------------------------------------------------------------------------
30
31XML_PARSER_IMPL(void)::
32init
33(
34        XMLCharacterSetAdapter *        adapter
35        , XMLTranscoder *                       transcoder
36        , const XMLFileLoc                      line
37        , const XMLFileLoc                      column
38    , const XMLVersion          /* version */
39)
40{
41        // reset the curr and next line col trackers to the actual position
42        fCurrLineCol.set(line, column);
43        fCharacterSetAdapter = adapter;
44        fSymbolTable.setTranscoder(transcoder);
45        adapter->init(fScanner, &fSymbolTable, fCurrLineCol);
46        // registerSegFaultHandler();
47}
48
49// ---------------------------------------------------------------------------------------------------------
50
51XML_PARSER_IMPL(void)::
52preScanDocumentPage
53(
54        XMLByte                                 *       rawByteBuf
55        , const XMLSize_t                       rawBytesAvail
56        , const bool                            noMore
57        , XMLReader                             *       reader
58)
59{
60        /** ---------------------------------- BEGIN PARALLEL XML PARSING ---------------------------------- **/
61
62        DEBUG_TRANSITION_MESSAGE("######################################################################");
63        DEBUG_TRANSITION_MESSAGE("BEGIN XML PARSING SCAN: avail=" << rawBytesAvail << " noMore=" << noMore);
64        DEBUG_TRANSITION_MESSAGE("######################################################################");
65
66        fRawBytesAvail = rawBytesAvail;
67    fNoMore = noMore;
68
69        unsigned int bytesEaten;
70
71        fCursorEndPtr =
72                fCharacterSetAdapter->parse
73                (
74                        rawByteBuf
75                        , fRawDataOffset
76                        , fRawBytesAvail
77                        , noMore
78                        , fContentBuf
79                        , (fCursorPtr - &fContentBuf[0]) // contentOffset
80                        , fMarkupCount
81                        , fSymbolArray
82                        , fSymbolCount
83                        , fStringEndPtr
84                        , fStringCount
85                        , fNextLineCol
86                        , &fNewLineOrSkipMaskStream[fLineColStartIndex]
87                        , &fDelMaskStream[fLineColStartIndex]
88                        , fIncompleteMarkupBytes
89                        , fUnusedSymbols
90                        , fUnusedContent
91                        , bytesEaten
92                );
93
94        fSymbolIdx = 0;
95        fCursorPtr = &fContentBuf[0];
96        fCurrentStringEndPtr = &fStringEndPtr[0];
97
98        /** ----------------------------------- BUFFER SYMBOL STREAM ----------------------------------- **/
99
100        if (likely(!noMore))
101        {
102                // what's the earliest symbol position that we need to copy back? all data after
103                // this point must be copied back and will not be modified or inspected when
104                // parsing the next document page.
105
106                if (bytesEaten == XMLReader::kRawBufSize)
107                {
108                        fRawDataOffset = 0;
109                        reader->refreshRawBuffer(XMLReader::kRawBufSize, 0);
110                }
111                else
112                {
113                        // NOTE: the utf16 issue is probably here; its doing a copyback but does not
114                        // realize that a 128-bit bitblock is actually 256 bytes of raw data.
115                        const size_t blockSize = BLOCK_SIZE * fCharacterSetAdapter->getCodeUnitSize();
116                        fRawDataOffset = (fRawBytesAvail - bytesEaten + (blockSize - 1)) & ~(blockSize - 1);
117                        reader->refreshRawBuffer(bytesEaten, bytesEaten & (blockSize - 1));
118                }
119        }
120
121        /** ----------------------------------- BEGIN NAMESPACE SCAN ----------------------------------- **/
122
123        if (fScanner->getDoNamespaces())
124        {
125                resolveDocumentPageNamespaces();
126        }
127
128        /** ----------------------------------- BEGIN VALIDATION SCAN ----------------------------------- **/
129
130        // TODO: currently the scanMarkup and scanContent functions call the Scanner functions that both
131        // perform Element/Attribute validation and emit the final element/attribute lists to the user. If we
132        // remove the emit feature from those functions and instead create a third "output" pass, we could
133        // simply store the resolved ElemDecl and AttDefs in a void* list and have validation be its own stage.
134
135}
136
137// -------------------------------------------------------------------------------------------
138
139/**
140Scan the prolog portion of the document, which includes everything before the root element
141including the DTD subsets. Returns true if it successfully found the end of the prolog.
142(i.e., no more data is required to complete it.)
143**/
144XML_PARSER_IMPL(bool)::
145scanProlog()
146{
147    DEBUG_TRANSITION_MESSAGE(" ---------------------------- SCANNING PROLOG -------------------------------");
148
149        int r;
150        switch (fInMarkup)
151        {
152                case 0:     for (;;)
153                                        {
154                                                size_t length;
155                        r = scanContent<XMLParser::Prolog>(length);
156                                                if (r) break;
157
158        case 1:         r = scanMarkup<XMLParser::Prolog>();
159                                                if (r) break;
160                                        }
161                                        break;
162
163                default:    UNREACHABLE
164        }
165
166    return (r == XMLParser::EndOfPage);
167}
168
169// -------------------------------------------------------------------------------------------
170
171XML_PARSER_IMPL(bool)::
172scanDocumentPage()
173{
174        DEBUG_TRANSITION_MESSAGE("######################################################################");
175        DEBUG_TRANSITION_MESSAGE("BEGIN SCAN DOCUMENT PAGE:");
176        DEBUG_TRANSITION_MESSAGE("######################################################################");
177
178    XMLParser::XMLScanState retVal;
179        switch (fInMarkup)
180        {
181                case 0:     for (;;)
182                                        {
183                                                size_t length;
184                        retVal = scanContent<XMLParser::Element>(length);
185                        if (retVal) break;
186
187        case 1:                 retVal = scanMarkup<XMLParser::Element>();
188                        if (retVal) break;
189                                        }
190                                        break;
191
192                default:    UNREACHABLE
193        }
194
195    if (unlikely(retVal == XMLParser::EndOfDocumentSection && fScope != 0))
196    {
197        fScanner->emitError(XMLErrs::EndedWithTagsOnStack, fElement[fElementIndex]->getRawName());
198    }
199
200    return (retVal == XMLParser::EndOfPage);
201}
202
203// -------------------------------------------------------------------------------------------
204
205/** This function automatically sends the next piece of content / markup to the
206        appropriate scanner event handler. If it cannot complete a particular piece
207        of content or markup, it returns false.
208 **/
209XML_PARSER_IMPL(XMLParser::XMLScanState)::
210scanNext()
211{
212    XMLParser::XMLScanState retVal;
213        switch (fInMarkup)
214        {
215                case 0:     size_t length;
216                    retVal = scanContent<XMLParser::Element>(length);
217                    if (retVal) break;
218                                        fInMarkup = 1;
219                                        // if we obtained a zero-length content string, just fall through
220                                        // and grab the next piece of markup instead.
221                                        if (length) break;
222
223        case 1:     retVal = scanMarkup<XMLParser::Element>();
224                    if (retVal) break;
225                                        fInMarkup = 0;
226                                        break;
227
228                default:    UNREACHABLE
229        }
230
231    if (unlikely(retVal == XMLParser::EndOfDocumentSection && fScope != 0))
232    {
233        fScanner->emitError(XMLErrs::EndedWithTagsOnStack, fElement[fElementIndex]->getRawName());
234    }
235
236    return retVal;
237}
238
239// -------------------------------------------------------------------------------------------
240
241XML_PARSER_IMPL(bool)::
242scanMiscellaneous()
243{
244        XMLParser::XMLScanState r;
245        switch (fInMarkup)
246        {
247                case 0:     for (;;)
248                                        {
249                                                size_t length;
250                        r = scanContent<XMLParser::Miscellaneous>(length);
251                                                if (r) break;
252
253        case 1:         r = scanMarkup<XMLParser::Miscellaneous>();
254                                                if (r) break;
255                                        }
256                                        break;
257                default:    UNREACHABLE
258        }
259        return (r == XMLParser::EndOfPage);
260}
261
262// ---------------------------------------------------------------------------------------------------------
263
264XML_PARSER_IMPL_WITH_DOCUMENT_STATE_PARAMS(XMLParser::XMLScanState)::
265scanContent(size_t & length)
266{
267        length = getStringLength(fCursorPtr, fCurrentStringEndPtr);
268
269        // *fCurrentStringEndPtr will be 0 if and only if this is a complete string
270        if (unlikely(fCursorPtr[length] != 0))
271        {
272                DEBUG_TRANSITION_MESSAGE(" +++ aborting due to partial content string +++");
273                fCurrentStringEndPtr--;
274                fInMarkup = 0;
275                #ifndef CALCULATE_COPY_BACK_POSITION
276                // fUnusedContent = length;
277                // fUnusedSymbols = 0;
278                #endif
279        XMLParser::XMLScanState RET_VAL[2] = {XMLParser::EndOfPage, XMLParser::EndOfDocumentSection};
280        return RET_VAL[this->fNoMore];
281        }
282
283    DEBUG_TRANSITION_MESSAGE(" --- atContent: {" << length << ',' << fCursorPtr << '}')
284
285        // note: the startOfContentPtr stores the fCursorPtr position so that we can
286        // advance the fCursorPtr prior to giving the content to the scanner. This
287        // ensures that if an error occurs, we'll report the error line/col at the end
288        // of the content string, which is how Xerces reports it.
289
290        if (likely(length != 0))
291        {
292                const XMLCh * startOfContentPtr = fCursorPtr;
293                fCursorPtr += length;
294
295                if ((docStateType == XMLParser::Prolog) || (docStateType == XMLParser::Miscellaneous))
296                {
297                        if (unlikely(!XMLStringU::isWhitespace(startOfContentPtr, length)))
298                        {
299                                fScanner->emitError(XMLErrs::ExpectedCommentOrPI);
300                                return XMLParser::EndOfDocumentSection;
301                        }
302                        fScanner->handleIgnorableWhitespace
303                        (
304                                startOfContentPtr,
305                                length
306                        );
307                }
308                else
309                {
310                        fScanner->handleContent
311                        (
312                                startOfContentPtr,
313                                length
314                        );
315                }
316        }
317        ++fCursorPtr;
318        return XMLParser::HasMoreData;
319}
320
321// ---------------------------------------------------------------------------------------------------------
322
323XML_PARSER_IMPL_WITH_DOCUMENT_STATE_PARAMS(XMLParser::XMLScanState)::
324scanMarkup()
325{
326        if (unlikely(fMarkupCount == 0))
327        {
328                DEBUG_TRANSITION_MESSAGE(" +++ aborting due to incomplete pending markup +++");
329                fInMarkup = 1;
330                #ifndef CALCULATE_COPY_BACK_POSITION
331                // fUnusedContent = 0;
332                // fUnusedSymbols = fSymbolCount - fSymbolIdx;
333                #endif
334
335        XMLParser::XMLScanState RET_VAL[2] = {XMLParser::EndOfPage, XMLParser::EndOfDocumentSection};
336        return RET_VAL[fNoMore];
337        }
338
339        DEBUG_TRANSITION_MESSAGE("----------------------------------------------------------------------");
340
341        XMLCh markupChar = *fCursorPtr;
342
343        XMLParser::XMLScanState retVal = XMLParser::HasMoreData;
344
345        switch (markupChar & (EmptyTag - 1))
346        {
347                /// ------------------------------------------------------------------------ ///
348                case StartTagWithoutAttributes:
349                        if (docStateType == XMLParser::Prolog)
350                        {
351                                fInMarkup = 1;
352                                return XMLParser::EndOfDocumentSection;
353                        }
354                        else if (docStateType == XMLParser::Miscellaneous)
355                        {
356                                fScanner->emitError(XMLErrs::ExpectedCommentOrPI);
357                                return XMLParser::EndOfDocumentSection;
358                        }
359                        else if (docStateType == XMLParser::Element)
360                        {
361                                const gid_t elementGid = fSymbolArray[fSymbolIdx];
362                                XMLSymbol * const element = getSymbol(fSymbolIdx);
363                                unsigned int uriId = getSymbolUri(fSymbolIdx);
364                                // the namespace context id allows the scanner to map prefix to uris and determine what
365                                // prefixes and uris are in scope.
366                                fScanner->setContextId(fContextIdArray[fSymbolIdx]);
367                                element->getQName()->setURI(uriId);
368
369                                element->fLastOccurence = ++fElementCount;
370
371                                const bool isRoot = (fScope == 0);
372                                const bool isEmpty = (markupChar >> 3);
373
374                                DEBUG_TRANSITION_MESSAGE
375                                (
376                                        (char*)(isEmpty ? " --- atEmptyTag=" : " --- atStartTag=")
377                                        << element
378                                        << " idx="
379                                        << fSymbolIdx
380                                );
381
382                                XMLElementDecl * elemDecl =
383                                        fScanner->handleStartTag
384                                        (
385                                                element
386                                                , fAttributeList
387                                                , 0
388                                                , isRoot
389                                                , isEmpty
390                                                , fReaderNum
391                                        );
392
393                                addElement(elemDecl, elementGid, isRoot, isEmpty);
394
395                                // verify that this gets properly 'calculated' to remove the if test
396                                retVal = (isRoot & isEmpty) ? XMLParser::EndOfDocumentSection : XMLParser::HasMoreData;
397
398                                ++fSymbolIdx;
399                                ++fCursorPtr;
400                        }
401                        break;
402                /// ------------------------------------------------------------------------ ///
403                case StartTagWithAttributes:
404                        if (docStateType == XMLParser::Prolog)
405                        {
406                                fInMarkup = 1;
407                                return XMLParser::EndOfDocumentSection;
408                        }
409                        else if (docStateType == XMLParser::Miscellaneous)
410                        {
411                                fScanner->emitError(XMLErrs::ExpectedCommentOrPI);
412                                return XMLParser::EndOfDocumentSection;
413                        }
414                        else if (docStateType == XMLParser::Element)
415                        {
416                                // v        v            v
417                                // <...> OR <... ...> OR <... .../>
418                                const gid_t elementGid = fSymbolArray[fSymbolIdx];
419                                XMLSymbol * const element = getSymbol(fSymbolIdx);
420                                const unsigned int uriId = getSymbolUri(fSymbolIdx);
421                                fScanner->setContextId(fContextIdArray[fSymbolIdx]);
422                                element->getQName()->setURI(uriId);
423
424                                const unsigned int elementCount = ++fElementCount;
425
426                                DEBUG_TRANSITION_MESSAGE
427                                (
428                                        " --- atStartOrEmptyTag:"
429                                        << element
430                                        << " idx="
431                                        << fSymbolIdx
432                                );
433
434                                // QUESTION: using some sort of switch dispatch, could it be possible to feed in some
435                                // sort of branch prediction-friendly algorithms for this, assuming we have a schema/
436                                // dtd or can reuse the symbol's known attribute count? would the cost of the dispatch
437                                // exceed the cost of just doing it as is given hardware prefetchers? Even if was better
438                                // would the difference even be noticable?
439
440                                XMLSize_t attCount = 0;
441
442                                do
443                                {
444                                        // we're at an attribute; grab it
445
446                                        //             v
447                                        // <... ATTNAME='VALUE' ...
448                                        XMLSymbol * attribute = getSymbol(++fSymbolIdx);
449                                        unsigned int attrUriId = getSymbolUri(fSymbolIdx);
450                                        attribute->getQName()->setURI(attrUriId);
451
452                                        // duplicate attribute check: if we have observed the attribute symbol in this
453                                        // element, then it must have been used within the current attribute list; if so,
454                                        // it's a duplicate symbol.
455                                        if (unlikely(attribute->fLastOccurence == elementCount))
456                                        {
457                                                fScanner->emitError
458                                                (
459                                                        XMLErrs::AttrAlreadyUsedInSTag, attribute->getName(), element->getName()
460                                                );
461                                        }
462                                        attribute->fLastOccurence = elementCount;
463
464                                        // pull the attribute value out of the content buffer
465                                        const ptrdiff_t length = getStringLength(++fCursorPtr, fCurrentStringEndPtr);
466
467                                        if (unlikely(attrUriId == XMLNamespaceResolver::fSchemaInstanceUriId))
468                                        {
469                                                const XMLCh * localPart = attribute->getQName()->getLocalPart();
470                                                if (XMLString::equals(localPart, SchemaSymbols::fgXSI_TYPE))
471                                                {
472                                                        fScanner->setXsiType(fCursorPtr, length);
473                                                }
474                                                else if (XMLString::equals(localPart, SchemaSymbols::fgATT_NILL))
475                                                {
476                                                        fScanner->setXsiNil(fCursorPtr, length);
477                                                }
478                                        }
479
480                                        if (unlikely(attCount == fAttributeList.capacity()))
481                                        {
482                                                fAttributeList.expand(fAttributeList.capacity());
483                                        }
484
485                                        fAttributeList[attCount].set(attribute, fCursorPtr, length);
486
487                                        DEBUG_TRANSITION_MESSAGE
488                                        (
489                                                " --- atAttribute" << (attCount + 1) << ':'
490                                                << attribute
491                        << '='
492                                                << fCursorPtr
493                        << " idx="
494                                                << fSymbolIdx
495                                        );
496
497                                        fCursorPtr += length;
498                                        attCount++;
499                                        markupChar = *++fCursorPtr;
500                                }
501                                while ((markupChar & (EmptyTag - 1)) != 0);
502
503                                // set the element occurance back to its 'original' value.
504                                element->fLastOccurence = elementCount;
505
506                                const bool isEmpty = (markupChar >> 3);
507                                const bool isRoot = (fScope == 0);
508
509                                DEBUG_TRANSITION_MESSAGE
510                                (
511                                        (char*)(isEmpty ? " --- atEndOfEmptyTag: " : " --- atEndOfStartTag: ")
512                                        << element
513                                );
514
515                                XMLElementDecl * elemDecl =
516                                        fScanner->handleStartTag
517                                        (
518                                                element
519                                                , fAttributeList
520                                                , attCount
521                                                , isRoot
522                                                , isEmpty
523                                                , fReaderNum
524                                        );
525
526                                ++fCursorPtr;
527                                ++fSymbolIdx;
528
529                                addElement(elemDecl, elementGid, isRoot, isEmpty);
530
531                                retVal = (isRoot & isEmpty) ? XMLParser::EndOfDocumentSection : XMLParser::HasMoreData;
532                        }
533                        break;
534                /// ------------------------------------------------------------------------ ///
535                case EndTag:
536                        if (docStateType == XMLParser::Prolog)
537                        {
538                                fScanner->emitError(XMLErrs::MoreEndThanStartTags);
539                        }
540                        else if (docStateType == XMLParser::Miscellaneous)
541                        {
542                                fScanner->emitError(XMLErrs::ExpectedCommentOrPI);
543                        }
544                        else if (docStateType == XMLParser::Element)
545                        {
546                                const gid_t elementGid = fSymbolArray[fSymbolIdx];
547                                XMLSymbol * element = getSymbol(fSymbolIdx);
548                                // the namespace context id allows the scanner to map prefix to uris and determine what
549                                // prefixes and uris are in scope.
550                                fScanner->setContextId(fContextIdArray[fSymbolIdx]);
551
552                                DEBUG_TRANSITION_MESSAGE
553                                (
554                                        " --- atEndTag "
555                                        << element
556                                        << " idx="
557                                        << fSymbolIdx
558                                );
559
560                                unsigned int uriId = getSymbolUri(fSymbolIdx);
561
562                                element->getQName()->setURI(uriId);
563
564                                // check to be sure that we can "pop" the element off the stack
565                                if (unlikely(fScope == 0))
566                                {
567                                        fScanner->emitError(XMLErrs::MoreEndThanStartTags);
568                                        return XMLParser::EndOfDocumentSection;
569                                }
570                                else
571                                {
572                                        // we can; move back to the top element
573                                        fElementIndex -= fChildren[fScope];
574                                        --fScope;
575
576                                        const bool isRoot = (fScope == 0);
577
578                                        // get the elem decl
579                                        XMLElementDecl * elemDecl = fElement[fElementIndex];
580
581                                        // now check that this is the element we were looking for...
582                                        if (unlikely(fGidStack[fScope] != elementGid))
583                                        {
584                                                // check the uri and local parts? would that be legal?
585                                                fScanner->emitError(XMLErrs::ExpectedEndOfTagX, elemDecl->getRawName());
586                                        }
587
588                                        // swap the elem decl's qname with the element (symbol)'s qname
589                                        const QName * elementName = elemDecl->fElementName;
590
591                                        elemDecl->fElementName = element->fQName;
592
593                                        fScanner->handleEndTag
594                                        (
595                                                elemDecl
596                                                , uriId
597                                                , fReaderNum
598                                                , isRoot
599                                                , &fElement[fElementIndex + 1]
600                                                , fChildren[fScope + 1]
601                                        );
602
603                                        // swap the qname back to the grammar element
604                                        elemDecl->fElementName = elementName;
605
606                                        ++fSymbolIdx;
607                                        ++fCursorPtr;
608
609                                        retVal = (isRoot) ? XMLParser::EndOfDocumentSection : XMLParser::HasMoreData;
610                                }
611                        }
612                        break;
613                /// ------------------------------------------------------------------------ ///
614                case ProcessingInstruction:
615
616                        if ((docStateType == XMLParser::Prolog) || (docStateType == XMLParser::Element) || (docStateType == XMLParser::Miscellaneous))
617                        {
618                const XMLCh * target = ++fCursorPtr;
619
620                DEBUG_TRANSITION_MESSAGE
621                (
622                    " --- atProcessingInstruction="
623                    << target
624                );
625
626                fCursorPtr += getStringLength(fCursorPtr, fCurrentStringEndPtr);
627
628                                // When we get a pi, the pi string will read "data?"; we adjust for this and replace the
629                                // trailing "?" with null to terminate the string so the end user only receives "data".
630
631                                const size_t length = getStringLength(++fCursorPtr, fCurrentStringEndPtr);
632                                const_cast<XMLCh*>(fCursorPtr)[length - 1] = 0x0000;
633
634
635                                // <?xml ... ?> is allowed if and only if we're in the prolog
636                if (unlikely(XMLStringU::isXML(target)))
637                                {
638                                        fScanner->emitError(XMLErrs::NoPIStartsWithXML);
639                                }
640                                else
641                                {
642                                        fScanner->handlePI(target, fCursorPtr, length);
643                                }
644
645                                fCursorPtr += length + 1;
646                        }
647                        break;
648                /// ------------------------------------------------------------------------ ///
649                case Comment:
650                        if ((docStateType == XMLParser::Prolog) || (docStateType == XMLParser::Element) || (docStateType == XMLParser::Miscellaneous))
651                        {
652                                //   v
653                                // <!--COMMENT-->
654
655                                // When we get a comment, the content string will read "--COMMENT--"; we offset the
656                                // start position and length to adjust for this and replace the first "-" in the trailing
657                                // "--" with null to terminate the string so the end user only receives "COMMENT".
658
659                const size_t length = getStringLength(++fCursorPtr, fCurrentStringEndPtr);
660
661                                const_cast<XMLCh*>(fCursorPtr)[length - 2] = chNull;
662
663                DEBUG_TRANSITION_MESSAGE(" --- atComment " << fCursorPtr);
664
665                                fScanner->handleComment(fCursorPtr, length - 2);
666
667                                fCursorPtr += length + 1;
668                        }
669                        break;
670                /// ------------------------------------------------------------------------ ///
671                case CDATA:
672            if (docStateType == XMLParser::Element)
673                        {
674                                //   v
675                                // <![CDATA[ ... ]]>
676
677                                if (unlikely(!XMLStringU::isCDATA(++fCursorPtr)))
678                                {
679                                        // "CDATA[" check failed
680                                        fScanner->emitError(XMLErrs::ExpectedCommentOrCDATA);
681                                        return XMLParser::EndOfDocumentSection;
682                                }
683
684                                fCursorPtr += 6;
685
686                                //                 v
687                                // <![CDATA[ ... ]]>
688                                const size_t length = getStringLength(fCursorPtr, fCurrentStringEndPtr);
689
690                                // When we get a CDATA, the string will read "[CDATA[data]]"; we offset the
691                                // start position and length to adjust for this and replace the first "[" in the trailing
692                                // "]]>" with null to terminate the string so the end user only receives "data".
693
694                                const_cast<XMLCh*>(fCursorPtr)[length - 2] = chNull;
695
696                DEBUG_TRANSITION_MESSAGE(" --- atCDATA " << fCursorPtr);
697
698                                fScanner->handleCDATA(fCursorPtr, length - 2);
699
700                                fCursorPtr += length + 1;
701                        }
702            else
703            {
704                fScanner->emitError(XMLErrs::ExpectedCommentOrPI);
705                return XMLParser::EndOfDocumentSection;
706            }
707                        break;
708                default:
709                        UNREACHABLE
710        }
711
712        DEBUG_TRANSITION_MESSAGE("----------------------------------------------------------------------");
713
714        --fMarkupCount;
715        return retVal;
716}
717
718// ---------------------------------------------------------------------------------------------------------
719
720XML_PARSER_IMPL(void)::
721resolveDocumentPageNamespaces()
722{
723        const XMLCh * cursorPtr = &fContentBuf[0];
724        unsigned int markupCount = fMarkupCount;
725        unsigned int symbolIdx = 0;
726        const XMLCh ** stringEndPtr = &fStringEndPtr[0];
727        XMLCh markupChar;
728
729        DEBUG_NAMESPACE_MESSAGE("######################################################################");
730        DEBUG_NAMESPACE_MESSAGE("BEGIN NAMESPACE RESOLUTION:");
731        DEBUG_NAMESPACE_MESSAGE("######################################################################");
732
733        if (unlikely(markupCount == 0))
734        {
735                return;
736        }
737
738        // make sure there is enough room in the URI array for the symbols
739        if (unlikely(fSymbolUriArray.capacity() < fSymbolArray.capacity()))
740        {
741        DEBUG_TRANSITION_MESSAGE("RESIZING URI AND CONTEXT ID ARRAYS TO " << fSymbolArray.capacity())
742                fSymbolUriArray.resizeToFit(0, fSymbolArray.capacity());
743                fContextIdArray.resizeToFit(0, fSymbolArray.capacity());
744        }
745
746        switch (fInMarkup)
747        {
748                do
749                {
750        case 0: // skip this piece of content
751                        DEBUG_NAMESPACE_MESSAGE("==================================================================");
752            DEBUG_NAMESPACE_MESSAGE(" --- atContent " << cursorPtr)
753                        cursorPtr = *stringEndPtr++ + 1;
754        case 1: // process the next markup element
755                        markupChar = *cursorPtr;
756            #ifdef PRINT_DEBUG_MESSAGE
757                        switch (markupChar)
758                        {
759                                case StartTagWithoutAttributes:
760                                case StartTagWithAttributes:
761                                case EndTag:
762                                case ProcessingInstruction:
763                                case Comment:
764                                case CDATA:
765                                case EmptyTag:
766                                        break;
767                                default:
768                    DEBUG_TRANSITION_MESSAGE("ERROR! UNKNOWN MARKUP CHAR: " << markupChar);
769                                        exit(-1);
770                        }
771                        #endif
772                        switch (markupChar & (EmptyTag - 1))
773                        {
774                                /// ------------------------------------------------------------------------ ///
775                                case StartTagWithoutAttributes:
776                                        do {
777                                        fNamespaceResolver.enterScope();
778
779                                        // no speculation is required since new namespaces are not possible here!
780                                        const XMLSymbol * element = getSymbol(symbolIdx);
781
782                                        bool unknownPrefix = 0;
783
784                                        const unsigned int uriId = fNamespaceResolver.resolveUriId(element, false, unknownPrefix);
785
786                    DEBUG_NAMESPACE_MESSAGE(" --- atStartTag: " << element << " uriId=" << uriId << " idx=" << symbolIdx << " scope=" << fNamespaceResolver.getScope());
787
788                                        if (unlikely(unknownPrefix))
789                                        {
790                                                fScanner->emitError(XMLErrs::UnknownPrefix, element->getQName()->getPrefix());
791                                        }
792
793                                        if (unlikely(element->isXMLNS() && fScanner->fValidate))
794                                        {
795                                                fScanner->emitError(XMLErrs::NoXMLNSAsElementPrefix, element->getName());
796                                        }
797
798                                        const bool isEmpty = (markupChar >> 3);
799
800                                        storeSymbolUri(symbolIdx, uriId);
801
802                                        fContextIdArray[symbolIdx] = fNamespaceResolver.getCurrentContextId();
803
804                                        // outdent scope by one if and only if this element is an empty tag
805                                        if (isEmpty) fNamespaceResolver.leaveScope();
806
807                                        ++cursorPtr;
808                                        ++symbolIdx;
809
810                                        } while (0);
811                                        break;
812                                /// ------------------------------------------------------------------------ ///
813                                case StartTagWithAttributes:
814                                        do {
815
816                                        // a new namespaces is possible here! speculate that any unknown ones are acceptable, but
817                                        // verify after the fact if any new ones occured.
818
819                                        const XMLCh * storedCursorPtr = cursorPtr;
820                                        const unsigned int storedSymbolIdx = symbolIdx;
821                                        const XMLCh ** storedStringEndPtr = stringEndPtr;
822                                        const XMLSymbol * element = getSymbol(symbolIdx);
823
824                                        bool mispeculatedOnceAlready = 0;
825                                        bool observedXsi = 0;
826                                        bool unknownPrefix = 0;
827                                        bool speculatedCorrectly = 1;
828
829                                        fNamespaceResolver.enterScope();
830
831                                        DEBUG_NAMESPACE_MESSAGE("==================================================================");
832
833                                        for (;;)
834                                        {
835                                                unsigned int uriId = fNamespaceResolver.resolveUriId(element, false, unknownPrefix);
836
837                        DEBUG_NAMESPACE_MESSAGE(" --- atStartTag: " << element << " uriId=" << uriId << " idx=" << symbolIdx<< " scope=" << fNamespaceResolver.getScope());
838
839                                                storeSymbolUri(symbolIdx, uriId);
840
841                                                if (unlikely(element->isXMLNS() && fScanner->fValidate))
842                                                {
843                                                        fScanner->emitError(XMLErrs::NoXMLNSAsElementPrefix, element->getName());
844                                                }
845
846                                                do
847                                                {
848                                                        DEBUG_NAMESPACE_MESSAGE("------------------------------------------------------------------");
849
850                                                        // extract the next attribute ...
851                                                        XMLSymbol * attribute = getSymbol(++symbolIdx);
852
853                                                        // pull the attribute value out of the content buffer
854                                                        const size_t length = getStringLength(++cursorPtr, stringEndPtr);
855
856                                                        // is this an "xmlns" attribute? if so, then we want to update the namespace table.
857                                                        if (unlikely(attribute->isXMLNS()))
858                                                        {
859                                XMLErrs::Codes bindingError;
860
861                                uriId = fNamespaceResolver.bindNamespace(attribute, cursorPtr, length, speculatedCorrectly, bindingError);
862
863                                if (unlikely(bindingError != XMLErrs::NoError))
864                                {
865                                    fScanner->emitError(bindingError);
866                                }
867                                                        }
868                                                        else
869                                                        {
870                                                                uriId = fNamespaceResolver.resolveUriId(attribute, true, unknownPrefix);
871
872                                                                observedXsi |= (uriId == XMLNamespaceResolver::fSchemaInstanceUriId);
873                                                        }
874
875                            DEBUG_NAMESPACE_MESSAGE(" --- atAttribute: " << attribute << " uriId=" << uriId << " idx=" << symbolIdx);
876
877                                                        storeSymbolUri(symbolIdx, uriId);
878
879                                                        cursorPtr += length;
880
881                                                        // if the next markup char's value is 0 (8), then we're at the end of the start/empty tag
882                                                        markupChar = *++cursorPtr;
883
884                            #ifdef PRINT_DEBUG_MESSAGE
885                                                        switch (markupChar)
886                                                        {
887                                                                case StartTagWithAttributes:
888                                                                case StartTagWithoutAttributes:
889                                                                case EmptyTag:
890                                                                        break;
891                                                                case EndTag:
892                                                                case ProcessingInstruction:
893                                                                case Comment:
894                                                                case CDATA:
895                                    DEBUG_TRANSITION_MESSAGE("ERROR! UNEXPECTED MARKUP CHAR: " << markupChar);
896                                                                        exit(-1);
897                                                                        break;
898                                                                default:
899                                    DEBUG_TRANSITION_MESSAGE("ERROR! UNKNOWN MARKUP CHAR: " << markupChar);
900                                                                        exit(-1);
901                                                        }
902                                                        #endif
903                                                }
904                                                while ((markupChar & (EmptyTag - 1)) != 0);
905
906                                                if (likely(speculatedCorrectly))
907                                                {
908                                                        break;
909                                                }
910                                                else
911                                                {
912                                                        if (unlikely(mispeculatedOnceAlready))
913                                                        {
914                                                                // ERROR! the only way that this occured was that there are two or more
915                                                                // xmlns attributes with the same local part but different uris.
916
917                                                                const XMLCh * duplicatedAttribute = 0;
918                                                                const unsigned int firstAttribute = storedSymbolIdx + 1;
919                                                                for (unsigned int i = firstAttribute + 1; i <= symbolIdx; i++)
920                                                                {
921                                                                        const XMLSymbol * attribute = getSymbol(i);
922                                                                        for (unsigned int j = firstAttribute; j < i; j++)
923                                                                        {
924                                                                                if (getSymbol(j) == attribute)
925                                                                                {
926                                                                                        duplicatedAttribute = attribute->getName();
927                                                                                        break;
928                                                                                }
929                                                                        }
930                                                                }
931                                                                fScanner->emitError(XMLErrs::AttrAlreadyUsedInSTag, duplicatedAttribute, element->getName());
932                                                                break;
933                                                        }
934
935                                                        DEBUG_NAMESPACE_MESSAGE("------------------------------------------------------------------");
936                                                        DEBUG_NAMESPACE_MESSAGE(" *** uri speculation failed! retrying!");
937                                                        DEBUG_NAMESPACE_MESSAGE("==================================================================");
938
939                                                        mispeculatedOnceAlready = 1;
940                                                        speculatedCorrectly = 1;
941                                                        unknownPrefix = 0;
942                                                        cursorPtr = storedCursorPtr;
943                                                        symbolIdx = storedSymbolIdx;
944                                                        stringEndPtr = storedStringEndPtr;
945                                                }
946                                        }
947
948                                        if (unlikely(unknownPrefix && fScanner->fValidate))
949                                        {
950                                                unsigned int dupSymbolIdx = storedSymbolIdx;
951                                                const XMLSymbol * symbol = element;
952                                                unknownPrefix = 0;
953                                                do
954                                                {
955                                                        fNamespaceResolver.resolveUriId(symbol, false, unknownPrefix);
956                                                        if (unknownPrefix)
957                                                        {
958                                                                fScanner->emitError(XMLErrs::UnknownPrefix, symbol->getQName()->getPrefix());
959                                                        }
960                                                        symbol = getSymbol(++dupSymbolIdx);
961                                                }
962                                                while (dupSymbolIdx <= symbolIdx);
963                                        }
964
965
966                                        if (unlikely(observedXsi && fScanner->getDoSchema()))
967                                        {
968                                                cursorPtr = storedCursorPtr;
969                                                symbolIdx = storedSymbolIdx;
970                                                stringEndPtr = storedStringEndPtr;
971
972                                                do
973                                                {
974                                                        XMLSymbol * attribute = getSymbol(++symbolIdx);
975
976                                                        // pull the attribute value out of the content buffer
977                                                        const size_t length = getStringLength(++cursorPtr, stringEndPtr);
978
979                                                        if (unlikely(getSymbolUri(symbolIdx) == XMLNamespaceResolver::fSchemaInstanceUriId))
980                                                        {
981                                                                // is this an "xsi:schemaLocation" or "xsi:noNamespaceSchemaLocation" attribute? if so, then
982                                                                // tell the scanner to handle it.
983
984                                                                const XMLCh * const localPart = attribute->getQName()->getLocalPart();
985                                                                if (XMLString::equals(localPart, SchemaSymbols::fgXSI_SCHEMALOCATION))
986                                                                {
987                                                                        DEBUG_NAMESPACE_MESSAGE("##==================================================================##");
988                                                                        fScanner->parseSchemaLocation(cursorPtr, length);
989                                                                        DEBUG_NAMESPACE_MESSAGE("##==================================================================##");
990                                                                }
991                                                                else if (XMLString::equals(localPart, SchemaSymbols::fgXSI_NONAMESPACESCHEMALOCATION))
992                                                                {
993                                                                        DEBUG_NAMESPACE_MESSAGE("##==================================================================##");
994                                                                        fScanner->resolveSchemaGrammar(cursorPtr, XMLUni::fgZeroLenString);
995                                                                        DEBUG_NAMESPACE_MESSAGE("##==================================================================##");
996                                                                }
997                                                        }
998
999                                                        cursorPtr += length;
1000                                                }
1001                                                while ((*++cursorPtr & (EmptyTag - 1)) != 0);
1002
1003                                        }
1004
1005                                        fContextIdArray[storedSymbolIdx] = fNamespaceResolver.getCurrentContextId();
1006
1007                                        const bool isEmpty = (markupChar >> 3);
1008
1009                                        // outdent scope by one if and only if this element is an empty tag
1010                                        if (isEmpty) fNamespaceResolver.leaveScope();
1011
1012                                        ++cursorPtr;
1013                                        ++symbolIdx;
1014
1015                                        } while (0);
1016                                        break;
1017
1018                                /// ------------------------------------------------------------------------ ///
1019                                case EndTag:
1020                                        do {
1021                                                // no new namespaces are possible here! no speculation required
1022                                                const XMLSymbol * element = getSymbol(symbolIdx);
1023
1024                                                const unsigned int uriId = fNamespaceResolver.resolveUriId(element, false);
1025
1026                        DEBUG_NAMESPACE_MESSAGE(" --- atEndTag: " << element << " uriId=" << uriId << " idx=" << symbolIdx << " scope=" << fNamespaceResolver.getScope());
1027
1028                                                storeSymbolUri(symbolIdx, uriId);
1029
1030                                                // should this simply be pulled from a stack?
1031                                                fContextIdArray[symbolIdx] = fNamespaceResolver.getCurrentContextId();
1032
1033                                                symbolIdx++;
1034
1035                                                fNamespaceResolver.leaveScope();
1036
1037                                                cursorPtr++;
1038                                        } while (0);
1039                                        break;
1040                                /// ------------------------------------------------------------------------ ///
1041                                case ProcessingInstruction:
1042                    stringEndPtr++;
1043                                case Comment:
1044                                case CDATA:
1045                    DEBUG_TRANSITION_MESSAGE(" --- atCDCtPI " << cursorPtr);                                   
1046                    stringEndPtr++;
1047                                        break;
1048                                default:
1049                                        UNREACHABLE
1050                        /// ------------------------------------------------------------------------ ///
1051                        }
1052                }
1053                while (likely(--markupCount != 0));
1054                break;
1055        default:
1056                UNREACHABLE
1057        }
1058}
1059
1060// ---------------------------------------------------------------------------------------------------------
1061
1062XML_PARSER_IMPL(void)::
1063prepareForNextDocumentPage()
1064{
1065        #ifdef CALCULATE_COPY_BACK_POSITION
1066        const XMLCh * cursorPtr = fCursorEndPtr - fUnusedContent;
1067        unsigned int symbolIdx = fSymbolCount - fUnusedSymbols;
1068        // calculate how many string end pointers will be copied over
1069        unsigned int stringIndex = fStringCount;
1070        if (unlikely(cursorPtr < fCursorEndPtr))
1071        {
1072                const XMLCh * cursor = cursorPtr;
1073                for (;;)
1074                {
1075                        cursor += XMLStringU::stringLen(cursor) + 1;
1076                        if (likely(cursor > fCursorEndPtr)) break;
1077                        stringIndex--;
1078                }
1079        }
1080
1081    DEBUG_TRANSITION_MESSAGE("stringCount=" << fStringCount);
1082    DEBUG_TRANSITION_MESSAGE("cursorOffset=" << (size_t)(cursorPtr - &fContentBuf[0]) << " (" << (size_t)(fCursorPtr - &fContentBuf[0]) << ')');
1083    DEBUG_TRANSITION_MESSAGE("symbolIndex=" << symbolIdx << " (" << fSymbolIdx << ')');
1084    DEBUG_TRANSITION_MESSAGE("stringIndex=" << stringIndex << " (" << (size_t)(fCurrentStringEndPtr - &fStringEndPtr[0]) << ')');
1085
1086        assert (cursorPtr == fCursorPtr);
1087        assert (symbolIdx == fSymbolIdx);
1088        assert (stringIndex == (fCurrentStringEndPtr - &fStringEndPtr[0]));
1089
1090        #else
1091        const XMLCh * cursorPtr = fCursorPtr;
1092        const unsigned int symbolIdx = fSymbolIdx;
1093        const unsigned int stringIndex = (fCurrentStringEndPtr - &fStringEndPtr[0]);
1094        #endif
1095
1096
1097
1098        /** ------------------- BUFFER LINE/COL TRACKING STREAM --------------------- **/
1099
1100    #ifndef DISABLE_PARSER_LINE_COLUMN_CALCULATION
1101        if (likely(fIncompleteMarkupBytes != 0))
1102        {
1103                size_t index =
1104                        fLineColStartIndex
1105                        + ((fRawBytesAvail - fIncompleteMarkupBytes) >> LOG_2_BLOCK_SIZE);
1106
1107                const size_t count =
1108                        fLineColStartIndex
1109                        - (fRawDataOffset >> LOG_2_BLOCK_SIZE)
1110                        + (fRawBytesAvail >> LOG_2_BLOCK_SIZE);
1111
1112                size_t lineColStartIndex = 1;
1113
1114                // get the first block but mask off the used portion of it
1115                const BitBlock newLineOrSkipMask =
1116                        bitblock::load_aligned(&fNewLineOrSkipMaskStream[index]);
1117                const BitBlock delMask =
1118                        bitblock::load_aligned(&fDelMaskStream[index]);
1119                const BitBlock mask =
1120                        bitblock::srl(simd<1>::constant<1>(), convert((fIncompleteMarkupBytes - 1) & (BLOCK_SIZE - 1)));
1121
1122                bitblock::store_aligned(simd_andc(newLineOrSkipMask, mask), &fNewLineOrSkipMaskStream[0]);
1123                bitblock::store_aligned(simd_or(delMask, mask), &fDelMaskStream[0]);
1124
1125                // now if there are any remaining blocks to copy over, copy them over directly
1126                while (++index < count)
1127                {
1128                        // NOTE: these fences are required here or we may get arbitary data from the
1129                        // streams
1130                        const BitBlock newLineOrSkipMask =
1131                                bitblock::load_aligned(&fNewLineOrSkipMaskStream[index]);
1132                        const BitBlock delMask =
1133                                bitblock::load_aligned(&fDelMaskStream[index]);
1134
1135                        bitblock::store_aligned(newLineOrSkipMask, &fNewLineOrSkipMaskStream[lineColStartIndex]);
1136                        bitblock::store_aligned(delMask, &fDelMaskStream[lineColStartIndex]);
1137                        lineColStartIndex++;
1138                }
1139
1140                fLineColStartIndex = lineColStartIndex;
1141        }
1142        else
1143        {
1144                fLineColStartIndex = 0;
1145        }
1146    #endif
1147
1148        fCurrLineCol = fNextLineCol;
1149
1150        /** ----------------------- BUFFER CONTENT STREAM --------------------------- **/
1151
1152        // stringEndOffset is required to adjust the string end position of the unused strings
1153        ptrdiff_t cursorOffset = (cursorPtr - &fContentBuf[0]);
1154
1155        if (likely(cursorPtr < fCursorEndPtr))
1156        {
1157                const XMLCh * const contentBuf0 = &fContentBuf[0];
1158                const unsigned int unusedLength =
1159                        fContentBuf.copy_to_front(cursorOffset, (fCursorEndPtr - &fContentBuf[0]));
1160                // just incase the content buffer was expanded, adjust the string end offset
1161                cursorOffset += (contentBuf0 - &fContentBuf[0]);
1162
1163                fCursorPtr = &fContentBuf[unusedLength];
1164        }
1165        else
1166        {
1167                fCursorPtr = &fContentBuf[0];
1168        }
1169
1170        /** -------------------- MOVE STRING END POINTERS BACK ------------------------ **/
1171
1172        if (stringIndex < fStringCount)
1173        {
1174                fStringEndPtr.adjust(stringIndex, fStringCount, cursorOffset);
1175                fStringCount = fStringEndPtr.copy_to_front(stringIndex, fStringCount);
1176        }
1177        else
1178        {
1179                fStringCount = 0;
1180        }
1181
1182        /** ------------------------ BUFFER SYMBOL STREAM ------------------------------ **/
1183
1184        fSymbolUriArray.copy_to_front(symbolIdx, fSymbolCount);
1185        fContextIdArray.copy_to_front(symbolIdx, fSymbolCount);
1186        fSymbolCount = fSymbolArray.copy_to_front(symbolIdx, fSymbolCount);
1187}
1188
1189// -------------------------------------------------------------------------------------------
1190
1191XML_PARSER_IMPL(void)::
1192popReader()
1193{
1194        fScanner->fReaderMgr.popReader();
1195}
1196
1197// ---------------------------------------------------------------------------------------------------------
1198
1199XML_PARSER_IMPL(XMLSymbol *)::
1200getSymbol(const unsigned int index)
1201{
1202        const gid_t gid = fSymbolArray[index];
1203        #ifdef USE_SIMPLE_HASH_TABLE_LOOKUP
1204        XMLSymbol * symbol = fSymbolTable[gid];
1205        #else
1206        XMLSymbol * symbol = fSymbolResolver[gid];
1207        #endif
1208        return symbol;
1209}
1210
1211XML_PARSER_IMPL(const XMLSymbol *)::
1212getSymbol(const unsigned int index) const
1213{
1214        const gid_t gid = fSymbolArray[index];
1215        #ifdef USE_SIMPLE_HASH_TABLE_LOOKUP
1216        XMLSymbol * symbol = fSymbolTable[gid];
1217        #else
1218        XMLSymbol * symbol = fSymbolResolver[gid];
1219        #endif
1220        return symbol;
1221}
1222
1223// ---------------------------------------------------------------------------------------------------------
1224
1225XML_PARSER_IMPL(void)::
1226storeSymbolUri(const unsigned int index, const unsigned int uriId)
1227{
1228        fSymbolUriArray[index] = uriId;
1229}
1230
1231XML_PARSER_IMPL(unsigned int)::
1232getSymbolUri(const unsigned int index) const
1233{
1234        return fSymbolUriArray[index];
1235}
1236
1237// ---------------------------------------------------------------------------------------------------------
1238
1239XML_PARSER_IMPL(void)::
1240addElement(XMLElementDecl * elemDecl, const gid_t gid, const bool isRoot, const bool isEmpty)
1241{
1242        fChildren[fScope]++;
1243        if (unlikely(++fElementIndex == fElement.capacity()))
1244        {
1245                fElement.expand(fElement.capacity());
1246        }
1247        fElement[fElementIndex] = elemDecl;
1248        if (likely(!isEmpty))
1249        {
1250                if (unlikely(fScope == fChildren.capacity() - 1))
1251                {
1252                        fChildren.expand(fChildren.capacity());
1253                        fGidStack.expand(fGidStack.capacity());
1254                }
1255                fGidStack[fScope] = gid;
1256                fChildren[++fScope] = 0;
1257        }
1258}
1259
1260// ---------------------------------------------------------------------------------------------------------
1261
1262XML_PARSER_IMPL(ptrdiff_t)::
1263getStringLength(const XMLCh * stringStartPtr, const XMLCh ** & stringEndPtr)
1264{
1265        const XMLCh * nextStringEndPtr = *stringEndPtr++;
1266
1267    #ifdef PRINT_DEBUG_MESSAGE
1268        assert (stringStartPtr <= nextStringEndPtr);
1269        assert (&fContentBuf[0] <= nextStringEndPtr);
1270        assert (nextStringEndPtr < fContentBuf.limit());
1271        #endif
1272
1273        return nextStringEndPtr - stringStartPtr;
1274}
1275
1276#undef XML_PARSER_IMPL
1277#undef XML_PARSER_IMPL_WITH_DOCUMENT_STATE_PARAMS
1278
1279XERCES_CPP_NAMESPACE_END
Note: See TracBrowser for help on using the repository browser.