LCOV - code coverage report
Current view: top level - libreoffice/xmlreader/source - xmlreader.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 281 537 52.3 %
Date: 2012-12-27 Functions: 23 26 88.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include "sal/config.h"
      21             : 
      22             : #include <cassert>
      23             : #include <climits>
      24             : #include <cstddef>
      25             : 
      26             : #include "com/sun/star/container/NoSuchElementException.hpp"
      27             : #include "com/sun/star/uno/Reference.hxx"
      28             : #include "com/sun/star/uno/RuntimeException.hpp"
      29             : #include "com/sun/star/uno/XInterface.hpp"
      30             : #include "osl/file.h"
      31             : #include "rtl/string.h"
      32             : #include "rtl/ustring.h"
      33             : #include "rtl/ustring.hxx"
      34             : #include "sal/log.hxx"
      35             : #include "sal/types.h"
      36             : #include "xmlreader/pad.hxx"
      37             : #include "xmlreader/span.hxx"
      38             : #include "xmlreader/xmlreader.hxx"
      39             : 
      40             : namespace xmlreader {
      41             : 
      42             : namespace {
      43             : 
      44    12443399 : bool isSpace(char c) {
      45    12443399 :     switch (c) {
      46             :     case '\x09':
      47             :     case '\x0A':
      48             :     case '\x0D':
      49             :     case ' ':
      50     1852177 :         return true;
      51             :     default:
      52    10591222 :         return false;
      53             :     }
      54             : }
      55             : 
      56             : }
      57             : 
      58        8079 : XmlReader::XmlReader(rtl::OUString const & fileUrl)
      59             :     SAL_THROW((
      60             :         css::container::NoSuchElementException, css::uno::RuntimeException)):
      61        8079 :     fileUrl_(fileUrl)
      62             : {
      63             :     oslFileError e = osl_openFile(
      64        8079 :         fileUrl_.pData, &fileHandle_, osl_File_OpenFlag_Read);
      65        8079 :     switch (e)
      66             :     {
      67             :     case osl_File_E_None:
      68        8079 :         break;
      69             :     case osl_File_E_NOENT:
      70             :         throw css::container::NoSuchElementException(
      71           0 :             fileUrl_, css::uno::Reference< css::uno::XInterface >());
      72             :     default:
      73             :         throw css::uno::RuntimeException(
      74             :             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("cannot open ")) +
      75           0 :              fileUrl_ + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(": ")) +
      76           0 :              rtl::OUString::valueOf(static_cast< sal_Int32 >(e))),
      77           0 :             css::uno::Reference< css::uno::XInterface >());
      78             :     }
      79        8079 :     e = osl_getFileSize(fileHandle_, &fileSize_);
      80        8079 :     if (e == osl_File_E_None) {
      81             :         e = osl_mapFile(
      82             :             fileHandle_, &fileAddress_, fileSize_, 0,
      83        8079 :             osl_File_MapFlag_WillNeed);
      84             :     }
      85        8079 :     if (e != osl_File_E_None) {
      86           0 :         oslFileError e2 = osl_closeFile(fileHandle_);
      87             :         if (e2 != osl_File_E_None) {
      88             :             SAL_WARN(
      89             :                 "xmlreader",
      90             :                 "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e2);
      91             :         }
      92             :         throw css::uno::RuntimeException(
      93           0 :             ("cannot mmap " + fileUrl_ + " (" +
      94           0 :              rtl::OUString::valueOf(static_cast< sal_Int32 >(e)) + ")"),
      95           0 :             css::uno::Reference< css::uno::XInterface >());
      96             :     }
      97             :     namespaceIris_.push_back(
      98             :         Span(
      99             :             RTL_CONSTASCII_STRINGPARAM(
     100        8079 :                 "http://www.w3.org/XML/1998/namespace")));
     101             :     namespaces_.push_back(
     102        8079 :         NamespaceData(Span(RTL_CONSTASCII_STRINGPARAM("xml")), NAMESPACE_XML));
     103        8079 :     pos_ = static_cast< char * >(fileAddress_);
     104        8079 :     end_ = pos_ + fileSize_;
     105        8079 :     state_ = STATE_CONTENT;
     106        8079 :     firstAttribute_ = true;
     107        8079 : }
     108             : 
     109       16158 : XmlReader::~XmlReader() {
     110        8079 :     oslFileError e = osl_unmapMappedFile(fileHandle_, fileAddress_, fileSize_);
     111             :     if (e != osl_File_E_None) {
     112             :         SAL_WARN(
     113             :             "xmlreader",
     114             :             "osl_unmapMappedFile of \"" << fileUrl_ << "\" failed with " << +e);
     115             :     }
     116        8079 :     e = osl_closeFile(fileHandle_);
     117             :     if (e != osl_File_E_None) {
     118             :         SAL_WARN(
     119             :             "xmlreader",
     120             :             "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e);
     121             :     }
     122        8079 : }
     123             : 
     124       22277 : int XmlReader::registerNamespaceIri(Span const & iri) {
     125       22277 :     int id = toNamespaceId(namespaceIris_.size());
     126       22277 :     namespaceIris_.push_back(iri);
     127       44554 :     if (iri.equals(
     128             :             Span(
     129             :                 RTL_CONSTASCII_STRINGPARAM(
     130       44554 :                     "http://www.w3.org/2001/XMLSchema-instance"))))
     131             :     {
     132             :         // Old user layer .xcu files used the xsi namespace prefix without
     133             :         // declaring a corresponding namespace binding, see issue 77174; reading
     134             :         // those files during migration would fail without this hack that can be
     135             :         // removed once migration is no longer relevant (see
     136             :         // configmgr::Components::parseModificationLayer):
     137             :         namespaces_.push_back(
     138        7099 :             NamespaceData(Span(RTL_CONSTASCII_STRINGPARAM("xsi")), id));
     139             :     }
     140       22277 :     return id;
     141             : }
     142             : 
     143     4508051 : XmlReader::Result XmlReader::nextItem(Text reportText, Span * data, int * nsId)
     144             : {
     145     4508051 :     switch (state_) {
     146             :     case STATE_CONTENT:
     147     3836523 :         switch (reportText) {
     148             :         case TEXT_NONE:
     149     3257807 :             return handleSkippedText(data, nsId);
     150             :         case TEXT_RAW:
     151      406222 :             return handleRawText(data);
     152             :         case TEXT_NORMALIZED:
     153      172494 :             return handleNormalizedText(data);
     154             :         }
     155             :     case STATE_START_TAG:
     156         930 :         return handleStartTag(nsId, data);
     157             :     case STATE_END_TAG:
     158      577786 :         return handleEndTag();
     159             :     case STATE_EMPTY_ELEMENT_TAG:
     160       84733 :         handleElementEnd();
     161       84733 :         return RESULT_END;
     162             :     default: // STATE_DONE
     163        8079 :         return RESULT_DONE;
     164             :     }
     165             : }
     166             : 
     167     3399876 : bool XmlReader::nextAttribute(int * nsId, Span * localName) {
     168             :     assert(nsId != 0 && localName != 0);
     169     3399876 :     if (firstAttribute_) {
     170     1604069 :         currentAttribute_ = attributes_.begin();
     171     1604069 :         firstAttribute_ = false;
     172             :     } else {
     173     1795807 :         ++currentAttribute_;
     174             :     }
     175     3399876 :     if (currentAttribute_ == attributes_.end()) {
     176     1604069 :         return false;
     177             :     }
     178     1795807 :     if (currentAttribute_->nameColon == 0) {
     179       59752 :         *nsId = NAMESPACE_NONE;
     180             :         *localName = Span(
     181       59752 :             currentAttribute_->nameBegin,
     182      119504 :             currentAttribute_->nameEnd - currentAttribute_->nameBegin);
     183             :     } else {
     184             :         *nsId = getNamespaceId(
     185             :             Span(
     186     1736055 :                 currentAttribute_->nameBegin,
     187     3472110 :                 currentAttribute_->nameColon - currentAttribute_->nameBegin));
     188             :         *localName = Span(
     189     1736055 :             currentAttribute_->nameColon + 1,
     190     3472110 :             currentAttribute_->nameEnd - (currentAttribute_->nameColon + 1));
     191             :     }
     192     1795807 :     return true;
     193             : }
     194             : 
     195     1791560 : Span XmlReader::getAttributeValue(bool fullyNormalize) {
     196             :     return handleAttributeValue(
     197     3583120 :         currentAttribute_->valueBegin, currentAttribute_->valueEnd,
     198     5374680 :         fullyNormalize);
     199             : }
     200             : 
     201     1998760 : int XmlReader::getNamespaceId(Span const & prefix) const {
     202    21009699 :     for (NamespaceList::const_reverse_iterator i(namespaces_.rbegin());
     203    14006466 :          i != namespaces_.rend(); ++i)
     204             :     {
     205     7003233 :         if (prefix.equals(i->prefix)) {
     206     1998760 :             return i->nsId;
     207             :         }
     208             :     }
     209           0 :     return NAMESPACE_UNKNOWN;
     210             : }
     211             : 
     212       26106 : rtl::OUString XmlReader::getUrl() const {
     213       26106 :     return fileUrl_;
     214             : }
     215             : 
     216           0 : void XmlReader::normalizeLineEnds(Span const & text) {
     217           0 :     char const * p = text.begin;
     218           0 :     sal_Int32 n = text.length;
     219           0 :     for (;;) {
     220           0 :         sal_Int32 i = rtl_str_indexOfChar_WithLength(p, n, '\x0D');
     221           0 :         if (i < 0) {
     222           0 :             break;
     223             :         }
     224           0 :         pad_.add(p, i);
     225           0 :         p += i + 1;
     226           0 :         n -= i + 1;
     227           0 :         if (n == 0 || *p != '\x0A') {
     228           0 :             pad_.add(RTL_CONSTASCII_STRINGPARAM("\x0A"));
     229             :         }
     230             :     }
     231           0 :     pad_.add(p, n);
     232           0 : }
     233             : 
     234     9382758 : void XmlReader::skipSpace() {
     235    20617693 :     while (isSpace(peek())) {
     236     1852177 :         ++pos_;
     237             :     }
     238     9382758 : }
     239             : 
     240        5987 : bool XmlReader::skipComment() {
     241        5987 :     if (rtl_str_shortenedCompare_WithLength(
     242             :             pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"),
     243        5987 :             RTL_CONSTASCII_LENGTH("--")) !=
     244             :         0)
     245             :     {
     246           0 :         return false;
     247             :     }
     248        5987 :     pos_ += RTL_CONSTASCII_LENGTH("--");
     249             :     sal_Int32 i = rtl_str_indexOfStr_WithLength(
     250        5987 :         pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"));
     251        5987 :     if (i < 0) {
     252             :         throw css::uno::RuntimeException(
     253             :             (rtl::OUString(
     254             :                 RTL_CONSTASCII_USTRINGPARAM(
     255             :                     "premature end (within comment) of ")) +
     256           0 :              fileUrl_),
     257           0 :             css::uno::Reference< css::uno::XInterface >());
     258             :     }
     259        5987 :     pos_ += i + RTL_CONSTASCII_LENGTH("--");
     260        5987 :     if (read() != '>') {
     261             :         throw css::uno::RuntimeException(
     262             :             (rtl::OUString(
     263             :                 RTL_CONSTASCII_USTRINGPARAM(
     264             :                     "illegal \"--\" within comment in ")) +
     265           0 :              fileUrl_),
     266           0 :             css::uno::Reference< css::uno::XInterface >());
     267             :     }
     268        5987 :     return true;
     269             : }
     270             : 
     271        8079 : void XmlReader::skipProcessingInstruction() {
     272             :     sal_Int32 i = rtl_str_indexOfStr_WithLength(
     273        8079 :         pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("?>"));
     274        8079 :     if (i < 0) {
     275             :         throw css::uno::RuntimeException(
     276             :             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad '<?' in ")) +
     277           0 :              fileUrl_),
     278           0 :             css::uno::Reference< css::uno::XInterface >());
     279             :     }
     280        8079 :     pos_ += i + RTL_CONSTASCII_LENGTH("?>");
     281        8079 : }
     282             : 
     283           0 : void XmlReader::skipDocumentTypeDeclaration() {
     284             :     // Neither is it checked that the doctypedecl is at the correct position in
     285             :     // the document, nor that it is well-formed:
     286           0 :     for (;;) {
     287           0 :         char c = read();
     288           0 :         switch (c) {
     289             :         case '\0': // i.e., EOF
     290             :             throw css::uno::RuntimeException(
     291             :                 (rtl::OUString(
     292             :                     RTL_CONSTASCII_USTRINGPARAM(
     293             :                         "premature end (within DTD) of ")) +
     294           0 :                  fileUrl_),
     295           0 :                 css::uno::Reference< css::uno::XInterface >());
     296             :         case '"':
     297             :         case '\'':
     298             :             {
     299             :                 sal_Int32 i = rtl_str_indexOfChar_WithLength(
     300           0 :                     pos_, end_ - pos_, c);
     301           0 :                 if (i < 0) {
     302             :                     throw css::uno::RuntimeException(
     303             :                         (rtl::OUString(
     304             :                             RTL_CONSTASCII_USTRINGPARAM(
     305             :                                 "premature end (within DTD) of ")) +
     306           0 :                          fileUrl_),
     307           0 :                         css::uno::Reference< css::uno::XInterface >());
     308             :                 }
     309           0 :                 pos_ += i + 1;
     310             :             }
     311           0 :             break;
     312             :         case '>':
     313           0 :             return;
     314             :         case '[':
     315           0 :             for (;;) {
     316           0 :                 c = read();
     317           0 :                 switch (c) {
     318             :                 case '\0': // i.e., EOF
     319             :                     throw css::uno::RuntimeException(
     320             :                         (rtl::OUString(
     321             :                             RTL_CONSTASCII_USTRINGPARAM(
     322             :                                 "premature end (within DTD) of ")) +
     323           0 :                          fileUrl_),
     324           0 :                         css::uno::Reference< css::uno::XInterface >());
     325             :                 case '"':
     326             :                 case '\'':
     327             :                     {
     328             :                         sal_Int32 i = rtl_str_indexOfChar_WithLength(
     329           0 :                             pos_, end_ - pos_, c);
     330           0 :                         if (i < 0) {
     331             :                             throw css::uno::RuntimeException(
     332             :                             (rtl::OUString(
     333             :                                 RTL_CONSTASCII_USTRINGPARAM(
     334             :                                     "premature end (within DTD) of ")) +
     335           0 :                              fileUrl_),
     336           0 :                             css::uno::Reference< css::uno::XInterface >());
     337             :                         }
     338           0 :                         pos_ += i + 1;
     339             :                     }
     340           0 :                     break;
     341             :                 case '<':
     342           0 :                     switch (read()) {
     343             :                     case '\0': // i.e., EOF
     344             :                         throw css::uno::RuntimeException(
     345             :                             (rtl::OUString(
     346             :                                 RTL_CONSTASCII_USTRINGPARAM(
     347             :                                     "premature end (within DTD) of ")) +
     348           0 :                              fileUrl_),
     349           0 :                             css::uno::Reference< css::uno::XInterface >());
     350             :                     case '!':
     351           0 :                         skipComment();
     352           0 :                         break;
     353             :                     case '?':
     354           0 :                         skipProcessingInstruction();
     355           0 :                         break;
     356             :                     default:
     357           0 :                         break;
     358             :                     }
     359           0 :                     break;
     360             :                 case ']':
     361           0 :                     skipSpace();
     362           0 :                     if (read() != '>') {
     363             :                         throw css::uno::RuntimeException(
     364             :                             (rtl::OUString(
     365             :                                 RTL_CONSTASCII_USTRINGPARAM(
     366             :                                     "missing \">\" of DTD in ")) +
     367           0 :                              fileUrl_),
     368           0 :                             css::uno::Reference< css::uno::XInterface >());
     369             :                     }
     370           0 :                     return;
     371             :                 default:
     372           0 :                     break;
     373             :                 }
     374             :             }
     375             :         default:
     376           0 :             break;
     377             :         }
     378             :     }
     379             : }
     380             : 
     381           0 : Span XmlReader::scanCdataSection() {
     382           0 :     if (rtl_str_shortenedCompare_WithLength(
     383             :             pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("[CDATA["),
     384           0 :             RTL_CONSTASCII_LENGTH("[CDATA[")) !=
     385             :         0)
     386             :     {
     387           0 :         return Span();
     388             :     }
     389           0 :     pos_ += RTL_CONSTASCII_LENGTH("[CDATA[");
     390           0 :     char const * begin = pos_;
     391             :     sal_Int32 i = rtl_str_indexOfStr_WithLength(
     392           0 :         pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("]]>"));
     393           0 :     if (i < 0) {
     394             :         throw css::uno::RuntimeException(
     395             :             (rtl::OUString(
     396             :                 RTL_CONSTASCII_USTRINGPARAM(
     397             :                     "premature end (within CDATA section) of ")) +
     398           0 :              fileUrl_),
     399           0 :             css::uno::Reference< css::uno::XInterface >());
     400             :     }
     401           0 :     pos_ += i + RTL_CONSTASCII_LENGTH("]]>");
     402           0 :     return Span(begin, i);
     403             : }
     404             : 
     405     5685268 : bool XmlReader::scanName(char const ** nameColon) {
     406             :     assert(nameColon != 0 && *nameColon == 0);
     407    38332341 :     for (char const * begin = pos_;; ++pos_) {
     408    38332341 :         switch (peek()) {
     409             :         case '\0': // i.e., EOF
     410             :         case '\x09':
     411             :         case '\x0A':
     412             :         case '\x0D':
     413             :         case ' ':
     414             :         case '/':
     415             :         case '=':
     416             :         case '>':
     417    11370536 :             return pos_ != begin;
     418             :         case ':':
     419     1801978 :             *nameColon = pos_;
     420     1801978 :             break;
     421             :         default:
     422    30845095 :             break;
     423             :         }
     424             :     }
     425             : }
     426             : 
     427       24573 : int XmlReader::scanNamespaceIri(char const * begin, char const * end) {
     428             :     assert(begin != 0 && begin <= end);
     429       24573 :     Span iri(handleAttributeValue(begin, end, false));
     430       78274 :     for (NamespaceIris::size_type i = 0; i < namespaceIris_.size(); ++i) {
     431       75088 :         if (namespaceIris_[i].equals(iri)) {
     432       21387 :             return toNamespaceId(i);
     433             :         }
     434             :     }
     435        3186 :     return XmlReader::NAMESPACE_UNKNOWN;
     436             : }
     437             : 
     438        5827 : char const * XmlReader::handleReference(char const * position, char const * end)
     439             : {
     440             :     assert(position != 0 && *position == '&' && position < end);
     441        5827 :     ++position;
     442        5827 :     if (*position == '#') {
     443           0 :         ++position;
     444           0 :         sal_Int32 val = 0;
     445             :         char const * p;
     446           0 :         if (*position == 'x') {
     447           0 :             ++position;
     448           0 :             p = position;
     449           0 :             for (;; ++position) {
     450           0 :                 char c = *position;
     451           0 :                 if (c >= '0' && c <= '9') {
     452           0 :                     val = 16 * val + (c - '0');
     453           0 :                 } else if (c >= 'A' && c <= 'F') {
     454           0 :                     val = 16 * val + (c - 'A') + 10;
     455           0 :                 } else if (c >= 'a' && c <= 'f') {
     456           0 :                     val = 16 * val + (c - 'a') + 10;
     457             :                 } else {
     458           0 :                     break;
     459             :                 }
     460           0 :                 if (val > 0x10FFFF) { // avoid overflow
     461             :                     throw css::uno::RuntimeException(
     462             :                         (rtl::OUString(
     463             :                             RTL_CONSTASCII_USTRINGPARAM(
     464             :                                 "'&#x...' too large in ")) +
     465           0 :                          fileUrl_),
     466           0 :                         css::uno::Reference< css::uno::XInterface >());
     467             :                 }
     468             :             }
     469             :         } else {
     470           0 :             p = position;
     471           0 :             for (;; ++position) {
     472           0 :                 char c = *position;
     473           0 :                 if (c >= '0' && c <= '9') {
     474           0 :                     val = 10 * val + (c - '0');
     475             :                 } else {
     476           0 :                     break;
     477             :                 }
     478           0 :                 if (val > 0x10FFFF) { // avoid overflow
     479             :                     throw css::uno::RuntimeException(
     480             :                         (rtl::OUString(
     481             :                             RTL_CONSTASCII_USTRINGPARAM(
     482             :                                 "'&#...' too large in ")) +
     483           0 :                          fileUrl_),
     484           0 :                         css::uno::Reference< css::uno::XInterface >());
     485             :                 }
     486             :             }
     487             :         }
     488           0 :         if (position == p || *position++ != ';') {
     489             :             throw css::uno::RuntimeException(
     490             :                 (rtl::OUString(
     491             :                     RTL_CONSTASCII_USTRINGPARAM("'&#...' missing ';' in ")) +
     492           0 :                  fileUrl_),
     493           0 :                 css::uno::Reference< css::uno::XInterface >());
     494             :         }
     495             :         assert(val >= 0 && val <= 0x10FFFF);
     496           0 :         if ((val < 0x20 && val != 0x9 && val != 0xA && val != 0xD) ||
     497             :             (val >= 0xD800 && val <= 0xDFFF) || val == 0xFFFE || val == 0xFFFF)
     498             :         {
     499             :             throw css::uno::RuntimeException(
     500             :                 (rtl::OUString(
     501             :                     RTL_CONSTASCII_USTRINGPARAM(
     502             :                         "character reference denoting invalid character in ")) +
     503           0 :                  fileUrl_),
     504           0 :                 css::uno::Reference< css::uno::XInterface >());
     505             :         }
     506             :         char buf[4];
     507             :         sal_Int32 len;
     508           0 :         if (val < 0x80) {
     509           0 :             buf[0] = static_cast< char >(val);
     510           0 :             len = 1;
     511           0 :         } else if (val < 0x800) {
     512           0 :             buf[0] = static_cast< char >((val >> 6) | 0xC0);
     513           0 :             buf[1] = static_cast< char >((val & 0x3F) | 0x80);
     514           0 :             len = 2;
     515           0 :         } else if (val < 0x10000) {
     516           0 :             buf[0] = static_cast< char >((val >> 12) | 0xE0);
     517           0 :             buf[1] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
     518           0 :             buf[2] = static_cast< char >((val & 0x3F) | 0x80);
     519           0 :             len = 3;
     520             :         } else {
     521           0 :             buf[0] = static_cast< char >((val >> 18) | 0xF0);
     522           0 :             buf[1] = static_cast< char >(((val >> 12) & 0x3F) | 0x80);
     523           0 :             buf[2] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
     524           0 :             buf[3] = static_cast< char >((val & 0x3F) | 0x80);
     525           0 :             len = 4;
     526             :         }
     527           0 :         pad_.addEphemeral(buf, len);
     528           0 :         return position;
     529             :     } else {
     530             :         struct EntityRef {
     531             :             char const * inBegin;
     532             :             sal_Int32 inLength;
     533             :             char const * outBegin;
     534             :             sal_Int32 outLength;
     535             :         };
     536             :         static EntityRef const refs[] = {
     537             :             { RTL_CONSTASCII_STRINGPARAM("amp;"),
     538             :               RTL_CONSTASCII_STRINGPARAM("&") },
     539             :             { RTL_CONSTASCII_STRINGPARAM("lt;"),
     540             :               RTL_CONSTASCII_STRINGPARAM("<") },
     541             :             { RTL_CONSTASCII_STRINGPARAM("gt;"),
     542             :               RTL_CONSTASCII_STRINGPARAM(">") },
     543             :             { RTL_CONSTASCII_STRINGPARAM("apos;"),
     544             :               RTL_CONSTASCII_STRINGPARAM("'") },
     545             :             { RTL_CONSTASCII_STRINGPARAM("quot;"),
     546             :               RTL_CONSTASCII_STRINGPARAM("\"") } };
     547       11655 :         for (std::size_t i = 0; i < sizeof refs / sizeof refs[0]; ++i) {
     548       11655 :             if (rtl_str_shortenedCompare_WithLength(
     549             :                     position, end - position, refs[i].inBegin, refs[i].inLength,
     550       11655 :                     refs[i].inLength) ==
     551             :                 0)
     552             :             {
     553        5827 :                 position += refs[i].inLength;
     554        5827 :                 pad_.add(refs[i].outBegin, refs[i].outLength);
     555        5827 :                 return position;
     556             :             }
     557             :         }
     558             :         throw css::uno::RuntimeException(
     559             :             (rtl::OUString(
     560             :                 RTL_CONSTASCII_USTRINGPARAM("unknown entity reference in ")) +
     561           0 :              fileUrl_),
     562           0 :             css::uno::Reference< css::uno::XInterface >());
     563             :     }
     564             : }
     565             : 
     566     1816133 : Span XmlReader::handleAttributeValue(
     567             :     char const * begin, char const * end, bool fullyNormalize)
     568             : {
     569     1816133 :     pad_.clear();
     570     1816133 :     if (fullyNormalize) {
     571     1208464 :         while (begin != end && isSpace(*begin)) {
     572           0 :             ++begin;
     573             :         }
     574     1208464 :         while (end != begin && isSpace(end[-1])) {
     575           0 :             --end;
     576             :         }
     577      604232 :         char const * p = begin;
     578             :         enum Space { SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
     579             :             // a single true space character can go into the current span,
     580             :             // everything else breaks the span
     581      604232 :         Space space = SPACE_NONE;
     582     5634076 :         while (p != end) {
     583     4425612 :             switch (*p) {
     584             :             case '\x09':
     585             :             case '\x0A':
     586             :             case '\x0D':
     587           0 :                 switch (space) {
     588             :                 case SPACE_NONE:
     589           0 :                     pad_.add(begin, p - begin);
     590           0 :                     pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
     591           0 :                     space = SPACE_BREAK;
     592           0 :                     break;
     593             :                 case SPACE_SPAN:
     594           0 :                     pad_.add(begin, p - begin);
     595           0 :                     space = SPACE_BREAK;
     596           0 :                     break;
     597             :                 case SPACE_BREAK:
     598           0 :                     break;
     599             :                 }
     600           0 :                 begin = ++p;
     601           0 :                 break;
     602             :             case ' ':
     603         834 :                 switch (space) {
     604             :                 case SPACE_NONE:
     605         834 :                     ++p;
     606         834 :                     space = SPACE_SPAN;
     607         834 :                     break;
     608             :                 case SPACE_SPAN:
     609           0 :                     pad_.add(begin, p - begin);
     610           0 :                     begin = ++p;
     611           0 :                     space = SPACE_BREAK;
     612           0 :                     break;
     613             :                 case SPACE_BREAK:
     614           0 :                     begin = ++p;
     615           0 :                     break;
     616             :                 }
     617         834 :                 break;
     618             :             case '&':
     619           0 :                 pad_.add(begin, p - begin);
     620           0 :                 p = handleReference(p, end);
     621           0 :                 begin = p;
     622           0 :                 space = SPACE_NONE;
     623           0 :                 break;
     624             :             default:
     625     4424778 :                 ++p;
     626     4424778 :                 space = SPACE_NONE;
     627     4424778 :                 break;
     628             :             }
     629             :         }
     630      604232 :         pad_.add(begin, p - begin);
     631             :     } else {
     632     1211901 :         char const * p = begin;
     633    15816261 :         while (p != end) {
     634    13392459 :             switch (*p) {
     635             :             case '\x09':
     636             :             case '\x0A':
     637           0 :                 pad_.add(begin, p - begin);
     638           0 :                 begin = ++p;
     639           0 :                 pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
     640           0 :                 break;
     641             :             case '\x0D':
     642           0 :                 pad_.add(begin, p - begin);
     643           0 :                 ++p;
     644           0 :                 if (peek() == '\x0A') {
     645           0 :                     ++p;
     646             :                 }
     647           0 :                 begin = p;
     648           0 :                 pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
     649           0 :                 break;
     650             :             case '&':
     651           0 :                 pad_.add(begin, p - begin);
     652           0 :                 p = handleReference(p, end);
     653           0 :                 begin = p;
     654           0 :                 break;
     655             :             default:
     656    13392459 :                 ++p;
     657    13392459 :                 break;
     658             :             }
     659             :         }
     660     1211901 :         pad_.add(begin, p - begin);
     661             :     }
     662     1816133 :     return pad_.get();
     663             : }
     664             : 
     665     1960628 : XmlReader::Result XmlReader::handleStartTag(int * nsId, Span * localName) {
     666             :     assert(nsId != 0 && localName);
     667     1960628 :     char const * nameBegin = pos_;
     668     1960628 :     char const * nameColon = 0;
     669     1960628 :     if (!scanName(&nameColon)) {
     670             :         throw css::uno::RuntimeException(
     671             :             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad tag name in ")) +
     672           0 :              fileUrl_),
     673           0 :             css::uno::Reference< css::uno::XInterface >());
     674             :     }
     675     1960628 :     char const * nameEnd = pos_;
     676     1960628 :     NamespaceList::size_type inheritedNamespaces = namespaces_.size();
     677     1960628 :     bool hasDefaultNs = false;
     678     1960628 :     int defaultNsId = NAMESPACE_NONE;
     679     1960628 :     attributes_.clear();
     680     1848745 :     for (;;) {
     681     3809373 :         char const * p = pos_;
     682     3809373 :         skipSpace();
     683     3809373 :         if (peek() == '/' || peek() == '>') {
     684             :             break;
     685             :         }
     686     1848745 :         if (pos_ == p) {
     687             :             throw css::uno::RuntimeException(
     688             :                 (rtl::OUString(
     689             :                     RTL_CONSTASCII_USTRINGPARAM(
     690             :                         "missing whitespace before attribute in ")) +
     691           0 :                  fileUrl_),
     692           0 :                 css::uno::Reference< css::uno::XInterface >());
     693             :         }
     694     1848745 :         char const * attrNameBegin = pos_;
     695     1848745 :         char const * attrNameColon = 0;
     696     1848745 :         if (!scanName(&attrNameColon)) {
     697             :             throw css::uno::RuntimeException(
     698             :                 (rtl::OUString(
     699             :                     RTL_CONSTASCII_USTRINGPARAM("bad attribute name in ")) +
     700           0 :                  fileUrl_),
     701           0 :                 css::uno::Reference< css::uno::XInterface >());
     702             :         }
     703     1848745 :         char const * attrNameEnd = pos_;
     704     1848745 :         skipSpace();
     705     1848745 :         if (read() != '=') {
     706             :             throw css::uno::RuntimeException(
     707             :                 (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("missing '=' in ")) +
     708           0 :                  fileUrl_),
     709           0 :                 css::uno::Reference< css::uno::XInterface >());
     710             :         }
     711     1848745 :         skipSpace();
     712     1848745 :         char del = read();
     713     1848745 :         if (del != '\'' && del != '"') {
     714             :             throw css::uno::RuntimeException(
     715             :                 (rtl::OUString(
     716             :                     RTL_CONSTASCII_USTRINGPARAM("bad attribute value in ")) +
     717           0 :                  fileUrl_),
     718           0 :                 css::uno::Reference< css::uno::XInterface >());
     719             :         }
     720     1848745 :         char const * valueBegin = pos_;
     721     1848745 :         sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, del);
     722     1848745 :         if (i < 0) {
     723             :             throw css::uno::RuntimeException(
     724             :                 (rtl::OUString(
     725             :                     RTL_CONSTASCII_USTRINGPARAM(
     726             :                         "unterminated attribute value in ")) +
     727           0 :                  fileUrl_),
     728           0 :                 css::uno::Reference< css::uno::XInterface >());
     729             :         }
     730     1848745 :         char const * valueEnd = pos_ + i;
     731     1848745 :         pos_ += i + 1;
     732     3818954 :         if (attrNameColon == 0 &&
     733             :             Span(attrNameBegin, attrNameEnd - attrNameBegin).equals(
     734     1970209 :                 RTL_CONSTASCII_STRINGPARAM("xmlns")))
     735             :         {
     736         980 :             hasDefaultNs = true;
     737         980 :             defaultNsId = scanNamespaceIri(valueBegin, valueEnd);
     738     7271556 :         } else if (attrNameColon != 0 &&
     739             :                    Span(attrNameBegin, attrNameColon - attrNameBegin).equals(
     740     5423791 :                        RTL_CONSTASCII_STRINGPARAM("xmlns")))
     741             :         {
     742             :             namespaces_.push_back(
     743             :                 NamespaceData(
     744       23593 :                     Span(attrNameColon + 1, attrNameEnd - (attrNameColon + 1)),
     745       47186 :                     scanNamespaceIri(valueBegin, valueEnd)));
     746             :         } else {
     747             :             attributes_.push_back(
     748             :                 AttributeData(
     749             :                     attrNameBegin, attrNameEnd, attrNameColon, valueBegin,
     750     1824172 :                     valueEnd));
     751             :         }
     752             :     }
     753     1960628 :     if (!hasDefaultNs && !elements_.empty()) {
     754     1952549 :         defaultNsId = elements_.top().defaultNamespaceId;
     755             :     }
     756     1960628 :     firstAttribute_ = true;
     757     1960628 :     if (peek() == '/') {
     758       84733 :         state_ = STATE_EMPTY_ELEMENT_TAG;
     759       84733 :         ++pos_;
     760             :     } else {
     761     1875895 :         state_ = STATE_CONTENT;
     762             :     }
     763     1960628 :     if (peek() != '>') {
     764             :         throw css::uno::RuntimeException(
     765             :             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("missing '>' in ")) +
     766           0 :              fileUrl_),
     767           0 :             css::uno::Reference< css::uno::XInterface >());
     768             :     }
     769     1960628 :     ++pos_;
     770             :     elements_.push(
     771             :         ElementData(
     772             :             Span(nameBegin, nameEnd - nameBegin), inheritedNamespaces,
     773     1960628 :             defaultNsId));
     774     1960628 :     if (nameColon == 0) {
     775     1953511 :         *nsId = defaultNsId;
     776     1953511 :         *localName = Span(nameBegin, nameEnd - nameBegin);
     777             :     } else {
     778        7117 :         *nsId = getNamespaceId(Span(nameBegin, nameColon - nameBegin));
     779        7117 :         *localName = Span(nameColon + 1, nameEnd - (nameColon + 1));
     780             :     }
     781     1960628 :     return RESULT_BEGIN;
     782             : }
     783             : 
     784     1875895 : XmlReader::Result XmlReader::handleEndTag() {
     785     1875895 :     if (elements_.empty()) {
     786             :         throw css::uno::RuntimeException(
     787             :             (rtl::OUString(
     788             :                 RTL_CONSTASCII_USTRINGPARAM("spurious end tag in ")) +
     789           0 :              fileUrl_),
     790           0 :             css::uno::Reference< css::uno::XInterface >());
     791             :     }
     792     1875895 :     char const * nameBegin = pos_;
     793     1875895 :     char const * nameColon = 0;
     794     3751790 :     if (!scanName(&nameColon) ||
     795     1875895 :         !elements_.top().name.equals(nameBegin, pos_ - nameBegin))
     796             :     {
     797             :         throw css::uno::RuntimeException(
     798             :             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("tag mismatch in ")) +
     799           0 :              fileUrl_),
     800           0 :             css::uno::Reference< css::uno::XInterface >());
     801             :     }
     802     1875895 :     handleElementEnd();
     803     1875895 :     skipSpace();
     804     1875895 :     if (peek() != '>') {
     805             :         throw css::uno::RuntimeException(
     806             :             (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("missing '>' in ")) +
     807           0 :              fileUrl_),
     808           0 :             css::uno::Reference< css::uno::XInterface >());
     809             :     }
     810     1875895 :     ++pos_;
     811     1875895 :     return RESULT_END;
     812             : }
     813             : 
     814     1960628 : void XmlReader::handleElementEnd() {
     815             :     assert(!elements_.empty());
     816     1960628 :     namespaces_.resize(elements_.top().inheritedNamespaces);
     817     1960628 :     elements_.pop();
     818     1960628 :     state_ = elements_.empty() ? STATE_DONE : STATE_CONTENT;
     819     1960628 : }
     820             : 
     821     3271687 : XmlReader::Result XmlReader::handleSkippedText(Span * data, int * nsId) {
     822       13880 :     for (;;) {
     823     3271687 :         sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, '<');
     824     3271687 :         if (i < 0) {
     825             :             throw css::uno::RuntimeException(
     826             :                 (rtl::OUString(
     827             :                     RTL_CONSTASCII_USTRINGPARAM("premature end of ")) +
     828           0 :                  fileUrl_),
     829           0 :                 css::uno::Reference< css::uno::XInterface >());
     830             :         }
     831     3271687 :         pos_ += i + 1;
     832     3271687 :         switch (peek()) {
     833             :         case '!':
     834        5801 :             ++pos_;
     835        5801 :             if (!skipComment() && !scanCdataSection().is()) {
     836           0 :                 skipDocumentTypeDeclaration();
     837             :             }
     838        5801 :             break;
     839             :         case '/':
     840     1298109 :             ++pos_;
     841     1298109 :             return handleEndTag();
     842             :         case '?':
     843        8079 :             ++pos_;
     844        8079 :             skipProcessingInstruction();
     845        8079 :             break;
     846             :         default:
     847     1959698 :             return handleStartTag(nsId, data);
     848             :         }
     849             :     }
     850             : }
     851             : 
     852      406222 : XmlReader::Result XmlReader::handleRawText(Span * text) {
     853      406222 :     pad_.clear();
     854    11931642 :     for (char const * begin = pos_;;) {
     855    11931642 :         switch (peek()) {
     856             :         case '\0': // i.e., EOF
     857             :             throw css::uno::RuntimeException(
     858             :                 (rtl::OUString(
     859             :                     RTL_CONSTASCII_USTRINGPARAM("premature end of ")) +
     860           0 :                  fileUrl_),
     861           0 :                 css::uno::Reference< css::uno::XInterface >());
     862             :         case '\x0D':
     863           0 :             pad_.add(begin, pos_ - begin);
     864           0 :             ++pos_;
     865           0 :             if (peek() != '\x0A') {
     866           0 :                 pad_.add(RTL_CONSTASCII_STRINGPARAM("\x0A"));
     867             :             }
     868           0 :             begin = pos_;
     869           0 :             break;
     870             :         case '&':
     871        5827 :             pad_.add(begin, pos_ - begin);
     872        5827 :             pos_ = handleReference(pos_, end_);
     873        5827 :             begin = pos_;
     874        5827 :             break;
     875             :         case '<':
     876      406222 :             pad_.add(begin, pos_ - begin);
     877      406222 :             ++pos_;
     878      406222 :             switch (peek()) {
     879             :             case '!':
     880           0 :                 ++pos_;
     881           0 :                 if (!skipComment()) {
     882           0 :                     Span cdata(scanCdataSection());
     883           0 :                     if (cdata.is()) {
     884           0 :                         normalizeLineEnds(cdata);
     885             :                     } else {
     886           0 :                         skipDocumentTypeDeclaration();
     887             :                     }
     888             :                 }
     889           0 :                 begin = pos_;
     890           0 :                 break;
     891             :             case '/':
     892      405292 :                 *text = pad_.get();
     893      405292 :                 ++pos_;
     894      405292 :                 state_ = STATE_END_TAG;
     895      405292 :                 return RESULT_TEXT;
     896             :             case '?':
     897           0 :                 ++pos_;
     898           0 :                 skipProcessingInstruction();
     899           0 :                 begin = pos_;
     900           0 :                 break;
     901             :             default:
     902         930 :                 *text = pad_.get();
     903         930 :                 state_ = STATE_START_TAG;
     904         930 :                 return RESULT_TEXT;
     905             :             }
     906           0 :             break;
     907             :         default:
     908    11519593 :             ++pos_;
     909    11519593 :             break;
     910             :         }
     911             :     }
     912             : }
     913             : 
     914      172494 : XmlReader::Result XmlReader::handleNormalizedText(Span * text) {
     915      172494 :     pad_.clear();
     916      172494 :     char const * flowBegin = pos_;
     917      172494 :     char const * flowEnd = pos_;
     918             :     enum Space { SPACE_START, SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
     919             :         // a single true space character can go into the current flow,
     920             :         // everything else breaks the flow
     921      172494 :     Space space = SPACE_START;
     922      469412 :     for (;;) {
     923      641906 :         switch (peek()) {
     924             :         case '\0': // i.e., EOF
     925             :             throw css::uno::RuntimeException(
     926             :                 (rtl::OUString(
     927             :                     RTL_CONSTASCII_USTRINGPARAM("premature end of ")) +
     928           0 :                  fileUrl_),
     929           0 :                 css::uno::Reference< css::uno::XInterface >());
     930             :         case '\x09':
     931             :         case '\x0A':
     932             :         case '\x0D':
     933         403 :             switch (space) {
     934             :             case SPACE_START:
     935             :             case SPACE_BREAK:
     936         217 :                 break;
     937             :             case SPACE_NONE:
     938             :             case SPACE_SPAN:
     939         186 :                 space = SPACE_BREAK;
     940         186 :                 break;
     941             :             }
     942         403 :             ++pos_;
     943         403 :             break;
     944             :         case ' ':
     945        3348 :             switch (space) {
     946             :             case SPACE_START:
     947             :             case SPACE_BREAK:
     948        1581 :                 break;
     949             :             case SPACE_NONE:
     950        1767 :                 space = SPACE_SPAN;
     951        1767 :                 break;
     952             :             case SPACE_SPAN:
     953           0 :                 space = SPACE_BREAK;
     954           0 :                 break;
     955             :             }
     956        3348 :             ++pos_;
     957        3348 :             break;
     958             :         case '&':
     959           0 :             switch (space) {
     960             :             case SPACE_START:
     961           0 :                 break;
     962             :             case SPACE_NONE:
     963             :             case SPACE_SPAN:
     964           0 :                 pad_.add(flowBegin, pos_ - flowBegin);
     965           0 :                 break;
     966             :             case SPACE_BREAK:
     967           0 :                 pad_.add(flowBegin, flowEnd - flowBegin);
     968           0 :                 pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
     969           0 :                 break;
     970             :             }
     971           0 :             pos_ = handleReference(pos_, end_);
     972           0 :             flowBegin = pos_;
     973           0 :             flowEnd = pos_;
     974           0 :             space = SPACE_NONE;
     975           0 :             break;
     976             :         case '<':
     977      172680 :             ++pos_;
     978      172680 :             switch (peek()) {
     979             :             case '!':
     980         186 :                 ++pos_;
     981         186 :                 if (skipComment()) {
     982         186 :                     space = SPACE_BREAK;
     983             :                 } else {
     984           0 :                     Span cdata(scanCdataSection());
     985           0 :                     if (cdata.is()) {
     986             :                         // CDATA is not normalized (similar to character
     987             :                         // references; it keeps the code simple), but it might
     988             :                         // arguably be better to normalize it:
     989           0 :                         switch (space) {
     990             :                         case SPACE_START:
     991           0 :                             break;
     992             :                         case SPACE_NONE:
     993             :                         case SPACE_SPAN:
     994           0 :                             pad_.add(flowBegin, pos_ - flowBegin);
     995           0 :                             break;
     996             :                         case SPACE_BREAK:
     997           0 :                             pad_.add(flowBegin, flowEnd - flowBegin);
     998           0 :                             pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
     999           0 :                             break;
    1000             :                         }
    1001           0 :                         normalizeLineEnds(cdata);
    1002           0 :                         flowBegin = pos_;
    1003           0 :                         flowEnd = pos_;
    1004           0 :                         space = SPACE_NONE;
    1005             :                     } else {
    1006           0 :                         skipDocumentTypeDeclaration();
    1007             :                     }
    1008             :                 }
    1009         186 :                 break;
    1010             :             case '/':
    1011      172494 :                 ++pos_;
    1012      172494 :                 pad_.add(flowBegin, flowEnd - flowBegin);
    1013      172494 :                 *text = pad_.get();
    1014      172494 :                 state_ = STATE_END_TAG;
    1015      172494 :                 return RESULT_TEXT;
    1016             :             case '?':
    1017           0 :                 ++pos_;
    1018           0 :                 skipProcessingInstruction();
    1019           0 :                 space = SPACE_BREAK;
    1020           0 :                 break;
    1021             :             default:
    1022           0 :                 pad_.add(flowBegin, flowEnd - flowBegin);
    1023           0 :                 *text = pad_.get();
    1024           0 :                 state_ = STATE_START_TAG;
    1025           0 :                 return RESULT_TEXT;
    1026             :             }
    1027         186 :             break;
    1028             :         default:
    1029      465475 :             switch (space) {
    1030             :             case SPACE_START:
    1031      172463 :                 flowBegin = pos_;
    1032      172463 :                 break;
    1033             :             case SPACE_NONE:
    1034             :             case SPACE_SPAN:
    1035      292826 :                 break;
    1036             :             case SPACE_BREAK:
    1037         186 :                 pad_.add(flowBegin, flowEnd - flowBegin);
    1038         186 :                 pad_.add(RTL_CONSTASCII_STRINGPARAM(" "));
    1039         186 :                 flowBegin = pos_;
    1040         186 :                 break;
    1041             :             }
    1042      465475 :             flowEnd = ++pos_;
    1043      465475 :             space = SPACE_NONE;
    1044      465475 :             break;
    1045             :         }
    1046             :     }
    1047             : }
    1048             : 
    1049       43664 : int XmlReader::toNamespaceId(NamespaceIris::size_type pos) {
    1050             :     assert(pos <= INT_MAX);
    1051       43664 :     return static_cast< int >(pos);
    1052             : }
    1053             : 
    1054             : }
    1055             : 
    1056             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10