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 94003235 : bool isSpace(char c) {
44 94003235 : switch (c) {
45 : case '\x09':
46 : case '\x0A':
47 : case '\x0D':
48 : case ' ':
49 14840964 : return true;
50 : default:
51 79162271 : 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 10748 : XmlReader::XmlReader(OUString const & fileUrl)
72 : : fileUrl_(fileUrl)
73 10928 : , fileHandle_(0)
74 : {
75 : oslFileError e = osl_openFile(
76 10748 : fileUrl_.pData, &fileHandle_, osl_File_OpenFlag_Read);
77 10748 : switch (e)
78 : {
79 : case osl_File_E_None:
80 10570 : break;
81 : case osl_File_E_NOENT:
82 178 : throw css::container::NoSuchElementException( fileUrl_ );
83 : default:
84 : throw css::uno::RuntimeException(
85 0 : "cannot open " + fileUrl_ + ": " + OUString::number(e));
86 : }
87 10570 : e = osl_getFileSize(fileHandle_, &fileSize_);
88 10570 : if (e == osl_File_E_None) {
89 : e = osl_mapFile(
90 : fileHandle_, &fileAddress_, fileSize_, 0,
91 10570 : osl_File_MapFlag_WillNeed);
92 : }
93 10570 : if (e != osl_File_E_None) {
94 2 : oslFileError e2 = osl_closeFile(fileHandle_);
95 2 : 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 2 : "cannot mmap " + fileUrl_ + " (" + OUString::number(e) + ")" );
102 : }
103 10568 : namespaceIris_.push_back(Span("http://www.w3.org/XML/1998/namespace"));
104 10568 : namespaces_.push_back(NamespaceData(Span("xml"), NAMESPACE_XML));
105 10568 : pos_ = static_cast< char * >(fileAddress_);
106 10568 : end_ = pos_ + fileSize_;
107 10568 : state_ = STATE_CONTENT;
108 10568 : firstAttribute_ = true;
109 10568 : }
110 :
111 21136 : XmlReader::~XmlReader() {
112 10568 : if (!fileHandle_)
113 0 : return;
114 10568 : oslFileError e = osl_unmapMappedFile(fileHandle_, fileAddress_, fileSize_);
115 10568 : if (e != osl_File_E_None) {
116 : SAL_WARN(
117 : "xmlreader",
118 : "osl_unmapMappedFile of \"" << fileUrl_ << "\" failed with " << +e);
119 : }
120 10568 : e = osl_closeFile(fileHandle_);
121 10568 : if (e != osl_File_E_None) {
122 : SAL_WARN(
123 : "xmlreader",
124 : "osl_closeFile of \"" << fileUrl_ << "\" failed with " << +e);
125 : }
126 10568 : }
127 :
128 19276 : int XmlReader::registerNamespaceIri(Span const & iri) {
129 19276 : int id = toNamespaceId(namespaceIris_.size());
130 19276 : namespaceIris_.push_back(iri);
131 19276 : 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 5173 : namespaces_.push_back(NamespaceData(Span("xsi"), id));
138 : }
139 19276 : return id;
140 : }
141 :
142 32146119 : XmlReader::Result XmlReader::nextItem(Text reportText, Span * data, int * nsId)
143 : {
144 32146119 : switch (state_) {
145 : case STATE_CONTENT:
146 25925275 : switch (reportText) {
147 : case TEXT_NONE:
148 20961356 : return handleSkippedText(data, nsId);
149 : case TEXT_RAW:
150 3410411 : return handleRawText(data);
151 : case TEXT_NORMALIZED:
152 1553508 : return handleNormalizedText(data);
153 : }
154 : case STATE_START_TAG:
155 7410 : return handleStartTag(nsId, data);
156 : case STATE_END_TAG:
157 4956509 : return handleEndTag();
158 : case STATE_EMPTY_ELEMENT_TAG:
159 1246357 : handleElementEnd();
160 1246357 : return RESULT_END;
161 : default: // STATE_DONE
162 10568 : return RESULT_DONE;
163 : }
164 : }
165 :
166 28219585 : bool XmlReader::nextAttribute(int * nsId, Span * localName) {
167 : assert(nsId != 0 && localName != 0);
168 28219585 : if (firstAttribute_) {
169 13441258 : currentAttribute_ = attributes_.begin();
170 13441258 : firstAttribute_ = false;
171 : } else {
172 14778327 : ++currentAttribute_;
173 : }
174 28219585 : if (currentAttribute_ == attributes_.end()) {
175 13441258 : return false;
176 : }
177 14778327 : if (currentAttribute_->nameColon == 0) {
178 1119974 : *nsId = NAMESPACE_NONE;
179 : *localName = Span(
180 1119974 : currentAttribute_->nameBegin,
181 2239948 : currentAttribute_->nameEnd - currentAttribute_->nameBegin);
182 : } else {
183 : *nsId = getNamespaceId(
184 : Span(
185 13658353 : currentAttribute_->nameBegin,
186 27316706 : currentAttribute_->nameColon - currentAttribute_->nameBegin));
187 : *localName = Span(
188 13658353 : currentAttribute_->nameColon + 1,
189 27316706 : currentAttribute_->nameEnd - (currentAttribute_->nameColon + 1));
190 : }
191 14778327 : return true;
192 : }
193 :
194 14749577 : Span XmlReader::getAttributeValue(bool fullyNormalize) {
195 : return handleAttributeValue(
196 29499154 : currentAttribute_->valueBegin, currentAttribute_->valueEnd,
197 44248731 : fullyNormalize);
198 : }
199 :
200 15947173 : int XmlReader::getNamespaceId(Span const & prefix) const {
201 114003705 : for (NamespaceList::const_reverse_iterator i(namespaces_.rbegin());
202 76002470 : i != namespaces_.rend(); ++i)
203 : {
204 38001235 : if (prefix.equals(i->prefix)) {
205 15947173 : 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 70443127 : void XmlReader::skipSpace() {
231 155727218 : while (isSpace(peek())) {
232 14840964 : ++pos_;
233 : }
234 70443127 : }
235 :
236 2370 : bool XmlReader::skipComment() {
237 2370 : if (rtl_str_shortenedCompare_WithLength(
238 2370 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"),
239 4740 : RTL_CONSTASCII_LENGTH("--")) !=
240 : 0)
241 : {
242 0 : return false;
243 : }
244 2370 : pos_ += RTL_CONSTASCII_LENGTH("--");
245 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
246 2370 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("--"));
247 2370 : if (i < 0) {
248 : throw css::uno::RuntimeException(
249 0 : "premature end (within comment) of " + fileUrl_ );
250 : }
251 2370 : pos_ += i + RTL_CONSTASCII_LENGTH("--");
252 2370 : if (read() != '>') {
253 : throw css::uno::RuntimeException(
254 0 : "illegal \"--\" within comment in " + fileUrl_ );
255 : }
256 2370 : return true;
257 : }
258 :
259 10568 : void XmlReader::skipProcessingInstruction() {
260 : sal_Int32 i = rtl_str_indexOfStr_WithLength(
261 10568 : pos_, end_ - pos_, RTL_CONSTASCII_STRINGPARAM("?>"));
262 10568 : if (i < 0) {
263 : throw css::uno::RuntimeException(
264 0 : "bad '<?' in " + fileUrl_ );
265 : }
266 10568 : pos_ += i + RTL_CONSTASCII_LENGTH("?>");
267 10568 : }
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 40764559 : bool XmlReader::scanName(char const ** nameColon) {
364 : assert(nameColon != 0 && *nameColon == 0);
365 279752664 : for (char const * begin = pos_;; ++pos_) {
366 279752664 : 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 81529118 : return pos_ != begin;
376 : case ':':
377 13860994 : *nameColon = pos_;
378 13860994 : break;
379 : default:
380 225127111 : break;
381 : }
382 238988105 : }
383 : }
384 :
385 56960 : int XmlReader::scanNamespaceIri(char const * begin, char const * end) {
386 : assert(begin != 0 && begin <= end);
387 56960 : Span iri(handleAttributeValue(begin, end, false));
388 242854 : for (NamespaceIris::size_type i = 0; i < namespaceIris_.size(); ++i) {
389 204928 : if (namespaceIris_[i].equals(iri)) {
390 19034 : return toNamespaceId(i);
391 : }
392 : }
393 37926 : return XmlReader::NAMESPACE_UNKNOWN;
394 : }
395 :
396 53500 : char const * XmlReader::handleReference(char const * position, char const * end)
397 : {
398 : assert(position != 0 && *position == '&' && position < end);
399 53500 : ++position;
400 53500 : if (*position == '#') {
401 3000 : ++position;
402 3000 : sal_Int32 val = 0;
403 : char const * p;
404 3000 : if (*position == 'x') {
405 3000 : ++position;
406 3000 : p = position;
407 12000 : for (;; ++position) {
408 15000 : char c = *position;
409 15000 : if (c >= '0' && c <= '9') {
410 5500 : val = 16 * val + (c - '0');
411 9500 : } else if (c >= 'A' && c <= 'F') {
412 6500 : val = 16 * val + (c - 'A') + 10;
413 3000 : } else if (c >= 'a' && c <= 'f') {
414 0 : val = 16 * val + (c - 'a') + 10;
415 : } else {
416 : break;
417 : }
418 12000 : if (val > 0x10FFFF) { // avoid overflow
419 : throw css::uno::RuntimeException(
420 0 : "'&#x...' too large in " + fileUrl_ );
421 : }
422 12000 : }
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 3000 : if (position == p || *position++ != ';') {
439 : throw css::uno::RuntimeException(
440 0 : "'&#...' missing ';' in " + fileUrl_ );
441 : }
442 : assert(val >= 0 && val <= 0x10FFFF);
443 3000 : 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 3000 : if (val < 0x80) {
452 0 : buf[0] = static_cast< char >(val);
453 0 : len = 1;
454 3000 : } 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 3000 : } else if (val < 0x10000) {
459 3000 : buf[0] = static_cast< char >((val >> 12) | 0xE0);
460 3000 : buf[1] = static_cast< char >(((val >> 6) & 0x3F) | 0x80);
461 3000 : buf[2] = static_cast< char >((val & 0x3F) | 0x80);
462 3000 : 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 3000 : pad_.addEphemeral(buf, len);
471 3000 : 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 95250 : for (std::size_t i = 0; i < sizeof refs / sizeof refs[0]; ++i) {
491 95250 : if (rtl_str_shortenedCompare_WithLength(
492 95250 : position, end - position, refs[i].inBegin, refs[i].inLength,
493 190500 : refs[i].inLength) ==
494 : 0)
495 : {
496 50500 : position += refs[i].inLength;
497 50500 : pad_.add(refs[i].outBegin, refs[i].outLength);
498 50500 : return position;
499 : }
500 : }
501 : throw css::uno::RuntimeException(
502 0 : "unknown entity reference in " + fileUrl_ );
503 : }
504 : }
505 :
506 14806537 : Span XmlReader::handleAttributeValue(
507 : char const * begin, char const * end, bool fullyNormalize)
508 : {
509 14806537 : pad_.clear();
510 14806537 : if (fullyNormalize) {
511 8719144 : while (begin != end && isSpace(*begin)) {
512 0 : ++begin;
513 : }
514 8719144 : while (end != begin && isSpace(end[-1])) {
515 0 : --end;
516 : }
517 4359572 : 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 4359572 : Space space = SPACE_NONE;
522 42152066 : while (p != end) {
523 33432922 : 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 4750 : switch (space) {
544 : case SPACE_NONE:
545 4750 : ++p;
546 4750 : space = SPACE_SPAN;
547 4750 : 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 4750 : 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 33428172 : ++p;
566 33428172 : space = SPACE_NONE;
567 33428172 : break;
568 : }
569 : }
570 4359572 : pad_.add(begin, p - begin);
571 : } else {
572 10446965 : char const * p = begin;
573 145700588 : while (p != end) {
574 124806658 : 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 5500 : pad_.add(begin, p - begin);
592 5500 : p = handleReference(p, end);
593 5500 : begin = p;
594 5500 : break;
595 : default:
596 124801158 : ++p;
597 124801158 : break;
598 : }
599 : }
600 10446965 : pad_.add(begin, p - begin);
601 : }
602 14806537 : return pad_.get();
603 : }
604 :
605 13585816 : XmlReader::Result XmlReader::handleStartTag(int * nsId, Span * localName) {
606 : assert(nsId != 0 && localName);
607 13585816 : char const * nameBegin = pos_;
608 13585816 : char const * nameColon = 0;
609 13585816 : if (!scanName(&nameColon)) {
610 : throw css::uno::RuntimeException(
611 0 : "bad tag name in " + fileUrl_ );
612 : }
613 13585816 : char const * nameEnd = pos_;
614 13585816 : NamespaceList::size_type inheritedNamespaces = namespaces_.size();
615 13585816 : bool hasDefaultNs = false;
616 13585816 : int defaultNsId = NAMESPACE_NONE;
617 13585816 : attributes_.clear();
618 : for (;;) {
619 28425100 : char const * p = pos_;
620 28425100 : skipSpace();
621 28425100 : if (peek() == '/' || peek() == '>') {
622 13585816 : break;
623 : }
624 14839284 : if (pos_ == p) {
625 : throw css::uno::RuntimeException(
626 0 : "missing whitespace before attribute in " + fileUrl_ );
627 : }
628 14839284 : char const * attrNameBegin = pos_;
629 14839284 : char const * attrNameColon = 0;
630 14839284 : if (!scanName(&attrNameColon)) {
631 : throw css::uno::RuntimeException(
632 0 : "bad attribute name in " + fileUrl_ );
633 : }
634 14839284 : char const * attrNameEnd = pos_;
635 14839284 : skipSpace();
636 14839284 : if (read() != '=') {
637 : throw css::uno::RuntimeException(
638 0 : "missing '=' in " + fileUrl_ );
639 : }
640 14839284 : skipSpace();
641 14839284 : char del = read();
642 14839284 : if (del != '\'' && del != '"') {
643 : throw css::uno::RuntimeException(
644 0 : "bad attribute value in " + fileUrl_ );
645 : }
646 14839284 : char const * valueBegin = pos_;
647 14839284 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, del);
648 14839284 : if (i < 0) {
649 : throw css::uno::RuntimeException(
650 0 : "unterminated attribute value in " + fileUrl_ );
651 : }
652 14839284 : char const * valueEnd = pos_ + i;
653 14839284 : pos_ += i + 1;
654 45644866 : if (attrNameColon == 0 &&
655 18220326 : Span(attrNameBegin, attrNameEnd - attrNameBegin).equals("xmlns"))
656 : {
657 3757 : hasDefaultNs = true;
658 3757 : defaultNsId = scanNamespaceIri(valueBegin, valueEnd);
659 58218851 : } else if (attrNameColon != 0 &&
660 13712270 : Span(attrNameBegin, attrNameColon - attrNameBegin).equals(
661 55972337 : "xmlns"))
662 : {
663 : namespaces_.push_back(
664 : NamespaceData(
665 53203 : Span(attrNameColon + 1, attrNameEnd - (attrNameColon + 1)),
666 106406 : scanNamespaceIri(valueBegin, valueEnd)));
667 : } else {
668 : attributes_.push_back(
669 : AttributeData(
670 : attrNameBegin, attrNameEnd, attrNameColon, valueBegin,
671 14782324 : valueEnd));
672 : }
673 14839284 : }
674 27167875 : if (!hasDefaultNs && !elements_.empty()) {
675 13575248 : defaultNsId = elements_.top().defaultNamespaceId;
676 : }
677 13585816 : firstAttribute_ = true;
678 13585816 : if (peek() == '/') {
679 1246357 : state_ = STATE_EMPTY_ELEMENT_TAG;
680 1246357 : ++pos_;
681 : } else {
682 12339459 : state_ = STATE_CONTENT;
683 : }
684 13585816 : if (peek() != '>') {
685 : throw css::uno::RuntimeException(
686 0 : "missing '>' in " + fileUrl_ );
687 : }
688 13585816 : ++pos_;
689 : elements_.push(
690 : ElementData(
691 13585816 : Span(nameBegin, nameEnd - nameBegin), inheritedNamespaces,
692 13585816 : defaultNsId));
693 13585816 : if (nameColon == 0) {
694 13507247 : *nsId = defaultNsId;
695 13507247 : *localName = Span(nameBegin, nameEnd - nameBegin);
696 : } else {
697 78569 : *nsId = getNamespaceId(Span(nameBegin, nameColon - nameBegin));
698 78569 : *localName = Span(nameColon + 1, nameEnd - (nameColon + 1));
699 : }
700 13585816 : return RESULT_BEGIN;
701 : }
702 :
703 12339459 : XmlReader::Result XmlReader::handleEndTag() {
704 12339459 : if (elements_.empty()) {
705 : throw css::uno::RuntimeException(
706 0 : "spurious end tag in " + fileUrl_ );
707 : }
708 12339459 : char const * nameBegin = pos_;
709 12339459 : char const * nameColon = 0;
710 24678918 : if (!scanName(&nameColon) ||
711 12339459 : !elements_.top().name.equals(nameBegin, pos_ - nameBegin))
712 : {
713 : throw css::uno::RuntimeException(
714 0 : "tag mismatch in " + fileUrl_ );
715 : }
716 12339459 : handleElementEnd();
717 12339459 : skipSpace();
718 12339459 : if (peek() != '>') {
719 : throw css::uno::RuntimeException(
720 0 : "missing '>' in " + fileUrl_ );
721 : }
722 12339459 : ++pos_;
723 12339459 : return RESULT_END;
724 : }
725 :
726 13585816 : void XmlReader::handleElementEnd() {
727 : assert(!elements_.empty());
728 13585816 : namespaces_.resize(elements_.top().inheritedNamespaces);
729 13585816 : elements_.pop();
730 13585816 : state_ = elements_.empty() ? STATE_DONE : STATE_CONTENT;
731 13585816 : }
732 :
733 20974294 : XmlReader::Result XmlReader::handleSkippedText(Span * data, int * nsId) {
734 : for (;;) {
735 20974294 : sal_Int32 i = rtl_str_indexOfChar_WithLength(pos_, end_ - pos_, '<');
736 20974294 : if (i < 0) {
737 : throw css::uno::RuntimeException(
738 0 : "premature end of " + fileUrl_ );
739 : }
740 20974294 : pos_ += i + 1;
741 20974294 : switch (peek()) {
742 : case '!':
743 2370 : ++pos_;
744 2370 : if (!skipComment() && !scanCdataSection().is()) {
745 0 : skipDocumentTypeDeclaration();
746 : }
747 2370 : break;
748 : case '/':
749 7382950 : ++pos_;
750 7382950 : return handleEndTag();
751 : case '?':
752 10568 : ++pos_;
753 10568 : skipProcessingInstruction();
754 10568 : break;
755 : default:
756 13578406 : return handleStartTag(nsId, data);
757 : }
758 12938 : }
759 : }
760 :
761 3410411 : XmlReader::Result XmlReader::handleRawText(Span * text) {
762 3410411 : pad_.clear();
763 3410411 : for (char const * begin = pos_;;) {
764 83657725 : 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 48000 : pad_.add(begin, pos_ - begin);
778 48000 : pos_ = handleReference(pos_, end_);
779 48000 : begin = pos_;
780 48000 : break;
781 : case '<':
782 3410411 : pad_.add(begin, pos_ - begin);
783 3410411 : ++pos_;
784 3410411 : 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 3403001 : *text = pad_.get();
799 3403001 : ++pos_;
800 3403001 : state_ = STATE_END_TAG;
801 3403001 : return RESULT_TEXT;
802 : case '?':
803 0 : ++pos_;
804 0 : skipProcessingInstruction();
805 0 : begin = pos_;
806 0 : break;
807 : default:
808 7410 : *text = pad_.get();
809 7410 : state_ = STATE_START_TAG;
810 7410 : return RESULT_TEXT;
811 : }
812 0 : break;
813 : default:
814 80199314 : ++pos_;
815 80199314 : break;
816 : }
817 80247314 : }
818 : }
819 :
820 1553508 : XmlReader::Result XmlReader::handleNormalizedText(Span * text) {
821 1553508 : pad_.clear();
822 1553508 : char const * flowBegin = pos_;
823 1553508 : 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 1553508 : Space space = SPACE_START;
828 : for (;;) {
829 5791376 : 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 3000 : switch (space) {
837 : case SPACE_START:
838 : case SPACE_BREAK:
839 1500 : break;
840 : case SPACE_NONE:
841 : case SPACE_SPAN:
842 1500 : space = SPACE_BREAK;
843 1500 : break;
844 : }
845 3000 : ++pos_;
846 3000 : break;
847 : case ' ':
848 37750 : switch (space) {
849 : case SPACE_START:
850 : case SPACE_BREAK:
851 23500 : break;
852 : case SPACE_NONE:
853 14250 : space = SPACE_SPAN;
854 14250 : break;
855 : case SPACE_SPAN:
856 0 : space = SPACE_BREAK;
857 0 : break;
858 : }
859 37750 : ++pos_;
860 37750 : 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 1553508 : ++pos_;
881 1553508 : 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 1553508 : ++pos_;
915 1553508 : pad_.add(flowBegin, flowEnd - flowBegin);
916 1553508 : *text = pad_.get();
917 1553508 : state_ = STATE_END_TAG;
918 1553508 : 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 4197118 : switch (space) {
933 : case SPACE_START:
934 1553508 : flowBegin = pos_;
935 1553508 : break;
936 : case SPACE_NONE:
937 : case SPACE_SPAN:
938 2642360 : break;
939 : case SPACE_BREAK:
940 1250 : pad_.add(flowBegin, flowEnd - flowBegin);
941 1250 : pad_.add(" ");
942 1250 : flowBegin = pos_;
943 1250 : break;
944 : }
945 4197118 : flowEnd = ++pos_;
946 4197118 : space = SPACE_NONE;
947 4197118 : break;
948 : }
949 4237868 : }
950 : }
951 :
952 38310 : int XmlReader::toNamespaceId(NamespaceIris::size_type pos) {
953 : assert(pos <= INT_MAX);
954 38310 : return static_cast< int >(pos);
955 : }
956 :
957 : }
958 :
959 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|