1 mike 1.3 //%/////////////////////////////////////////////////////////////////////////////
|
2 mike 1.1 //
3 // Copyright (c) 2000 The Open Group, BMC Software, Tivoli Systems, IBM
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
15 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18 // DEALINGS IN THE SOFTWARE.
19 //
|
20 mike 1.3 //==============================================================================
|
21 mike 1.1 //
|
22 mike 1.3 // Author: Mike Brasher (mbrasher@bmc.com)
|
23 mike 1.1 //
|
24 mike 1.3 // Modified By:
|
25 mike 1.2 //
|
26 mike 1.3 //%/////////////////////////////////////////////////////////////////////////////
|
27 mike 1.1
28 ////////////////////////////////////////////////////////////////////////////////
29 //
30 // XmlParser
31 //
32 // This file contains a simple non-validating XML parser. Here are
33 // serveral rules for well-formed XML:
34 //
35 // 1. Documents must begin with an XML declaration:
36 //
37 // <?xml version="1.0" standalone="yes"?>
38 //
39 // 2. Comments have the form:
40 //
41 // <!-- blah blah blah -->
42 //
43 // 3. The following entity references are supported:
44 //
45 // & - ampersand
46 // < - less-than
47 // > - greater-than
48 mike 1.1 // " - full quote
49 // &apos - apostrophe
50 //
51 // 4. Element names and attribute names take the following form:
52 //
53 // [A-Za-z_][A-Za-z_0-9-.:]
54 //
55 // 5. Arbitrary data (CDATA) can be enclosed like this:
56 //
57 // <![CDATA[
58 // ...
59 // ]]>
60 //
61 // 6. Element names and attributes names are case-sensitive.
62 //
63 // 7. XmlAttribute values must be delimited by full or half quotes.
64 // XmlAttribute values must be delimited.
65 //
66 // 8. <!DOCTYPE...>
67 //
68 // TODO:
69 mike 1.1 //
70 // Handle <!DOCTYPE...> sections which are complicated (containing
71 // rules rather than references to files).
72 //
73 // Handle reference of this form: "Α"
74 //
75 // Remove newlines from string literals:
76 //
77 // Example: <xyz x="hello
78 // world">
79 //
80 ////////////////////////////////////////////////////////////////////////////////
81
82 #include <cctype>
83 #include <cassert>
84 #include <cstdio>
85 #include <cstdlib>
86 #include <cstring>
87 #include "XmlParser.h"
88
89 PEGASUS_NAMESPACE_BEGIN
90 mike 1.1
91 ////////////////////////////////////////////////////////////////////////////////
92 //
93 // Static helper functions
94 //
95 ////////////////////////////////////////////////////////////////////////////////
96
97 static void _printValue(const char* p)
98 {
99 for (; *p; p++)
100 {
101 if (*p == '\n')
|
102 mike 1.7 PEGASUS_STD(cout) << "\\n";
|
103 mike 1.1 else if (*p == '\r')
|
104 mike 1.7 PEGASUS_STD(cout) << "\\r";
|
105 mike 1.1 else if (*p == '\t')
|
106 mike 1.7 PEGASUS_STD(cout) << "\\t";
|
107 mike 1.1 else
|
108 mike 1.7 PEGASUS_STD(cout) << *p;
|
109 mike 1.1 }
110 }
111
112 struct EntityReference
113 {
114 const char* match;
115 Uint32 length;
116 char replacement;
117 };
118
119 static EntityReference _references[] =
120 {
121 { "&", 5, '&' },
122 { "<", 4, '<' },
123 { ">", 4, '>' },
124 { """, 6, '"' },
125 { "'", 6, '\'' }
126 };
127
128 static Uint32 _REFERENCES_SIZE = (sizeof(_references) / sizeof(_references[0]));
129
130 mike 1.1 // Remove all redundant spaces from the given string:
131
132 static void _normalize(char* text)
133 {
134 Uint32 length = strlen(text);
135 char* p = text;
136 char* end = p + length;
137
138 // Remove leading spaces:
139
140 while (isspace(*p))
141 p++;
142
143 if (p != text)
144 memmove(text, p, end - p + 1);
145
146 p = text;
147
148 // Look for sequences of more than one space and remove all but one.
149
150 for (;;)
151 mike 1.1 {
152 // Advance to the next space:
153
154 while (*p && !isspace(*p))
155 p++;
156
157 if (!*p)
158 break;
159
160 // Advance to the next non-space:
161
162 char* q = p++;
163
164 while (isspace(*p))
165 p++;
166
167 // Discard trailing spaces (if we are at the end):
168
169 if (!*p)
170 {
171 *q = '\0';
172 mike 1.1 break;
173 }
174
175 // Remove the redundant spaces:
176
177 Uint32 n = p - q;
178
179 if (n > 1)
180 {
181 *q++ = ' ';
182 memmove(q, p, end - p + 1);
183 p = q;
184 }
185 }
186 }
187
188 ////////////////////////////////////////////////////////////////////////////////
189 //
190 // XmlException
191 //
192 ////////////////////////////////////////////////////////////////////////////////
193 mike 1.1
194 static char* _xmlMessages[] =
195 {
196 "Bad opening element",
197 "Bad closing element",
198 "Bad attribute name",
199 "Exepected equal sign",
200 "Bad attribute value",
201 "A \"--\" sequence found within comment",
202 "Unterminated comment",
203 "Unterminated CDATA block",
204 "Unterminated DOCTYPE",
205 "Too many attributes: parser only handles 10",
206 "Malformed reference",
207 "Expected a comment or CDATA following \"<!\" sequence",
208 "Closing element does not match opening element",
209 "One or more tags are still open",
210 "More than one root element was encountered",
211 "Validation error",
212 "Semantic error"
213 };
214 mike 1.1
215 static String _formMessage(Uint32 code, Uint32 line, const String& message)
216 {
217 String result = _xmlMessages[Uint32(code) - 1];
218
219 char buffer[32];
220 sprintf(buffer, "%d", line);
221 result.append(": on line ");
222 result.append(buffer);
223
|
224 mike 1.4 if (message.size())
|
225 mike 1.1 {
226 result.append(": ");
227 result.append(message);
228 }
229
230 return result;
231 }
232
233 XmlException::XmlException(
234 XmlException::Code code,
235 Uint32 lineNumber,
236 const String& message)
237 : Exception(_formMessage(code, lineNumber, message))
238 {
239
240 }
241
242 ////////////////////////////////////////////////////////////////////////////////
243 //
244 // XmlValidationError
245 //
246 mike 1.1 ////////////////////////////////////////////////////////////////////////////////
247
248 XmlValidationError::XmlValidationError(
249 Uint32 lineNumber,
250 const String& message)
251 : XmlException(XmlException::VALIDATION_ERROR, lineNumber, message)
252 {
253
254 }
255
256 ////////////////////////////////////////////////////////////////////////////////
257 //
258 // XmlSemanticError
259 //
260 ////////////////////////////////////////////////////////////////////////////////
261
262 XmlSemanticError::XmlSemanticError(
263 Uint32 lineNumber,
264 const String& message)
|
265 sage 1.5 : XmlException(XmlException::SEMANTIC_ERROR, lineNumber, message)
|
266 mike 1.1 {
267
268 }
269
270 ////////////////////////////////////////////////////////////////////////////////
271 //
272 // XmlParser
273 //
274 ////////////////////////////////////////////////////////////////////////////////
275
276 XmlParser::XmlParser(char* text) : _line(1), _text(text), _current(text),
277 _restoreChar('\0'), _foundRoot(false)
278 {
279
280 }
281
282 Boolean XmlParser::next(XmlEntry& entry)
283 {
284 if (!_putBackStack.isEmpty())
285 {
286 entry = _putBackStack.top();
287 mike 1.1 _putBackStack.pop();
288 return true;
289 }
290
291 // If a character was overwritten with a null-terminator the last
292 // time this routine was called, then put back that character. Before
293 // exiting of course, restore the null-terminator.
294
295 char* nullTerminator = 0;
296
297 if (_restoreChar && !*_current)
298 {
299 nullTerminator = _current;
300 *_current = _restoreChar;
301 _restoreChar = '\0';
302 }
303
304 // Skip over any whitespace:
305
306 _skipWhitespace(_current);
307
308 mike 1.1 if (!*_current)
309 {
310 if (nullTerminator)
311 *nullTerminator = '\0';
312
313 if (!_stack.isEmpty())
314 throw XmlException(XmlException::UNCLOSED_TAGS, _line);
315
316 return false;
317 }
318
319 // Either a "<...>" or content begins next:
320
321 if (*_current == '<')
322 {
323 _current++;
324 _getElement(_current, entry);
325
326 if (nullTerminator)
327 *nullTerminator = '\0';
328
329 mike 1.1 if (entry.type == XmlEntry::START_TAG)
330 {
331 if (_stack.isEmpty() && _foundRoot)
332 throw XmlException(XmlException::MULTIPLE_ROOTS, _line);
333
334 _foundRoot = true;
335 _stack.push((char*)entry.text);
336 }
337 else if (entry.type == XmlEntry::END_TAG)
338 {
339 if (_stack.isEmpty())
340 throw XmlException(XmlException::START_END_MISMATCH, _line);
341
342 if (strcmp(_stack.top(), entry.text) != 0)
343 throw XmlException(XmlException::START_END_MISMATCH, _line);
344
345 _stack.pop();
346 }
347
348 return true;
349 }
350 mike 1.1 else
351 {
352 entry.type = XmlEntry::CONTENT;
353 entry.text = _current;
354 _getContent(_current);
355 _restoreChar = *_current;
356 *_current = '\0';
357
358 if (nullTerminator)
359 *nullTerminator = '\0';
360
361 _substituteReferences((char*)entry.text);
362 _normalize((char*)entry.text);
363
364 return true;
365 }
366 }
367
368 void XmlParser::putBack(XmlEntry& entry)
369 {
370 _putBackStack.push(entry);
371 mike 1.1 }
372
373 XmlParser::~XmlParser()
374 {
375 // Nothing to do!
376 }
377
378 void XmlParser::_skipWhitespace(char*& p)
379 {
380 while (*p && isspace(*p))
381 {
382 if (*p == '\n')
383 _line++;
384
385 p++;
386 }
387 }
388
389 Boolean XmlParser::_getElementName(char*& p)
390 {
391 if (!isalpha(*p) && *p != '_')
392 mike 1.1 throw XmlException(XmlException::BAD_START_TAG, _line);
393
394 while (*p &&
395 (isalnum(*p) || *p == '_' || *p == '-' || *p == ':' || *p == '.'))
396 p++;
397
398 // The next character must be a space:
399
400 if (isspace(*p))
401 {
402 *p++ = '\0';
403 _skipWhitespace(p);
404 }
405
406 if (*p == '>')
407 {
408 *p++ = '\0';
409 return true;
410 }
411
412 return false;
413 mike 1.1 }
414
415 Boolean XmlParser::_getOpenElementName(char*& p, Boolean& openCloseElement)
416 {
417 openCloseElement = false;
418
419 if (!isalpha(*p) && *p != '_')
420 throw XmlException(XmlException::BAD_START_TAG, _line);
421
422 while (*p &&
423 (isalnum(*p) || *p == '_' || *p == '-' || *p == ':' || *p == '.'))
424 p++;
425
426 // The next character must be a space:
427
428 if (isspace(*p))
429 {
430 *p++ = '\0';
431 _skipWhitespace(p);
432 }
433
434 mike 1.1 if (*p == '>')
435 {
436 *p++ = '\0';
437 return true;
438 }
439
440 if (p[0] == '/' && p[1] == '>')
441 {
442 openCloseElement = true;
443 *p = '\0';
444 p += 2;
445 return true;
446 }
447
448 return false;
449 }
450
451 void XmlParser::_getAttributeNameAndEqual(char*& p)
452 {
453 if (!isalpha(*p) && *p != '_')
454 throw XmlException(XmlException::BAD_ATTRIBUTE_NAME, _line);
455 mike 1.1
456 while (*p &&
457 (isalnum(*p) || *p == '_' || *p == '-' || *p == ':' || *p == '.'))
458 p++;
459
460 char* term = p;
461
462 _skipWhitespace(p);
463
464 if (*p != '=')
465 throw XmlException(XmlException::BAD_ATTRIBUTE_NAME, _line);
466
467 p++;
468
469 _skipWhitespace(p);
470
471 *term = '\0';
472 }
473
474 void XmlParser::_getAttributeValue(char*& p)
475 {
476 mike 1.1 // ATTN-B: handle values contained in semiquotes:
477
478 if (*p != '"' && *p != '\'')
479 throw XmlException(XmlException::BAD_ATTRIBUTE_VALUE, _line);
480
481 char startChar = *p++;
482
483 while (*p && *p != startChar)
484 p++;
485
486 if (*p != startChar)
487 throw XmlException(XmlException::BAD_ATTRIBUTE_VALUE, _line);
488
489 *p++ = '\0';
490 }
491
492 void XmlParser::_getComment(char*& p)
493 {
494 // Now p points to first non-whitespace character beyond "<--" sequence:
495
496 for (; *p; p++)
497 mike 1.1 {
498 if (p[0] == '-' && p[1] == '-')
499 {
500 if (p[2] != '>')
501 {
502 throw XmlException(
503 XmlException::MINUS_MINUS_IN_COMMENT, _line);
504 }
505
506 // Find end of comment (excluding whitespace):
507
508 *p = '\0';
509 p += 3;
510 return;
511 }
512 }
513
514 // If it got this far, then the comment is unterminated:
515
516 throw XmlException(XmlException::UNTERMINATED_COMMENT, _line);
517 }
518 mike 1.1
519 void XmlParser::_getCData(char*& p)
520 {
521 // At this point p points one past "<![CDATA[" sequence:
522
523 for (; *p; p++)
524 {
525 if (p[0] == ']' && p[1] == ']' && p[2] == '>')
526 {
527 *p = '\0';
528 p += 3;
529 return;
530 }
531 else if (*p == '\n')
532 _line++;
533 }
534
535 // If it got this far, then the comment is unterminated:
536
537 throw XmlException(XmlException::UNTERMINATED_CDATA, _line);
538 }
539 mike 1.1
540 void XmlParser::_getDocType(char*& p)
541 {
542 // Just ignore the DOCTYPE command for now:
543
544 for (; *p && *p != '>'; p++)
545 {
546 if (*p == '\n')
547 _line++;
548 }
549
550 if (*p != '>')
551 throw XmlException(XmlException::UNTERMINATED_DOCTYPE, _line);
552
553 p++;
554 }
555
556 void XmlParser::_getContent(char*& p)
557 {
558 while (*p && *p != '<')
559 {
560 mike 1.1 if (*p == '\n')
561 _line++;
562
563 p++;
564 }
565 }
566
567 void XmlParser::_substituteReferences(char* text)
568 {
569 Uint32 rem = strlen(text);
570
571 for (char* p = text; *p; p++, rem--)
572 {
573 if (*p == '&')
574 {
575 // Look for predefined entity reference:
576
577 Boolean found = false;
578
579 for (Uint32 i = 0; i < _REFERENCES_SIZE; i++)
580 {
581 mike 1.1 Uint32 length = _references[i].length;
582 const char* match = _references[i].match;
583
584 if (strncmp(p, _references[i].match, length) == 0)
585 {
586 found = true;
587 *p = _references[i].replacement;
588 char* q = p + length;
589 rem = rem - length + 1;
590 memmove(p + 1, q, rem);
591 }
592 }
593
594 // If not found, then at least make sure it is well formed:
595
596 if (!found)
597 {
598 char* start = p;
599 p++;
600
601 XmlException::Code code = XmlException::MALFORMED_REFERENCE;
602 mike 1.1
603 if (isalpha(*p) || *p == '_')
604 {
605 for (p++; *p && *p != ';'; p++)
606 {
607 if (!isalnum(*p) && *p != '_')
608 throw XmlException(code, _line);
609 }
610 }
611 else if (*p == '#')
612 {
613 for (p++ ; *p && *p != ';'; p++)
614 {
615 if (!isdigit(*p))
616 throw XmlException(code, _line);
617 }
618 }
619
620 if (*p != ';')
621 throw XmlException(code, _line);
622
623 mike 1.1 rem -= p - start;
624 }
625 }
626 }
627 }
628
629 static const char _EMPTY_STRING[] = "";
630
631 void XmlParser::_getElement(char*& p, XmlEntry& entry)
632 {
633 entry.attributeCount = 0;
634
635 //--------------------------------------------------------------------------
636 // Get the element name (expect one of these: '?', '!', [A-Za-z_])
637 //--------------------------------------------------------------------------
638
639 if (*p == '?')
640 {
641 entry.type = XmlEntry::XML_DECLARATION;
642 entry.text = ++p;
643
644 mike 1.1 Boolean openCloseElement = false;
645
646 if (_getElementName(p))
647 return;
648 }
649 else if (*p == '!')
650 {
651 p++;
652
653 // Expect a comment or CDATA:
654
655 if (p[0] == '-' && p[1] == '-')
656 {
657 p += 2;
658 entry.type = XmlEntry::COMMENT;
659 entry.text = p;
660 _getComment(p);
661 return;
662 }
663 else if (memcmp(p, "[CDATA[", 7) == 0)
664 {
665 mike 1.1 p += 7;
666 entry.type = XmlEntry::CDATA;
667 entry.text = p;
668 _getCData(p);
669 return;
670 }
671 else if (memcmp(p, "DOCTYPE", 7) == 0)
672 {
673 entry.type = XmlEntry::DOCTYPE;
674 entry.text = _EMPTY_STRING;
675 _getDocType(p);
676 return;
677 }
678 throw(XmlException(XmlException::EXPECTED_COMMENT_OR_CDATA, _line));
679 }
680 else if (*p == '/')
681 {
682 entry.type = XmlEntry::END_TAG;
683 entry.text = ++p;
684
685 if (!_getElementName(p))
686 mike 1.1 throw(XmlException(XmlException::BAD_END_TAG, _line));
687
688 return;
689 }
690 else if (isalpha(*p) || *p == '_')
691 {
692 entry.type = XmlEntry::START_TAG;
693 entry.text = p;
694
695 Boolean openCloseElement = false;
696
697 if (_getOpenElementName(p, openCloseElement))
698 {
699 if (openCloseElement)
700 entry.type = XmlEntry::EMPTY_TAG;
701 return;
702 }
703 }
704 else
705 throw XmlException(XmlException::BAD_START_TAG, _line);
706
707 mike 1.1 //--------------------------------------------------------------------------
708 // Grab all the attributes:
709 //--------------------------------------------------------------------------
710
711 for (;;)
712 {
713 if (entry.type == XmlEntry::XML_DECLARATION)
714 {
715 if (p[0] == '?' && p[1] == '>')
716 {
717 p += 2;
718 return;
719 }
720 }
721 else if (entry.type == XmlEntry::START_TAG && p[0] == '/' && p[1] =='>')
722 {
723 entry.type = XmlEntry::EMPTY_TAG;
724 p += 2;
725 return;
726 }
727 else if (*p == '>')
728 mike 1.1 {
729 p++;
730 return;
731 }
732
733 XmlAttribute attr;
734 attr.name = p;
735 _getAttributeNameAndEqual(p);
736
737 if (*p != '"' && *p != '\'')
738 throw XmlException(XmlException::BAD_ATTRIBUTE_VALUE, _line);
739
740 attr.value = p + 1;
741 _getAttributeValue(p);
742
743 if (entry.type == XmlEntry::XML_DECLARATION)
744 {
745 // The next thing must a space or a "?>":
746
747 if (!(p[0] == '?' && p[1] == '>') && !isspace(*p))
748 {
749 mike 1.1 throw XmlException(
750 XmlException::BAD_ATTRIBUTE_VALUE, _line);
751 }
752 }
753 else if (!(*p == '>' || (p[0] == '/' && p[1] == '>') || isspace(*p)))
754 {
755 // The next thing must be a space or a '>':
756
757 throw XmlException(XmlException::BAD_ATTRIBUTE_VALUE, _line);
758 }
759
760 _skipWhitespace(p);
761
762 if (entry.attributeCount == XmlEntry::MAX_ATTRIBUTES)
763 throw XmlException(XmlException::TOO_MANY_ATTRIBUTES, _line);
764
765 _substituteReferences((char*)attr.value);
766 entry.attributes[entry.attributeCount++] = attr;
767 }
768 }
769
770 mike 1.1 static const char* _typeStrings[] =
771 {
772 "XML_DECLARATION",
773 "START_TAG",
774 "EMPTY_TAG",
775 "END_TAG",
776 "COMMENT",
777 "CDATA",
778 "DOCTYPE",
779 "CONTENT"
780 };
781
782 void XmlEntry::print() const
783 {
|
784 mike 1.7 PEGASUS_STD(cout) << "=== " << _typeStrings[type] << " ";
|
785 mike 1.1
786 Boolean needQuotes = type == XmlEntry::CDATA || type == XmlEntry::CONTENT;
787
788 if (needQuotes)
|
789 mike 1.7 PEGASUS_STD(cout) << "\"";
|
790 mike 1.1
791 _printValue(text);
792
793 if (needQuotes)
|
794 mike 1.7 PEGASUS_STD(cout) << "\"";
|
795 mike 1.1
|
796 mike 1.7 PEGASUS_STD(cout) << '\n';
|
797 mike 1.1
798 for (Uint32 i = 0; i < attributeCount; i++)
799 {
|
800 mike 1.7 PEGASUS_STD(cout) << " " << attributes[i].name << "=\"";
|
801 mike 1.1 _printValue(attributes[i].value);
|
802 mike 1.7 PEGASUS_STD(cout) << "\"" << PEGASUS_STD(endl);
|
803 mike 1.1 }
804 }
805
806 const XmlAttribute* XmlEntry::findAttribute(
807 const char* name) const
808 {
809 for (Uint32 i = 0; i < attributeCount; i++)
810 {
811 if (strcmp(attributes[i].name, name) == 0)
812 return &attributes[i];
813 }
814
815 return 0;
816 }
817
818 // Find first non-whitespace character (set first) and last non-whitespace
819 // character (set last one past this). For example, consider this string:
820 //
821 // " 87 "
822 //
823 // The first pointer would point to '8' and the last pointer woudl point one
824 mike 1.1 // beyond '7'.
825
826 static void _findEnds(
827 const char* str,
828 const char*& first,
829 const char*& last)
830 {
831 first = str;
832
833 while (isspace(*first))
834 first++;
835
836 if (!*first)
837 {
838 last = first;
839 return;
840 }
841
842 last = first + strlen(first);
843
844 while (last != first && isspace(last[-1]))
845 mike 1.1 last--;
846 }
847
848 Boolean XmlEntry::getAttributeValue(
849 const char* name,
850 Uint32& value) const
851 {
852 const XmlAttribute* attr = findAttribute(name);
853
854 if (!attr)
855 return false;
856
857 const char* first;
858 const char* last;
859 _findEnds(attr->value, first, last);
860
861 char* end = 0;
862 long tmp = strtol(first, &end, 10);
863
864 if (!end || end != last)
865 return false;
866 mike 1.1
867 value = Uint32(tmp);
868 return true;
869 }
870
871 Boolean XmlEntry::getAttributeValue(
872 const char* name,
873 Real32& value) const
874 {
875 const XmlAttribute* attr = findAttribute(name);
876
877 if (!attr)
878 return false;
879
880 const char* first;
881 const char* last;
882 _findEnds(attr->value, first, last);
883
884 char* end = 0;
885 double tmp = strtod(first, &end);
886
887 mike 1.1 if (!end || end != last)
888 return false;
889
890 value = Uint32(tmp);
891 return true;
892 }
893
894 Boolean XmlEntry::getAttributeValue(
895 const char* name,
896 const char*& value) const
897 {
898 const XmlAttribute* attr = findAttribute(name);
899
900 if (!attr)
901 return false;
902
903 value = attr->value;
904 return true;
905 }
906
907 Boolean XmlEntry::getAttributeValue(const char* name, String& value) const
908 mike 1.1 {
909 const char* tmp;
910
911 if (!getAttributeValue(name, tmp))
912 return false;
913
914 value = tmp;
915 return true;
916 }
917
|
918 mike 1.6 void XmlAppendCString(Array<Sint8>& out, const char* str)
919 {
920 out.append(str, strlen(str));
921 }
922
|
923 mike 1.1 PEGASUS_NAMESPACE_END
|