LCOV - code coverage report
Current view: directory - port - cpl_minixml.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 626 526 84.0 %
Date: 2012-04-28 Functions: 27 27 100.0 %

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

Generated by: LCOV version 1.7