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