LTP GCOV extension - code coverage report
Current view: directory - port - cpl_minixml.cpp
Test: gdal_filtered.info
Date: 2010-07-12 Instrumented lines: 605
Code covered: 80.2 % Executed lines: 485

       1                 : /**********************************************************************
       2                 :  * $Id: cpl_minixml.cpp 19859 2010-06-13 13:59:57Z rouault $
       3                 :  *
       4                 :  * Project:  CPL - Common Portability Library
       5                 :  * Purpose:  Implementation of MiniXML Parser and handling.
       6                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7                 :  *
       8                 :  **********************************************************************
       9                 :  * Copyright (c) 2001, Frank Warmerdam
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  * 
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  * 
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      22                 :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  **********************************************************************
      29                 :  *
      30                 :  * Independent Security Audit 2003/04/05 Andrey Kiselev:
      31                 :  *   Completed audit of this module. Any documents may be parsed without
      32                 :  *   buffer overflows and stack corruptions.
      33                 :  * 
      34                 :  * Security Audit 2003/03/28 warmerda:
      35                 :  *   Completed security audit.  I believe that this module may be safely used 
      36                 :  *   to parse, and serialize arbitrary documents provided by a potentially 
      37                 :  *   hostile source.
      38                 :  *
      39                 :  */
      40                 : 
      41                 : #include "cpl_minixml.h"
      42                 : #include "cpl_error.h"
      43                 : #include "cpl_conv.h"
      44                 : #include "cpl_string.h"
      45                 : #include <ctype.h>
      46                 : 
      47                 : CPL_CVSID("$Id: cpl_minixml.cpp 19859 2010-06-13 13:59:57Z rouault $");
      48                 : 
      49                 : typedef enum {
      50                 :     TNone,
      51                 :     TString, 
      52                 :     TOpen, 
      53                 :     TClose,
      54                 :     TEqual,
      55                 :     TToken,
      56                 :     TSlashClose,
      57                 :     TQuestionClose,
      58                 :     TComment,
      59                 :     TLiteral
      60                 : } XMLTokenType;
      61                 : 
      62                 : typedef struct
      63                 : {
      64                 :     CPLXMLNode *psFirstNode;
      65                 :     CPLXMLNode *psLastChild;
      66                 : } StackContext;
      67                 : 
      68                 : typedef struct {
      69                 :     const char *pszInput;
      70                 :     int        nInputOffset;
      71                 :     int        nInputLine;
      72                 :     int        bInElement;
      73                 :     XMLTokenType  eTokenType;
      74                 :     char       *pszToken;
      75                 :     size_t     nTokenMaxSize;
      76                 :     size_t     nTokenSize;
      77                 : 
      78                 :     int        nStackMaxSize;
      79                 :     int        nStackSize;
      80                 :     StackContext *papsStack;
      81                 : 
      82                 :     CPLXMLNode *psFirstNode;
      83                 :     CPLXMLNode *psLastNode;
      84                 : } ParseContext;
      85                 : 
      86                 : static CPLXMLNode *_CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, 
      87                 :                                       const char *pszText );
      88                 : 
      89                 : /************************************************************************/
      90                 : /*                              ReadChar()                              */
      91                 : /************************************************************************/
      92                 : 
      93         1822198 : static CPL_INLINE char ReadChar( ParseContext *psContext )
      94                 : 
      95                 : {
      96                 :     char        chReturn;
      97                 : 
      98         1822198 :     chReturn = psContext->pszInput[psContext->nInputOffset++];
      99                 : 
     100         1822198 :     if( chReturn == '\0' )
     101            1371 :         psContext->nInputOffset--;
     102         1820827 :     else if( chReturn == 10 )
     103           19088 :         psContext->nInputLine++;
     104                 :     
     105         1822198 :     return chReturn;
     106                 : }
     107                 : 
     108                 : /************************************************************************/
     109                 : /*                             UnreadChar()                             */
     110                 : /************************************************************************/
     111                 : 
     112           94310 : static CPL_INLINE void UnreadChar( ParseContext *psContext, char chToUnread )
     113                 : 
     114                 : {
     115           94310 :     if( chToUnread == '\0' )
     116                 :     {
     117                 :         /* do nothing */
     118                 :     }
     119                 :     else
     120                 :     {
     121           94309 :         CPLAssert( chToUnread 
     122                 :                    == psContext->pszInput[psContext->nInputOffset-1] );
     123                 : 
     124           94309 :         psContext->nInputOffset--;
     125                 : 
     126           94309 :         if( chToUnread == 10 )
     127               8 :             psContext->nInputLine--;
     128                 :     }
     129           94310 : }
     130                 : 
     131                 : /************************************************************************/
     132                 : /*                           ReallocToken()                             */
     133                 : /************************************************************************/
     134                 : 
     135            4055 : static int ReallocToken( ParseContext *psContext )
     136                 : {
     137            4055 :     if (psContext->nTokenMaxSize > INT_MAX / 2)
     138                 :     {
     139                 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     140               0 :                  "Out of memory allocating %d*2 bytes", (int)psContext->nTokenMaxSize);
     141               0 :         VSIFree(psContext->pszToken);
     142               0 :         psContext->pszToken = NULL;
     143               0 :         return FALSE;
     144                 :     }
     145                 : 
     146            4055 :     psContext->nTokenMaxSize *= 2;
     147                 :     char* pszToken = (char *) 
     148            4055 :         VSIRealloc(psContext->pszToken,psContext->nTokenMaxSize);
     149            4055 :     if (pszToken == NULL)
     150                 :     {
     151                 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     152               0 :                  "Out of memory allocating %d bytes", (int)psContext->nTokenMaxSize);
     153               0 :         VSIFree(psContext->pszToken);
     154               0 :         psContext->pszToken = NULL;
     155               0 :         return FALSE;
     156                 :     }
     157            4055 :     psContext->pszToken = pszToken;
     158            4055 :     return TRUE;
     159                 : }
     160                 : 
     161                 : /************************************************************************/
     162                 : /*                             AddToToken()                             */
     163                 : /************************************************************************/
     164                 : 
     165         1312918 : static CPL_INLINE int _AddToToken( ParseContext *psContext, char chNewChar )
     166                 : 
     167                 : {
     168         1312918 :     if( psContext->nTokenSize >= psContext->nTokenMaxSize - 2 )
     169                 :     {
     170            4055 :         if (!ReallocToken(psContext))
     171               0 :             return FALSE;
     172                 :     }
     173                 : 
     174         1312918 :     psContext->pszToken[psContext->nTokenSize++] = chNewChar;
     175         1312918 :     psContext->pszToken[psContext->nTokenSize] = '\0';
     176         1312918 :     return TRUE;
     177                 : }
     178                 : 
     179                 : #define AddToToken(psContext, chNewChar) if (!_AddToToken(psContext, chNewChar)) goto fail;
     180                 : 
     181                 : /************************************************************************/
     182                 : /*                             ReadToken()                              */
     183                 : /************************************************************************/
     184                 : 
     185          258232 : static XMLTokenType ReadToken( ParseContext *psContext )
     186                 : 
     187                 : {
     188                 :     char        chNext;
     189                 : 
     190          258232 :     psContext->nTokenSize = 0;
     191          258232 :     psContext->pszToken[0] = '\0';
     192                 :     
     193          258232 :     chNext = ReadChar( psContext );
     194          726872 :     while( isspace((unsigned char)chNext) )
     195          210408 :         chNext = ReadChar( psContext );
     196                 : 
     197                 : /* -------------------------------------------------------------------- */
     198                 : /*      Handle comments.                                                */
     199                 : /* -------------------------------------------------------------------- */
     200          258243 :     if( chNext == '<' 
     201                 :         && EQUALN(psContext->pszInput+psContext->nInputOffset,"!--",3) )
     202                 :     {
     203              11 :         psContext->eTokenType = TComment;
     204                 : 
     205                 :         // Skip "!--" characters
     206              11 :         ReadChar(psContext);
     207              11 :         ReadChar(psContext);
     208              11 :         ReadChar(psContext);
     209                 : 
     210            1167 :         while( !EQUALN(psContext->pszInput+psContext->nInputOffset,"-->",3)
     211                 :                && (chNext = ReadChar(psContext)) != '\0' )
     212            1145 :             AddToToken( psContext, chNext );
     213                 : 
     214                 :         // Skip "-->" characters
     215              11 :         ReadChar(psContext);
     216              11 :         ReadChar(psContext);
     217              11 :         ReadChar(psContext);
     218                 :     }
     219                 : /* -------------------------------------------------------------------- */
     220                 : /*      Handle DOCTYPE.                                                 */
     221                 : /* -------------------------------------------------------------------- */
     222          258222 :     else if( chNext == '<' 
     223                 :           && EQUALN(psContext->pszInput+psContext->nInputOffset,"!DOCTYPE",8) )
     224                 :     {
     225               1 :         int   bInQuotes = FALSE;
     226               1 :         psContext->eTokenType = TLiteral;
     227                 :         
     228               1 :         AddToToken( psContext, '<' );
     229              17 :         do { 
     230              18 :             chNext = ReadChar(psContext);
     231              18 :             if( chNext == '\0' )
     232                 :             {
     233                 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     234                 :                           "Parse error in DOCTYPE on or before line %d, "
     235                 :                           "reached end of file without '>'.", 
     236               0 :                           psContext->nInputLine );
     237                 :                 
     238               0 :                 break;
     239                 :             }
     240                 :             
     241                 :             /* The markup declaration block within a DOCTYPE tag consists of:
     242                 :              * - a left square bracket [
     243                 :              * - a list of declarations
     244                 :              * - a right square bracket ]
     245                 :              * Example:
     246                 :              * <!DOCTYPE RootElement [ ...declarations... ]>
     247                 :              */
     248              18 :             if( chNext == '[' )
     249                 :             {
     250               1 :                 AddToToken( psContext, chNext );
     251                 : 
     252              99 :                 do
     253                 :                 {
     254              99 :                     chNext = ReadChar( psContext );
     255              99 :                     AddToToken( psContext, chNext );
     256                 :                 }
     257                 :                 while( chNext != ']' && chNext != '\0'
     258                 :                     && !EQUALN(psContext->pszInput+psContext->nInputOffset,"]>", 2) );
     259                 :                     
     260               1 :                 if (chNext == '\0')
     261                 :                 {
     262                 :                     CPLError( CE_Failure, CPLE_AppDefined, 
     263                 :                           "Parse error in DOCTYPE on or before line %d, "
     264                 :                           "reached end of file without ']'.", 
     265               0 :                           psContext->nInputLine );
     266               0 :                     break;
     267                 :                 }
     268                 : 
     269               1 :                 chNext = ReadChar( psContext );
     270               1 :                 AddToToken( psContext, chNext );
     271                 : 
     272                 :                 // Skip ">" character, will be consumed below
     273               1 :                 chNext = ReadChar( psContext );
     274                 :             }
     275                 : 
     276                 : 
     277              18 :             if( chNext == '\"' )
     278               0 :                 bInQuotes = !bInQuotes;
     279                 : 
     280              18 :              if( chNext == '>' && !bInQuotes )
     281                 :             {
     282               1 :                 AddToToken( psContext, '>' );
     283               1 :                 break;
     284                 :             }
     285                 : 
     286              17 :             AddToToken( psContext, chNext );
     287                 :         } while( TRUE );
     288                 :     }
     289                 : /* -------------------------------------------------------------------- */
     290                 : /*      Handle CDATA.                                                   */
     291                 : /* -------------------------------------------------------------------- */
     292          258220 :     else if( chNext == '<' 
     293                 :           && EQUALN(psContext->pszInput+psContext->nInputOffset,"![CDATA[",8) )
     294                 :     {
     295               0 :         psContext->eTokenType = TString;
     296                 : 
     297                 :         // Skip !CDATA[
     298               0 :         ReadChar( psContext );
     299               0 :         ReadChar( psContext );
     300               0 :         ReadChar( psContext );
     301               0 :         ReadChar( psContext );
     302               0 :         ReadChar( psContext );
     303               0 :         ReadChar( psContext );
     304               0 :         ReadChar( psContext );
     305               0 :         ReadChar( psContext );
     306                 : 
     307               0 :         while( !EQUALN(psContext->pszInput+psContext->nInputOffset,"]]>",3)
     308                 :                && (chNext = ReadChar(psContext)) != '\0' )
     309               0 :             AddToToken( psContext, chNext );
     310                 : 
     311                 :         // Skip "]]>" characters
     312               0 :         ReadChar(psContext);
     313               0 :         ReadChar(psContext);
     314               0 :         ReadChar(psContext);
     315                 :     }
     316                 : /* -------------------------------------------------------------------- */
     317                 : /*      Simple single tokens of interest.                               */
     318                 : /* -------------------------------------------------------------------- */
     319          305633 :     else if( chNext == '<' && !psContext->bInElement )
     320                 :     {
     321           47413 :         psContext->eTokenType = TOpen;
     322           47413 :         psContext->bInElement = TRUE;
     323                 :     }
     324          251495 :     else if( chNext == '>' && psContext->bInElement )
     325                 :     {
     326           40688 :         psContext->eTokenType = TClose;
     327           40688 :         psContext->bInElement = FALSE;
     328                 :     }
     329          203983 :     else if( chNext == '=' && psContext->bInElement )
     330                 :     {
     331           33864 :         psContext->eTokenType = TEqual;
     332                 :     }
     333          136255 :     else if( chNext == '\0' )
     334                 :     {
     335            1370 :         psContext->eTokenType = TNone;
     336                 :     }
     337                 : /* -------------------------------------------------------------------- */
     338                 : /*      Handle the /> token terminator.                                 */
     339                 : /* -------------------------------------------------------------------- */
     340          141561 :     else if( chNext == '/' && psContext->bInElement 
     341                 :              && psContext->pszInput[psContext->nInputOffset] == '>' )
     342                 :     {
     343            6676 :         chNext = ReadChar( psContext );
     344            6676 :         CPLAssert( chNext == '>' );
     345                 : 
     346            6676 :         psContext->eTokenType = TSlashClose;
     347            6676 :         psContext->bInElement = FALSE;
     348                 :     }
     349                 : /* -------------------------------------------------------------------- */
     350                 : /*      Handle the ?> token terminator.                                 */
     351                 : /* -------------------------------------------------------------------- */
     352          128257 :     else if( chNext == '?' && psContext->bInElement 
     353                 :              && psContext->pszInput[psContext->nInputOffset] == '>' )
     354                 :     {
     355              48 :         chNext = ReadChar( psContext );
     356                 :         
     357              48 :         CPLAssert( chNext == '>' );
     358                 : 
     359              48 :         psContext->eTokenType = TQuestionClose;
     360              48 :         psContext->bInElement = FALSE;
     361                 :     }
     362                 : 
     363                 : /* -------------------------------------------------------------------- */
     364                 : /*      Collect a quoted string.                                        */
     365                 : /* -------------------------------------------------------------------- */
     366          139888 :     else if( psContext->bInElement && chNext == '"' )
     367                 :     {
     368           11727 :         psContext->eTokenType = TString;
     369                 : 
     370          119304 :         while( (chNext = ReadChar(psContext)) != '"' 
     371                 :                && chNext != '\0' )
     372           95850 :             AddToToken( psContext, chNext );
     373                 :         
     374           11727 :         if( chNext != '"' )
     375                 :         {
     376               0 :             psContext->eTokenType = TNone;
     377                 :             CPLError( CE_Failure, CPLE_AppDefined, 
     378                 :                   "Parse error on line %d, reached EOF before closing quote.", 
     379               0 :                       psContext->nInputLine );
     380                 :         }
     381                 : 
     382                 :         /* Do we need to unescape it? */
     383           11727 :         if( strchr(psContext->pszToken,'&') != NULL )
     384                 :         {
     385                 :             int  nLength;
     386                 :             char *pszUnescaped = CPLUnescapeString( psContext->pszToken, 
     387               9 :                                                     &nLength, CPLES_XML );
     388               9 :             strcpy( psContext->pszToken, pszUnescaped );
     389               9 :             CPLFree( pszUnescaped );
     390               9 :             psContext->nTokenSize = strlen(psContext->pszToken );
     391                 :         }
     392                 :     }
     393                 : 
     394          138558 :     else if( psContext->bInElement && chNext == '\'' )
     395                 :     {
     396           22124 :         psContext->eTokenType = TString;
     397                 : 
     398          312430 :         while( (chNext = ReadChar(psContext)) != '\'' 
     399                 :                && chNext != '\0' )
     400          268182 :             AddToToken( psContext, chNext );
     401                 :         
     402           22124 :         if( chNext != '\'' )
     403                 :         {
     404               0 :             psContext->eTokenType = TNone;
     405                 :             CPLError( CE_Failure, CPLE_AppDefined, 
     406                 :                   "Parse error on line %d, reached EOF before closing quote.", 
     407               0 :                       psContext->nInputLine );
     408                 :         }
     409                 : 
     410                 :         /* Do we need to unescape it? */
     411           22124 :         if( strchr(psContext->pszToken,'&') != NULL )
     412                 :         {
     413                 :             int  nLength;
     414                 :             char *pszUnescaped = CPLUnescapeString( psContext->pszToken, 
     415               0 :                                                     &nLength, CPLES_XML );
     416               0 :             strcpy( psContext->pszToken, pszUnescaped );
     417               0 :             CPLFree( pszUnescaped );
     418               0 :             psContext->nTokenSize = strlen(psContext->pszToken );
     419                 :         }
     420                 :     }
     421                 : 
     422                 : /* -------------------------------------------------------------------- */
     423                 : /*      Collect an unquoted string, terminated by a open angle          */
     424                 : /*      bracket.                                                        */
     425                 : /* -------------------------------------------------------------------- */
     426           94310 :     else if( !psContext->bInElement )
     427                 :     {
     428           13020 :         psContext->eTokenType = TString;
     429                 : 
     430           13020 :         AddToToken( psContext, chNext );
     431          393736 :         while( (chNext = ReadChar(psContext)) != '<' 
     432                 :                && chNext != '\0' )
     433          367696 :             AddToToken( psContext, chNext );
     434           13020 :         UnreadChar( psContext, chNext );
     435                 : 
     436                 :         /* Do we need to unescape it? */
     437           13020 :         if( strchr(psContext->pszToken,'&') != NULL )
     438                 :         {
     439                 :             int  nLength;
     440                 :             char *pszUnescaped = CPLUnescapeString( psContext->pszToken, 
     441             362 :                                                     &nLength, CPLES_XML );
     442             362 :             strcpy( psContext->pszToken, pszUnescaped );
     443             362 :             CPLFree( pszUnescaped );
     444             362 :             psContext->nTokenSize = strlen(psContext->pszToken );
     445                 :         }
     446                 :     }
     447                 :     
     448                 : /* -------------------------------------------------------------------- */
     449                 : /*      Collect a regular token terminated by white space, or           */
     450                 : /*      special character(s) like an equal sign.                        */
     451                 : /* -------------------------------------------------------------------- */
     452                 :     else
     453                 :     {
     454           81290 :         psContext->eTokenType = TToken;
     455                 : 
     456                 :         /* add the first character to the token regardless of what it is */
     457           81290 :         AddToToken( psContext, chNext );
     458                 : 
     459          566905 :         for( chNext = ReadChar(psContext); 
     460                 :              (chNext >= 'A' && chNext <= 'Z')
     461                 :                  || (chNext >= 'a' && chNext <= 'z')
     462                 :                  || chNext == '-'
     463                 :                  || chNext == '_'
     464                 :                  || chNext == '.'
     465                 :                  || chNext == ':'
     466                 :                  || (chNext >= '0' && chNext <= '9');
     467                 :              chNext = ReadChar(psContext) ) 
     468                 :         {
     469          485615 :             AddToToken( psContext, chNext );
     470                 :         }
     471                 : 
     472           81290 :         UnreadChar(psContext, chNext);
     473                 :     }
     474                 :     
     475          258232 :     return psContext->eTokenType;
     476                 : 
     477               0 : fail:
     478               0 :     psContext->eTokenType = TNone;
     479               0 :     return TNone;
     480                 : }
     481                 : 
     482                 : /************************************************************************/
     483                 : /*                              PushNode()                              */
     484                 : /************************************************************************/
     485                 : 
     486           27069 : static int PushNode( ParseContext *psContext, CPLXMLNode *psNode )
     487                 : 
     488                 : {
     489           27069 :     if( psContext->nStackMaxSize <= psContext->nStackSize )
     490                 :     {
     491            1374 :         psContext->nStackMaxSize += 10;
     492                 : 
     493            1374 :         if (psContext->nStackMaxSize >= (int)(INT_MAX / sizeof(StackContext)))
     494                 :         {
     495                 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     496               0 :                  "Out of memory allocating %d*%d bytes", (int)sizeof(StackContext), psContext->nStackMaxSize);
     497               0 :             VSIFree(psContext->papsStack);
     498               0 :             psContext->papsStack = NULL;
     499               0 :             return FALSE;
     500                 :         }
     501                 :         StackContext* papsStack;
     502                 :         papsStack = (StackContext *)VSIRealloc(psContext->papsStack, 
     503            1374 :                     sizeof(StackContext) * psContext->nStackMaxSize);
     504            1374 :         if (papsStack == NULL)
     505                 :         {
     506                 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     507               0 :                  "Out of memory allocating %d bytes", (int)(sizeof(StackContext) * psContext->nStackMaxSize));
     508               0 :             VSIFree(psContext->papsStack);
     509               0 :             psContext->papsStack = NULL;
     510               0 :             return FALSE;
     511                 :         }
     512            1374 :         psContext->papsStack = papsStack;
     513                 :     }
     514                 : 
     515           27069 :     psContext->papsStack[psContext->nStackSize].psFirstNode = psNode;
     516           27069 :     psContext->papsStack[psContext->nStackSize].psLastChild = NULL;
     517           27069 :     psContext->nStackSize ++;
     518           27069 :     return TRUE;
     519                 : }
     520                 :     
     521                 : /************************************************************************/
     522                 : /*                             AttachNode()                             */
     523                 : /*                                                                      */
     524                 : /*      Attach the passed node as a child of the current node.          */
     525                 : /*      Special handling exists for adding siblings to psFirst if       */
     526                 : /*      there is nothing on the stack.                                  */
     527                 : /************************************************************************/
     528                 : 
     529           73965 : static void AttachNode( ParseContext *psContext, CPLXMLNode *psNode )
     530                 : 
     531                 : {
     532           73965 :     if( psContext->psFirstNode == NULL )
     533                 :     {
     534            1368 :         psContext->psFirstNode = psNode;
     535            1368 :         psContext->psLastNode = psNode;
     536                 :     }
     537           72597 :     else if( psContext->nStackSize == 0 )
     538                 :     {
     539              53 :         psContext->psLastNode->psNext = psNode;
     540              53 :         psContext->psLastNode = psNode;
     541                 :     }
     542           72544 :     else if( psContext->papsStack[psContext->nStackSize-1].psFirstNode->psChild == NULL )
     543                 :     {
     544           26999 :         psContext->papsStack[psContext->nStackSize-1].psFirstNode->psChild = psNode;
     545           26999 :         psContext->papsStack[psContext->nStackSize-1].psLastChild = psNode;
     546                 :     }
     547                 :     else
     548                 :     {
     549           45545 :         psContext->papsStack[psContext->nStackSize-1].psLastChild->psNext = psNode;
     550           45545 :         psContext->papsStack[psContext->nStackSize-1].psLastChild = psNode;
     551                 :     }
     552           73965 : }
     553                 : 
     554                 : /************************************************************************/
     555                 : /*                         CPLParseXMLString()                          */
     556                 : /************************************************************************/
     557                 : 
     558                 : /**
     559                 :  * \brief Parse an XML string into tree form.
     560                 :  *
     561                 :  * The passed document is parsed into a CPLXMLNode tree representation. 
     562                 :  * If the document is not well formed XML then NULL is returned, and errors
     563                 :  * are reported via CPLError().  No validation beyond wellformedness is
     564                 :  * done.  The CPLParseXMLFile() convenience function can be used to parse
     565                 :  * from a file. 
     566                 :  *
     567                 :  * The returned document tree is is owned by the caller and should be freed
     568                 :  * with CPLDestroyXMLNode() when no longer needed.
     569                 :  *
     570                 :  * If the document has more than one "root level" element then those after the 
     571                 :  * first will be attached to the first as siblings (via the psNext pointers)
     572                 :  * even though there is no common parent.  A document with no XML structure
     573                 :  * (no angle brackets for instance) would be considered well formed, and 
     574                 :  * returned as a single CXT_Text node.  
     575                 :  * 
     576                 :  * @param pszString the document to parse. 
     577                 :  *
     578                 :  * @return parsed tree or NULL on error. 
     579                 :  */
     580                 : 
     581            1371 : CPLXMLNode *CPLParseXMLString( const char *pszString )
     582                 : 
     583                 : {
     584                 :     ParseContext sContext;
     585                 : 
     586            1371 :     CPLErrorReset();
     587                 : 
     588            1371 :     if( pszString == NULL )
     589                 :     {
     590                 :         CPLError( CE_Failure, CPLE_AppDefined, 
     591               0 :                   "CPLParseXMLString() called with NULL pointer." );
     592               0 :         return NULL;
     593                 :     }
     594                 : 
     595                 : /* -------------------------------------------------------------------- */
     596                 : /*      Initialize parse context.                                       */
     597                 : /* -------------------------------------------------------------------- */
     598            1371 :     sContext.pszInput = pszString;
     599            1371 :     sContext.nInputOffset = 0;
     600            1371 :     sContext.nInputLine = 0;
     601            1371 :     sContext.bInElement = FALSE;
     602            1371 :     sContext.nTokenMaxSize = 10;
     603            1371 :     sContext.pszToken = (char *) VSIMalloc(sContext.nTokenMaxSize);
     604            1371 :     if (sContext.pszToken == NULL)
     605               0 :         return NULL;
     606            1371 :     sContext.nTokenSize = 0;
     607            1371 :     sContext.eTokenType = TNone;
     608            1371 :     sContext.nStackMaxSize = 0;
     609            1371 :     sContext.nStackSize = 0;
     610            1371 :     sContext.papsStack = NULL;
     611            1371 :     sContext.psFirstNode = NULL;
     612            1371 :     sContext.psLastNode = NULL;
     613                 : 
     614                 : /* ==================================================================== */
     615                 : /*      Loop reading tokens.                                            */
     616                 : /* ==================================================================== */
     617          124119 :     while( ReadToken( &sContext ) != TNone )
     618                 :     {
     619                 : /* -------------------------------------------------------------------- */
     620                 : /*      Create a new element.                                           */
     621                 : /* -------------------------------------------------------------------- */
     622          121378 :         if( sContext.eTokenType == TOpen )
     623                 :         {
     624                 :             CPLXMLNode *psElement;
     625                 : 
     626           47413 :             if( ReadToken(&sContext) != TToken )
     627                 :             {
     628                 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     629                 :                           "Line %d: Didn't find element token after open angle bracket.",
     630               0 :                           sContext.nInputLine );
     631               0 :                 break;
     632                 :             }
     633                 : 
     634           47413 :             if( sContext.pszToken[0] != '/' )
     635                 :             {
     636                 :                 psElement = _CPLCreateXMLNode( NULL, CXT_Element,
     637           27069 :                                               sContext.pszToken );
     638           27069 :                 if (!psElement) break;
     639           27069 :                 AttachNode( &sContext, psElement );
     640           27069 :                 if (!PushNode( &sContext, psElement ))
     641               0 :                     break;
     642                 :             }
     643                 :             else 
     644                 :             {
     645           20344 :                 if( sContext.nStackSize == 0
     646                 :                     || !EQUAL(sContext.pszToken+1,
     647                 :                          sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue) )
     648                 :                 {
     649                 :                     CPLError( CE_Failure, CPLE_AppDefined, 
     650                 :                               "Line %d: <%.500s> doesn't have matching <%.500s>.",
     651                 :                               sContext.nInputLine,
     652               1 :                               sContext.pszToken, sContext.pszToken+1 );
     653               1 :                     break;
     654                 :                 }
     655                 :                 else
     656                 :                 {
     657           20343 :                     if( ReadToken(&sContext) != TClose )
     658                 :                     {
     659                 :                         CPLError( CE_Failure, CPLE_AppDefined, 
     660                 :                                   "Line %d: Missing close angle bracket after <%.500s.",
     661                 :                                   sContext.nInputLine,
     662               0 :                                   sContext.pszToken );
     663               0 :                         break;
     664                 :                     }
     665                 : 
     666                 :                     /* pop element off stack */
     667           20343 :                     sContext.nStackSize--;
     668                 :                 }
     669                 :             }
     670                 :         }
     671                 : 
     672                 : /* -------------------------------------------------------------------- */
     673                 : /*      Add an attribute to a token.                                    */
     674                 : /* -------------------------------------------------------------------- */
     675           73965 :         else if( sContext.eTokenType == TToken )
     676                 :         {
     677                 :             CPLXMLNode *psAttr;
     678                 : 
     679           33864 :             psAttr = _CPLCreateXMLNode(NULL, CXT_Attribute, sContext.pszToken);
     680           33864 :             if (!psAttr) break;
     681           33864 :             AttachNode( &sContext, psAttr );
     682                 :             
     683           33864 :             if( ReadToken(&sContext) != TEqual )
     684                 :             {
     685                 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     686                 :                           "Line %d: Didn't find expected '=' for value of attribute '%.500s'.",
     687               0 :                           sContext.nInputLine, psAttr->pszValue );
     688               0 :                 break;
     689                 :             }
     690                 : 
     691           33864 :             if( ReadToken(&sContext) != TString 
     692                 :                 && sContext.eTokenType != TToken )
     693                 :             {
     694                 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     695                 :                           "Line %d: Didn't find expected attribute value.",
     696               0 :                           sContext.nInputLine );
     697               0 :                 break;
     698                 :             }
     699                 : 
     700           33864 :             if (!_CPLCreateXMLNode( psAttr, CXT_Text, sContext.pszToken )) break;
     701                 :         }
     702                 : 
     703                 : /* -------------------------------------------------------------------- */
     704                 : /*      Close the start section of an element.                          */
     705                 : /* -------------------------------------------------------------------- */
     706           40101 :         else if( sContext.eTokenType == TClose )
     707                 :         {
     708           20345 :             if( sContext.nStackSize == 0 )
     709                 :             {
     710                 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     711                 :                           "Line %d: Found unbalanced '>'.",
     712               0 :                           sContext.nInputLine );
     713               0 :                 break;
     714                 :             }
     715                 :         }
     716                 : 
     717                 : /* -------------------------------------------------------------------- */
     718                 : /*      Close the start section of an element, and pop it               */
     719                 : /*      immediately.                                                    */
     720                 : /* -------------------------------------------------------------------- */
     721           19756 :         else if( sContext.eTokenType == TSlashClose )
     722                 :         {
     723            6676 :             if( sContext.nStackSize == 0 )
     724                 :             {
     725                 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     726                 :                           "Line %d: Found unbalanced '/>'.",
     727               0 :                           sContext.nInputLine );
     728               0 :                 break;
     729                 :             }
     730                 : 
     731            6676 :             sContext.nStackSize--;
     732                 :         }
     733                 : 
     734                 : /* -------------------------------------------------------------------- */
     735                 : /*      Close the start section of a <?...?> element, and pop it        */
     736                 : /*      immediately.                                                    */
     737                 : /* -------------------------------------------------------------------- */
     738           13080 :         else if( sContext.eTokenType == TQuestionClose )
     739                 :         {
     740              48 :             if( sContext.nStackSize == 0 )
     741                 :             {
     742                 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     743                 :                           "Line %d: Found unbalanced '?>'.",
     744               0 :                           sContext.nInputLine );
     745               0 :                 break;
     746                 :             }
     747              48 :             else if( sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue[0] != '?' )
     748                 :             {
     749                 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     750                 :                           "Line %d: Found '?>' without matching '<?'.",
     751               0 :                           sContext.nInputLine );
     752               0 :                 break;
     753                 :             }
     754                 : 
     755              48 :             sContext.nStackSize--;
     756                 :         }
     757                 : 
     758                 : /* -------------------------------------------------------------------- */
     759                 : /*      Handle comments.  They are returned as a whole token with the     */
     760                 : /*      prefix and postfix omitted.  No processing of white space       */
     761                 : /*      will be done.                                                   */
     762                 : /* -------------------------------------------------------------------- */
     763           13032 :         else if( sContext.eTokenType == TComment )
     764                 :         {
     765                 :             CPLXMLNode *psValue;
     766                 : 
     767              11 :             psValue = _CPLCreateXMLNode(NULL, CXT_Comment, sContext.pszToken);
     768              11 :             if (!psValue) break;
     769              11 :             AttachNode( &sContext, psValue );
     770                 :         }
     771                 : 
     772                 : /* -------------------------------------------------------------------- */
     773                 : /*      Handle literals.  They are returned without processing.         */
     774                 : /* -------------------------------------------------------------------- */
     775           13021 :         else if( sContext.eTokenType == TLiteral )
     776                 :         {
     777                 :             CPLXMLNode *psValue;
     778                 : 
     779               1 :             psValue = _CPLCreateXMLNode(NULL, CXT_Literal, sContext.pszToken);
     780               1 :             if (!psValue) break;
     781               1 :             AttachNode( &sContext, psValue );
     782                 :         }
     783                 : 
     784                 : /* -------------------------------------------------------------------- */
     785                 : /*      Add a text value node as a child of the current element.        */
     786                 : /* -------------------------------------------------------------------- */
     787           26040 :         else if( sContext.eTokenType == TString && !sContext.bInElement )
     788                 :         {
     789                 :             CPLXMLNode *psValue;
     790                 : 
     791           13020 :             psValue = _CPLCreateXMLNode(NULL, CXT_Text, sContext.pszToken);
     792           13020 :             if (!psValue) break;
     793           13020 :             AttachNode( &sContext, psValue );
     794                 :         }
     795                 : /* -------------------------------------------------------------------- */
     796                 : /*      Anything else is an error.                                      */
     797                 : /* -------------------------------------------------------------------- */
     798                 :         else
     799                 :         {
     800                 :             CPLError( CE_Failure, CPLE_AppDefined, 
     801                 :                       "Parse error at line %d, unexpected token:%.500s\n", 
     802               0 :                       sContext.nInputLine, sContext.pszToken );
     803               0 :             break;
     804                 :         }
     805                 :     }
     806                 : 
     807                 : /* -------------------------------------------------------------------- */
     808                 : /*      Did we pop all the way out of our stack?                        */
     809                 : /* -------------------------------------------------------------------- */
     810            1371 :     if( CPLGetLastErrorType() == CE_None && sContext.nStackSize != 0 )
     811                 :     {
     812                 :         CPLError( CE_Failure, CPLE_AppDefined, 
     813                 :                   "Parse error at EOF, not all elements have been closed,\n"
     814                 :                   "starting with %.500s\n", 
     815               1 :                   sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue );
     816                 :     }
     817                 : 
     818                 : /* -------------------------------------------------------------------- */
     819                 : /*      Cleanup                                                         */
     820                 : /* -------------------------------------------------------------------- */
     821            1371 :     CPLFree( sContext.pszToken );
     822            1371 :     if( sContext.papsStack != NULL )
     823            1367 :         CPLFree( sContext.papsStack );
     824                 : 
     825            1371 :     if( CPLGetLastErrorType() != CE_None )
     826                 :     {
     827               2 :         CPLDestroyXMLNode( sContext.psFirstNode );
     828               2 :         sContext.psFirstNode = NULL;
     829               2 :         sContext.psLastNode = NULL;
     830                 :     }
     831                 : 
     832            1371 :     return sContext.psFirstNode;
     833                 : }
     834                 : 
     835                 : /************************************************************************/
     836                 : /*                            _GrowBuffer()                             */
     837                 : /************************************************************************/
     838                 : 
     839                 : static void _GrowBuffer( size_t nNeeded, 
     840           53218 :                          char **ppszText, unsigned int *pnMaxLength )
     841                 : 
     842                 : {
     843           53218 :     if( nNeeded+1 >= *pnMaxLength )
     844                 :     {
     845            1918 :         *pnMaxLength = MAX(*pnMaxLength * 2,nNeeded+1);
     846            1918 :         *ppszText = (char *) CPLRealloc(*ppszText, *pnMaxLength);
     847                 :     }
     848           53218 : }
     849                 : 
     850                 : /************************************************************************/
     851                 : /*                        CPLSerializeXMLNode()                         */
     852                 : /************************************************************************/
     853                 : 
     854                 : static void
     855                 : CPLSerializeXMLNode( CPLXMLNode *psNode, int nIndent, 
     856                 :                      char **ppszText, unsigned int *pnLength, 
     857           30697 :                      unsigned int *pnMaxLength )
     858                 : 
     859                 : {
     860           30697 :     if( psNode == NULL )
     861               0 :         return;
     862                 :     
     863                 : /* -------------------------------------------------------------------- */
     864                 : /*      Ensure the buffer is plenty large to hold this additional       */
     865                 : /*      string.                                                         */
     866                 : /* -------------------------------------------------------------------- */
     867           30697 :     *pnLength += strlen(*ppszText + *pnLength);
     868                 :     _GrowBuffer( strlen(psNode->pszValue) + *pnLength + 40 + nIndent, 
     869           30697 :                  ppszText, pnMaxLength );
     870                 :     
     871                 : /* -------------------------------------------------------------------- */
     872                 : /*      Text is just directly emitted.                                  */
     873                 : /* -------------------------------------------------------------------- */
     874           30697 :     if( psNode->eType == CXT_Text )
     875                 :     {
     876           13461 :         char *pszEscaped = CPLEscapeString( psNode->pszValue, -1, CPLES_XML );
     877                 : 
     878           13461 :         CPLAssert( psNode->psChild == NULL );
     879                 : 
     880                 :         /* Escaped text might be bigger than expected. */
     881                 :         _GrowBuffer( strlen(pszEscaped) + *pnLength,
     882           13461 :                      ppszText, pnMaxLength );
     883           13461 :         strcat( *ppszText + *pnLength, pszEscaped );
     884                 : 
     885           13461 :         CPLFree( pszEscaped );
     886                 :     }
     887                 : 
     888                 : /* -------------------------------------------------------------------- */
     889                 : /*      Attributes require a little formatting.                         */
     890                 : /* -------------------------------------------------------------------- */
     891           17236 :     else if( psNode->eType == CXT_Attribute )
     892                 :     {
     893            7541 :         CPLAssert( psNode->psChild != NULL 
     894                 :                    && psNode->psChild->eType == CXT_Text );
     895                 : 
     896            7541 :         sprintf( *ppszText + *pnLength, " %s=\"", psNode->pszValue );
     897                 :         CPLSerializeXMLNode( psNode->psChild, 0, ppszText, 
     898            7541 :                              pnLength, pnMaxLength );
     899            7541 :         strcat( *ppszText + *pnLength, "\"" );
     900                 :     }
     901                 : 
     902                 : /* -------------------------------------------------------------------- */
     903                 : /*      Handle comment output.                                          */
     904                 : /* -------------------------------------------------------------------- */
     905            9695 :     else if( psNode->eType == CXT_Comment )
     906                 :     {
     907                 :         int     i;
     908                 : 
     909               1 :         CPLAssert( psNode->psChild == NULL );
     910                 : 
     911               3 :         for( i = 0; i < nIndent; i++ )
     912               2 :             (*ppszText)[(*pnLength)++] = ' ';
     913                 : 
     914                 :         sprintf( *ppszText + *pnLength, "<!--%s-->\n", 
     915               1 :                  psNode->pszValue );
     916                 :     }
     917                 : 
     918                 : /* -------------------------------------------------------------------- */
     919                 : /*      Handle literal output (like <!DOCTYPE...>)                      */
     920                 : /* -------------------------------------------------------------------- */
     921            9694 :     else if( psNode->eType == CXT_Literal )
     922                 :     {
     923                 :         int     i;
     924                 : 
     925               0 :         CPLAssert( psNode->psChild == NULL );
     926                 : 
     927               0 :         for( i = 0; i < nIndent; i++ )
     928               0 :             (*ppszText)[(*pnLength)++] = ' ';
     929                 : 
     930               0 :         strcpy( *ppszText + *pnLength, psNode->pszValue );
     931               0 :         strcat( *ppszText + *pnLength, "\n" );
     932                 :     }
     933                 : 
     934                 : /* -------------------------------------------------------------------- */
     935                 : /*      Elements actually have to deal with general children, and       */
     936                 : /*      various formatting issues.                                      */
     937                 : /* -------------------------------------------------------------------- */
     938            9694 :     else if( psNode->eType == CXT_Element )
     939                 :     {
     940            9694 :         int             bHasNonAttributeChildren = FALSE;
     941                 :         CPLXMLNode      *psChild;
     942                 :         
     943            9694 :         memset( *ppszText + *pnLength, ' ', nIndent );
     944            9694 :         *pnLength += nIndent;
     945            9694 :         (*ppszText)[*pnLength] = '\0';
     946                 : 
     947            9694 :         sprintf( *ppszText + *pnLength, "<%s", psNode->pszValue );
     948                 : 
     949                 :         /* Serialize *all* the attribute children, regardless of order */
     950           32066 :         for( psChild = psNode->psChild; 
     951                 :              psChild != NULL; 
     952                 :              psChild = psChild->psNext )
     953                 :         {
     954           22372 :             if( psChild->eType == CXT_Attribute )
     955                 :                 CPLSerializeXMLNode( psChild, 0, ppszText, pnLength, 
     956            7541 :                                      pnMaxLength );
     957                 :             else
     958           14831 :                 bHasNonAttributeChildren = TRUE;
     959                 :         }
     960                 :         
     961            9694 :         if( !bHasNonAttributeChildren )
     962                 :         {
     963             634 :             if( psNode->pszValue[0] == '?' )
     964              24 :                 strcat( *ppszText + *pnLength, "?>\n" );
     965                 :             else
     966             610 :                 strcat( *ppszText + *pnLength, " />\n" );
     967                 :         }
     968                 :         else
     969                 :         {
     970            9060 :             int         bJustText = TRUE;
     971                 : 
     972            9060 :             strcat( *ppszText + *pnLength, ">" );
     973                 : 
     974           29151 :             for( psChild = psNode->psChild; 
     975                 :                  psChild != NULL; 
     976                 :                  psChild = psChild->psNext )
     977                 :             {
     978           20091 :                 if( psChild->eType == CXT_Attribute )
     979            5260 :                     continue;
     980                 : 
     981           14831 :                 if( psChild->eType != CXT_Text && bJustText )
     982                 :                 {
     983            3141 :                     bJustText = FALSE;
     984            3141 :                     strcat( *ppszText + *pnLength, "\n" );
     985                 :                 }
     986                 : 
     987                 :                 CPLSerializeXMLNode( psChild, nIndent + 2, ppszText, pnLength, 
     988           14831 :                                      pnMaxLength );
     989                 :             }
     990                 :         
     991            9060 :             *pnLength += strlen(*ppszText + *pnLength);
     992                 :             _GrowBuffer( strlen(psNode->pszValue) + *pnLength + 40 + nIndent, 
     993            9060 :                          ppszText, pnMaxLength );
     994                 : 
     995            9060 :             if( !bJustText )
     996                 :             {
     997            3141 :                 memset( *ppszText + *pnLength, ' ', nIndent );
     998            3141 :                 *pnLength += nIndent;
     999            3141 :                 (*ppszText)[*pnLength] = '\0';
    1000                 :             }
    1001                 : 
    1002            9060 :             *pnLength += strlen(*ppszText + *pnLength);
    1003            9060 :             sprintf( *ppszText + *pnLength, "</%s>\n", psNode->pszValue );
    1004                 :         }
    1005                 :     }
    1006                 : }
    1007                 :                                 
    1008                 : /************************************************************************/
    1009                 : /*                        CPLSerializeXMLTree()                         */
    1010                 : /************************************************************************/
    1011                 : 
    1012                 : /**
    1013                 :  * \brief Convert tree into string document.
    1014                 :  *
    1015                 :  * This function converts a CPLXMLNode tree representation of a document
    1016                 :  * into a flat string representation.  White space indentation is used
    1017                 :  * visually preserve the tree structure of the document.  The returned 
    1018                 :  * document becomes owned by the caller and should be freed with CPLFree()
    1019                 :  * when no longer needed.
    1020                 :  *
    1021                 :  * @param psNode
    1022                 :  *
    1023                 :  * @return the document on success or NULL on failure. 
    1024                 :  */
    1025                 : 
    1026             760 : char *CPLSerializeXMLTree( CPLXMLNode *psNode )
    1027                 : 
    1028                 : {
    1029             760 :     unsigned int nMaxLength = 100, nLength = 0;
    1030             760 :     char *pszText = NULL;
    1031                 :     CPLXMLNode *psThis;
    1032                 : 
    1033             760 :     pszText = (char *) CPLMalloc(nMaxLength);
    1034             760 :     pszText[0] = '\0';
    1035                 : 
    1036            1544 :     for( psThis = psNode; psThis != NULL; psThis = psThis->psNext )
    1037             784 :         CPLSerializeXMLNode( psThis, 0, &pszText, &nLength, &nMaxLength );
    1038                 : 
    1039             760 :     return pszText;
    1040                 : }
    1041                 : 
    1042                 : /************************************************************************/
    1043                 : /*                          CPLCreateXMLNode()                          */
    1044                 : /************************************************************************/
    1045                 : 
    1046                 : /**
    1047                 :  * \brief Create an document tree item.
    1048                 :  *
    1049                 :  * Create a single CPLXMLNode object with the desired value and type, and
    1050                 :  * attach it as a child of the indicated parent.  
    1051                 :  *
    1052                 :  * @param poParent the parent to which this node should be attached as a 
    1053                 :  * child.  May be NULL to keep as free standing. 
    1054                 :  * @param eType the type of the newly created node
    1055                 :  * @param pszText the value of the newly created node
    1056                 :  *
    1057                 :  * @return the newly created node, now owned by the caller (or parent node).
    1058                 :  */
    1059                 : 
    1060                 : CPLXMLNode *CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, 
    1061           31965 :                               const char *pszText )
    1062                 : 
    1063                 : {
    1064                 :     CPLXMLNode  *psNode;
    1065                 : 
    1066                 : /* -------------------------------------------------------------------- */
    1067                 : /*      Create new node.                                                */
    1068                 : /* -------------------------------------------------------------------- */
    1069           31965 :     psNode = (CPLXMLNode *) CPLCalloc(sizeof(CPLXMLNode),1);
    1070                 :     
    1071           31965 :     psNode->eType = eType;
    1072           31965 :     psNode->pszValue = CPLStrdup( pszText );
    1073                 : 
    1074                 : /* -------------------------------------------------------------------- */
    1075                 : /*      Attach to parent, if provided.                                  */
    1076                 : /* -------------------------------------------------------------------- */
    1077           31965 :     if( poParent != NULL )
    1078                 :     {
    1079           27508 :         if( poParent->psChild == NULL )
    1080           17064 :             poParent->psChild = psNode;
    1081                 :         else
    1082                 :         {
    1083           10444 :             CPLXMLNode  *psLink = poParent->psChild;
    1084                 : 
    1085          113550 :             while( psLink->psNext != NULL )
    1086           92662 :                 psLink = psLink->psNext;
    1087                 : 
    1088           10444 :             psLink->psNext = psNode;
    1089                 :         }
    1090                 :     }
    1091                 :     
    1092           31965 :     return psNode;
    1093                 : }
    1094                 : 
    1095                 : /************************************************************************/
    1096                 : /*                         _CPLCreateXMLNode()                          */
    1097                 : /************************************************************************/
    1098                 : 
    1099                 : /* Same as CPLCreateXMLNode() but can return NULL in case of out-of-memory */
    1100                 : /* situation */
    1101                 : 
    1102                 : static CPLXMLNode *_CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, 
    1103          107829 :                                const char *pszText )
    1104                 : 
    1105                 : {
    1106                 :     CPLXMLNode  *psNode;
    1107                 : 
    1108                 : /* -------------------------------------------------------------------- */
    1109                 : /*      Create new node.                                                */
    1110                 : /* -------------------------------------------------------------------- */
    1111          107829 :     psNode = (CPLXMLNode *) VSICalloc(sizeof(CPLXMLNode),1);
    1112          107829 :     if (psNode == NULL)
    1113                 :     {
    1114               0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate CPLXMLNode");
    1115               0 :         return NULL;
    1116                 :     }
    1117                 :     
    1118          107829 :     psNode->eType = eType;
    1119          107829 :     psNode->pszValue = VSIStrdup( pszText );
    1120          107829 :     if (psNode->pszValue == NULL)
    1121                 :     {
    1122               0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate psNode->pszValue");
    1123               0 :         VSIFree(psNode);
    1124               0 :         return NULL;
    1125                 :     }
    1126                 : 
    1127                 : /* -------------------------------------------------------------------- */
    1128                 : /*      Attach to parent, if provided.                                  */
    1129                 : /* -------------------------------------------------------------------- */
    1130          107829 :     if( poParent != NULL )
    1131                 :     {
    1132           33864 :         if( poParent->psChild == NULL )
    1133           33864 :             poParent->psChild = psNode;
    1134                 :         else
    1135                 :         {
    1136               0 :             CPLXMLNode  *psLink = poParent->psChild;
    1137                 : 
    1138               0 :             while( psLink->psNext != NULL )
    1139               0 :                 psLink = psLink->psNext;
    1140                 : 
    1141               0 :             psLink->psNext = psNode;
    1142                 :         }
    1143                 :     }
    1144                 :     
    1145          107829 :     return psNode;
    1146                 : }
    1147                 : 
    1148                 : /************************************************************************/
    1149                 : /*                         CPLDestroyXMLNode()                          */
    1150                 : /************************************************************************/
    1151                 : 
    1152                 : /**
    1153                 :  * \brief Destroy a tree. 
    1154                 :  *
    1155                 :  * This function frees resources associated with a CPLXMLNode and all its
    1156                 :  * children nodes.  
    1157                 :  *
    1158                 :  * @param psNode the tree to free.
    1159                 :  */
    1160                 : 
    1161            2640 : void CPLDestroyXMLNode( CPLXMLNode *psNode )
    1162                 : 
    1163                 : {
    1164          145074 :     while(psNode != NULL)
    1165                 :     {
    1166          139794 :         if( psNode->pszValue != NULL )
    1167          139794 :             CPLFree( psNode->pszValue );
    1168                 : 
    1169          139794 :         if( psNode->psChild != NULL )
    1170                 :         {
    1171           78860 :             CPLXMLNode* psNext = psNode->psNext;
    1172           78860 :             psNode->psNext = psNode->psChild;
    1173                 :             /* Move the child and its siblings as the next */
    1174                 :             /* siblings of the current node */
    1175           78860 :             if (psNext != NULL)
    1176                 :             {
    1177           71239 :                 CPLXMLNode* psIter = psNode->psChild;
    1178          182190 :                 while(psIter->psNext != NULL)
    1179           39712 :                     psIter = psIter->psNext;
    1180           71239 :                 psIter->psNext = psNext;
    1181                 :             }
    1182                 :         }
    1183                 : 
    1184          139794 :         CPLXMLNode* psNext = psNode->psNext;
    1185                 : 
    1186          139794 :         CPLFree( psNode );
    1187                 : 
    1188          139794 :         psNode = psNext;
    1189                 :     }
    1190            2640 : }
    1191                 : 
    1192                 : /************************************************************************/
    1193                 : /*                           CPLSearchXMLNode()                         */
    1194                 : /************************************************************************/
    1195                 : 
    1196                 : /**
    1197                 :  * \brief Search for a node in document.
    1198                 :  *
    1199                 :  * Searches the children (and potentially siblings) of the documented
    1200                 :  * passed in for the named element or attribute.  To search following
    1201                 :  * siblings as well as children, prefix the pszElement name with an equal
    1202                 :  * sign.  This function does an in-order traversal of the document tree.
    1203                 :  * So it will first match against the current node, then it's first child,
    1204                 :  * that childs first child, and so on. 
    1205                 :  *
    1206                 :  * Use CPLGetXMLNode() to find a specific child, or along a specific
    1207                 :  * node path. 
    1208                 :  *
    1209                 :  * @param psRoot the subtree to search.  This should be a node of type
    1210                 :  * CXT_Element.  NULL is safe. 
    1211                 :  *
    1212                 :  * @param pszElement the name of the element or attribute to search for.
    1213                 :  *
    1214                 :  * @return The matching node or NULL on failure. 
    1215                 :  */
    1216                 : 
    1217            1629 : CPLXMLNode *CPLSearchXMLNode( CPLXMLNode *psRoot, const char *pszElement )
    1218                 : 
    1219                 : {
    1220            1629 :     int         bSideSearch = FALSE;
    1221                 :     CPLXMLNode *psChild, *psResult;
    1222                 : 
    1223            1629 :     if( psRoot == NULL || pszElement == NULL )
    1224               2 :         return NULL;
    1225                 : 
    1226            1627 :     if( *pszElement == '=' )
    1227                 :     {
    1228               6 :         bSideSearch = TRUE;
    1229               6 :         pszElement++;
    1230                 :     }
    1231                 : 
    1232                 : /* -------------------------------------------------------------------- */
    1233                 : /*      Does this node match?                                           */
    1234                 : /* -------------------------------------------------------------------- */
    1235            1627 :     if( (psRoot->eType == CXT_Element 
    1236                 :          || psRoot->eType == CXT_Attribute)
    1237                 :         && EQUAL(pszElement,psRoot->pszValue) )
    1238               0 :         return psRoot;
    1239                 : 
    1240                 : /* -------------------------------------------------------------------- */
    1241                 : /*      Search children.                                                */
    1242                 : /* -------------------------------------------------------------------- */
    1243            4024 :     for( psChild = psRoot->psChild; psChild != NULL; psChild = psChild->psNext)
    1244                 :     {
    1245            2428 :         if( (psChild->eType == CXT_Element 
    1246                 :              || psChild->eType == CXT_Attribute)
    1247                 :             && EQUAL(pszElement,psChild->pszValue) )
    1248              10 :             return psChild;
    1249                 : 
    1250            2418 :         if( psChild->psChild != NULL )
    1251                 :         {
    1252            1609 :             psResult = CPLSearchXMLNode( psChild, pszElement );
    1253            1609 :             if( psResult != NULL )
    1254              21 :                 return psResult;
    1255                 :         }
    1256                 :     }
    1257                 : 
    1258                 : /* -------------------------------------------------------------------- */
    1259                 : /*      Search siblings if we are in side search mode.                  */
    1260                 : /* -------------------------------------------------------------------- */
    1261            1596 :     if( bSideSearch )
    1262                 :     {
    1263               8 :         for( psRoot = psRoot->psNext; psRoot != NULL; psRoot = psRoot->psNext )
    1264                 :         {
    1265               8 :             psResult = CPLSearchXMLNode( psRoot, pszElement );
    1266               8 :             if( psResult != NULL )
    1267               6 :                 return psResult;
    1268                 :         }
    1269                 :     }
    1270                 :     
    1271            1590 :     return NULL;
    1272                 : }
    1273                 : 
    1274                 : /************************************************************************/
    1275                 : /*                           CPLGetXMLNode()                            */
    1276                 : /************************************************************************/
    1277                 : 
    1278                 : /**
    1279                 :  * \brief Find node by path.
    1280                 :  *
    1281                 :  * Searches the document or subdocument indicated by psRoot for an element 
    1282                 :  * (or attribute) with the given path.  The path should consist of a set of
    1283                 :  * element names separated by dots, not including the name of the root 
    1284                 :  * element (psRoot).  If the requested element is not found NULL is returned.
    1285                 :  *
    1286                 :  * Attribute names may only appear as the last item in the path. 
    1287                 :  *
    1288                 :  * The search is done from the root nodes children, but all intermediate
    1289                 :  * nodes in the path must be specified.  Seaching for "name" would only find
    1290                 :  * a name element or attribute if it is a direct child of the root, not at any
    1291                 :  * level in the subdocument. 
    1292                 :  *
    1293                 :  * If the pszPath is prefixed by "=" then the search will begin with the
    1294                 :  * root node, and it's siblings, instead of the root nodes children.  This
    1295                 :  * is particularly useful when searching within a whole document which is
    1296                 :  * often prefixed by one or more "junk" nodes like the <?xml> declaration.
    1297                 :  *
    1298                 :  * @param psRoot the subtree in which to search.  This should be a node of 
    1299                 :  * type CXT_Element.  NULL is safe. 
    1300                 :  *
    1301                 :  * @param pszPath the list of element names in the path (dot separated). 
    1302                 :  *
    1303                 :  * @return the requested element node, or NULL if not found. 
    1304                 :  */
    1305                 : 
    1306           35482 : CPLXMLNode *CPLGetXMLNode( CPLXMLNode *psRoot, const char *pszPath )
    1307                 : 
    1308                 : {
    1309                 :     char        *apszTokens[2];
    1310                 :     char        **papszTokens;
    1311           35482 :     int         iToken = 0;
    1312           35482 :     int         bSideSearch = FALSE;
    1313                 : 
    1314           35482 :     if( psRoot == NULL || pszPath == NULL )
    1315               0 :         return NULL;
    1316                 : 
    1317           35482 :     if( *pszPath == '=' )
    1318                 :     {
    1319              25 :         bSideSearch = TRUE;
    1320              25 :         pszPath++;
    1321                 :     }
    1322                 : 
    1323                 :     /* Slight optimization : avoid using CSLTokenizeStringComplex that */
    1324                 :     /* does memory allocations when it is not really necessary */
    1325           35482 :     if (strchr(pszPath, '.'))
    1326             332 :         papszTokens = CSLTokenizeStringComplex( pszPath, ".", FALSE, FALSE );
    1327                 :     else
    1328                 :     {
    1329           35150 :         apszTokens[0] = (char*) pszPath;
    1330           35150 :         apszTokens[1] = NULL;
    1331           35150 :         papszTokens = apszTokens;
    1332                 :     }
    1333                 : 
    1334           90409 :     while( papszTokens[iToken] != NULL && psRoot != NULL )
    1335                 :     {
    1336                 :         CPLXMLNode *psChild;
    1337                 : 
    1338           35859 :         if( bSideSearch )
    1339                 :         {
    1340              25 :             psChild = psRoot;
    1341              25 :             bSideSearch = FALSE;
    1342                 :         }
    1343                 :         else
    1344           35834 :             psChild = psRoot->psChild;
    1345                 : 
    1346          115950 :         for( ; psChild != NULL; psChild = psChild->psNext ) 
    1347                 :         {
    1348           99536 :             if( psChild->eType != CXT_Text 
    1349                 :                 && EQUAL(papszTokens[iToken],psChild->pszValue) )
    1350           19445 :                 break;
    1351                 :         }
    1352                 : 
    1353           35859 :         if( psChild == NULL )
    1354                 :         {
    1355           16414 :             psRoot = NULL;
    1356           16414 :             break;
    1357                 :         }
    1358                 : 
    1359           19445 :         psRoot = psChild;
    1360           19445 :         iToken++;
    1361                 :     }
    1362                 : 
    1363           35482 :     if (papszTokens != apszTokens)
    1364             332 :         CSLDestroy( papszTokens );
    1365           35482 :     return psRoot;
    1366                 : }
    1367                 : 
    1368                 : /************************************************************************/
    1369                 : /*                           CPLGetXMLValue()                           */
    1370                 : /************************************************************************/
    1371                 : 
    1372                 : /**
    1373                 :  * \brief Fetch element/attribute value. 
    1374                 :  *
    1375                 :  * Searches the document for the element/attribute value associated with
    1376                 :  * the path.  The corresponding node is internally found with CPLGetXMLNode()
    1377                 :  * (see there for details on path handling).  Once found, the value is 
    1378                 :  * considered to be the first CXT_Text child of the node.
    1379                 :  *
    1380                 :  * If the attribute/element search fails, or if the found node has not
    1381                 :  * value then the passed default value is returned. 
    1382                 :  *
    1383                 :  * The returned value points to memory within the document tree, and should
    1384                 :  * not be altered or freed. 
    1385                 :  *
    1386                 :  * @param psRoot the subtree in which to search.  This should be a node of 
    1387                 :  * type CXT_Element.  NULL is safe. 
    1388                 :  *
    1389                 :  * @param pszPath the list of element names in the path (dot separated).  An
    1390                 :  * empty path means get the value of the psRoot node.
    1391                 :  *
    1392                 :  * @param pszDefault the value to return if a corresponding value is not
    1393                 :  * found, may be NULL.
    1394                 :  *
    1395                 :  * @return the requested value or pszDefault if not found.
    1396                 :  */
    1397                 : 
    1398                 : const char *CPLGetXMLValue( CPLXMLNode *psRoot, const char *pszPath, 
    1399           31009 :                             const char *pszDefault )
    1400                 : 
    1401                 : {
    1402                 :     CPLXMLNode  *psTarget;
    1403                 : 
    1404           31956 :     if( pszPath == NULL || *pszPath == '\0' )
    1405             947 :         psTarget  = psRoot;
    1406                 :     else
    1407           30062 :         psTarget = CPLGetXMLNode( psRoot, pszPath );
    1408                 : 
    1409           31009 :     if( psTarget == NULL )
    1410           12710 :         return pszDefault;
    1411                 : 
    1412           18299 :     if( psTarget->eType == CXT_Attribute )
    1413                 :     {
    1414           12935 :         CPLAssert( psTarget->psChild != NULL 
    1415                 :                    && psTarget->psChild->eType == CXT_Text );
    1416                 : 
    1417           12935 :         return psTarget->psChild->pszValue;
    1418                 :     }
    1419                 : 
    1420            5364 :     if( psTarget->eType == CXT_Element )
    1421                 :     {
    1422                 :         // Find first non-attribute child, and verify it is a single text 
    1423                 :         // with no siblings
    1424                 : 
    1425            5364 :         psTarget = psTarget->psChild;
    1426                 : 
    1427           11989 :         while( psTarget != NULL && psTarget->eType == CXT_Attribute )
    1428            1261 :             psTarget = psTarget->psNext;
    1429                 : 
    1430            5364 :         if( psTarget != NULL 
    1431                 :             && psTarget->eType == CXT_Text
    1432                 :             && psTarget->psNext == NULL )
    1433            5337 :             return psTarget->pszValue;
    1434                 :     }
    1435                 : 
    1436              27 :     return pszDefault;
    1437                 : }
    1438                 : 
    1439                 : /************************************************************************/
    1440                 : /*                           CPLAddXMLChild()                           */
    1441                 : /************************************************************************/
    1442                 : 
    1443                 : /**
    1444                 :  * \brief Add child node to parent. 
    1445                 :  *
    1446                 :  * The passed child is added to the list of children of the indicated
    1447                 :  * parent.  Normally the child is added at the end of the parents child
    1448                 :  * list, but attributes (CXT_Attribute) will be inserted after any other
    1449                 :  * attributes but before any other element type.  Ownership of the child
    1450                 :  * node is effectively assumed by the parent node.   If the child has
    1451                 :  * siblings (it's psNext is not NULL) they will be trimmed, but if the child
    1452                 :  * has children they are carried with it. 
    1453                 :  *
    1454                 :  * @param psParent the node to attach the child to.  May not be NULL.
    1455                 :  *
    1456                 :  * @param psChild the child to add to the parent.  May not be NULL.  Should 
    1457                 :  * not be a child of any other parent. 
    1458                 :  */
    1459                 : 
    1460            1532 : void CPLAddXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild )
    1461                 : 
    1462                 : {
    1463                 :     CPLXMLNode *psSib;
    1464                 : 
    1465            1532 :     if( psParent->psChild == NULL )
    1466                 :     {
    1467             419 :         psParent->psChild = psChild;
    1468             419 :         return;
    1469                 :     }
    1470                 : 
    1471                 :     // Insert at head of list if first child is not attribute.
    1472            1113 :     if( psChild->eType == CXT_Attribute 
    1473                 :         && psParent->psChild->eType != CXT_Attribute )
    1474                 :     {
    1475               0 :         psChild->psNext = psParent->psChild;
    1476               0 :         psParent->psChild = psChild;
    1477               0 :         return;
    1478                 :     }
    1479                 : 
    1480                 :     // Search for end of list.
    1481            2582 :     for( psSib = psParent->psChild; 
    1482                 :          psSib->psNext != NULL; 
    1483                 :          psSib = psSib->psNext ) 
    1484                 :     {
    1485                 :         // Insert attributes if the next node is not an attribute.
    1486            1469 :         if( psChild->eType == CXT_Attribute 
    1487                 :             && psSib->psNext != NULL 
    1488                 :             && psSib->psNext->eType != CXT_Attribute )
    1489                 :         {
    1490               0 :             psChild->psNext = psSib->psNext;
    1491               0 :             psSib->psNext = psChild;
    1492               0 :             return;
    1493                 :         }
    1494                 :     }
    1495                 : 
    1496            1113 :     psSib->psNext = psChild;
    1497                 : }
    1498                 : 
    1499                 : /************************************************************************/
    1500                 : /*                        CPLRemoveXMLChild()                           */
    1501                 : /************************************************************************/
    1502                 : 
    1503                 : /**
    1504                 :  * \brief Remove child node from parent. 
    1505                 :  *
    1506                 :  * The passed child is removed from the child list of the passed parent,
    1507                 :  * but the child is not destroyed.  The child retains ownership of it's
    1508                 :  * own children, but is cleanly removed from the child list of the parent.
    1509                 :  *
    1510                 :  * @param psParent the node to the child is attached to.
    1511                 :  *
    1512                 :  * @param psChild the child to remove.
    1513                 :  *
    1514                 :  * @return TRUE on success or FALSE if the child was not found.
    1515                 :  */
    1516                 : 
    1517               0 : int CPLRemoveXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild )
    1518                 : 
    1519                 : {
    1520               0 :     CPLXMLNode *psLast = NULL, *psThis;
    1521                 : 
    1522               0 :     if( psParent == NULL )
    1523               0 :         return FALSE;
    1524                 : 
    1525               0 :     for( psThis = psParent->psChild; 
    1526                 :          psThis != NULL; 
    1527                 :          psLast = psThis, psThis = psThis->psNext )
    1528                 :     {
    1529               0 :         if( psThis == psChild )
    1530                 :         {
    1531               0 :             if( psLast == NULL )
    1532               0 :                 psParent->psChild = psThis->psNext;
    1533                 :             else 
    1534               0 :                 psLast->psNext = psThis->psNext;
    1535                 : 
    1536               0 :             psThis->psNext = NULL;
    1537               0 :             return TRUE;
    1538                 :         }
    1539                 :     }
    1540                 : 
    1541               0 :     return FALSE;
    1542                 : }
    1543                 : 
    1544                 : /************************************************************************/
    1545                 : /*                          CPLAddXMLSibling()                          */
    1546                 : /************************************************************************/
    1547                 : 
    1548                 : /**
    1549                 :  * \brief Add new sibling.
    1550                 :  *
    1551                 :  * The passed psNewSibling is added to the end of siblings of the 
    1552                 :  * psOlderSibling node.  That is, it is added to the end of the psNext
    1553                 :  * chain.  There is no special handling if psNewSibling is an attribute. 
    1554                 :  * If this is required, use CPLAddXMLChild(). 
    1555                 :  *
    1556                 :  * @param psOlderSibling the node to attach the sibling after.
    1557                 :  *
    1558                 :  * @param psNewSibling the node to add at the end of psOlderSiblings psNext 
    1559                 :  * chain.
    1560                 :  */
    1561                 : 
    1562             195 : void CPLAddXMLSibling( CPLXMLNode *psOlderSibling, CPLXMLNode *psNewSibling )
    1563                 : 
    1564                 : {
    1565             195 :     if( psOlderSibling == NULL )
    1566               0 :         return;
    1567                 : 
    1568             392 :     while( psOlderSibling->psNext != NULL )
    1569               2 :         psOlderSibling = psOlderSibling->psNext;
    1570                 : 
    1571             195 :     psOlderSibling->psNext = psNewSibling;
    1572                 : }
    1573                 : 
    1574                 : /************************************************************************/
    1575                 : /*                    CPLCreateXMLElementAndValue()                     */
    1576                 : /************************************************************************/
    1577                 : 
    1578                 : /**
    1579                 :  * \brief Create an element and text value.
    1580                 :  *
    1581                 :  * This is function is a convenient short form for:
    1582                 :  *
    1583                 :  * \code
    1584                 :  *     CPLXMLNode *psTextNode;
    1585                 :  *     CPLXMLNode *psElementNode;
    1586                 :  *
    1587                 :  *     psElementNode = CPLCreateXMLNode( psParent, CXT_Element, pszName );
    1588                 :  *     psTextNode = CPLCreateXMLNode( psElementNode, CXT_Text, pszValue );
    1589                 :  * 
    1590                 :  *     return psElementNode;
    1591                 :  * \endcode
    1592                 :  *
    1593                 :  * It creates a CXT_Element node, with a CXT_Text child, and
    1594                 :  * attaches the element to the passed parent.
    1595                 :  *
    1596                 :  * @param psParent the parent node to which the resulting node should
    1597                 :  * be attached.  May be NULL to keep as freestanding. 
    1598                 :  *
    1599                 :  * @param pszName the element name to create.
    1600                 :  * @param pszValue the text to attach to the element. Must not be NULL. 
    1601                 :  *
    1602                 :  * @return the pointer to the new element node.
    1603                 :  */
    1604                 : 
    1605                 : CPLXMLNode *CPLCreateXMLElementAndValue( CPLXMLNode *psParent, 
    1606                 :                                          const char *pszName, 
    1607            1016 :                                          const char *pszValue )
    1608                 : 
    1609                 : {
    1610                 :     CPLXMLNode *psElementNode;
    1611                 : 
    1612            1016 :     psElementNode = CPLCreateXMLNode( psParent, CXT_Element, pszName );
    1613            1016 :     CPLCreateXMLNode( psElementNode, CXT_Text, pszValue );
    1614                 : 
    1615            1016 :     return psElementNode;
    1616                 : }
    1617                 : 
    1618                 : /************************************************************************/
    1619                 : /*                          CPLCloneXMLTree()                           */
    1620                 : /************************************************************************/
    1621                 : 
    1622                 : /**
    1623                 :  * \brief Copy tree.
    1624                 :  *
    1625                 :  * Creates a deep copy of a CPLXMLNode tree.  
    1626                 :  *
    1627                 :  * @param psTree the tree to duplicate. 
    1628                 :  *
    1629                 :  * @return a copy of the whole tree. 
    1630                 :  */
    1631                 : 
    1632             527 : CPLXMLNode *CPLCloneXMLTree( CPLXMLNode *psTree )
    1633                 : 
    1634                 : {
    1635             527 :     CPLXMLNode *psPrevious = NULL;
    1636             527 :     CPLXMLNode *psReturn = NULL;
    1637                 : 
    1638            1938 :     while( psTree != NULL )
    1639                 :     {
    1640                 :         CPLXMLNode *psCopy;
    1641                 : 
    1642             884 :         psCopy = CPLCreateXMLNode( NULL, psTree->eType, psTree->pszValue );
    1643             884 :         if( psReturn == NULL )
    1644             527 :             psReturn = psCopy;
    1645             884 :         if( psPrevious != NULL )
    1646             357 :             psPrevious->psNext = psCopy;
    1647                 : 
    1648             884 :         if( psTree->psChild != NULL )
    1649             513 :             psCopy->psChild = CPLCloneXMLTree( psTree->psChild );
    1650                 : 
    1651             884 :         psPrevious = psCopy;
    1652             884 :         psTree = psTree->psNext;
    1653                 :     }
    1654                 : 
    1655             527 :     return psReturn;
    1656                 : }
    1657                 : 
    1658                 : /************************************************************************/
    1659                 : /*                           CPLSetXMLValue()                           */
    1660                 : /************************************************************************/
    1661                 : 
    1662                 : /**
    1663                 :  * \brief Set element value by path. 
    1664                 :  *
    1665                 :  * Find (or create) the target element or attribute specified in the
    1666                 :  * path, and assign it the indicated value. 
    1667                 :  *
    1668                 :  * Any path elements that do not already exist will be created.  The target
    1669                 :  * nodes value (the first CXT_Text child) will be replaced with the provided
    1670                 :  * value.  
    1671                 :  *
    1672                 :  * If the target node is an attribute instead of an element, the name
    1673                 :  * should be prefixed with a #.
    1674                 :  *
    1675                 :  * Example:
    1676                 :  *   CPLSetXMLValue( "Citation.Id.Description", "DOQ dataset" );
    1677                 :  *   CPLSetXMLValue( "Citation.Id.Description.#name", "doq" );
    1678                 :  *
    1679                 :  * @param psRoot the subdocument to be updated. 
    1680                 :  *
    1681                 :  * @param pszPath the dot seperated path to the target element/attribute.
    1682                 :  *
    1683                 :  * @param pszValue the text value to assign. 
    1684                 :  *
    1685                 :  * @return TRUE on success.
    1686                 :  */
    1687                 : 
    1688                 : int CPLSetXMLValue( CPLXMLNode *psRoot,  const char *pszPath,
    1689            7905 :                     const char *pszValue )
    1690                 : 
    1691                 : {
    1692                 :     char        **papszTokens;
    1693            7905 :     int         iToken = 0;
    1694                 : 
    1695            7905 :     papszTokens = CSLTokenizeStringComplex( pszPath, ".", FALSE, FALSE );
    1696                 : 
    1697           24731 :     while( papszTokens[iToken] != NULL && psRoot != NULL )
    1698                 :     {
    1699                 :         CPLXMLNode *psChild;
    1700            8921 :         int        bIsAttribute = FALSE;
    1701            8921 :         const char *pszName = papszTokens[iToken];
    1702                 : 
    1703            8921 :         if( pszName[0] == '#' )
    1704                 :         {
    1705            6728 :             bIsAttribute = TRUE;
    1706            6728 :             pszName++;
    1707                 :         }
    1708                 : 
    1709            8921 :         if( psRoot->eType != CXT_Element )
    1710               0 :             return FALSE;
    1711                 : 
    1712           16815 :         for( psChild = psRoot->psChild; psChild != NULL; 
    1713                 :              psChild = psChild->psNext ) 
    1714                 :         {
    1715            8714 :             if( psChild->eType != CXT_Text 
    1716                 :                 && EQUAL(pszName,psChild->pszValue) )
    1717             820 :                 break;
    1718                 :         }
    1719                 : 
    1720            8921 :         if( psChild == NULL )
    1721                 :         {
    1722            8101 :             if( bIsAttribute )
    1723            6728 :                 psChild = CPLCreateXMLNode( psRoot, CXT_Attribute, pszName );
    1724                 :             else
    1725            1373 :                 psChild = CPLCreateXMLNode( psRoot, CXT_Element, pszName );
    1726                 :         }
    1727                 : 
    1728            8921 :         psRoot = psChild;
    1729            8921 :         iToken++;
    1730                 :     }
    1731                 : 
    1732            7905 :     CSLDestroy( papszTokens );
    1733                 : 
    1734                 : /* -------------------------------------------------------------------- */
    1735                 : /*      Find the "text" child if there is one.                          */
    1736                 : /* -------------------------------------------------------------------- */
    1737            7905 :     CPLXMLNode *psTextChild = psRoot->psChild;
    1738                 : 
    1739           15848 :     while( psTextChild != NULL && psTextChild->eType != CXT_Text )
    1740              38 :         psTextChild = psTextChild->psNext;
    1741                 : 
    1742                 : /* -------------------------------------------------------------------- */
    1743                 : /*      Now set a value node under this node.                           */
    1744                 : /* -------------------------------------------------------------------- */
    1745                 :     
    1746            7905 :     if( psTextChild == NULL )
    1747            7867 :         CPLCreateXMLNode( psRoot, CXT_Text, pszValue );
    1748                 :     else 
    1749                 :     {
    1750              38 :         CPLFree( psTextChild->pszValue );
    1751              38 :         psTextChild->pszValue = CPLStrdup( pszValue );
    1752                 :     }
    1753                 : 
    1754            7905 :     return TRUE;
    1755                 : }
    1756                 : 
    1757                 : /************************************************************************/
    1758                 : /*                        CPLStripXMLNamespace()                        */
    1759                 : /************************************************************************/
    1760                 : 
    1761                 : /**
    1762                 :  * \brief Strip indicated namespaces. 
    1763                 :  *
    1764                 :  * The subdocument (psRoot) is recursively examined, and any elements
    1765                 :  * with the indicated namespace prefix will have the namespace prefix
    1766                 :  * stripped from the element names.  If the passed namespace is NULL, then
    1767                 :  * all namespace prefixes will be stripped. 
    1768                 :  *
    1769                 :  * Nodes other than elements should remain unaffected.  The changes are
    1770                 :  * made "in place", and should not alter any node locations, only the 
    1771                 :  * pszValue field of affected nodes. 
    1772                 :  *
    1773                 :  * @param psRoot the document to operate on.
    1774                 :  * @param pszNamespace the name space prefix (not including colon), or NULL.
    1775                 :  * @param bRecurse TRUE to recurse over whole document, or FALSE to only
    1776                 :  * operate on the passed node.
    1777                 :  */
    1778                 : 
    1779                 : void CPLStripXMLNamespace( CPLXMLNode *psRoot, 
    1780                 :                            const char *pszNamespace, 
    1781            2182 :                            int bRecurse )
    1782                 : 
    1783                 : {
    1784            2182 :     size_t nNameSpaceLen = (pszNamespace) ? strlen(pszNamespace) : 0;
    1785                 : 
    1786            7686 :     while( psRoot != NULL )
    1787                 :     {
    1788                 : 
    1789            3322 :         if( psRoot->eType == CXT_Element || psRoot->eType == CXT_Attribute )
    1790                 :         {
    1791            2196 :             if( pszNamespace != NULL )
    1792                 :             {
    1793             126 :                 if( EQUALN(pszNamespace,psRoot->pszValue,nNameSpaceLen) 
    1794                 :                     && psRoot->pszValue[nNameSpaceLen] == ':' )
    1795                 :                 {
    1796                 :                     memmove(psRoot->pszValue, psRoot->pszValue+nNameSpaceLen+1,
    1797             120 :                            strlen(psRoot->pszValue+nNameSpaceLen+1) + 1);
    1798                 :                 }
    1799                 :             }
    1800                 :             else
    1801                 :             {
    1802                 :                 const char *pszCheck;
    1803                 : 
    1804           13898 :                 for( pszCheck = psRoot->pszValue; *pszCheck != '\0'; pszCheck++ )
    1805                 :                 {
    1806           13165 :                     if( *pszCheck == ':' )
    1807                 :                     {
    1808            1337 :                         memmove(psRoot->pszValue, pszCheck + 1, strlen(pszCheck + 1) + 1);
    1809            1337 :                         break;
    1810                 :                     }
    1811                 :                 }
    1812                 :             }
    1813                 :         }
    1814                 : 
    1815            3322 :         if( bRecurse )
    1816                 :         {
    1817            3322 :             if( psRoot->psChild != NULL )
    1818            2172 :                 CPLStripXMLNamespace( psRoot->psChild, pszNamespace, 1 );
    1819                 : 
    1820            3322 :             psRoot = psRoot->psNext;
    1821                 :         }
    1822                 :         else
    1823               0 :             break;
    1824                 :     }
    1825            2182 : }
    1826                 : 
    1827                 : /************************************************************************/
    1828                 : /*                          CPLParseXMLFile()                           */
    1829                 : /************************************************************************/
    1830                 : 
    1831                 : /**
    1832                 :  * \brief Parse XML file into tree.
    1833                 :  *
    1834                 :  * The named file is opened, loaded into memory as a big string, and
    1835                 :  * parsed with CPLParseXMLString().  Errors in reading the file or parsing
    1836                 :  * the XML will be reported by CPLError(). 
    1837                 :  *
    1838                 :  * The "large file" API is used, so XML files can come from virtualized
    1839                 :  * files. 
    1840                 :  *
    1841                 :  * @param pszFilename the file to open. 
    1842                 :  *
    1843                 :  * @return NULL on failure, or the document tree on success.
    1844                 :  */
    1845                 : 
    1846             545 : CPLXMLNode *CPLParseXMLFile( const char *pszFilename )
    1847                 : 
    1848                 : {
    1849                 :     FILE            *fp;
    1850                 :     vsi_l_offset    nLen;
    1851                 :     char            *pszDoc;
    1852                 :     CPLXMLNode      *psTree;
    1853                 : 
    1854                 : /* -------------------------------------------------------------------- */
    1855                 : /*      Read the file.                                                  */
    1856                 : /* -------------------------------------------------------------------- */
    1857             545 :     fp = VSIFOpenL( pszFilename, "rb" );
    1858             545 :     if( fp == NULL )
    1859                 :     {
    1860                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
    1861               4 :                   "Failed to open %.500s to read.", pszFilename );
    1862               4 :         return NULL;
    1863                 :     }
    1864                 : 
    1865             541 :     VSIFSeekL( fp, 0, SEEK_END );
    1866             541 :     nLen = VSIFTellL( fp );
    1867             541 :     VSIFSeekL( fp, 0, SEEK_SET );
    1868                 :     
    1869             541 :     pszDoc = (char *) VSIMalloc((size_t)nLen + 1);
    1870             541 :     if( pszDoc == NULL )
    1871                 :     {
    1872                 :         CPLError( CE_Failure, CPLE_OutOfMemory, 
    1873                 :                   "Out of memory allocating space for %d byte buffer in\n"
    1874                 :                   "CPLParseXMLFile(%.500s).", 
    1875               0 :                   (int)nLen+1, pszFilename );
    1876               0 :         VSIFCloseL( fp );
    1877               0 :         return NULL;
    1878                 :     }
    1879             541 :     if( VSIFReadL( pszDoc, 1, (size_t)nLen, fp ) < nLen )
    1880                 :     {
    1881                 :         CPLError( CE_Failure, CPLE_FileIO, 
    1882                 :                   "VSIFRead() result short of expected %d bytes from %.500s.", 
    1883               2 :                   (int)nLen, pszFilename );
    1884               2 :         pszDoc[0] = '\0';
    1885                 :     }
    1886             541 :     VSIFCloseL( fp );
    1887                 : 
    1888             541 :     pszDoc[nLen] = '\0';
    1889                 : 
    1890                 : /* -------------------------------------------------------------------- */
    1891                 : /*      Parse it.                                                       */
    1892                 : /* -------------------------------------------------------------------- */
    1893             541 :     psTree = CPLParseXMLString( pszDoc );
    1894             541 :     CPLFree( pszDoc );
    1895                 : 
    1896             541 :     return psTree;
    1897                 : }
    1898                 : 
    1899                 : /************************************************************************/
    1900                 : /*                     CPLSerializeXMLTreeToFile()                      */
    1901                 : /************************************************************************/
    1902                 : 
    1903                 : /**
    1904                 :  * \brief Write document tree to a file. 
    1905                 :  *
    1906                 :  * The passed document tree is converted into one big string (with 
    1907                 :  * CPLSerializeXMLTree()) and then written to the named file.  Errors writing
    1908                 :  * the file will be reported by CPLError().  The source document tree is
    1909                 :  * not altered.  If the output file already exists it will be overwritten. 
    1910                 :  *
    1911                 :  * @param psTree the document tree to write. 
    1912                 :  * @param pszFilename the name of the file to write to. 
    1913                 :  * @return TRUE on success, FALSE otherwise.
    1914                 :  */
    1915                 : 
    1916             632 : int CPLSerializeXMLTreeToFile( CPLXMLNode *psTree, const char *pszFilename )
    1917                 : 
    1918                 : {
    1919                 :     char    *pszDoc;
    1920                 :     FILE    *fp;
    1921                 :     vsi_l_offset nLength;
    1922                 : 
    1923                 : /* -------------------------------------------------------------------- */
    1924                 : /*      Serialize document.                                             */
    1925                 : /* -------------------------------------------------------------------- */
    1926             632 :     pszDoc = CPLSerializeXMLTree( psTree );
    1927             632 :     if( pszDoc == NULL )
    1928               0 :         return FALSE;
    1929                 : 
    1930             632 :     nLength = strlen(pszDoc);
    1931                 : 
    1932                 : /* -------------------------------------------------------------------- */
    1933                 : /*      Create file.                                                    */
    1934                 : /* -------------------------------------------------------------------- */
    1935             632 :     fp = VSIFOpenL( pszFilename, "wt" );
    1936             632 :     if( fp == NULL )
    1937                 :     {
    1938                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
    1939               0 :                   "Failed to open %.500s to write.", pszFilename );
    1940               0 :         CPLFree( pszDoc );
    1941               0 :         return FALSE;
    1942                 :     }
    1943                 : 
    1944                 : /* -------------------------------------------------------------------- */
    1945                 : /*      Write file.                                                     */
    1946                 : /* -------------------------------------------------------------------- */
    1947             632 :     if( VSIFWriteL( pszDoc, 1, (size_t)nLength, fp ) != nLength )
    1948                 :     {
    1949                 :         CPLError( CE_Failure, CPLE_FileIO, 
    1950                 :                   "Failed to write whole XML document (%.500s).",
    1951               0 :                   pszFilename );
    1952               0 :         VSIFCloseL( fp );
    1953               0 :         CPLFree( pszDoc );
    1954               0 :         return FALSE;
    1955                 :     }
    1956                 : 
    1957                 : /* -------------------------------------------------------------------- */
    1958                 : /*      Cleanup                                                         */
    1959                 : /* -------------------------------------------------------------------- */
    1960             632 :     VSIFCloseL( fp );
    1961             632 :     CPLFree( pszDoc );
    1962                 : 
    1963             632 :     return TRUE;
    1964                 : }
    1965                 : 
    1966                 : /************************************************************************/
    1967                 : /*                       CPLCleanXMLElementName()                       */
    1968                 : /************************************************************************/
    1969                 : 
    1970                 : /**
    1971                 :  * \brief Make string into safe XML token.
    1972                 :  *
    1973                 :  * Modififies a string in place to try and make it into a legal
    1974                 :  * XML token that can be used as an element name.   This is accomplished
    1975                 :  * by changing any characters not legal in a token into an underscore. 
    1976                 :  * 
    1977                 :  * NOTE: This function should implement the rules in section 2.3 of 
    1978                 :  * http://www.w3.org/TR/xml11/ but it doesn't yet do that properly.  We
    1979                 :  * only do a rough approximation of that.
    1980                 :  *
    1981                 :  * @param pszTarget the string to be adjusted.  It is altered in place. 
    1982                 :  */
    1983                 : 
    1984              10 : void CPLCleanXMLElementName( char *pszTarget )
    1985                 : {
    1986              10 :     if( pszTarget == NULL )
    1987               0 :         return;
    1988                 : 
    1989              68 :     for( ; *pszTarget != '\0'; pszTarget++ )
    1990                 :     {
    1991              58 :         if( (*((unsigned char *) pszTarget) & 0x80) || isalnum( *pszTarget )
    1992                 :             || *pszTarget == '_' || *pszTarget == '.' )
    1993                 :         {
    1994                 :             /* ok */
    1995                 :         }
    1996                 :         else
    1997                 :         {
    1998               0 :             *pszTarget = '_';
    1999                 :         }
    2000                 :     }
    2001                 : }

Generated by: LTP GCOV extension version 1.5