LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/xmlreader/source - xmlreader.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 323 546 59.2 %
Date: 2013-07-09 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.hxx"
      33             : #include "sal/log.hxx"
      34             : #include "sal/types.h"
      35             : #include "xmlreader/pad.hxx"
      36             : #include "xmlreader/span.hxx"
      37             : #include "xmlreader/xmlreader.hxx"
      38             : 
      39             : namespace xmlreader {
      40             : 
      41             : namespace {
      42             : 
      43    55017977 : bool isSpace(char c) {
      44    55017977 :     switch (c) {
      45             :     case '\x09':
      46             :     case '\x0A':
      47             :     case '\x0D':
      48             :     case ' ':
      49     8472516 :         return true;
      50             :     default:
      51    46545461 :         return false;
      52             :     }
      53             : }
      54             : 
      55             : }
      56             : 
      57       21966 : XmlReader::XmlReader(OUString const & fileUrl)
      58             :     SAL_THROW((
      59             :         css::container::NoSuchElementException, css::uno::RuntimeException)):
      60       22001 :     fileUrl_(fileUrl)
      61             : {
      62             :     oslFileError e = osl_openFile(
      63       21966 :         fileUrl_.pData, &fileHandle_, osl_File_OpenFlag_Read);
      64       21966 :     switch (e)
      65             :     {
      66             :     case osl_File_E_None:
      67       21933 :         break;
      68             :     case osl_File_E_NOENT:
      69             :         throw css::container::NoSuchElementException(
      70          33 :             fileUrl_, css::uno::Reference< css::uno::XInterface >());
      71             :     default:
      72             :         throw css::uno::RuntimeException(
      73           0 :             "cannot open " + fileUrl_ + ": " + OUString::number(e),
      74           0 :             css::uno::Reference< css::uno::XInterface >());
      75             :     }
      76       21933 :     e = osl_getFileSize(fileHandle_, &fileSize_);
      77       21933 :     if (e == osl_File_E_None) {
      78             :         e = osl_mapFile(
      79             :             fileHandle_, &fileAddress_, fileSize_, 0,
      80       21933 :             osl_File_MapFlag_WillNeed);
      81             :     }
      82       21933 :     if (e != osl_File_E_None) {
      83           2 :         oslFileError e2 = osl_closeFile(fileHandle_);
      84           2 :         if (e2 != osl_File_E_None) {
      85             :             SAL_WARN(
      86             :                 "xmlreader",
      87             :                 "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e2);
      88             :         }
      89             :         throw css::uno::RuntimeException(
      90           4 :             "cannot mmap " + fileUrl_ + " (" + OUString::number(e) + ")",
      91           6 :             css::uno::Reference< css::uno::XInterface >());
      92             :     }
      93       21931 :     namespaceIris_.push_back(Span("http://www.w3.org/XML/1998/namespace"));
      94       21931 :     namespaces_.push_back(NamespaceData(Span("xml"), NAMESPACE_XML));
      95       21931 :     pos_ = static_cast< char * >(fileAddress_);
      96       21931 :     end_ = pos_ + fileSize_;
      97       21931 :     state_ = STATE_CONTENT;
      98       21931 :     firstAttribute_ = true;
      99       21931 : }
     100             : 
     101       43862 : XmlReader::~XmlReader() {
     102       21931 :     oslFileError e = osl_unmapMappedFile(fileHandle_, fileAddress_, fileSize_);
     103       21931 :     if (e != osl_File_E_None) {
     104             :         SAL_WARN(
     105             :             "xmlreader",
     106             :             "osl_unmapMappedFile of \"" << fileUrl_ << "\" failed with " << +e);
     107             :     }
     108       21931 :     e = osl_closeFile(fileHandle_);
     109       21931 :     if (e != osl_File_E_None) {
     110             :         SAL_WARN(
     111             :             "xmlreader",
     112             :             "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e);
     113             :     }
     114       21931 : }
     115             : 
     116       60663 : int XmlReader::registerNamespaceIri(Span const & iri) {
     117       60663 :     int id = toNamespaceId(namespaceIris_.size());
     118       60663 :     namespaceIris_.push_back(iri);
     119       60663 :     if (iri.equals("http://www.w3.org/2001/XMLSchema-instance")) {
     120             :         // Old user layer .xcu files used the xsi namespace prefix without
     121             :         // declaring a corresponding namespace binding, see issue 77174; reading
     122             :         // those files during migration would fail without this hack that can be
     123             :         // removed once migration is no longer relevant (see
     124             :         // configmgr::Components::parseModificationLayer):
     125       19366 :         namespaces_.push_back(NamespaceData(Span("xsi"), id));
     126             :     }
     127       60663 :     return id;
     128             : }
     129             : 
     130    19258659 : XmlReader::Result XmlReader::nextItem(Text reportText, Span * data, int * nsId)
     131             : {
     132    19258659 :     switch (state_) {
     133             :     case STATE_CONTENT:
     134    15965779 :         switch (reportText) {
     135             :         case TEXT_NONE:
     136    13321509 :             return handleSkippedText(data, nsId);
     137             :         case TEXT_RAW:
     138     1733580 :             return handleRawText(data);
     139             :         case TEXT_NORMALIZED:
     140      910690 :             return handleNormalizedText(data);
     141             :         }
     142             :     case STATE_START_TAG:
     143        4922 :         return handleStartTag(nsId, data);
     144             :     case STATE_END_TAG:
     145     2639348 :         return handleEndTag();
     146             :     case STATE_EMPTY_ELEMENT_TAG:
     147      626679 :         handleElementEnd();
     148      626679 :         return RESULT_END;
     149             :     default: // STATE_DONE
     150       21931 :         return RESULT_DONE;
     151             :     }
     152             : }
     153             : 
     154    15803909 : bool XmlReader::nextAttribute(int * nsId, Span * localName) {
     155             :     assert(nsId != 0 && localName != 0);
     156    15803909 :     if (firstAttribute_) {
     157     7484295 :         currentAttribute_ = attributes_.begin();
     158     7484295 :         firstAttribute_ = false;
     159             :     } else {
     160     8319614 :         ++currentAttribute_;
     161             :     }
     162    15803909 :     if (currentAttribute_ == attributes_.end()) {
     163     7484295 :         return false;
     164             :     }
     165     8319614 :     if (currentAttribute_->nameColon == 0) {
     166      359031 :         *nsId = NAMESPACE_NONE;
     167             :         *localName = Span(
     168      359031 :             currentAttribute_->nameBegin,
     169      718062 :             currentAttribute_->nameEnd - currentAttribute_->nameBegin);
     170             :     } else {
     171             :         *nsId = getNamespaceId(
     172             :             Span(
     173     7960583 :                 currentAttribute_->nameBegin,
     174    15921166 :                 currentAttribute_->nameColon - currentAttribute_->nameBegin));
     175             :         *localName = Span(
     176     7960583 :             currentAttribute_->nameColon + 1,
     177    15921166 :             currentAttribute_->nameEnd - (currentAttribute_->nameColon + 1));
     178             :     }
     179     8319614 :     return true;
     180             : }
     181             : 
     182     8303021 : Span XmlReader::getAttributeValue(bool fullyNormalize) {
     183             :     return handleAttributeValue(
     184    16606042 :         currentAttribute_->valueBegin, currentAttribute_->valueEnd,
     185    24909063 :         fullyNormalize);
     186             : }
     187             : 
     188     9322496 : int XmlReader::getNamespaceId(Span const & prefix) const {
     189    80654256 :     for (NamespaceList::const_reverse_iterator i(namespaces_.rbegin());
     190    53769504 :          i != namespaces_.rend(); ++i)
     191             :     {
     192    26884752 :         if (prefix.equals(i->prefix)) {
     193     9322496 :             return i->nsId;
     194             :         }
     195             :     }
     196           0 :     return NAMESPACE_UNKNOWN;
     197             : }
     198             : 
     199      151353 : OUString XmlReader::getUrl() const {
     200      151353 :     return fileUrl_;
     201             : }
     202             : 
     203           0 : void XmlReader::normalizeLineEnds(Span const & text) {
     204           0 :     char const * p = text.begin;
     205           0 :     sal_Int32 n = text.length;
     206             :     for (;;) {
     207           0 :         sal_Int32 i = rtl_str_indexOfChar_WithLength(p, n, '\x0D');
     208           0 :         if (i < 0) {
     209           0 :             break;
     210             :         }
     211           0 :         pad_.add(p, i);
     212           0 :         p += i + 1;
     213           0 :         n -= i + 1;
     214           0 :         if (n == 0 || *p != '\x0A') {
     215           0 :             pad_.add("\x0A");
     216             :         }
     217           0 :     }
     218           0 :     pad_.add(p, n);
     219           0 : }
     220             : 
     221    41341687 : void XmlReader::skipSpace() {
     222    91155890 :     while (isSpace(peek())) {
     223     8472516 :         ++pos_;
     224             :     }
     225    41341687 : }
     226             : 
     227       22048 : bool XmlReader::skipComment() {
     228       22048 :     if (rtl_str_shortenedCompare_WithLength(
     229       22048 :             pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"),
     230       44096 :             RTL_CONSTASCII_LENGTH("--")) !=
     231             :         0)
     232             :     {
     233           0 :         return false;
     234             :     }
     235       22048 :     pos_ += RTL_CONSTASCII_LENGTH("--");
     236             :     sal_Int32 i = rtl_str_indexOfStr_WithLength(
     237       22048 :         pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"));
     238       22048 :     if (i < 0) {
     239             :         throw css::uno::RuntimeException(
     240           0 :             "premature end (within comment) of " + fileUrl_,
     241           0 :             css::uno::Reference< css::uno::XInterface >());
     242             :     }
     243       22048 :     pos_ += i + RTL_CONSTASCII_LENGTH("--");
     244       22048 :     if (read() != '>') {
     245             :         throw css::uno::RuntimeException(
     246           0 :             "illegal \"--\" within comment in " + fileUrl_,
     247           0 :             css::uno::Reference< css::uno::XInterface >());
     248             :     }
     249       22048 :     return true;
     250             : }
     251             : 
     252       21931 : void XmlReader::skipProcessingInstruction() {
     253             :     sal_Int32 i = rtl_str_indexOfStr_WithLength(
     254       21931 :         pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("?>"));
     255       21931 :     if (i < 0) {
     256             :         throw css::uno::RuntimeException(
     257           0 :             "bad '<?' in " + fileUrl_,
     258           0 :             css::uno::Reference< css::uno::XInterface >());
     259             :     }
     260       21931 :     pos_ += i + RTL_CONSTASCII_LENGTH("?>");
     261       21931 : }
     262             : 
     263           0 : void XmlReader::skipDocumentTypeDeclaration() {
     264             :     // Neither is it checked that the doctypedecl is at the correct position in
     265             :     // the document, nor that it is well-formed:
     266             :     for (;;) {
     267           0 :         char c = read();
     268           0 :         switch (c) {
     269             :         case '\0': // i.e., EOF
     270             :             throw css::uno::RuntimeException(
     271           0 :                 "premature end (within DTD) of " + fileUrl_,
     272           0 :                 css::uno::Reference< css::uno::XInterface >());
     273             :         case '"':
     274             :         case '\'':
     275             :             {
     276             :                 sal_Int32 i = rtl_str_indexOfChar_WithLength(
     277           0 :                     pos_, end_ - pos_, c);
     278           0 :                 if (i < 0) {
     279             :                     throw css::uno::RuntimeException(
     280           0 :                         "premature end (within DTD) of " + fileUrl_,
     281           0 :                         css::uno::Reference< css::uno::XInterface >());
     282             :                 }
     283           0 :                 pos_ += i + 1;
     284             :             }
     285           0 :             break;
     286             :         case '>':
     287           0 :             return;
     288             :         case '[':
     289             :             for (;;) {
     290           0 :                 c = read();
     291           0 :                 switch (c) {
     292             :                 case '\0': // i.e., EOF
     293             :                     throw css::uno::RuntimeException(
     294           0 :                         "premature end (within DTD) of " + 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           0 :                                 "premature end (within DTD) of " + fileUrl_,
     304           0 :                                 css::uno::Reference< css::uno::XInterface >());
     305             :                         }
     306           0 :                         pos_ += i + 1;
     307             :                     }
     308           0 :                     break;
     309             :                 case '<':
     310           0 :                     switch (read()) {
     311             :                     case '\0': // i.e., EOF
     312             :                         throw css::uno::RuntimeException(
     313           0 :                             "premature end (within DTD) of " + fileUrl_,
     314           0 :                             css::uno::Reference< css::uno::XInterface >());
     315             :                     case '!':
     316           0 :                         skipComment();
     317           0 :                         break;
     318             :                     case '?':
     319           0 :                         skipProcessingInstruction();
     320           0 :                         break;
     321             :                     default:
     322           0 :                         break;
     323             :                     }
     324           0 :                     break;
     325             :                 case ']':
     326           0 :                     skipSpace();
     327           0 :                     if (read() != '>') {
     328             :                         throw css::uno::RuntimeException(
     329           0 :                             "missing \">\" of DTD in " + fileUrl_,
     330           0 :                             css::uno::Reference< css::uno::XInterface >());
     331             :                     }
     332           0 :                     return;
     333             :                 default:
     334           0 :                     break;
     335             :                 }
     336           0 :             }
     337             :         default:
     338           0 :             break;
     339             :         }
     340           0 :     }
     341             : }
     342             : 
     343           0 : Span XmlReader::scanCdataSection() {
     344           0 :     if (rtl_str_shortenedCompare_WithLength(
     345           0 :             pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("[CDATA["),
     346           0 :             RTL_CONSTASCII_LENGTH("[CDATA[")) !=
     347             :         0)
     348             :     {
     349           0 :         return Span();
     350             :     }
     351           0 :     pos_ += RTL_CONSTASCII_LENGTH("[CDATA[");
     352           0 :     char const * begin = pos_;
     353             :     sal_Int32 i = rtl_str_indexOfStr_WithLength(
     354           0 :         pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("]]>"));
     355           0 :     if (i < 0) {
     356             :         throw css::uno::RuntimeException(
     357           0 :             "premature end (within CDATA section) of " + fileUrl_,
     358           0 :             css::uno::Reference< css::uno::XInterface >());
     359             :     }
     360           0 :     pos_ += i + RTL_CONSTASCII_LENGTH("]]>");
     361           0 :     return Span(begin, i);
     362             : }
     363             : 
     364    24424415 : bool XmlReader::scanName(char const ** nameColon) {
     365             :     assert(nameColon != 0 && *nameColon == 0);
     366   165842859 :     for (char const * begin = pos_;; ++pos_) {
     367   165842859 :         switch (peek()) {
     368             :         case '\0': // i.e., EOF
     369             :         case '\x09':
     370             :         case '\x0A':
     371             :         case '\x0D':
     372             :         case ' ':
     373             :         case '/':
     374             :         case '=':
     375             :         case '>':
     376    48848830 :             return pos_ != begin;
     377             :         case ':':
     378     8180877 :             *nameColon = pos_;
     379     8180877 :             break;
     380             :         default:
     381   133237567 :             break;
     382             :         }
     383   141418444 :     }
     384             : }
     385             : 
     386       76458 : int XmlReader::scanNamespaceIri(char const * begin, char const * end) {
     387             :     assert(begin != 0 && begin <= end);
     388       76458 :     Span iri(handleAttributeValue(begin, end, false));
     389      261687 :     for (NamespaceIris::size_type i = 0; i < namespaceIris_.size(); ++i) {
     390      242746 :         if (namespaceIris_[i].equals(iri)) {
     391       57517 :             return toNamespaceId(i);
     392             :         }
     393             :     }
     394       18941 :     return XmlReader::NAMESPACE_UNKNOWN;
     395             : }
     396             : 
     397       29423 : char const * XmlReader::handleReference(char const * position, char const * end)
     398             : {
     399             :     assert(position != 0 && *position == '&' && position < end);
     400       29423 :     ++position;
     401       29423 :     if (*position == '#') {
     402        1020 :         ++position;
     403        1020 :         sal_Int32 val = 0;
     404             :         char const * p;
     405        1020 :         if (*position == 'x') {
     406        1020 :             ++position;
     407        1020 :             p = position;
     408        4080 :             for (;; ++position) {
     409        5100 :                 char c = *position;
     410        5100 :                 if (c >= '0' && c <= '9') {
     411        1870 :                     val = 16 * val + (c - '0');
     412        3230 :                 } else if (c >= 'A' && c <= 'F') {
     413        2210 :                     val = 16 * val + (c - 'A') + 10;
     414        1020 :                 } else if (c >= 'a' && c <= 'f') {
     415           0 :                     val = 16 * val + (c - 'a') + 10;
     416             :                 } else {
     417             :                     break;
     418             :                 }
     419        4080 :                 if (val > 0x10FFFF) { // avoid overflow
     420             :                     throw css::uno::RuntimeException(
     421           0 :                         "'&#x...' too large in " + fileUrl_,
     422           0 :                         css::uno::Reference< css::uno::XInterface >());
     423             :                 }
     424        4080 :             }
     425             :         } else {
     426           0 :             p = position;
     427           0 :             for (;; ++position) {
     428           0 :                 char c = *position;
     429           0 :                 if (c >= '0' && c <= '9') {
     430           0 :                     val = 10 * val + (c - '0');
     431             :                 } else {
     432             :                     break;
     433             :                 }
     434           0 :                 if (val > 0x10FFFF) { // avoid overflow
     435             :                     throw css::uno::RuntimeException(
     436           0 :                         "'&#...' too large in " + fileUrl_,
     437           0 :                         css::uno::Reference< css::uno::XInterface >());
     438             :                 }
     439           0 :             }
     440             :         }
     441        1020 :         if (position == p || *position++ != ';') {
     442             :             throw css::uno::RuntimeException(
     443           0 :                 "'&#...' missing ';' in " + fileUrl_,
     444           0 :                 css::uno::Reference< css::uno::XInterface >());
     445             :         }
     446             :         assert(val >= 0 && val <= 0x10FFFF);
     447        1020 :         if ((val < 0x20 && val != 0x9 && val != 0xA && val != 0xD) ||
     448           0 :             (val >= 0xD800 && val <= 0xDFFF) || val == 0xFFFE || val == 0xFFFF)
     449             :         {
     450             :             throw css::uno::RuntimeException(
     451           0 :                 "character reference denoting invalid character in " + fileUrl_,
     452           0 :                 css::uno::Reference< css::uno::XInterface >());
     453             :         }
     454             :         char buf[4];
     455             :         sal_Int32 len;
     456        1020 :         if (val < 0x80) {
     457           0 :             buf[0] = static_cast< char >(val);
     458           0 :             len = 1;
     459        1020 :         } else if (val < 0x800) {
     460           0 :             buf[0] = static_cast< char >((val >> 6) | 0xC0);
     461           0 :             buf[1] = static_cast< char >((val & 0x3F) | 0x80);
     462           0 :             len = 2;
     463        1020 :         } else if (val < 0x10000) {
     464        1020 :             buf[0] = static_cast< char >((val >> 12) | 0xE0);
     465        1020 :             buf[1] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
     466        1020 :             buf[2] = static_cast< char >((val & 0x3F) | 0x80);
     467        1020 :             len = 3;
     468             :         } else {
     469           0 :             buf[0] = static_cast< char >((val >> 18) | 0xF0);
     470           0 :             buf[1] = static_cast< char >(((val >> 12) & 0x3F) | 0x80);
     471           0 :             buf[2] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
     472           0 :             buf[3] = static_cast< char >((val & 0x3F) | 0x80);
     473           0 :             len = 4;
     474             :         }
     475        1020 :         pad_.addEphemeral(buf, len);
     476        1020 :         return position;
     477             :     } else {
     478             :         struct EntityRef {
     479             :             char const * inBegin;
     480             :             sal_Int32 inLength;
     481             :             char const * outBegin;
     482             :             sal_Int32 outLength;
     483             :         };
     484             :         static EntityRef const refs[] = {
     485             :             { RTL_CONSTASCII_STRINGPARAM("amp;"),
     486             :               RTL_CONSTASCII_STRINGPARAM("&") },
     487             :             { RTL_CONSTASCII_STRINGPARAM("lt;"),
     488             :               RTL_CONSTASCII_STRINGPARAM("<") },
     489             :             { RTL_CONSTASCII_STRINGPARAM("gt;"),
     490             :               RTL_CONSTASCII_STRINGPARAM(">") },
     491             :             { RTL_CONSTASCII_STRINGPARAM("apos;"),
     492             :               RTL_CONSTASCII_STRINGPARAM("'") },
     493             :             { RTL_CONSTASCII_STRINGPARAM("quot;"),
     494             :               RTL_CONSTASCII_STRINGPARAM("\"") } };
     495       54527 :         for (std::size_t i = 0; i < sizeof refs / sizeof refs[0]; ++i) {
     496       54527 :             if (rtl_str_shortenedCompare_WithLength(
     497       54527 :                     position, end - position, refs[i].inBegin, refs[i].inLength,
     498      109054 :                     refs[i].inLength) ==
     499             :                 0)
     500             :             {
     501       28403 :                 position += refs[i].inLength;
     502       28403 :                 pad_.add(refs[i].outBegin, refs[i].outLength);
     503       28403 :                 return position;
     504             :             }
     505             :         }
     506             :         throw css::uno::RuntimeException(
     507           0 :             "unknown entity reference in " + fileUrl_,
     508           0 :             css::uno::Reference< css::uno::XInterface >());
     509             :     }
     510             : }
     511             : 
     512     8379479 : Span XmlReader::handleAttributeValue(
     513             :     char const * begin, char const * end, bool fullyNormalize)
     514             : {
     515     8379479 :     pad_.clear();
     516     8379479 :     if (fullyNormalize) {
     517     5203774 :         while (begin != end && isSpace(*begin)) {
     518           0 :             ++begin;
     519             :         }
     520     5203774 :         while (end != begin && isSpace(end[-1])) {
     521           0 :             --end;
     522             :         }
     523     2601887 :         char const * p = begin;
     524             :         enum Space { SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
     525             :             // a single true space character can go into the current span,
     526             :             // everything else breaks the span
     527     2601887 :         Space space = SPACE_NONE;
     528    25275763 :         while (p != end) {
     529    20071989 :             switch (*p) {
     530             :             case '\x09':
     531             :             case '\x0A':
     532             :             case '\x0D':
     533           0 :                 switch (space) {
     534             :                 case SPACE_NONE:
     535           0 :                     pad_.add(begin, p - begin);
     536           0 :                     pad_.add(" ");
     537           0 :                     space = SPACE_BREAK;
     538           0 :                     break;
     539             :                 case SPACE_SPAN:
     540           0 :                     pad_.add(begin, p - begin);
     541           0 :                     space = SPACE_BREAK;
     542           0 :                     break;
     543             :                 case SPACE_BREAK:
     544           0 :                     break;
     545             :                 }
     546           0 :                 begin = ++p;
     547           0 :                 break;
     548             :             case ' ':
     549        4344 :                 switch (space) {
     550             :                 case SPACE_NONE:
     551        4344 :                     ++p;
     552        4344 :                     space = SPACE_SPAN;
     553        4344 :                     break;
     554             :                 case SPACE_SPAN:
     555           0 :                     pad_.add(begin, p - begin);
     556           0 :                     begin = ++p;
     557           0 :                     space = SPACE_BREAK;
     558           0 :                     break;
     559             :                 case SPACE_BREAK:
     560           0 :                     begin = ++p;
     561           0 :                     break;
     562             :                 }
     563        4344 :                 break;
     564             :             case '&':
     565           0 :                 pad_.add(begin, p - begin);
     566           0 :                 p = handleReference(p, end);
     567           0 :                 begin = p;
     568           0 :                 space = SPACE_NONE;
     569           0 :                 break;
     570             :             default:
     571    20067645 :                 ++p;
     572    20067645 :                 space = SPACE_NONE;
     573    20067645 :                 break;
     574             :             }
     575             :         }
     576     2601887 :         pad_.add(begin, p - begin);
     577             :     } else {
     578     5777592 :         char const * p = begin;
     579    81082424 :         while (p != end) {
     580    69527240 :             switch (*p) {
     581             :             case '\x09':
     582             :             case '\x0A':
     583           0 :                 pad_.add(begin, p - begin);
     584           0 :                 begin = ++p;
     585           0 :                 pad_.add(" ");
     586           0 :                 break;
     587             :             case '\x0D':
     588           0 :                 pad_.add(begin, p - begin);
     589           0 :                 ++p;
     590           0 :                 if (peek() == '\x0A') {
     591           0 :                     ++p;
     592             :                 }
     593           0 :                 begin = p;
     594           0 :                 pad_.add(" ");
     595           0 :                 break;
     596             :             case '&':
     597        1020 :                 pad_.add(begin, p - begin);
     598        1020 :                 p = handleReference(p, end);
     599        1020 :                 begin = p;
     600        1020 :                 break;
     601             :             default:
     602    69526220 :                 ++p;
     603    69526220 :                 break;
     604             :             }
     605             :         }
     606     5777592 :         pad_.add(begin, p - begin);
     607             :     }
     608     8379479 :     return pad_.get();
     609             : }
     610             : 
     611     8296229 : XmlReader::Result XmlReader::handleStartTag(int * nsId, Span * localName) {
     612             :     assert(nsId != 0 && localName);
     613     8296229 :     char const * nameBegin = pos_;
     614     8296229 :     char const * nameColon = 0;
     615     8296229 :     if (!scanName(&nameColon)) {
     616             :         throw css::uno::RuntimeException(
     617           0 :             "bad tag name in " + fileUrl_,
     618           0 :             css::uno::Reference< css::uno::XInterface >());
     619             :     }
     620     8296229 :     char const * nameEnd = pos_;
     621     8296229 :     NamespaceList::size_type inheritedNamespaces = namespaces_.size();
     622     8296229 :     bool hasDefaultNs = false;
     623     8296229 :     int defaultNsId = NAMESPACE_NONE;
     624     8296229 :     attributes_.clear();
     625             :     for (;;) {
     626    16754865 :         char const * p = pos_;
     627    16754865 :         skipSpace();
     628    16754865 :         if (peek() == '/' || peek() == '>') {
     629     8296229 :             break;
     630             :         }
     631     8458636 :         if (pos_ == p) {
     632             :             throw css::uno::RuntimeException(
     633           0 :                 "missing whitespace before attribute in " + fileUrl_,
     634           0 :                 css::uno::Reference< css::uno::XInterface >());
     635             :         }
     636     8458636 :         char const * attrNameBegin = pos_;
     637     8458636 :         char const * attrNameColon = 0;
     638     8458636 :         if (!scanName(&attrNameColon)) {
     639             :             throw css::uno::RuntimeException(
     640           0 :                 "bad attribute name in " + fileUrl_,
     641           0 :                 css::uno::Reference< css::uno::XInterface >());
     642             :         }
     643     8458636 :         char const * attrNameEnd = pos_;
     644     8458636 :         skipSpace();
     645     8458636 :         if (read() != '=') {
     646             :             throw css::uno::RuntimeException(
     647           0 :                 "missing '=' in " + fileUrl_,
     648           0 :                 css::uno::Reference< css::uno::XInterface >());
     649             :         }
     650     8458636 :         skipSpace();
     651     8458636 :         char del = read();
     652     8458636 :         if (del != '\'' && del != '"') {
     653             :             throw css::uno::RuntimeException(
     654           0 :                 "bad attribute value in " + fileUrl_,
     655           0 :                 css::uno::Reference< css::uno::XInterface >());
     656             :         }
     657     8458636 :         char const * valueBegin = pos_;
     658     8458636 :         sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, del);
     659     8458636 :         if (i < 0) {
     660             :             throw css::uno::RuntimeException(
     661           0 :                 "unterminated attribute value in " + fileUrl_,
     662           0 :                 css::uno::Reference< css::uno::XInterface >());
     663             :         }
     664     8458636 :         char const * valueEnd = pos_ + i;
     665     8458636 :         pos_ += i + 1;
     666    25737504 :         if (attrNameColon == 0 &&
     667     9543424 :             Span(attrNameBegin, attrNameEnd - attrNameBegin).equals("xmlns"))
     668             :         {
     669        2565 :             hasDefaultNs = true;
     670        2565 :             defaultNsId = scanNamespaceIri(valueBegin, valueEnd);
     671    33465253 :         } else if (attrNameColon != 0 &&
     672     8097040 :                    Span(attrNameBegin, attrNameColon - attrNameBegin).equals(
     673    40844231 :                        "xmlns"))
     674             :         {
     675             :             namespaces_.push_back(
     676             :                 NamespaceData(
     677      147786 :                     Span(attrNameColon + 1, attrNameEnd - (attrNameColon + 1)),
     678      221679 :                     scanNamespaceIri(valueBegin, valueEnd)));
     679             :         } else {
     680             :             attributes_.push_back(
     681             :                 AttributeData(
     682             :                     attrNameBegin, attrNameEnd, attrNameColon, valueBegin,
     683     8382178 :                     valueEnd));
     684             :         }
     685     8458636 :     }
     686    16589893 :     if (!hasDefaultNs && !elements_.empty()) {
     687     8274298 :         defaultNsId = elements_.top().defaultNamespaceId;
     688             :     }
     689     8296229 :     firstAttribute_ = true;
     690     8296229 :     if (peek() == '/') {
     691      626679 :         state_ = STATE_EMPTY_ELEMENT_TAG;
     692      626679 :         ++pos_;
     693             :     } else {
     694     7669550 :         state_ = STATE_CONTENT;
     695             :     }
     696     8296229 :     if (peek() != '>') {
     697             :         throw css::uno::RuntimeException(
     698           0 :             "missing '>' in " + fileUrl_,
     699           0 :             css::uno::Reference< css::uno::XInterface >());
     700             :     }
     701     8296229 :     ++pos_;
     702             :     elements_.push(
     703             :         ElementData(
     704     8296229 :             Span(nameBegin, nameEnd - nameBegin), inheritedNamespaces,
     705    16592458 :             defaultNsId));
     706     8296229 :     if (nameColon == 0) {
     707     8253770 :         *nsId = defaultNsId;
     708     8253770 :         *localName = Span(nameBegin, nameEnd - nameBegin);
     709             :     } else {
     710       42459 :         *nsId = getNamespaceId(Span(nameBegin, nameColon - nameBegin));
     711       42459 :         *localName = Span(nameColon + 1, nameEnd - (nameColon + 1));
     712             :     }
     713     8296229 :     return RESULT_BEGIN;
     714             : }
     715             : 
     716     7669550 : XmlReader::Result XmlReader::handleEndTag() {
     717     7669550 :     if (elements_.empty()) {
     718             :         throw css::uno::RuntimeException(
     719           0 :             "spurious end tag in " + fileUrl_,
     720           0 :             css::uno::Reference< css::uno::XInterface >());
     721             :     }
     722     7669550 :     char const * nameBegin = pos_;
     723     7669550 :     char const * nameColon = 0;
     724    15339100 :     if (!scanName(&nameColon) ||
     725     7669550 :         !elements_.top().name.equals(nameBegin, pos_ - nameBegin))
     726             :     {
     727             :         throw css::uno::RuntimeException(
     728           0 :             "tag mismatch in " + fileUrl_,
     729           0 :             css::uno::Reference< css::uno::XInterface >());
     730             :     }
     731     7669550 :     handleElementEnd();
     732     7669550 :     skipSpace();
     733     7669550 :     if (peek() != '>') {
     734             :         throw css::uno::RuntimeException(
     735           0 :             "missing '>' in " + fileUrl_,
     736           0 :             css::uno::Reference< css::uno::XInterface >());
     737             :     }
     738     7669550 :     ++pos_;
     739     7669550 :     return RESULT_END;
     740             : }
     741             : 
     742     8296229 : void XmlReader::handleElementEnd() {
     743             :     assert(!elements_.empty());
     744     8296229 :     namespaces_.resize(elements_.top().inheritedNamespaces);
     745     8296229 :     elements_.pop();
     746     8296229 :     state_ = elements_.empty() ? STATE_DONE : STATE_CONTENT;
     747     8296229 : }
     748             : 
     749    13365080 : XmlReader::Result XmlReader::handleSkippedText(Span * data, int * nsId) {
     750             :     for (;;) {
     751    13365080 :         sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, '<');
     752    13365080 :         if (i < 0) {
     753             :             throw css::uno::RuntimeException(
     754           0 :                 "premature end of " + fileUrl_,
     755           0 :                 css::uno::Reference< css::uno::XInterface >());
     756             :         }
     757    13365080 :         pos_ += i + 1;
     758    13365080 :         switch (peek()) {
     759             :         case '!':
     760       21640 :             ++pos_;
     761       21640 :             if (!skipComment() && !scanCdataSection().is()) {
     762           0 :                 skipDocumentTypeDeclaration();
     763             :             }
     764       21640 :             break;
     765             :         case '/':
     766     5030202 :             ++pos_;
     767     5030202 :             return handleEndTag();
     768             :         case '?':
     769       21931 :             ++pos_;
     770       21931 :             skipProcessingInstruction();
     771       21931 :             break;
     772             :         default:
     773     8291307 :             return handleStartTag(nsId, data);
     774             :         }
     775       43571 :     }
     776             : }
     777             : 
     778     1733580 : XmlReader::Result XmlReader::handleRawText(Span * text) {
     779     1733580 :     pad_.clear();
     780     1733580 :     for (char const * begin = pos_;;) {
     781    45554121 :         switch (peek()) {
     782             :         case '\0': // i.e., EOF
     783             :             throw css::uno::RuntimeException(
     784           0 :                 "premature end of " + fileUrl_,
     785           0 :                 css::uno::Reference< css::uno::XInterface >());
     786             :         case '\x0D':
     787           0 :             pad_.add(begin, pos_ - begin);
     788           0 :             ++pos_;
     789           0 :             if (peek() != '\x0A') {
     790           0 :                 pad_.add("\x0A");
     791             :             }
     792           0 :             begin = pos_;
     793           0 :             break;
     794             :         case '&':
     795       28403 :             pad_.add(begin, pos_ - begin);
     796       28403 :             pos_ = handleReference(pos_, end_);
     797       28403 :             begin = pos_;
     798       28403 :             break;
     799             :         case '<':
     800     1733580 :             pad_.add(begin, pos_ - begin);
     801     1733580 :             ++pos_;
     802     1733580 :             switch (peek()) {
     803             :             case '!':
     804           0 :                 ++pos_;
     805           0 :                 if (!skipComment()) {
     806           0 :                     Span cdata(scanCdataSection());
     807           0 :                     if (cdata.is()) {
     808           0 :                         normalizeLineEnds(cdata);
     809             :                     } else {
     810           0 :                         skipDocumentTypeDeclaration();
     811             :                     }
     812             :                 }
     813           0 :                 begin = pos_;
     814           0 :                 break;
     815             :             case '/':
     816     1728658 :                 *text = pad_.get();
     817     1728658 :                 ++pos_;
     818     1728658 :                 state_ = STATE_END_TAG;
     819     1728658 :                 return RESULT_TEXT;
     820             :             case '?':
     821           0 :                 ++pos_;
     822           0 :                 skipProcessingInstruction();
     823           0 :                 begin = pos_;
     824           0 :                 break;
     825             :             default:
     826        4922 :                 *text = pad_.get();
     827        4922 :                 state_ = STATE_START_TAG;
     828        4922 :                 return RESULT_TEXT;
     829             :             }
     830           0 :             break;
     831             :         default:
     832    43792138 :             ++pos_;
     833    43792138 :             break;
     834             :         }
     835    43820541 :     }
     836             : }
     837             : 
     838      910690 : XmlReader::Result XmlReader::handleNormalizedText(Span * text) {
     839      910690 :     pad_.clear();
     840      910690 :     char const * flowBegin = pos_;
     841      910690 :     char const * flowEnd = pos_;
     842             :     enum Space { SPACE_START, SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
     843             :         // a single true space character can go into the current flow,
     844             :         // everything else breaks the flow
     845      910690 :     Space space = SPACE_START;
     846             :     for (;;) {
     847     3430678 :         switch (peek()) {
     848             :         case '\0': // i.e., EOF
     849             :             throw css::uno::RuntimeException(
     850           0 :                 "premature end of " + fileUrl_,
     851           0 :                 css::uno::Reference< css::uno::XInterface >());
     852             :         case '\x09':
     853             :         case '\x0A':
     854             :         case '\x0D':
     855        1836 :             switch (space) {
     856             :             case SPACE_START:
     857             :             case SPACE_BREAK:
     858         918 :                 break;
     859             :             case SPACE_NONE:
     860             :             case SPACE_SPAN:
     861         918 :                 space = SPACE_BREAK;
     862         918 :                 break;
     863             :             }
     864        1836 :             ++pos_;
     865        1836 :             break;
     866             :         case ' ':
     867       23103 :             switch (space) {
     868             :             case SPACE_START:
     869             :             case SPACE_BREAK:
     870       14382 :                 break;
     871             :             case SPACE_NONE:
     872        8721 :                 space = SPACE_SPAN;
     873        8721 :                 break;
     874             :             case SPACE_SPAN:
     875           0 :                 space = SPACE_BREAK;
     876           0 :                 break;
     877             :             }
     878       23103 :             ++pos_;
     879       23103 :             break;
     880             :         case '&':
     881           0 :             switch (space) {
     882             :             case SPACE_START:
     883           0 :                 break;
     884             :             case SPACE_NONE:
     885             :             case SPACE_SPAN:
     886           0 :                 pad_.add(flowBegin, pos_ - flowBegin);
     887           0 :                 break;
     888             :             case SPACE_BREAK:
     889           0 :                 pad_.add(flowBegin, flowEnd - flowBegin);
     890           0 :                 pad_.add(" ");
     891           0 :                 break;
     892             :             }
     893           0 :             pos_ = handleReference(pos_, end_);
     894           0 :             flowBegin = pos_;
     895           0 :             flowEnd = pos_;
     896           0 :             space = SPACE_NONE;
     897           0 :             break;
     898             :         case '<':
     899      911098 :             ++pos_;
     900      911098 :             switch (peek()) {
     901             :             case '!':
     902         408 :                 ++pos_;
     903         408 :                 if (skipComment()) {
     904         408 :                     space = SPACE_BREAK;
     905             :                 } else {
     906           0 :                     Span cdata(scanCdataSection());
     907           0 :                     if (cdata.is()) {
     908             :                         // CDATA is not normalized (similar to character
     909             :                         // references; it keeps the code simple), but it might
     910             :                         // arguably be better to normalize it:
     911           0 :                         switch (space) {
     912             :                         case SPACE_START:
     913           0 :                             break;
     914             :                         case SPACE_NONE:
     915             :                         case SPACE_SPAN:
     916           0 :                             pad_.add(flowBegin, pos_ - flowBegin);
     917           0 :                             break;
     918             :                         case SPACE_BREAK:
     919           0 :                             pad_.add(flowBegin, flowEnd - flowBegin);
     920           0 :                             pad_.add(" ");
     921           0 :                             break;
     922             :                         }
     923           0 :                         normalizeLineEnds(cdata);
     924           0 :                         flowBegin = pos_;
     925           0 :                         flowEnd = pos_;
     926           0 :                         space = SPACE_NONE;
     927             :                     } else {
     928           0 :                         skipDocumentTypeDeclaration();
     929             :                     }
     930             :                 }
     931         408 :                 break;
     932             :             case '/':
     933      910690 :                 ++pos_;
     934      910690 :                 pad_.add(flowBegin, flowEnd - flowBegin);
     935      910690 :                 *text = pad_.get();
     936      910690 :                 state_ = STATE_END_TAG;
     937      910690 :                 return RESULT_TEXT;
     938             :             case '?':
     939           0 :                 ++pos_;
     940           0 :                 skipProcessingInstruction();
     941           0 :                 space = SPACE_BREAK;
     942           0 :                 break;
     943             :             default:
     944           0 :                 pad_.add(flowBegin, flowEnd - flowBegin);
     945           0 :                 *text = pad_.get();
     946           0 :                 state_ = STATE_START_TAG;
     947           0 :                 return RESULT_TEXT;
     948             :             }
     949         408 :             break;
     950             :         default:
     951     2494641 :             switch (space) {
     952             :             case SPACE_START:
     953      910622 :                 flowBegin = pos_;
     954      910622 :                 break;
     955             :             case SPACE_NONE:
     956             :             case SPACE_SPAN:
     957     1583186 :                 break;
     958             :             case SPACE_BREAK:
     959         833 :                 pad_.add(flowBegin, flowEnd - flowBegin);
     960         833 :                 pad_.add(" ");
     961         833 :                 flowBegin = pos_;
     962         833 :                 break;
     963             :             }
     964     2494641 :             flowEnd = ++pos_;
     965     2494641 :             space = SPACE_NONE;
     966     2494641 :             break;
     967             :         }
     968     2519988 :     }
     969             : }
     970             : 
     971      118180 : int XmlReader::toNamespaceId(NamespaceIris::size_type pos) {
     972             :     assert(pos <= INT_MAX);
     973      118180 :     return static_cast< int >(pos);
     974             : }
     975             : 
     976             : }
     977             : 
     978             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10