00001 /********************************************************************** 00002 * $Id: cpl_minixml_cpp-source.html,v 1.2 2002/04/16 13:11:47 warmerda Exp $ 00003 * 00004 * Project: CPL - Common Portability Library 00005 * Purpose: Implementation of MiniXML Parser and handling. 00006 * Author: Frank Warmerdam, warmerdam@pobox.com 00007 * 00008 ********************************************************************** 00009 * Copyright (c) 2001, Frank Warmerdam 00010 * 00011 * Permission is hereby granted, free of charge, to any person obtaining a 00012 * copy of this software and associated documentation files (the "Software"), 00013 * to deal in the Software without restriction, including without limitation 00014 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00015 * and/or sell copies of the Software, and to permit persons to whom the 00016 * Software is furnished to do so, subject to the following conditions: 00017 * 00018 * The above copyright notice and this permission notice shall be included 00019 * in all copies or substantial portions of the Software. 00020 * 00021 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00022 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00023 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00024 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00025 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00026 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00027 * DEALINGS IN THE SOFTWARE. 00028 ********************************************************************** 00029 * 00030 * $Log: cpl_minixml_cpp-source.html,v $ 00030 * Revision 1.2 2002/04/16 13:11:47 warmerda 00030 * updated 00030 * 00031 * Revision 1.9 2002/03/07 22:19:20 warmerda 00032 * don't do operations within CPLAssert(), in UnreadChar() 00033 * 00034 * Revision 1.8 2002/03/05 14:26:57 warmerda 00035 * expanded tabs 00036 * 00037 * Revision 1.7 2002/01/23 20:45:05 warmerda 00038 * handle <?...?> and comment elements 00039 * 00040 * Revision 1.6 2002/01/22 18:54:48 warmerda 00041 * ensure text is property initialized when serializing 00042 * 00043 * Revision 1.5 2002/01/16 03:58:51 warmerda 00044 * support single quotes as well as double quotes 00045 * 00046 * Revision 1.4 2001/12/06 18:13:49 warmerda 00047 * added CPLAddXMLChild and CPLCreateElmentAndValue 00048 * 00049 * Revision 1.3 2001/11/16 21:20:16 warmerda 00050 * fixed typo 00051 * 00052 * Revision 1.2 2001/11/16 20:29:58 warmerda 00053 * fixed lost char in normal CString tokens 00054 * 00055 * Revision 1.1 2001/11/16 15:39:48 warmerda 00056 * New 00057 */ 00058 00059 #include <ctype.h> 00060 #include "cpl_minixml.h" 00061 #include "cpl_error.h" 00062 #include "cpl_conv.h" 00063 #include "cpl_string.h" 00064 00065 CPL_CVSID("$Id: cpl_minixml_cpp-source.html,v 1.2 2002/04/16 13:11:47 warmerda Exp $"); 00066 00067 typedef enum { 00068 TNone, 00069 TString, 00070 TOpen, 00071 TClose, 00072 TEqual, 00073 TToken, 00074 TSlashClose, 00075 TQuestionClose, 00076 TComment 00077 } TokenType; 00078 00079 typedef struct { 00080 const char *pszInput; 00081 int nInputOffset; 00082 int nInputLine; 00083 00084 int bInElement; 00085 TokenType eTokenType; 00086 char *pszToken; 00087 int nTokenMaxSize; 00088 int nTokenSize; 00089 00090 int nStackMaxSize; 00091 int nStackSize; 00092 CPLXMLNode **papsStack; 00093 00094 CPLXMLNode *psFirstNode; 00095 } ParseContext; 00096 00097 /************************************************************************/ 00098 /* ReadChar() */ 00099 /************************************************************************/ 00100 00101 static char ReadChar( ParseContext *psContext ) 00102 00103 { 00104 char chReturn; 00105 00106 chReturn = psContext->pszInput[psContext->nInputOffset++]; 00107 00108 if( chReturn == '\0' ) 00109 psContext->nInputOffset--; 00110 else if( chReturn == 10 ) 00111 psContext->nInputLine++; 00112 00113 return chReturn; 00114 } 00115 00116 /************************************************************************/ 00117 /* UnreadChar() */ 00118 /************************************************************************/ 00119 00120 static void UnreadChar( ParseContext *psContext, char chToUnread ) 00121 00122 { 00123 if( chToUnread == '\0' ) 00124 { 00125 /* do nothing */ 00126 } 00127 else 00128 { 00129 CPLAssert( chToUnread 00130 == psContext->pszInput[psContext->nInputOffset-1] ); 00131 00132 psContext->nInputOffset--; 00133 00134 if( chToUnread == 10 ) 00135 psContext->nInputLine--; 00136 } 00137 } 00138 00139 /************************************************************************/ 00140 /* AddToToken() */ 00141 /************************************************************************/ 00142 00143 static void AddToToken( ParseContext *psContext, char chNewChar ) 00144 00145 { 00146 if( psContext->pszToken == NULL ) 00147 { 00148 psContext->nTokenMaxSize = 10; 00149 psContext->pszToken = (char *) CPLMalloc(psContext->nTokenMaxSize); 00150 } 00151 else if( psContext->nTokenSize >= psContext->nTokenMaxSize - 2 ) 00152 { 00153 psContext->nTokenMaxSize *= 2; 00154 psContext->pszToken = (char *) 00155 CPLRealloc(psContext->pszToken,psContext->nTokenMaxSize); 00156 } 00157 00158 psContext->pszToken[psContext->nTokenSize++] = chNewChar; 00159 psContext->pszToken[psContext->nTokenSize] = '\0'; 00160 } 00161 00162 /************************************************************************/ 00163 /* ReadToken() */ 00164 /************************************************************************/ 00165 00166 static TokenType ReadToken( ParseContext *psContext ) 00167 00168 { 00169 char chNext; 00170 00171 psContext->nTokenSize = 0; 00172 psContext->pszToken[0] = '\0'; 00173 00174 chNext = ReadChar( psContext ); 00175 while( isspace(chNext) ) 00176 chNext = ReadChar( psContext ); 00177 00178 /* -------------------------------------------------------------------- */ 00179 /* Handle comments. */ 00180 /* -------------------------------------------------------------------- */ 00181 if( chNext == '<' 00182 && EQUALN(psContext->pszInput+psContext->nInputOffset,"!--",3) ) 00183 { 00184 psContext->eTokenType = TComment; 00185 00186 // Skip "!--" characters 00187 ReadChar(psContext); 00188 ReadChar(psContext); 00189 ReadChar(psContext); 00190 00191 while( !EQUALN(psContext->pszInput+psContext->nInputOffset,"-->",3) 00192 && (chNext = ReadChar(psContext)) != '\0' ) 00193 AddToToken( psContext, chNext ); 00194 00195 // Skip "-->" characters 00196 ReadChar(psContext); 00197 ReadChar(psContext); 00198 ReadChar(psContext); 00199 } 00200 /* -------------------------------------------------------------------- */ 00201 /* Simple single tokens of interest. */ 00202 /* -------------------------------------------------------------------- */ 00203 else if( chNext == '<' && !psContext->bInElement ) 00204 { 00205 psContext->eTokenType = TOpen; 00206 psContext->bInElement = TRUE; 00207 } 00208 else if( chNext == '>' && psContext->bInElement ) 00209 { 00210 psContext->eTokenType = TClose; 00211 psContext->bInElement = FALSE; 00212 } 00213 else if( chNext == '=' && psContext->bInElement ) 00214 { 00215 psContext->eTokenType = TEqual; 00216 } 00217 else if( chNext == '\0' ) 00218 { 00219 psContext->eTokenType = TNone; 00220 } 00221 /* -------------------------------------------------------------------- */ 00222 /* Handle the /> token terminator. */ 00223 /* -------------------------------------------------------------------- */ 00224 else if( chNext == '/' && psContext->bInElement 00225 && psContext->pszInput[psContext->nInputOffset] == '>' ) 00226 { 00227 chNext = ReadChar( psContext ); 00228 if( chNext != '>' ) 00229 { 00230 psContext->eTokenType = TNone; 00231 CPLError( CE_Failure, CPLE_AppDefined, 00232 "Parse error at '/' on line %d, expected '>'.", 00233 psContext->nInputLine ); 00234 } 00235 else 00236 { 00237 psContext->eTokenType = TSlashClose; 00238 psContext->bInElement = FALSE; 00239 } 00240 } 00241 /* -------------------------------------------------------------------- */ 00242 /* Handle the ?> token terminator. */ 00243 /* -------------------------------------------------------------------- */ 00244 else if( chNext == '?' && psContext->bInElement 00245 && psContext->pszInput[psContext->nInputOffset] == '>' ) 00246 { 00247 chNext = ReadChar( psContext ); 00248 if( chNext != '>' ) 00249 { 00250 psContext->eTokenType = TNone; 00251 CPLError( CE_Failure, CPLE_AppDefined, 00252 "Parse error at '?' on line %d, expected '>'.", 00253 psContext->nInputLine ); 00254 } 00255 else 00256 { 00257 psContext->eTokenType = TQuestionClose; 00258 psContext->bInElement = FALSE; 00259 } 00260 } 00261 00262 /* -------------------------------------------------------------------- */ 00263 /* Collect a quoted string. */ 00264 /* -------------------------------------------------------------------- */ 00265 else if( psContext->bInElement && chNext == '"' ) 00266 { 00267 psContext->eTokenType = TString; 00268 00269 while( (chNext = ReadChar(psContext)) != '"' 00270 && chNext != '\0' ) 00271 AddToToken( psContext, chNext ); 00272 00273 if( chNext != '"' ) 00274 { 00275 psContext->eTokenType = TNone; 00276 CPLError( CE_Failure, CPLE_AppDefined, 00277 "Parse error on line %d, reached EOF before closing quote.", 00278 psContext->nInputLine ); 00279 } 00280 } 00281 00282 else if( psContext->bInElement && chNext == '\'' ) 00283 { 00284 psContext->eTokenType = TString; 00285 00286 while( (chNext = ReadChar(psContext)) != '\'' 00287 && chNext != '\0' ) 00288 AddToToken( psContext, chNext ); 00289 00290 if( chNext != '\'' ) 00291 { 00292 psContext->eTokenType = TNone; 00293 CPLError( CE_Failure, CPLE_AppDefined, 00294 "Parse error on line %d, reached EOF before closing quote.", 00295 psContext->nInputLine ); 00296 } 00297 } 00298 00299 /* -------------------------------------------------------------------- */ 00300 /* Collect an unquoted string, terminated by a open angle */ 00301 /* bracket. */ 00302 /* -------------------------------------------------------------------- */ 00303 else if( !psContext->bInElement ) 00304 { 00305 psContext->eTokenType = TString; 00306 00307 AddToToken( psContext, chNext ); 00308 while( (chNext = ReadChar(psContext)) != '<' 00309 && chNext != '\0' ) 00310 AddToToken( psContext, chNext ); 00311 UnreadChar( psContext, chNext ); 00312 } 00313 00314 /* -------------------------------------------------------------------- */ 00315 /* Collect a regular token terminated by white space, or */ 00316 /* special character(s) like an equal sign. */ 00317 /* -------------------------------------------------------------------- */ 00318 else 00319 { 00320 psContext->eTokenType = TToken; 00321 00322 /* add the first character to the token regardless of what it is */ 00323 AddToToken( psContext, chNext ); 00324 00325 for( chNext = ReadChar(psContext); 00326 (chNext >= 'A' && chNext <= 'Z') 00327 || (chNext >= 'a' && chNext <= 'z') 00328 || chNext == '_' 00329 || chNext == ':' 00330 || (chNext >= '0' && chNext <= '9'); 00331 chNext = ReadChar(psContext) ) 00332 { 00333 AddToToken( psContext, chNext ); 00334 } 00335 00336 UnreadChar(psContext, chNext); 00337 } 00338 00339 return psContext->eTokenType; 00340 } 00341 00342 /************************************************************************/ 00343 /* PushNode() */ 00344 /************************************************************************/ 00345 00346 static void PushNode( ParseContext *psContext, CPLXMLNode *psNode ) 00347 00348 { 00349 if( psContext->nStackMaxSize <= psContext->nStackSize ) 00350 { 00351 psContext->nStackMaxSize += 10; 00352 psContext->papsStack = (CPLXMLNode **) 00353 CPLRealloc(psContext->papsStack, 00354 sizeof(CPLXMLNode*) * psContext->nStackMaxSize); 00355 } 00356 00357 psContext->papsStack[psContext->nStackSize++] = psNode; 00358 } 00359 00360 /************************************************************************/ 00361 /* AttachNode() */ 00362 /* */ 00363 /* Attach the passed node as a child of the current node. */ 00364 /* Special handling exists for adding siblings to psFirst if */ 00365 /* there is nothing on the stack. */ 00366 /************************************************************************/ 00367 00368 static void AttachNode( ParseContext *psContext, CPLXMLNode *psNode ) 00369 00370 { 00371 if( psContext->psFirstNode == NULL ) 00372 psContext->psFirstNode = psNode; 00373 else if( psContext->nStackSize == 0 ) 00374 { 00375 CPLXMLNode *psSibling; 00376 00377 psSibling = psContext->psFirstNode; 00378 while( psSibling->psNext != NULL ) 00379 psSibling = psSibling->psNext; 00380 psSibling->psNext = psNode; 00381 } 00382 else if( psContext->papsStack[psContext->nStackSize-1]->psChild == NULL ) 00383 { 00384 psContext->papsStack[psContext->nStackSize-1]->psChild = psNode; 00385 } 00386 else 00387 { 00388 CPLXMLNode *psSibling; 00389 00390 psSibling = psContext->papsStack[psContext->nStackSize-1]->psChild; 00391 while( psSibling->psNext != NULL ) 00392 psSibling = psSibling->psNext; 00393 psSibling->psNext = psNode; 00394 } 00395 } 00396 00397 /************************************************************************/ 00398 /* CPLParseXMLString() */ 00399 /************************************************************************/ 00400 00401 CPLXMLNode *CPLParseXMLString( const char *pszString ) 00402 00403 { 00404 ParseContext sContext; 00405 00406 CPLErrorReset(); 00407 00408 /* -------------------------------------------------------------------- */ 00409 /* Initialize parse context. */ 00410 /* -------------------------------------------------------------------- */ 00411 sContext.pszInput = pszString; 00412 sContext.nInputOffset = 0; 00413 sContext.nInputLine = 0; 00414 sContext.bInElement = FALSE; 00415 sContext.pszToken = NULL; 00416 sContext.nTokenMaxSize = 0; 00417 sContext.nTokenSize = 0; 00418 sContext.eTokenType = TNone; 00419 sContext.nStackMaxSize = 0; 00420 sContext.nStackSize = 0; 00421 sContext.papsStack = NULL; 00422 sContext.psFirstNode = NULL; 00423 00424 /* ensure token is initialized */ 00425 AddToToken( &sContext, ' ' ); 00426 00427 /* ==================================================================== */ 00428 /* Loop reading tokens. */ 00429 /* ==================================================================== */ 00430 while( ReadToken( &sContext ) != TNone ) 00431 { 00432 /* -------------------------------------------------------------------- */ 00433 /* Create a new element. */ 00434 /* -------------------------------------------------------------------- */ 00435 if( sContext.eTokenType == TOpen ) 00436 { 00437 CPLXMLNode *psElement; 00438 00439 if( ReadToken(&sContext) != TToken ) 00440 { 00441 CPLError( CE_Failure, CPLE_AppDefined, 00442 "Line %d: Didn't find element token after open angle bracket.", 00443 sContext.nInputLine ); 00444 break; 00445 } 00446 00447 if( sContext.pszToken[0] != '/' ) 00448 { 00449 psElement = CPLCreateXMLNode( NULL, CXT_Element, 00450 sContext.pszToken ); 00451 AttachNode( &sContext, psElement ); 00452 PushNode( &sContext, psElement ); 00453 } 00454 else 00455 { 00456 if( sContext.nStackSize == 0 00457 || !EQUAL(sContext.pszToken+1, 00458 sContext.papsStack[sContext.nStackSize-1]->pszValue) ) 00459 { 00460 CPLError( CE_Failure, CPLE_AppDefined, 00461 "Line %d: <%s> doesn't have matching <%s>.", 00462 sContext.nInputLine, 00463 sContext.pszToken, sContext.pszToken+1 ); 00464 break; 00465 } 00466 else 00467 { 00468 if( ReadToken(&sContext) != TClose ) 00469 { 00470 CPLError( CE_Failure, CPLE_AppDefined, 00471 "Line %d: Missing close angle bracket after <%s.", 00472 sContext.nInputLine, 00473 sContext.pszToken ); 00474 break; 00475 } 00476 00477 /* pop element off stack */ 00478 sContext.nStackSize--; 00479 } 00480 } 00481 } 00482 00483 /* -------------------------------------------------------------------- */ 00484 /* Add an attribute to a token. */ 00485 /* -------------------------------------------------------------------- */ 00486 else if( sContext.eTokenType == TToken ) 00487 { 00488 CPLXMLNode *psAttr; 00489 00490 psAttr = CPLCreateXMLNode(NULL, CXT_Attribute, sContext.pszToken); 00491 AttachNode( &sContext, psAttr ); 00492 00493 if( ReadToken(&sContext) != TEqual ) 00494 { 00495 CPLError( CE_Failure, CPLE_AppDefined, 00496 "Line %d: Didn't find expected '=' for attribute value.", 00497 sContext.nInputLine ); 00498 break; 00499 } 00500 00501 if( ReadToken(&sContext) != TString 00502 && sContext.eTokenType != TToken ) 00503 { 00504 CPLError( CE_Failure, CPLE_AppDefined, 00505 "Line %d: Didn't find expected attribute value.", 00506 sContext.nInputLine ); 00507 break; 00508 } 00509 00510 CPLCreateXMLNode( psAttr, CXT_Text, sContext.pszToken ); 00511 } 00512 00513 /* -------------------------------------------------------------------- */ 00514 /* Close the start section of an element. */ 00515 /* -------------------------------------------------------------------- */ 00516 else if( sContext.eTokenType == TClose ) 00517 { 00518 if( sContext.nStackSize == 0 ) 00519 { 00520 CPLError( CE_Failure, CPLE_AppDefined, 00521 "Line %d: Found unbalanced '>'.", 00522 sContext.nInputLine ); 00523 break; 00524 } 00525 } 00526 00527 /* -------------------------------------------------------------------- */ 00528 /* Close the start section of an element, and pop it */ 00529 /* immediately. */ 00530 /* -------------------------------------------------------------------- */ 00531 else if( sContext.eTokenType == TSlashClose ) 00532 { 00533 if( sContext.nStackSize == 0 ) 00534 { 00535 CPLError( CE_Failure, CPLE_AppDefined, 00536 "Line %d: Found unbalanced '/>'.", 00537 sContext.nInputLine ); 00538 break; 00539 } 00540 00541 sContext.nStackSize--; 00542 } 00543 00544 /* -------------------------------------------------------------------- */ 00545 /* Close the start section of a <?...?> element, and pop it */ 00546 /* immediately. */ 00547 /* -------------------------------------------------------------------- */ 00548 else if( sContext.eTokenType == TQuestionClose ) 00549 { 00550 if( sContext.nStackSize == 0 ) 00551 { 00552 CPLError( CE_Failure, CPLE_AppDefined, 00553 "Line %d: Found unbalanced '?>'.", 00554 sContext.nInputLine ); 00555 break; 00556 } 00557 else if( sContext.papsStack[sContext.nStackSize-1]->pszValue[0] != '?' ) 00558 { 00559 CPLError( CE_Failure, CPLE_AppDefined, 00560 "Line %d: Found '?>' without matching '<?'.", 00561 sContext.nInputLine ); 00562 break; 00563 } 00564 00565 sContext.nStackSize--; 00566 } 00567 00568 /* -------------------------------------------------------------------- */ 00569 /* Handle comments. They are returned as a whole token with the */ 00570 /* prefix and postfix omitted. No processing of white space */ 00571 /* will be done. */ 00572 /* -------------------------------------------------------------------- */ 00573 else if( sContext.eTokenType == TComment ) 00574 { 00575 CPLXMLNode *psValue; 00576 00577 psValue = CPLCreateXMLNode(NULL, CXT_Comment, sContext.pszToken); 00578 AttachNode( &sContext, psValue ); 00579 } 00580 00581 /* -------------------------------------------------------------------- */ 00582 /* Add a text value node as a child of the current element. */ 00583 /* -------------------------------------------------------------------- */ 00584 else if( sContext.eTokenType == TString && !sContext.bInElement ) 00585 { 00586 CPLXMLNode *psValue; 00587 00588 psValue = CPLCreateXMLNode(NULL, CXT_Text, sContext.pszToken); 00589 AttachNode( &sContext, psValue ); 00590 } 00591 /* -------------------------------------------------------------------- */ 00592 /* Anything else is an error. */ 00593 /* -------------------------------------------------------------------- */ 00594 else 00595 { 00596 CPLError( CE_Failure, CPLE_AppDefined, 00597 "Parse error at line %d, unexpected token:%s\n", 00598 sContext.nInputLine, sContext.pszToken ); 00599 break; 00600 } 00601 } 00602 00603 /* -------------------------------------------------------------------- */ 00604 /* Did we pop all the way out of our stack? */ 00605 /* -------------------------------------------------------------------- */ 00606 if( CPLGetLastErrorType() == CE_None && sContext.nStackSize != 0 ) 00607 { 00608 CPLError( CE_Failure, CPLE_AppDefined, 00609 "Parse error at EOF, not all elements have been closed,\n" 00610 "starting with %s\n", 00611 sContext.papsStack[sContext.nStackSize-1]->pszValue ); 00612 } 00613 00614 /* -------------------------------------------------------------------- */ 00615 /* Cleanup */ 00616 /* -------------------------------------------------------------------- */ 00617 CPLFree( sContext.pszToken ); 00618 if( sContext.papsStack != NULL ) 00619 CPLFree( sContext.papsStack ); 00620 00621 if( CPLGetLastErrorType() != CE_None ) 00622 { 00623 CPLDestroyXMLNode( sContext.psFirstNode ); 00624 sContext.psFirstNode = NULL; 00625 } 00626 00627 return sContext.psFirstNode; 00628 } 00629 00630 /************************************************************************/ 00631 /* _GrowBuffer() */ 00632 /************************************************************************/ 00633 00634 static void _GrowBuffer( unsigned int nNeeded, 00635 char **ppszText, unsigned int *pnMaxLength ) 00636 00637 { 00638 if( nNeeded+1 >= *pnMaxLength ) 00639 { 00640 *pnMaxLength = MAX(*pnMaxLength * 2,nNeeded+1); 00641 *ppszText = (char *) CPLRealloc(*ppszText, *pnMaxLength); 00642 } 00643 } 00644 00645 /************************************************************************/ 00646 /* CPLSerializeXMLNode() */ 00647 /************************************************************************/ 00648 00649 static void 00650 CPLSerializeXMLNode( CPLXMLNode *psNode, int nIndent, 00651 char **ppszText, unsigned int *pnLength, 00652 unsigned int *pnMaxLength ) 00653 00654 { 00655 if( psNode == NULL ) 00656 return; 00657 00658 /* -------------------------------------------------------------------- */ 00659 /* Ensure the buffer is plenty large to hold this additional */ 00660 /* string. */ 00661 /* -------------------------------------------------------------------- */ 00662 *pnLength += strlen(*ppszText + *pnLength); 00663 if(strlen(psNode->pszValue) + *pnLength + 40 + nIndent > *pnMaxLength) 00664 _GrowBuffer( strlen(psNode->pszValue) + *pnLength + 40 + nIndent, 00665 ppszText, pnMaxLength ); 00666 00667 /* -------------------------------------------------------------------- */ 00668 /* Text is just directly emitted. */ 00669 /* -------------------------------------------------------------------- */ 00670 if( psNode->eType == CXT_Text ) 00671 { 00672 CPLAssert( psNode->psChild == NULL ); 00673 00674 strcat( *ppszText + *pnLength, psNode->pszValue ); 00675 } 00676 00677 /* -------------------------------------------------------------------- */ 00678 /* Attributes require a little formatting. */ 00679 /* -------------------------------------------------------------------- */ 00680 else if( psNode->eType == CXT_Attribute ) 00681 { 00682 CPLAssert( psNode->psChild != NULL 00683 && psNode->psChild->eType == CXT_Text ); 00684 00685 sprintf( *ppszText + *pnLength, " %s=\"", psNode->pszValue ); 00686 CPLSerializeXMLNode( psNode->psChild, 0, ppszText, 00687 pnLength, pnMaxLength ); 00688 strcat( *ppszText + *pnLength, "\"" ); 00689 } 00690 00691 /* -------------------------------------------------------------------- */ 00692 /* Attributes require a little formatting. */ 00693 /* -------------------------------------------------------------------- */ 00694 else if( psNode->eType == CXT_Comment ) 00695 { 00696 int i; 00697 00698 CPLAssert( psNode->psChild == NULL ); 00699 00700 for( i = 0; i < nIndent; i++ ) 00701 (*ppszText)[(*pnLength)++] = ' '; 00702 00703 sprintf( *ppszText + *pnLength, "<!--%s-->\n", 00704 psNode->pszValue ); 00705 } 00706 00707 /* -------------------------------------------------------------------- */ 00708 /* Elements actually have to deal with general children, and */ 00709 /* various formatting issues. */ 00710 /* -------------------------------------------------------------------- */ 00711 else if( psNode->eType == CXT_Element ) 00712 { 00713 CPLXMLNode *psChild = psNode->psChild; 00714 char *pszIndent; 00715 00716 pszIndent = (char *) CPLCalloc(nIndent + 1,1); 00717 memset(pszIndent, ' ', nIndent ); 00718 00719 strcat( *ppszText + *pnLength, pszIndent ); 00720 *pnLength += nIndent; 00721 sprintf( *ppszText + *pnLength, "<%s", psNode->pszValue ); 00722 00723 while( psChild != NULL && psChild->eType == CXT_Attribute ) 00724 { 00725 CPLSerializeXMLNode( psChild, 0, ppszText, pnLength, 00726 pnMaxLength ); 00727 psChild = psChild->psNext; 00728 } 00729 00730 if( psChild == NULL ) 00731 { 00732 if( psNode->pszValue[0] == '?' ) 00733 strcat( *ppszText + *pnLength, "?>\n" ); 00734 else 00735 strcat( *ppszText + *pnLength, "/>\n" ); 00736 } 00737 else 00738 { 00739 int bJustText = TRUE; 00740 00741 strcat( *ppszText + *pnLength, ">" ); 00742 00743 while( psChild != NULL ) 00744 { 00745 if( psChild->eType != CXT_Text && bJustText ) 00746 { 00747 bJustText = FALSE; 00748 strcat( *ppszText + *pnLength, "\n" ); 00749 } 00750 00751 CPLSerializeXMLNode( psChild, nIndent + 2, ppszText, pnLength, 00752 pnMaxLength ); 00753 psChild = psChild->psNext; 00754 } 00755 00756 if( strlen(psNode->pszValue)+*pnLength+40+nIndent > *pnMaxLength) 00757 _GrowBuffer( strlen(psNode->pszValue)+*pnLength+40+nIndent, 00758 ppszText, pnMaxLength ); 00759 00760 if( !bJustText ) 00761 strcat( *ppszText + *pnLength, pszIndent ); 00762 00763 *pnLength += strlen(*ppszText + *pnLength); 00764 sprintf( *ppszText + *pnLength, "</%s>\n", psNode->pszValue ); 00765 } 00766 00767 CPLFree( pszIndent ); 00768 } 00769 } 00770 00771 /************************************************************************/ 00772 /* CPLSerializeXMLTree() */ 00773 /************************************************************************/ 00774 00775 char *CPLSerializeXMLTree( CPLXMLNode *psNode ) 00776 00777 { 00778 unsigned int nMaxLength = 10000, nLength = 0; 00779 char *pszText = NULL; 00780 CPLXMLNode *psThis; 00781 00782 pszText = (char *) CPLMalloc(nMaxLength); 00783 pszText[0] = '\0'; 00784 00785 for( psThis = psNode; psThis != NULL; psThis = psThis->psNext ) 00786 CPLSerializeXMLNode( psThis, 0, &pszText, &nLength, &nMaxLength ); 00787 00788 return pszText; 00789 } 00790 00791 /************************************************************************/ 00792 /* CPLCreateXMLNode() */ 00793 /************************************************************************/ 00794 00795 CPLXMLNode *CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, 00796 const char *pszText ) 00797 00798 { 00799 CPLXMLNode *psNode; 00800 00801 /* -------------------------------------------------------------------- */ 00802 /* Create new node. */ 00803 /* -------------------------------------------------------------------- */ 00804 psNode = (CPLXMLNode *) CPLCalloc(sizeof(CPLXMLNode),1); 00805 00806 psNode->eType = eType; 00807 psNode->pszValue = CPLStrdup( pszText ); 00808 00809 /* -------------------------------------------------------------------- */ 00810 /* Attach to parent, if provided. */ 00811 /* -------------------------------------------------------------------- */ 00812 if( poParent != NULL ) 00813 { 00814 if( poParent->psChild == NULL ) 00815 poParent->psChild = psNode; 00816 else 00817 { 00818 CPLXMLNode *psLink = poParent->psChild; 00819 00820 while( psLink->psNext != NULL ) 00821 psLink = psLink->psNext; 00822 00823 psLink->psNext = psNode; 00824 } 00825 } 00826 00827 return psNode; 00828 } 00829 00830 /************************************************************************/ 00831 /* CPLDestroyXMLNode() */ 00832 /************************************************************************/ 00833 00834 void CPLDestroyXMLNode( CPLXMLNode *psNode ) 00835 00836 { 00837 if( psNode->psChild != NULL ) 00838 CPLDestroyXMLNode( psNode->psChild ); 00839 00840 if( psNode->psNext != NULL ) 00841 CPLDestroyXMLNode( psNode->psNext ); 00842 00843 CPLFree( psNode->pszValue ); 00844 CPLFree( psNode ); 00845 } 00846 00847 /************************************************************************/ 00848 /* CPLGetXMLNode() */ 00849 /************************************************************************/ 00850 00851 CPLXMLNode *CPLGetXMLNode( CPLXMLNode *poRoot, const char *pszPath ) 00852 00853 { 00854 char **papszTokens; 00855 int iToken = 0; 00856 00857 papszTokens = CSLTokenizeStringComplex( pszPath, ".", FALSE, FALSE ); 00858 00859 while( papszTokens[iToken] != NULL && poRoot != NULL ) 00860 { 00861 CPLXMLNode *psChild; 00862 00863 for( psChild = poRoot->psChild; psChild != NULL; 00864 psChild = psChild->psNext ) 00865 { 00866 if( psChild->eType != CXT_Text 00867 && EQUAL(papszTokens[iToken],psChild->pszValue) ) 00868 break; 00869 } 00870 00871 if( psChild == NULL ) 00872 { 00873 poRoot = NULL; 00874 break; 00875 } 00876 00877 poRoot = psChild; 00878 iToken++; 00879 } 00880 00881 CSLDestroy( papszTokens ); 00882 return poRoot; 00883 } 00884 00885 /************************************************************************/ 00886 /* CPLGetXMLValue() */ 00887 /************************************************************************/ 00888 00889 const char *CPLGetXMLValue( CPLXMLNode *poRoot, const char *pszPath, 00890 const char *pszDefault ) 00891 00892 { 00893 CPLXMLNode *psTarget; 00894 00895 psTarget = CPLGetXMLNode( poRoot, pszPath ); 00896 if( psTarget == NULL ) 00897 return pszDefault; 00898 00899 if( psTarget->eType == CXT_Attribute ) 00900 { 00901 CPLAssert( psTarget->psChild != NULL 00902 && psTarget->psChild->eType == CXT_Text ); 00903 00904 return psTarget->psChild->pszValue; 00905 } 00906 00907 if( psTarget->eType == CXT_Element 00908 && psTarget->psChild != NULL 00909 && psTarget->psChild->eType == CXT_Text 00910 && psTarget->psChild->psNext == NULL ) 00911 { 00912 return psTarget->psChild->pszValue; 00913 } 00914 00915 return pszDefault; 00916 } 00917 00918 /************************************************************************/ 00919 /* CPLAddXMLChild() */ 00920 /* */ 00921 /* Add a node as a child of another. */ 00922 /************************************************************************/ 00923 00924 void CPLAddXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild ) 00925 00926 { 00927 CPLXMLNode *psSib; 00928 00929 CPLAssert( psChild->psNext == NULL ); 00930 psChild->psNext = NULL; 00931 00932 if( psParent->psChild == NULL ) 00933 { 00934 psParent->psChild = psChild; 00935 return; 00936 } 00937 00938 for( psSib = psParent->psChild; 00939 psSib->psNext != NULL; 00940 psSib = psSib->psNext ) {} 00941 00942 psSib->psNext = psChild; 00943 } 00944 00945 /************************************************************************/ 00946 /* CPLCreateXMLElementAndValue() */ 00947 /************************************************************************/ 00948 00949 CPLXMLNode *CPLCreateXMLElementAndValue( CPLXMLNode *psParent, 00950 const char *pszName, 00951 const char *pszValue ) 00952 00953 { 00954 return CPLCreateXMLNode( 00955 CPLCreateXMLNode( psParent, CXT_Element, pszName ), 00956 CXT_Text, pszValue ); 00957 } 00958