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 142154587 : bool isSpace(char c) {
44 142154587 : switch (c) {
45 : case '\x09':
46 : case '\x0A':
47 : case '\x0D':
48 : case ' ':
49 22413102 : return true;
50 : default:
51 119741485 : return false;
52 : }
53 : }
54 :
55 : }
56 :
57 0 : XmlReader::XmlReader(char const *sStr, size_t nLength)
58 : : fileUrl_("stream")
59 : , fileHandle_(0)
60 : , fileSize_(0)
61 0 : , fileAddress_(0)
62 : {
63 0 : namespaceIris_.push_back(Span("http://www.w3.org/XML/1998/namespace"));
64 0 : namespaces_.push_back(NamespaceData(Span("xml"), NAMESPACE_XML));
65 0 : pos_ = sStr;
66 0 : end_ = pos_ + nLength;
67 0 : state_ = STATE_CONTENT;
68 0 : firstAttribute_ = true;
69 0 : }
70 :
71 16995 : XmlReader::XmlReader(OUString const & fileUrl)
72 : : fileUrl_(fileUrl)
73 17067 : , fileHandle_(0)
74 : {
75 : oslFileError e = osl_openFile(
76 16995 : fileUrl_.pData, &fileHandle_, osl_File_OpenFlag_Read);
77 16995 : switch (e)
78 : {
79 : case osl_File_E_None:
80 16927 : break;
81 : case osl_File_E_NOENT:
82 68 : throw css::container::NoSuchElementException( fileUrl_ );
83 : default:
84 : throw css::uno::RuntimeException(
85 0 : "cannot open " + fileUrl_ + ": " + OUString::number(e));
86 : }
87 16927 : e = osl_getFileSize(fileHandle_, &fileSize_);
88 16927 : if (e == osl_File_E_None) {
89 : e = osl_mapFile(
90 : fileHandle_, &fileAddress_, fileSize_, 0,
91 16927 : osl_File_MapFlag_WillNeed);
92 : }
93 16927 : if (e != osl_File_E_None) {
94 4 : oslFileError e2 = osl_closeFile(fileHandle_);
95 4 : if (e2 != osl_File_E_None) {
96 : SAL_WARN(
97 : "xmlreader",
98 : "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e2);
99 : }
100 : throw css::uno::RuntimeException(
101 4 : "cannot mmap " + fileUrl_ + " (" + OUString::number(e) + ")" );
102 : }
103 16923 : namespaceIris_.push_back(Span("http://www.w3.org/XML/1998/namespace"));
104 16923 : namespaces_.push_back(NamespaceData(Span("xml"), NAMESPACE_XML));
105 16923 : pos_ = static_cast< char * >(fileAddress_);
106 16923 : end_ = pos_ + fileSize_;
107 16923 : state_ = STATE_CONTENT;
108 16923 : firstAttribute_ = true;
109 16923 : }
110 :
111 33846 : XmlReader::~XmlReader() {
112 16923 : if (!fileHandle_)
113 0 : return;
114 16923 : oslFileError e = osl_unmapMappedFile(fileHandle_, fileAddress_, fileSize_);
115 16923 : if (e != osl_File_E_None) {
116 : SAL_WARN(
117 : "xmlreader",
118 : "osl_unmapMappedFile of \"" << fileUrl_ << "\" failed with " << +e);
119 : }
120 16923 : e = osl_closeFile(fileHandle_);
121 16923 : if (e != osl_File_E_None) {
122 : SAL_WARN(
123 : "xmlreader",
124 : "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e);
125 : }
126 16923 : }
127 :
128 31731 : int XmlReader::registerNamespaceIri(Span const & iri) {
129 31731 : int id = toNamespaceId(namespaceIris_.size());
130 31731 : namespaceIris_.push_back(iri);
131 31731 : if (iri.equals("http://www.w3.org/2001/XMLSchema-instance")) {
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 8531 : namespaces_.push_back(NamespaceData(Span("xsi"), id));
138 : }
139 31731 : return id;
140 : }
141 :
142 48622134 : XmlReader::Result XmlReader::nextItem(Text reportText, Span * data, int * nsId)
143 : {
144 48622134 : switch (state_) {
145 : case STATE_CONTENT:
146 39220833 : switch (reportText) {
147 : case TEXT_NONE:
148 31705668 : return handleSkippedText(data, nsId);
149 : case TEXT_RAW:
150 5162529 : return handleRawText(data);
151 : case TEXT_NORMALIZED:
152 2352636 : return handleNormalizedText(data);
153 : }
154 : case STATE_START_TAG:
155 12630 : return handleStartTag(nsId, data);
156 : case STATE_END_TAG:
157 7502535 : return handleEndTag();
158 : case STATE_EMPTY_ELEMENT_TAG:
159 1869213 : handleElementEnd();
160 1869213 : return RESULT_END;
161 : default: // STATE_DONE
162 16923 : return RESULT_DONE;
163 : }
164 : }
165 :
166 42641687 : bool XmlReader::nextAttribute(int * nsId, Span * localName) {
167 : assert(nsId != 0 && localName != 0);
168 42641687 : if (firstAttribute_) {
169 20325052 : currentAttribute_ = attributes_.begin();
170 20325052 : firstAttribute_ = false;
171 : } else {
172 22316635 : ++currentAttribute_;
173 : }
174 42641687 : if (currentAttribute_ == attributes_.end()) {
175 20325052 : return false;
176 : }
177 22316635 : if (currentAttribute_->nameColon == 0) {
178 1602602 : *nsId = NAMESPACE_NONE;
179 : *localName = Span(
180 1602602 : currentAttribute_->nameBegin,
181 3205204 : currentAttribute_->nameEnd - currentAttribute_->nameBegin);
182 : } else {
183 : *nsId = getNamespaceId(
184 : Span(
185 20714033 : currentAttribute_->nameBegin,
186 41428066 : currentAttribute_->nameColon - currentAttribute_->nameBegin));
187 : *localName = Span(
188 20714033 : currentAttribute_->nameColon + 1,
189 41428066 : currentAttribute_->nameEnd - (currentAttribute_->nameColon + 1));
190 : }
191 22316635 : return true;
192 : }
193 :
194 22270915 : Span XmlReader::getAttributeValue(bool fullyNormalize) {
195 : return handleAttributeValue(
196 44541830 : currentAttribute_->valueBegin, currentAttribute_->valueEnd,
197 66812745 : fullyNormalize);
198 : }
199 :
200 24184382 : int XmlReader::getNamespaceId(Span const & prefix) const {
201 171819387 : for (NamespaceList::const_reverse_iterator i(namespaces_.rbegin());
202 114546258 : i != namespaces_.rend(); ++i)
203 : {
204 57273129 : if (prefix.equals(i->prefix)) {
205 24184382 : return i->nsId;
206 : }
207 : }
208 0 : return NAMESPACE_UNKNOWN;
209 : }
210 :
211 :
212 0 : void XmlReader::normalizeLineEnds(Span const & text) {
213 0 : char const * p = text.begin;
214 0 : sal_Int32 n = text.length;
215 : for (;;) {
216 0 : sal_Int32 i = rtl_str_indexOfChar_WithLength(p, n, '\x0D');
217 0 : if (i < 0) {
218 0 : break;
219 : }
220 0 : pad_.add(p, i);
221 0 : p += i + 1;
222 0 : n -= i + 1;
223 0 : if (n == 0 || *p != '\x0A') {
224 0 : pad_.add("\x0A");
225 : }
226 0 : }
227 0 : pad_.add(p, n);
228 0 : }
229 :
230 106453167 : void XmlReader::skipSpace() {
231 235319436 : while (isSpace(peek())) {
232 22413102 : ++pos_;
233 : }
234 106453167 : }
235 :
236 3998 : bool XmlReader::skipComment() {
237 3998 : if (rtl_str_shortenedCompare_WithLength(
238 3998 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"),
239 7996 : RTL_CONSTASCII_LENGTH("--")) !=
240 : 0)
241 : {
242 0 : return false;
243 : }
244 3998 : pos_ += RTL_CONSTASCII_LENGTH("--");
245 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
246 3998 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"));
247 3998 : if (i < 0) {
248 : throw css::uno::RuntimeException(
249 0 : "premature end (within comment) of " + fileUrl_ );
250 : }
251 3998 : pos_ += i + RTL_CONSTASCII_LENGTH("--");
252 3998 : if (read() != '>') {
253 : throw css::uno::RuntimeException(
254 0 : "illegal \"--\" within comment in " + fileUrl_ );
255 : }
256 3998 : return true;
257 : }
258 :
259 16923 : void XmlReader::skipProcessingInstruction() {
260 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
261 16923 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("?>"));
262 16923 : if (i < 0) {
263 : throw css::uno::RuntimeException(
264 0 : "bad '<?' in " + fileUrl_ );
265 : }
266 16923 : pos_ += i + RTL_CONSTASCII_LENGTH("?>");
267 16923 : }
268 :
269 0 : void XmlReader::skipDocumentTypeDeclaration() {
270 : // Neither is it checked that the doctypedecl is at the correct position in
271 : // the document, nor that it is well-formed:
272 : for (;;) {
273 0 : char c = read();
274 0 : switch (c) {
275 : case '\0': // i.e., EOF
276 : throw css::uno::RuntimeException(
277 0 : "premature end (within DTD) of " + fileUrl_ );
278 : case '"':
279 : case '\'':
280 : {
281 : sal_Int32 i = rtl_str_indexOfChar_WithLength(
282 0 : pos_, end_ - pos_, c);
283 0 : if (i < 0) {
284 : throw css::uno::RuntimeException(
285 0 : "premature end (within DTD) of " + fileUrl_ );
286 : }
287 0 : pos_ += i + 1;
288 : }
289 0 : break;
290 : case '>':
291 0 : return;
292 : case '[':
293 : for (;;) {
294 0 : c = read();
295 0 : switch (c) {
296 : case '\0': // i.e., EOF
297 : throw css::uno::RuntimeException(
298 0 : "premature end (within DTD) of " + fileUrl_ );
299 : case '"':
300 : case '\'':
301 : {
302 : sal_Int32 i = rtl_str_indexOfChar_WithLength(
303 0 : pos_, end_ - pos_, c);
304 0 : if (i < 0) {
305 : throw css::uno::RuntimeException(
306 0 : "premature end (within DTD) of " + fileUrl_ );
307 : }
308 0 : pos_ += i + 1;
309 : }
310 0 : break;
311 : case '<':
312 0 : switch (read()) {
313 : case '\0': // i.e., EOF
314 : throw css::uno::RuntimeException(
315 0 : "premature end (within DTD) of " + fileUrl_ );
316 : case '!':
317 0 : skipComment();
318 0 : break;
319 : case '?':
320 0 : skipProcessingInstruction();
321 0 : break;
322 : default:
323 0 : break;
324 : }
325 0 : break;
326 : case ']':
327 0 : skipSpace();
328 0 : if (read() != '>') {
329 : throw css::uno::RuntimeException(
330 0 : "missing \">\" of DTD in " + fileUrl_ );
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 : }
359 0 : pos_ += i + RTL_CONSTASCII_LENGTH("]]>");
360 0 : return Span(begin, i);
361 : }
362 :
363 61631611 : bool XmlReader::scanName(char const ** nameColon) {
364 : assert(nameColon != 0 && *nameColon == 0);
365 422633192 : for (char const * begin = pos_;; ++pos_) {
366 422633192 : switch (peek()) {
367 : case '\0': // i.e., EOF
368 : case '\x09':
369 : case '\x0A':
370 : case '\x0D':
371 : case ' ':
372 : case '/':
373 : case '=':
374 : case '>':
375 123263222 : return pos_ != begin;
376 : case ':':
377 21037329 : *nameColon = pos_;
378 21037329 : break;
379 : default:
380 339964252 : break;
381 : }
382 361001581 : }
383 : }
384 :
385 91119 : int XmlReader::scanNamespaceIri(char const * begin, char const * end) {
386 : assert(begin != 0 && begin <= end);
387 91119 : Span iri(handleAttributeValue(begin, end, false));
388 386295 : for (NamespaceIris::size_type i = 0; i < namespaceIris_.size(); ++i) {
389 326707 : if (namespaceIris_[i].equals(iri)) {
390 31531 : return toNamespaceId(i);
391 : }
392 : }
393 59588 : return XmlReader::NAMESPACE_UNKNOWN;
394 : }
395 :
396 78105 : char const * XmlReader::handleReference(char const * position, char const * end)
397 : {
398 : assert(position != 0 && *position == '&' && position < end);
399 78105 : ++position;
400 78105 : if (*position == '#') {
401 4572 : ++position;
402 4572 : sal_Int32 val = 0;
403 : char const * p;
404 4572 : if (*position == 'x') {
405 4572 : ++position;
406 4572 : p = position;
407 18288 : for (;; ++position) {
408 22860 : char c = *position;
409 22860 : if (c >= '0' && c <= '9') {
410 8382 : val = 16 * val + (c - '0');
411 14478 : } else if (c >= 'A' && c <= 'F') {
412 9906 : val = 16 * val + (c - 'A') + 10;
413 4572 : } else if (c >= 'a' && c <= 'f') {
414 0 : val = 16 * val + (c - 'a') + 10;
415 : } else {
416 : break;
417 : }
418 18288 : if (val > 0x10FFFF) { // avoid overflow
419 : throw css::uno::RuntimeException(
420 0 : "'&#x...' too large in " + fileUrl_ );
421 : }
422 18288 : }
423 : } else {
424 0 : p = position;
425 0 : for (;; ++position) {
426 0 : char c = *position;
427 0 : if (c >= '0' && c <= '9') {
428 0 : val = 10 * val + (c - '0');
429 : } else {
430 : break;
431 : }
432 0 : if (val > 0x10FFFF) { // avoid overflow
433 : throw css::uno::RuntimeException(
434 0 : "'&#...' too large in " + fileUrl_ );
435 : }
436 0 : }
437 : }
438 4572 : if (position == p || *position++ != ';') {
439 : throw css::uno::RuntimeException(
440 0 : "'&#...' missing ';' in " + fileUrl_ );
441 : }
442 : assert(val >= 0 && val <= 0x10FFFF);
443 4572 : if ((val < 0x20 && val != 0x9 && val != 0xA && val != 0xD) ||
444 0 : (val >= 0xD800 && val <= 0xDFFF) || val == 0xFFFE || val == 0xFFFF)
445 : {
446 : throw css::uno::RuntimeException(
447 0 : "character reference denoting invalid character in " + fileUrl_ );
448 : }
449 : char buf[4];
450 : sal_Int32 len;
451 4572 : if (val < 0x80) {
452 0 : buf[0] = static_cast< char >(val);
453 0 : len = 1;
454 4572 : } else if (val < 0x800) {
455 0 : buf[0] = static_cast< char >((val >> 6) | 0xC0);
456 0 : buf[1] = static_cast< char >((val & 0x3F) | 0x80);
457 0 : len = 2;
458 4572 : } else if (val < 0x10000) {
459 4572 : buf[0] = static_cast< char >((val >> 12) | 0xE0);
460 4572 : buf[1] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
461 4572 : buf[2] = static_cast< char >((val & 0x3F) | 0x80);
462 4572 : len = 3;
463 : } else {
464 0 : buf[0] = static_cast< char >((val >> 18) | 0xF0);
465 0 : buf[1] = static_cast< char >(((val >> 12) & 0x3F) | 0x80);
466 0 : buf[2] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
467 0 : buf[3] = static_cast< char >((val & 0x3F) | 0x80);
468 0 : len = 4;
469 : }
470 4572 : pad_.addEphemeral(buf, len);
471 4572 : return position;
472 : } else {
473 : struct EntityRef {
474 : char const * inBegin;
475 : sal_Int32 inLength;
476 : char const * outBegin;
477 : sal_Int32 outLength;
478 : };
479 : static EntityRef const refs[] = {
480 : { RTL_CONSTASCII_STRINGPARAM("amp;"),
481 : RTL_CONSTASCII_STRINGPARAM("&") },
482 : { RTL_CONSTASCII_STRINGPARAM("lt;"),
483 : RTL_CONSTASCII_STRINGPARAM("<") },
484 : { RTL_CONSTASCII_STRINGPARAM("gt;"),
485 : RTL_CONSTASCII_STRINGPARAM(">") },
486 : { RTL_CONSTASCII_STRINGPARAM("apos;"),
487 : RTL_CONSTASCII_STRINGPARAM("'") },
488 : { RTL_CONSTASCII_STRINGPARAM("quot;"),
489 : RTL_CONSTASCII_STRINGPARAM("\"") } };
490 140589 : for (std::size_t i = 0; i < sizeof refs / sizeof refs[0]; ++i) {
491 140589 : if (rtl_str_shortenedCompare_WithLength(
492 140589 : position, end - position, refs[i].inBegin, refs[i].inLength,
493 281178 : refs[i].inLength) ==
494 : 0)
495 : {
496 73533 : position += refs[i].inLength;
497 73533 : pad_.add(refs[i].outBegin, refs[i].outLength);
498 73533 : return position;
499 : }
500 : }
501 : throw css::uno::RuntimeException(
502 0 : "unknown entity reference in " + fileUrl_ );
503 : }
504 : }
505 :
506 22362034 : Span XmlReader::handleAttributeValue(
507 : char const * begin, char const * end, bool fullyNormalize)
508 : {
509 22362034 : pad_.clear();
510 22362034 : if (fullyNormalize) {
511 13288318 : while (begin != end && isSpace(*begin)) {
512 0 : ++begin;
513 : }
514 13288318 : while (end != begin && isSpace(end[-1])) {
515 0 : --end;
516 : }
517 6644159 : char const * p = begin;
518 : enum Space { SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
519 : // a single true space character can go into the current span,
520 : // everything else breaks the span
521 6644159 : Space space = SPACE_NONE;
522 64272975 : while (p != end) {
523 50984657 : switch (*p) {
524 : case '\x09':
525 : case '\x0A':
526 : case '\x0D':
527 0 : switch (space) {
528 : case SPACE_NONE:
529 0 : pad_.add(begin, p - begin);
530 0 : pad_.add(" ");
531 0 : space = SPACE_BREAK;
532 0 : break;
533 : case SPACE_SPAN:
534 0 : pad_.add(begin, p - begin);
535 0 : space = SPACE_BREAK;
536 0 : break;
537 : case SPACE_BREAK:
538 0 : break;
539 : }
540 0 : begin = ++p;
541 0 : break;
542 : case ' ':
543 7239 : switch (space) {
544 : case SPACE_NONE:
545 7239 : ++p;
546 7239 : space = SPACE_SPAN;
547 7239 : break;
548 : case SPACE_SPAN:
549 0 : pad_.add(begin, p - begin);
550 0 : begin = ++p;
551 0 : space = SPACE_BREAK;
552 0 : break;
553 : case SPACE_BREAK:
554 0 : begin = ++p;
555 0 : break;
556 : }
557 7239 : break;
558 : case '&':
559 0 : pad_.add(begin, p - begin);
560 0 : p = handleReference(p, end);
561 0 : begin = p;
562 0 : space = SPACE_NONE;
563 0 : break;
564 : default:
565 50977418 : ++p;
566 50977418 : space = SPACE_NONE;
567 50977418 : break;
568 : }
569 : }
570 6644159 : pad_.add(begin, p - begin);
571 : } else {
572 15717875 : char const * p = begin;
573 215690682 : while (p != end) {
574 184254932 : switch (*p) {
575 : case '\x09':
576 : case '\x0A':
577 0 : pad_.add(begin, p - begin);
578 0 : begin = ++p;
579 0 : pad_.add(" ");
580 0 : break;
581 : case '\x0D':
582 0 : pad_.add(begin, p - begin);
583 0 : ++p;
584 0 : if (peek() == '\x0A') {
585 0 : ++p;
586 : }
587 0 : begin = p;
588 0 : pad_.add(" ");
589 0 : break;
590 : case '&':
591 4572 : pad_.add(begin, p - begin);
592 4572 : p = handleReference(p, end);
593 4572 : begin = p;
594 4572 : break;
595 : default:
596 184250360 : ++p;
597 184250360 : break;
598 : }
599 : }
600 15717875 : pad_.add(begin, p - begin);
601 : }
602 22362034 : return pad_.get();
603 : }
604 :
605 20545023 : XmlReader::Result XmlReader::handleStartTag(int * nsId, Span * localName) {
606 : assert(nsId != 0 && localName);
607 20545023 : char const * nameBegin = pos_;
608 20545023 : char const * nameColon = 0;
609 20545023 : if (!scanName(&nameColon)) {
610 : throw css::uno::RuntimeException(
611 0 : "bad tag name in " + fileUrl_ );
612 : }
613 20545023 : char const * nameEnd = pos_;
614 20545023 : NamespaceList::size_type inheritedNamespaces = namespaces_.size();
615 20545023 : bool hasDefaultNs = false;
616 20545023 : int defaultNsId = NAMESPACE_NONE;
617 20545023 : attributes_.clear();
618 : for (;;) {
619 42955801 : char const * p = pos_;
620 42955801 : skipSpace();
621 42955801 : if (peek() == '/' || peek() == '>') {
622 20545023 : break;
623 : }
624 22410778 : if (pos_ == p) {
625 : throw css::uno::RuntimeException(
626 0 : "missing whitespace before attribute in " + fileUrl_ );
627 : }
628 22410778 : char const * attrNameBegin = pos_;
629 22410778 : char const * attrNameColon = 0;
630 22410778 : if (!scanName(&attrNameColon)) {
631 : throw css::uno::RuntimeException(
632 0 : "bad attribute name in " + fileUrl_ );
633 : }
634 22410778 : char const * attrNameEnd = pos_;
635 22410778 : skipSpace();
636 22410778 : if (read() != '=') {
637 : throw css::uno::RuntimeException(
638 0 : "missing '=' in " + fileUrl_ );
639 : }
640 22410778 : skipSpace();
641 22410778 : char del = read();
642 22410778 : if (del != '\'' && del != '"') {
643 : throw css::uno::RuntimeException(
644 0 : "bad attribute value in " + fileUrl_ );
645 : }
646 22410778 : char const * valueBegin = pos_;
647 22410778 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, del);
648 22410778 : if (i < 0) {
649 : throw css::uno::RuntimeException(
650 0 : "unterminated attribute value in " + fileUrl_ );
651 : }
652 22410778 : char const * valueEnd = pos_ + i;
653 22410778 : pos_ += i + 1;
654 68844098 : if (attrNameColon == 0 &&
655 27246070 : Span(attrNameBegin, attrNameEnd - attrNameBegin).equals("xmlns"))
656 : {
657 6138 : hasDefaultNs = true;
658 6138 : defaultNsId = scanNamespaceIri(valueBegin, valueEnd);
659 88012934 : } else if (attrNameColon != 0 &&
660 20799014 : Span(attrNameBegin, attrNameColon - attrNameBegin).equals(
661 84801682 : "xmlns"))
662 : {
663 : namespaces_.push_back(
664 : NamespaceData(
665 84981 : Span(attrNameColon + 1, attrNameEnd - (attrNameColon + 1)),
666 169962 : scanNamespaceIri(valueBegin, valueEnd)));
667 : } else {
668 : attributes_.push_back(
669 : AttributeData(
670 : attrNameBegin, attrNameEnd, attrNameColon, valueBegin,
671 22319659 : valueEnd));
672 : }
673 22410778 : }
674 41083908 : if (!hasDefaultNs && !elements_.empty()) {
675 20528100 : defaultNsId = elements_.top().defaultNamespaceId;
676 : }
677 20545023 : firstAttribute_ = true;
678 20545023 : if (peek() == '/') {
679 1869213 : state_ = STATE_EMPTY_ELEMENT_TAG;
680 1869213 : ++pos_;
681 : } else {
682 18675810 : state_ = STATE_CONTENT;
683 : }
684 20545023 : if (peek() != '>') {
685 : throw css::uno::RuntimeException(
686 0 : "missing '>' in " + fileUrl_ );
687 : }
688 20545023 : ++pos_;
689 : elements_.push(
690 : ElementData(
691 20545023 : Span(nameBegin, nameEnd - nameBegin), inheritedNamespaces,
692 20545023 : defaultNsId));
693 20545023 : if (nameColon == 0) {
694 20419718 : *nsId = defaultNsId;
695 20419718 : *localName = Span(nameBegin, nameEnd - nameBegin);
696 : } else {
697 125305 : *nsId = getNamespaceId(Span(nameBegin, nameColon - nameBegin));
698 125305 : *localName = Span(nameColon + 1, nameEnd - (nameColon + 1));
699 : }
700 20545023 : return RESULT_BEGIN;
701 : }
702 :
703 18675810 : XmlReader::Result XmlReader::handleEndTag() {
704 18675810 : if (elements_.empty()) {
705 : throw css::uno::RuntimeException(
706 0 : "spurious end tag in " + fileUrl_ );
707 : }
708 18675810 : char const * nameBegin = pos_;
709 18675810 : char const * nameColon = 0;
710 37351620 : if (!scanName(&nameColon) ||
711 18675810 : !elements_.top().name.equals(nameBegin, pos_ - nameBegin))
712 : {
713 : throw css::uno::RuntimeException(
714 0 : "tag mismatch in " + fileUrl_ );
715 : }
716 18675810 : handleElementEnd();
717 18675810 : skipSpace();
718 18675810 : if (peek() != '>') {
719 : throw css::uno::RuntimeException(
720 0 : "missing '>' in " + fileUrl_ );
721 : }
722 18675810 : ++pos_;
723 18675810 : return RESULT_END;
724 : }
725 :
726 20545023 : void XmlReader::handleElementEnd() {
727 : assert(!elements_.empty());
728 20545023 : namespaces_.resize(elements_.top().inheritedNamespaces);
729 20545023 : elements_.pop();
730 20545023 : state_ = elements_.empty() ? STATE_DONE : STATE_CONTENT;
731 20545023 : }
732 :
733 31726589 : XmlReader::Result XmlReader::handleSkippedText(Span * data, int * nsId) {
734 : for (;;) {
735 31726589 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, '<');
736 31726589 : if (i < 0) {
737 : throw css::uno::RuntimeException(
738 0 : "premature end of " + fileUrl_ );
739 : }
740 31726589 : pos_ += i + 1;
741 31726589 : switch (peek()) {
742 : case '!':
743 3998 : ++pos_;
744 3998 : if (!skipComment() && !scanCdataSection().is()) {
745 0 : skipDocumentTypeDeclaration();
746 : }
747 3998 : break;
748 : case '/':
749 11173275 : ++pos_;
750 11173275 : return handleEndTag();
751 : case '?':
752 16923 : ++pos_;
753 16923 : skipProcessingInstruction();
754 16923 : break;
755 : default:
756 20532393 : return handleStartTag(nsId, data);
757 : }
758 20921 : }
759 : }
760 :
761 5162529 : XmlReader::Result XmlReader::handleRawText(Span * text) {
762 5162529 : pad_.clear();
763 5162529 : for (char const * begin = pos_;;) {
764 126123737 : switch (peek()) {
765 : case '\0': // i.e., EOF
766 : throw css::uno::RuntimeException(
767 0 : "premature end of " + fileUrl_ );
768 : case '\x0D':
769 0 : pad_.add(begin, pos_ - begin);
770 0 : ++pos_;
771 0 : if (peek() != '\x0A') {
772 0 : pad_.add("\x0A");
773 : }
774 0 : begin = pos_;
775 0 : break;
776 : case '&':
777 73533 : pad_.add(begin, pos_ - begin);
778 73533 : pos_ = handleReference(pos_, end_);
779 73533 : begin = pos_;
780 73533 : break;
781 : case '<':
782 5162529 : pad_.add(begin, pos_ - begin);
783 5162529 : ++pos_;
784 5162529 : switch (peek()) {
785 : case '!':
786 0 : ++pos_;
787 0 : if (!skipComment()) {
788 0 : Span cdata(scanCdataSection());
789 0 : if (cdata.is()) {
790 0 : normalizeLineEnds(cdata);
791 : } else {
792 0 : skipDocumentTypeDeclaration();
793 : }
794 : }
795 0 : begin = pos_;
796 0 : break;
797 : case '/':
798 5149899 : *text = pad_.get();
799 5149899 : ++pos_;
800 5149899 : state_ = STATE_END_TAG;
801 5149899 : return RESULT_TEXT;
802 : case '?':
803 0 : ++pos_;
804 0 : skipProcessingInstruction();
805 0 : begin = pos_;
806 0 : break;
807 : default:
808 12630 : *text = pad_.get();
809 12630 : state_ = STATE_START_TAG;
810 12630 : return RESULT_TEXT;
811 : }
812 0 : break;
813 : default:
814 120887675 : ++pos_;
815 120887675 : break;
816 : }
817 120961208 : }
818 : }
819 :
820 2352636 : XmlReader::Result XmlReader::handleNormalizedText(Span * text) {
821 2352636 : pad_.clear();
822 2352636 : char const * flowBegin = pos_;
823 2352636 : char const * flowEnd = pos_;
824 : enum Space { SPACE_START, SPACE_NONE, SPACE_SPAN, SPACE_BREAK };
825 : // a single true space character can go into the current flow,
826 : // everything else breaks the flow
827 2352636 : Space space = SPACE_START;
828 : for (;;) {
829 8859052 : switch (peek()) {
830 : case '\0': // i.e., EOF
831 : throw css::uno::RuntimeException(
832 0 : "premature end of " + fileUrl_ );
833 : case '\x09':
834 : case '\x0A':
835 : case '\x0D':
836 4572 : switch (space) {
837 : case SPACE_START:
838 : case SPACE_BREAK:
839 2286 : break;
840 : case SPACE_NONE:
841 : case SPACE_SPAN:
842 2286 : space = SPACE_BREAK;
843 2286 : break;
844 : }
845 4572 : ++pos_;
846 4572 : break;
847 : case ' ':
848 57531 : switch (space) {
849 : case SPACE_START:
850 : case SPACE_BREAK:
851 35814 : break;
852 : case SPACE_NONE:
853 21717 : space = SPACE_SPAN;
854 21717 : break;
855 : case SPACE_SPAN:
856 0 : space = SPACE_BREAK;
857 0 : break;
858 : }
859 57531 : ++pos_;
860 57531 : break;
861 : case '&':
862 0 : switch (space) {
863 : case SPACE_START:
864 0 : break;
865 : case SPACE_NONE:
866 : case SPACE_SPAN:
867 0 : pad_.add(flowBegin, pos_ - flowBegin);
868 0 : break;
869 : case SPACE_BREAK:
870 0 : pad_.add(flowBegin, flowEnd - flowBegin);
871 0 : pad_.add(" ");
872 0 : break;
873 : }
874 0 : pos_ = handleReference(pos_, end_);
875 0 : flowBegin = pos_;
876 0 : flowEnd = pos_;
877 0 : space = SPACE_NONE;
878 0 : break;
879 : case '<':
880 2352636 : ++pos_;
881 2352636 : switch (peek()) {
882 : case '!':
883 0 : ++pos_;
884 0 : if (skipComment()) {
885 0 : space = SPACE_BREAK;
886 : } else {
887 0 : Span cdata(scanCdataSection());
888 0 : if (cdata.is()) {
889 : // CDATA is not normalized (similar to character
890 : // references; it keeps the code simple), but it might
891 : // arguably be better to normalize it:
892 0 : switch (space) {
893 : case SPACE_START:
894 0 : break;
895 : case SPACE_NONE:
896 : case SPACE_SPAN:
897 0 : pad_.add(flowBegin, pos_ - flowBegin);
898 0 : break;
899 : case SPACE_BREAK:
900 0 : pad_.add(flowBegin, flowEnd - flowBegin);
901 0 : pad_.add(" ");
902 0 : break;
903 : }
904 0 : normalizeLineEnds(cdata);
905 0 : flowBegin = pos_;
906 0 : flowEnd = pos_;
907 0 : space = SPACE_NONE;
908 : } else {
909 0 : skipDocumentTypeDeclaration();
910 : }
911 : }
912 0 : break;
913 : case '/':
914 2352636 : ++pos_;
915 2352636 : pad_.add(flowBegin, flowEnd - flowBegin);
916 2352636 : *text = pad_.get();
917 2352636 : state_ = STATE_END_TAG;
918 2352636 : return RESULT_TEXT;
919 : case '?':
920 0 : ++pos_;
921 0 : skipProcessingInstruction();
922 0 : space = SPACE_BREAK;
923 0 : break;
924 : default:
925 0 : pad_.add(flowBegin, flowEnd - flowBegin);
926 0 : *text = pad_.get();
927 0 : state_ = STATE_START_TAG;
928 0 : return RESULT_TEXT;
929 : }
930 0 : break;
931 : default:
932 6444313 : switch (space) {
933 : case SPACE_START:
934 2352636 : flowBegin = pos_;
935 2352636 : break;
936 : case SPACE_NONE:
937 : case SPACE_SPAN:
938 4089772 : break;
939 : case SPACE_BREAK:
940 1905 : pad_.add(flowBegin, flowEnd - flowBegin);
941 1905 : pad_.add(" ");
942 1905 : flowBegin = pos_;
943 1905 : break;
944 : }
945 6444313 : flowEnd = ++pos_;
946 6444313 : space = SPACE_NONE;
947 6444313 : break;
948 : }
949 6506416 : }
950 : }
951 :
952 63262 : int XmlReader::toNamespaceId(NamespaceIris::size_type pos) {
953 : assert(pos <= INT_MAX);
954 63262 : return static_cast< int >(pos);
955 : }
956 :
957 : }
958 :
959 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|