|
Teuchos Package Browser (Single Doxygen Collection)
Version of the Day
|
00001 // @HEADER 00002 // *********************************************************************** 00003 // 00004 // Teuchos: Common Tools Package 00005 // Copyright (2004) Sandia Corporation 00006 // 00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive 00008 // license for use of this work by or on behalf of the U.S. Government. 00009 // 00010 // Redistribution and use in source and binary forms, with or without 00011 // modification, are permitted provided that the following conditions are 00012 // met: 00013 // 00014 // 1. Redistributions of source code must retain the above copyright 00015 // notice, this list of conditions and the following disclaimer. 00016 // 00017 // 2. Redistributions in binary form must reproduce the above copyright 00018 // notice, this list of conditions and the following disclaimer in the 00019 // documentation and/or other materials provided with the distribution. 00020 // 00021 // 3. Neither the name of the Corporation nor the names of the 00022 // contributors may be used to endorse or promote products derived from 00023 // this software without specific prior written permission. 00024 // 00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY 00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE 00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00036 // 00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 00038 // 00039 // *********************************************************************** 00040 // @HEADER 00041 00042 // BUGS: There is a bug in Teuchos_XMLObjectImplem.cpp, line 82 00043 // when printing attribute values, one must check if the value contains quote 00044 // or apost; 00045 // a quot'd attval cannot contain literal quot 00046 // a apos'd attval cannot contain literal apos 00047 // either they have to be matched appropriately or (easier) all quot and apos must 00048 // be replaced by " and ' 00049 00050 #include "Teuchos_XMLParser.hpp" 00051 #include "Teuchos_TreeBuildingXMLHandler.hpp" 00052 #include "Teuchos_Assert.hpp" 00053 #include <stack> 00054 00055 using namespace Teuchos; 00056 00057 // this parser currently does not support: 00058 // * processing instructions 00059 // * XML schemas 00060 // * CDATA sections...see http://www.w3.org/TR/2004/REC-xml-20040204/#dt-cdsection 00061 // * full Unicode support (we read unsigned bytes, so we get only 0x00 through 0xFF) 00062 // 00063 // it tolerates (read: ignores) xml declarations, at any point in the file where a tag would be valid 00064 // 00065 // it currently does support: 00066 // * comments 00067 // * empty element tags, e.g. <hello /> 00068 // * entity references: & < > ' " 00069 // * numeric character references:   00070 // * std::exception/error handling on parse errors 00071 00072 00073 /* From the W3C XML 1.0 Third Edition 00074 http://www.w3.org/TR/2004/REC-xml-20040204/ 00075 00076 The following productions specify well-formed XML documents. 00077 These have been reduced to the support anticipated for support by this parser. 00078 00079 element ::= EmptyElemTag 00080 | STag content ETag 00081 STag ::= '<' Name (S Attribute)* S? '>' 00082 Attribute ::= Name Eq AttValue 00083 ETag ::= '</' Name S? '>' 00084 content ::= CharData? ((element | Reference | CDSect | Comment) CharData?)* 00085 EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' 00086 00087 AttValue ::= '"' ([^<&"] | Reference)* '"' 00088 | "'" ([^<&'] | Reference)* "'" 00089 00090 CharRef ::= '&#' [0-9]+ ';' 00091 EntityRef ::= '&' Name ';' 00092 Reference ::= EntityRef | CharRef 00093 00094 #x20 (space) 00095 #x9 (horizontal tab) 00096 #xD (carriage return) 00097 #xA (new line, new line line feed) 00098 00099 S ::= (#x20 | #x9 | #xD | #xA)+ 00100 Eq ::= S? '=' S? 00101 NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | #x00B7 00102 Name ::= (Letter | '_' | ':') (NameChar)* 00103 00104 Letter ::= [#x0041-#x005A] | [#x0061-#x007A] 00105 | [#x00C0-#x00D6] | [#x00D8-#x00F6] 00106 | [#x00F8-#x00FF] 00107 Digit ::= [#x0030-#x0039] 00108 00109 Char ::= #x9 | #xA | #xD | [#x20-#xFF] 00110 CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) 00111 that is, some std::string of characters not containing '<' or '&' or ']]>' 00112 Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' 00113 that is, '<!--' txt '-->', where txt does not contain '--' 00114 00115 CDSect ::= CDStart CData CDEnd 00116 CDStart ::= '<![CDATA[' 00117 CData ::= (Char* - (Char* ']]>' Char*)) 00118 CDEnd ::= ']]>' 00119 00120 document ::= prolog element Misc* 00121 prolog ::= XMLDecl? Misc* 00122 XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' 00123 Misc ::= Comment | S 00124 00125 VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') 00126 Eq ::= S? '=' S? 00127 VersionNum ::= '1.' [0-9]+ 00128 Misc ::= Comment | S 00129 00130 00131 00132 */ 00133 00134 #define XMLPARSER_TFE( T , S ) \ 00135 TEUCHOS_TEST_FOR_EXCEPTION( T, std::runtime_error, "XML parse error at line " << _lineNo << ": " << S ) 00136 00137 XMLObject XMLParser::parse() 00138 { 00139 00140 RCP<TreeBuildingXMLHandler> handler = rcp(new TreeBuildingXMLHandler()); 00141 00142 _entities.clear(); 00143 _entities["apos"] = "'"; 00144 _entities["quot"] = "\""; 00145 _entities["lt"] = "<"; 00146 _entities["gt"] = ">"; 00147 _entities["amp"] = "&"; 00148 00149 bool done = false; 00150 int curopen = 0; // number of currently open tags, or "do we process character data?" 00151 bool gotRoot = false; 00152 std::stack<long> tagLineStarts; 00153 std::stack<string> tags; 00154 00155 while (!done) { 00156 00157 std::string tag, cdata; 00158 unsigned char c1, c2; 00159 Teuchos::map<std::string,string> attrs; 00160 00161 // Consume any whitespace 00162 if (curopen == 0) { 00163 // this will leave a lookahead in c1 00164 c1 = '\0'; 00165 if ( getSpace(c1) ) { 00166 done = true; 00167 break; 00168 } 00169 } 00170 else { 00171 // need to manually lookahead 00172 if (_is->readBytes(&c1,1) < 1) { 00173 done = true; 00174 break; 00175 } 00176 if (c1 == '\n') ++_lineNo; // a newline while processing character data; not an error 00177 } 00178 00179 if (c1 == '<') { 00180 // determine if it is a STag/EmptyElemTag or ETag or Comment 00181 // get lookahead 00182 XMLPARSER_TFE( _is->readBytes(&c2,1) < 1 , "stream ended in tag begin/end"); 00183 00184 if (c2 == '/') { 00185 // we have: </ 00186 // try to get an ETag 00187 getETag(tag); 00188 // have to check whether we have an enclosing, otherwise tags and tagLineStarts have no top() 00189 XMLPARSER_TFE( curopen == 0, "document not well-formed: encountered end element '" << tag << "' while not enclosed." ); 00190 XMLPARSER_TFE( handler->endElement(tag)!=0, "document not well-formed: end element tag = '" << tag << "'" 00191 << " did not match start element '" << tags.top() 00192 << "' from line " << tagLineStarts.top() ); 00193 curopen--; 00194 tagLineStarts.pop(); 00195 tags.pop(); 00196 } 00197 else if (isLetter(c2) || c2==':' || c2=='_') { 00198 // it looks like a STag or an EmptyElemTag 00199 bool emptytag; 00200 tagLineStarts.push(_lineNo); 00201 getSTag(c2, tag, attrs, emptytag); 00202 tags.push(tag); 00203 handler->startElement(tag,attrs); 00204 if (curopen == 0) { 00205 XMLPARSER_TFE(gotRoot == true, "document not well-formed: more than one root element specified" ); 00206 gotRoot = true; 00207 } 00208 curopen++; 00209 if (emptytag) { 00210 // we just open this tag, so we should have any trouble closing it 00211 XMLPARSER_TFE( handler->endElement(tag)!=0, "unknown failure from handler while processing tag '" << tag << "'" ); 00212 curopen--; 00213 tagLineStarts.pop(); 00214 tags.pop(); 00215 } 00216 } 00217 else if (c2 == '?') { 00218 // it is starting to look like an xml declaration 00219 XMLPARSER_TFE( assertChar('x') != 0 , "was expecting an XML declaration; element not well-formed or exploits unsupported feature" ); 00220 XMLPARSER_TFE( assertChar('m') != 0 , "was expecting an XML declaration; element not well-formed or exploits unsupported feature" ); 00221 XMLPARSER_TFE( assertChar('l') != 0 , "was expecting an XML declaration; element not well-formed or exploits unsupported feature" ); 00222 ignoreXMLDeclaration(); 00223 } 00224 else if (c2 == '!') { 00225 // it is starting to look like a comment; we need '--' 00226 // if we don't get this, it means 00227 // * the document is not well-formed 00228 // * the document employs a feature not supported by this parser, 00229 // e.g. <!ELEMENT... <!ATTLIST... <!DOCTYPE... <![CDATA[... 00230 XMLPARSER_TFE( assertChar('-') != 0 , "element not well-formed or exploits unsupported feature" ); 00231 XMLPARSER_TFE( assertChar('-') != 0 , "element not well-formed or exploits unsupported feature" ); 00232 getComment(_lineNo); 00233 } 00234 else { 00235 XMLPARSER_TFE(true, "element not well-formed or exploits unsupported feature" ); 00236 } 00237 } 00238 else if ( (curopen > 0) && (c1 == '&') ) { 00239 std::string chars = ""; 00240 getReference(chars); 00241 handler->characters(chars); 00242 } 00243 else if ( (curopen > 0) ) { 00244 std::string chars = ""; 00245 chars.push_back(c1); 00246 handler->characters(chars); 00247 } 00248 else { 00249 XMLPARSER_TFE(1 , "document not well-formed: character data outside of an enclosing tag"); 00250 } 00251 } 00252 00253 XMLPARSER_TFE( curopen != 0 , "file ended before closing element '" << tags.top() << "' from line " << tagLineStarts.top() ); 00254 00255 return handler->getObject(); 00256 00257 } 00258 00259 00260 void XMLParser::getETag(std::string &tag) 00261 { 00262 /* Recall from the specification: 00263 ETag ::= '</' Name S? '>' 00264 Name ::= (Letter | '_' | ':') (NameChar)* 00265 00266 We have already consumed: </ 00267 */ 00268 00269 bool tagover = false; 00270 unsigned char c; 00271 // clear tag 00272 tag = ""; 00273 XMLPARSER_TFE( _is->readBytes(&c,1) < 1 , "EOF before end element was terminated"); 00274 XMLPARSER_TFE( !isLetter(c) && c!='_' && c!=':' , "tag not well-formed"); 00275 tag.push_back(c); 00276 while (1) { 00277 XMLPARSER_TFE( _is->readBytes(&c,1) < 1 , "EOF before end element was terminated"); 00278 if ( isNameChar(c) ) { 00279 if (tagover) { 00280 XMLPARSER_TFE(1, "end element not well-formed: expected '>'"); 00281 } 00282 tag.push_back(c); 00283 } 00284 else if (isSpace(c)) { 00285 // mark the end of the tag and consume the whitespace 00286 // if it is ia newline, it isn't an error 00287 if (c == '\n') ++_lineNo; 00288 tagover = true; 00289 } 00290 else if (c == '>') { 00291 break; 00292 } 00293 else { 00294 XMLPARSER_TFE(1, "end element not well-formed"); 00295 } 00296 } 00297 } 00298 00299 00300 void XMLParser::getSTag(unsigned char lookahead, std::string &tag, Teuchos::map<std::string,string> &attrs, bool &emptytag) 00301 { 00302 00303 /* Recall from the specification: 00304 00305 STag ::= '<' Name (S Attribute)* S? '>' 00306 EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' 00307 Name ::= (Letter | '_' | ':') (NameChar)* 00308 NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | #x00B7 00309 00310 S ::= (#x20 | #x9 | #xD | #xA)+ 00311 Attribute ::= Name Eq AttValue 00312 Eq ::= S? '=' S? 00313 AttValue ::= '"' ([^<&"] | Reference)* '"' 00314 | "'" ([^<&'] | Reference)* "'" 00315 Reference ::= EntityRef | CharRef 00316 CharRef ::= '&#' [0-9]+ ';' 00317 EntityRef ::= '&' Name ';' 00318 00319 We have already consumed: <lookahead 00320 */ 00321 00322 unsigned char c; 00323 attrs.clear(); 00324 00325 tag = lookahead; 00326 // get the rest of the tag: (NameChar)* 00327 while (1) { 00328 XMLPARSER_TFE( _is->readBytes(&c,1) < 1 , "EOF before start element was terminated"); 00329 if (isNameChar(c)) { 00330 tag.push_back(c); 00331 } 00332 else { 00333 break; 00334 } 00335 } 00336 00337 // after the name: should be one of the following 00338 // (S Attribute) | S? '>' | S? '/>' 00339 do { 00340 00341 bool hadspace = false; 00342 00343 // if space, consume the whitespace 00344 if ( isSpace(c) ) { 00345 hadspace = true; 00346 XMLPARSER_TFE( getSpace(c)!=0, "EOF before start element was terminated"); 00347 } 00348 00349 // now, either Attribute | '>' | '/>' 00350 if ( (isLetter(c) || c=='_' || c==':') && hadspace ) { 00351 00352 // Attribute 00353 // get attribute name, starting with contents of c 00354 std::string attname, attval; 00355 attname = c; 00356 do { 00357 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before start element was terminated"); 00358 if ( isNameChar(c) ) { 00359 attname.push_back(c); 00360 } 00361 else if ( isSpace(c) || c=='=' ) { 00362 break; 00363 } 00364 else { 00365 XMLPARSER_TFE(1, "attribute not well-formed: expected whitespace or '='"); 00366 } 00367 } while (1); 00368 00369 // if whitespace, consume it 00370 if (isSpace(c)) { 00371 getSpace(c); 00372 } 00373 // should be on '=' 00374 if (c != '=') { 00375 XMLPARSER_TFE(1, "attribute not well-formed: expected '='"); 00376 } 00377 00378 // get any whitespace following the '=' 00379 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before start element was terminated"); 00380 if (isSpace(c)) { 00381 getSpace(c); 00382 } 00383 00384 // now get the quoted attribute value 00385 bool apost; 00386 attval = ""; 00387 if (c == '\'') { 00388 apost = true; 00389 } 00390 else if (c == '\"') { 00391 apost = false; 00392 } 00393 else { 00394 XMLPARSER_TFE(1, "attribute value must be quoted with either ''' or '\"'"); 00395 } 00396 do { 00397 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before start element was terminated"); 00398 if (apost && c=='\'') { 00399 // end of attval 00400 break; 00401 } 00402 else if (!apost && c=='\"') { 00403 // end of attval 00404 break; 00405 } 00406 else if ( c == '&' ) { 00407 // finish: need to add support for Reference 00408 std::string refstr; 00409 getReference(refstr); 00410 attval += refstr; 00411 } 00412 else if ( c!='<' ) { 00413 // valid character for attval 00414 attval.push_back(c); 00415 } 00416 else { 00417 XMLPARSER_TFE(1, "invalid character in attribute value"); 00418 } 00419 } while(1); 00420 00421 // add attribute to list 00422 XMLPARSER_TFE( attrs.find(attname) != attrs.end() , "cannot have two attributes with the same name"); 00423 attrs[attname] = attval; 00424 } 00425 else if (c == '>') { 00426 emptytag = false; 00427 break; 00428 } 00429 else if (c == '/') { 00430 XMLPARSER_TFE(assertChar('>')!=0, "empty element tag not well-formed: expected '>'"); 00431 emptytag = true; 00432 break; 00433 } 00434 else { 00435 XMLPARSER_TFE(1, "start element not well-formed: invalid character"); 00436 } 00437 00438 // get next char 00439 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before start element was terminated"); 00440 00441 } while(1); 00442 } 00443 00444 00445 void XMLParser::getComment(long startLine) 00446 { 00447 /* Recall from the specification: 00448 Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' 00449 that is, '<!--' txt '-->', where txt does not contain '--' 00450 We have already consumed: <!-- 00451 00452 Be wary here of the fact that c=='-' implies isChar(c) 00453 */ 00454 unsigned char c; 00455 while (1) { 00456 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before terminating comment begun at line " << _lineNo ); 00457 if (c == '\n') ++_lineNo; 00458 // if we have a - 00459 if (c=='-') { 00460 // then it must be the end of the comment or be a Char 00461 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before terminating comment begun at line " << _lineNo ); 00462 if (c == '\n') ++_lineNo; 00463 if (c=='-') { 00464 // this had better be leading to the end of the comment 00465 XMLPARSER_TFE( assertChar('>')!=0, "comment not well-formed: missing expected '>' at line " << _lineNo ); 00466 break; 00467 } 00468 else if (!isChar(c)) { 00469 XMLPARSER_TFE(1, "comment not well-formed: invalid character at line " << _lineNo ); 00470 } 00471 } 00472 else if (!isChar(c)) { 00473 XMLPARSER_TFE(1, "comment not well-formed: invalid character at line " << _lineNo ); 00474 } 00475 } 00476 } 00477 00478 00479 void XMLParser::getReference(std::string &refstr) { 00480 // finish: does CharRef support only dec, or hex as well? 00481 unsigned char c; 00482 unsigned int num, base; 00483 refstr = ""; 00484 // none of these bytes read are allowed to be a newline, so don't do any incrementing of _lineNo 00485 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before reference was terminated"); 00486 if (c == '#') { 00487 // get a CharRef 00488 // CharRef ::= '&#' [0-9]+ ';' 00489 // | '&#x' [0-9]+ ';' 00490 // get first number 00491 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before reference was terminated"); 00492 if (c == 'x') { 00493 base = 16; 00494 num = 0; 00495 } 00496 else if ('0' <= c && c <= '9') { 00497 base = 10; 00498 num = c - '0'; 00499 } 00500 else { 00501 XMLPARSER_TFE(1, "invalid character in character reference: expected 'x' or [0-9]"); 00502 } 00503 00504 do { 00505 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before reference was terminated"); 00506 XMLPARSER_TFE( c != ';' && !('0' <= c && c <= '9') , "invalid character in character reference: expected [0-9] or ';'"); 00507 if (c == ';') { 00508 break; 00509 } 00510 num = num*base + (c-'0'); 00511 } while (1); 00512 XMLPARSER_TFE(num > 0xFF, "character reference value out of range"); 00513 refstr.push_back( (unsigned char)num ); 00514 } 00515 else if (isLetter(c) || c=='_' || c==':') { 00516 // get an EntityRef 00517 // EntityRef ::= '&' Name ';' 00518 std::string entname = ""; 00519 entname.push_back(c); 00520 do { 00521 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before reference was terminated"); 00522 if (c==';') { 00523 break; 00524 } 00525 else if ( isLetter(c) || ('0' <= c && c <= '9') 00526 || c=='.' || c=='-' || c=='_' || c==':' 00527 || c==0xB7 ) { 00528 entname.push_back(c); 00529 } 00530 else { 00531 XMLPARSER_TFE(1, "entity reference not well-formed: invalid character"); 00532 } 00533 } while (1); 00534 XMLPARSER_TFE( _entities.find(entname) == _entities.end(), "entity reference not well-formed: undefined entity"); 00535 refstr = _entities[entname]; 00536 } 00537 else { 00538 XMLPARSER_TFE(1, "reference not well-formed: expected name or '#'"); 00539 } 00540 } 00541 00542 00543 int XMLParser::getSpace(unsigned char &lookahead) { 00544 // if space, consume the whitespace 00545 do { 00546 if (lookahead == '\n') ++_lineNo; 00547 if (_is->readBytes(&lookahead,1) < 1) { 00548 return 1; // inform caller that we reached the end 00549 } 00550 } 00551 while (isSpace(lookahead)); 00552 return 0; 00553 } 00554 00555 00556 bool XMLParser::isLetter(unsigned char c) { 00557 if ( (0x41 <= c && c <= 0x5A) || (0x61 <= c && c <= 0x7A) || 00558 (0xC0 <= c && c <= 0xD6) || (0xD8 <= c && c <= 0xF6) || 00559 (0xF8 <= c) /* unsigned char must be <= 0xFF */ ) 00560 { 00561 return true; 00562 } 00563 return false; 00564 } 00565 00566 00567 bool XMLParser::isNameChar(unsigned char c) { 00568 if ( isLetter(c) || ('0' <= c && c <= '9') || 00569 c=='.' || c=='-' || c=='_' || c==':' || c==0xB7 ) 00570 { 00571 return true; 00572 } 00573 return false; 00574 } 00575 00576 00577 bool XMLParser::isSpace(unsigned char c) { 00578 if ( c==0x20 || c==0x9 || c==0xD || c==0xA ) 00579 { 00580 return true; 00581 } 00582 return false; 00583 } 00584 00585 00586 bool XMLParser::isChar(unsigned char c) { 00587 if ( c==0x9 || c==0xA || c==0xD || 0x20 <= c) { // unsigned char must be <= 0xFF 00588 return true; 00589 } 00590 return false; 00591 } 00592 00593 00594 int XMLParser::assertChar(unsigned char cexp) 00595 { 00596 // pull the next character off the stream and verify that it is what is expected 00597 // if not, return an error to the caller 00598 unsigned char c; 00599 // don't worry about newlines; assertChar is always wrapped in TEST_FOR_EXCEPTION, so we don't want to advance the line counter 00600 if (_is->readBytes(&c,1) < 1) { 00601 return 1; 00602 } 00603 if (c != cexp) { 00604 return 2; 00605 } 00606 return 0; 00607 } 00608 00609 void XMLParser::ignoreXMLDeclaration() 00610 { 00611 /* Be a little lax on the spec here; read until we get to '?', then assert '>' 00612 We have already consumed: <xml 00613 */ 00614 unsigned char c; 00615 while (1) { 00616 XMLPARSER_TFE(_is->readBytes(&c,1) < 1, "EOF before terminating XML declaration begun at line " << _lineNo ); 00617 if (c == '\n') ++_lineNo; 00618 // if we have a - 00619 if (c=='?') { 00620 // this had better be leading to the end of the declaration 00621 XMLPARSER_TFE( assertChar('>')!=0, "XML declaration not well-formed: missing expected '>' at line " << _lineNo ); 00622 break; 00623 } 00624 } 00625 }
1.7.6.1