LCOV - code coverage report
Current view: directory - port - cpl_minixml.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 619 523 84.5 %
Date: 2011-12-18 Functions: 27 27 100.0 %

       1                 : /**********************************************************************
       2                 :  * $Id: cpl_minixml.cpp 22178 2011-04-16 20:07:53Z 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 22178 2011-04-16 20:07:53Z 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        72608707 : static CPL_INLINE char ReadChar( ParseContext *psContext )
      94                 : 
      95                 : {
      96                 :     char        chReturn;
      97                 : 
      98        72608707 :     chReturn = psContext->pszInput[psContext->nInputOffset++];
      99                 : 
     100        72608707 :     if( chReturn == '\0' )
     101            9384 :         psContext->nInputOffset--;
     102        72599323 :     else if( chReturn == 10 )
     103         1417162 :         psContext->nInputLine++;
     104                 :     
     105        72608707 :     return chReturn;
     106                 : }
     107                 : 
     108                 : /************************************************************************/
     109                 : /*                             UnreadChar()                             */
     110                 : /************************************************************************/
     111                 : 
     112         3553567 : static CPL_INLINE void UnreadChar( ParseContext *psContext, char chToUnread )
     113                 : 
     114                 : {
     115         3553567 :     if( chToUnread == '\0' )
     116                 :     {
     117                 :         /* do nothing */
     118                 :     }
     119                 :     else
     120                 :     {
     121         3553567 :         CPLAssert( chToUnread 
     122         7107134 :                    == psContext->pszInput[psContext->nInputOffset-1] );
     123                 : 
     124         3553567 :         psContext->nInputOffset--;
     125                 : 
     126         3553567 :         if( chToUnread == 10 )
     127               2 :             psContext->nInputLine--;
     128                 :     }
     129         3553567 : }
     130                 : 
     131                 : /************************************************************************/
     132                 : /*                           ReallocToken()                             */
     133                 : /************************************************************************/
     134                 : 
     135           24558 : static int ReallocToken( ParseContext *psContext )
     136                 : {
     137           24558 :     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           24558 :     psContext->nTokenMaxSize *= 2;
     147                 :     char* pszToken = (char *) 
     148           24558 :         VSIRealloc(psContext->pszToken,psContext->nTokenMaxSize);
     149           24558 :     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           24558 :     psContext->pszToken = pszToken;
     158           24558 :     return TRUE;
     159                 : }
     160                 : 
     161                 : /************************************************************************/
     162                 : /*                             AddToToken()                             */
     163                 : /************************************************************************/
     164                 : 
     165        34707731 : static CPL_INLINE int _AddToToken( ParseContext *psContext, char chNewChar )
     166                 : 
     167                 : {
     168        34707731 :     if( psContext->nTokenSize >= psContext->nTokenMaxSize - 2 )
     169                 :     {
     170           24558 :         if (!ReallocToken(psContext))
     171               0 :             return FALSE;
     172                 :     }
     173                 : 
     174        34707731 :     psContext->pszToken[psContext->nTokenSize++] = chNewChar;
     175        34707731 :     psContext->pszToken[psContext->nTokenSize] = '\0';
     176        34707731 :     return TRUE;
     177                 : }
     178                 : 
     179                 : #define AddToToken(psContext, chNewChar) if (!_AddToToken(psContext, chNewChar)) goto fail;
     180                 : 
     181                 : /************************************************************************/
     182                 : /*                             ReadToken()                              */
     183                 : /************************************************************************/
     184                 : 
     185        10271192 : static XMLTokenType ReadToken( ParseContext *psContext )
     186                 : 
     187                 : {
     188                 :     char        chNext;
     189                 : 
     190        10271192 :     psContext->nTokenSize = 0;
     191        10271192 :     psContext->pszToken[0] = '\0';
     192                 :     
     193        10271192 :     chNext = ReadChar( psContext );
     194        45929754 :     while( isspace((unsigned char)chNext) )
     195        25387370 :         chNext = ReadChar( psContext );
     196                 : 
     197                 : /* -------------------------------------------------------------------- */
     198                 : /*      Handle comments.                                                */
     199                 : /* -------------------------------------------------------------------- */
     200        10276760 :     if( chNext == '<' 
     201                 :         && EQUALN(psContext->pszInput+psContext->nInputOffset,"!--",3) )
     202                 :     {
     203            5568 :         psContext->eTokenType = TComment;
     204                 : 
     205                 :         // Skip "!--" characters
     206            5568 :         ReadChar(psContext);
     207            5568 :         ReadChar(psContext);
     208            5568 :         ReadChar(psContext);
     209                 : 
     210         1164232 :         while( !EQUALN(psContext->pszInput+psContext->nInputOffset,"-->",3)
     211                 :                && (chNext = ReadChar(psContext)) != '\0' )
     212         1153096 :             AddToToken( psContext, chNext );
     213                 : 
     214                 :         // Skip "-->" characters
     215            5568 :         ReadChar(psContext);
     216            5568 :         ReadChar(psContext);
     217            5568 :         ReadChar(psContext);
     218                 :     }
     219                 : /* -------------------------------------------------------------------- */
     220                 : /*      Handle DOCTYPE.                                                 */
     221                 : /* -------------------------------------------------------------------- */
     222        10265628 :     else if( chNext == '<' 
     223                 :           && EQUALN(psContext->pszInput+psContext->nInputOffset,"!DOCTYPE",8) )
     224                 :     {
     225               4 :         int   bInQuotes = FALSE;
     226               4 :         psContext->eTokenType = TLiteral;
     227                 :         
     228               4 :         AddToToken( psContext, '<' );
     229             169 :         do { 
     230             173 :             chNext = ReadChar(psContext);
     231             173 :             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             173 :             if( chNext == '[' )
     249                 :             {
     250               4 :                 AddToToken( psContext, chNext );
     251                 : 
     252            7491 :                 do
     253                 :                 {
     254            7491 :                     chNext = ReadChar( psContext );
     255            7491 :                     if (chNext == ']')
     256               0 :                         break;
     257            7491 :                     AddToToken( psContext, chNext );
     258                 :                 }
     259                 :                 while( chNext != '\0'
     260                 :                     && !EQUALN(psContext->pszInput+psContext->nInputOffset,"]>", 2) );
     261                 :                     
     262               4 :                 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               4 :                 if (chNext != ']')
     272                 :                 {
     273               4 :                     chNext = ReadChar( psContext );
     274               4 :                     AddToToken( psContext, chNext );
     275                 : 
     276                 :                     // Skip ">" character, will be consumed below
     277               4 :                     chNext = ReadChar( psContext );
     278                 :                 }
     279                 :             }
     280                 : 
     281                 : 
     282             173 :             if( chNext == '\"' )
     283               2 :                 bInQuotes = !bInQuotes;
     284                 : 
     285             173 :              if( chNext == '>' && !bInQuotes )
     286                 :             {
     287               4 :                 AddToToken( psContext, '>' );
     288               4 :                 break;
     289                 :             }
     290                 : 
     291             169 :             AddToToken( psContext, chNext );
     292                 :         } while( TRUE );
     293                 :     }
     294                 : /* -------------------------------------------------------------------- */
     295                 : /*      Handle CDATA.                                                   */
     296                 : /* -------------------------------------------------------------------- */
     297        10266979 :     else if( chNext == '<' 
     298                 :           && EQUALN(psContext->pszInput+psContext->nInputOffset,"![CDATA[",8) )
     299                 :     {
     300            1359 :         psContext->eTokenType = TString;
     301                 : 
     302                 :         // Skip !CDATA[
     303            1359 :         ReadChar( psContext );
     304            1359 :         ReadChar( psContext );
     305            1359 :         ReadChar( psContext );
     306            1359 :         ReadChar( psContext );
     307            1359 :         ReadChar( psContext );
     308            1359 :         ReadChar( psContext );
     309            1359 :         ReadChar( psContext );
     310            1359 :         ReadChar( psContext );
     311                 : 
     312          238866 :         while( !EQUALN(psContext->pszInput+psContext->nInputOffset,"]]>",3)
     313                 :                && (chNext = ReadChar(psContext)) != '\0' )
     314          236148 :             AddToToken( psContext, chNext );
     315                 : 
     316                 :         // Skip "]]>" characters
     317            1359 :         ReadChar(psContext);
     318            1359 :         ReadChar(psContext);
     319            1359 :         ReadChar(psContext);
     320                 :     }
     321                 : /* -------------------------------------------------------------------- */
     322                 : /*      Simple single tokens of interest.                               */
     323                 : /* -------------------------------------------------------------------- */
     324        11887646 :     else if( chNext == '<' && !psContext->bInElement )
     325                 :     {
     326         1623385 :         psContext->eTokenType = TOpen;
     327         1623385 :         psContext->bInElement = TRUE;
     328                 :     }
     329         9797470 :     else if( chNext == '>' && psContext->bInElement )
     330                 :     {
     331         1156594 :         psContext->eTokenType = TClose;
     332         1156594 :         psContext->bInElement = FALSE;
     333                 :     }
     334         9211552 :     else if( chNext == '=' && psContext->bInElement )
     335                 :     {
     336         1727270 :         psContext->eTokenType = TEqual;
     337                 :     }
     338         5757012 :     else if( chNext == '\0' )
     339                 :     {
     340            9384 :         psContext->eTokenType = TNone;
     341                 :     }
     342                 : /* -------------------------------------------------------------------- */
     343                 : /*      Handle the /> token terminator.                                 */
     344                 : /* -------------------------------------------------------------------- */
     345         7258041 :     else if( chNext == '/' && psContext->bInElement 
     346         1044355 :              && psContext->pszInput[psContext->nInputOffset] == '>' )
     347                 :     {
     348          466058 :         chNext = ReadChar( psContext );
     349          466058 :         CPLAssert( chNext == '>' );
     350                 : 
     351          466058 :         psContext->eTokenType = TSlashClose;
     352          466058 :         psContext->bInElement = FALSE;
     353                 :     }
     354                 : /* -------------------------------------------------------------------- */
     355                 : /*      Handle the ?> token terminator.                                 */
     356                 : /* -------------------------------------------------------------------- */
     357         5283769 :     else if( chNext == '?' && psContext->bInElement 
     358            1466 :              && psContext->pszInput[psContext->nInputOffset] == '>' )
     359                 :     {
     360             733 :         chNext = ReadChar( psContext );
     361                 :         
     362             733 :         CPLAssert( chNext == '>' );
     363                 : 
     364             733 :         psContext->eTokenType = TQuestionClose;
     365             733 :         psContext->bInElement = FALSE;
     366                 :     }
     367                 : 
     368                 : /* -------------------------------------------------------------------- */
     369                 : /*      Collect a quoted string.                                        */
     370                 : /* -------------------------------------------------------------------- */
     371         6978400 :     else if( psContext->bInElement && chNext == '"' )
     372                 :     {
     373         1697563 :         psContext->eTokenType = TString;
     374                 : 
     375        10708099 :         while( (chNext = ReadChar(psContext)) != '"' 
     376                 :                && chNext != '\0' )
     377         7312973 :             AddToToken( psContext, chNext );
     378                 :         
     379         1697563 :         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         1697563 :         if( strchr(psContext->pszToken,'&') != NULL )
     389                 :         {
     390                 :             int  nLength;
     391                 :             char *pszUnescaped = CPLUnescapeString( psContext->pszToken, 
     392              75 :                                                     &nLength, CPLES_XML );
     393              75 :             strcpy( psContext->pszToken, pszUnescaped );
     394              75 :             CPLFree( pszUnescaped );
     395              75 :             psContext->nTokenSize = strlen(psContext->pszToken );
     396                 :         }
     397                 :     }
     398                 : 
     399         3612981 :     else if( psContext->bInElement && chNext == '\'' )
     400                 :     {
     401           29707 :         psContext->eTokenType = TString;
     402                 : 
     403          428547 :         while( (chNext = ReadChar(psContext)) != '\'' 
     404                 :                && chNext != '\0' )
     405          369133 :             AddToToken( psContext, chNext );
     406                 :         
     407           29707 :         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           29707 :         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         3553567 :     else if( !psContext->bInElement )
     432                 :     {
     433          202912 :         psContext->eTokenType = TString;
     434                 : 
     435          202912 :         AddToToken( psContext, chNext );
     436         5150221 :         while( (chNext = ReadChar(psContext)) != '<' 
     437                 :                && chNext != '\0' )
     438         4744397 :             AddToToken( psContext, chNext );
     439          202912 :         UnreadChar( psContext, chNext );
     440                 : 
     441                 :         /* Do we need to unescape it? */
     442          202912 :         if( strchr(psContext->pszToken,'&') != NULL )
     443                 :         {
     444                 :             int  nLength;
     445                 :             char *pszUnescaped = CPLUnescapeString( psContext->pszToken, 
     446             529 :                                                     &nLength, CPLES_XML );
     447             529 :             strcpy( psContext->pszToken, pszUnescaped );
     448             529 :             CPLFree( pszUnescaped );
     449             529 :             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         3350655 :         psContext->eTokenType = TToken;
     460                 : 
     461                 :         /* add the first character to the token regardless of what it is */
     462         3350655 :         AddToToken( psContext, chNext );
     463                 : 
     464        20681396 :         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        17330741 :             AddToToken( psContext, chNext );
     475                 :         }
     476                 : 
     477         3350655 :         UnreadChar(psContext, chNext);
     478                 :     }
     479                 :     
     480        10271192 :     return psContext->eTokenType;
     481                 : 
     482                 : fail:
     483               0 :     psContext->eTokenType = TNone;
     484               0 :     return TNone;
     485                 : }
     486                 : 
     487                 : /************************************************************************/
     488                 : /*                              PushNode()                              */
     489                 : /************************************************************************/
     490                 : 
     491         1045088 : static int PushNode( ParseContext *psContext, CPLXMLNode *psNode )
     492                 : 
     493                 : {
     494         1045088 :     if( psContext->nStackMaxSize <= psContext->nStackSize )
     495                 :     {
     496            9402 :         psContext->nStackMaxSize += 10;
     497                 : 
     498            9402 :         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            9402 :                     sizeof(StackContext) * psContext->nStackMaxSize);
     509            9402 :         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            9402 :         psContext->papsStack = papsStack;
     518                 :     }
     519                 : 
     520         1045088 :     psContext->papsStack[psContext->nStackSize].psFirstNode = psNode;
     521         1045088 :     psContext->papsStack[psContext->nStackSize].psLastChild = NULL;
     522         1045088 :     psContext->nStackSize ++;
     523         1045088 :     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         2982201 : static void AttachNode( ParseContext *psContext, CPLXMLNode *psNode )
     535                 : 
     536                 : {
     537         2982201 :     if( psContext->psFirstNode == NULL )
     538                 :     {
     539            9382 :         psContext->psFirstNode = psNode;
     540            9382 :         psContext->psLastNode = psNode;
     541                 :     }
     542         2972819 :     else if( psContext->nStackSize == 0 )
     543                 :     {
     544            1845 :         psContext->psLastNode->psNext = psNode;
     545            1845 :         psContext->psLastNode = psNode;
     546                 :     }
     547         2970974 :     else if( psContext->papsStack[psContext->nStackSize-1].psFirstNode->psChild == NULL )
     548                 :     {
     549         1044157 :         psContext->papsStack[psContext->nStackSize-1].psFirstNode->psChild = psNode;
     550         1044157 :         psContext->papsStack[psContext->nStackSize-1].psLastChild = psNode;
     551                 :     }
     552                 :     else
     553                 :     {
     554         1926817 :         psContext->papsStack[psContext->nStackSize-1].psLastChild->psNext = psNode;
     555         1926817 :         psContext->papsStack[psContext->nStackSize-1].psLastChild = psNode;
     556                 :     }
     557         2982201 : }
     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            9384 : CPLXMLNode *CPLParseXMLString( const char *pszString )
     587                 : 
     588                 : {
     589                 :     ParseContext sContext;
     590                 : 
     591            9384 :     CPLErrorReset();
     592                 : 
     593            9384 :     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            9384 :     sContext.pszInput = pszString;
     604            9384 :     sContext.nInputOffset = 0;
     605            9384 :     sContext.nInputLine = 0;
     606            9384 :     sContext.bInElement = FALSE;
     607            9384 :     sContext.nTokenMaxSize = 10;
     608            9384 :     sContext.pszToken = (char *) VSIMalloc(sContext.nTokenMaxSize);
     609            9384 :     if (sContext.pszToken == NULL)
     610               0 :         return NULL;
     611            9384 :     sContext.nTokenSize = 0;
     612            9384 :     sContext.eTokenType = TNone;
     613            9384 :     sContext.nStackMaxSize = 0;
     614            9384 :     sContext.nStackSize = 0;
     615            9384 :     sContext.papsStack = NULL;
     616            9384 :     sContext.psFirstNode = NULL;
     617            9384 :     sContext.psLastNode = NULL;
     618                 : 
     619                 : /* ==================================================================== */
     620                 : /*      Loop reading tokens.                                            */
     621                 : /* ==================================================================== */
     622         4624354 :     while( ReadToken( &sContext ) != TNone )
     623                 :     {
     624                 : /* -------------------------------------------------------------------- */
     625                 : /*      Create a new element.                                           */
     626                 : /* -------------------------------------------------------------------- */
     627         4605586 :         if( sContext.eTokenType == TOpen )
     628                 :         {
     629                 :             CPLXMLNode *psElement;
     630                 : 
     631         1623385 :             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         1623385 :             if( sContext.pszToken[0] != '/' )
     640                 :             {
     641                 :                 psElement = _CPLCreateXMLNode( NULL, CXT_Element,
     642         1045088 :                                               sContext.pszToken );
     643         1045088 :                 if (!psElement) break;
     644         1045088 :                 AttachNode( &sContext, psElement );
     645         1045088 :                 if (!PushNode( &sContext, psElement ))
     646               0 :                     break;
     647                 :             }
     648                 :             else 
     649                 :             {
     650         1156594 :                 if( sContext.nStackSize == 0
     651          578297 :                     || !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          578297 :                     if (strcmp(sContext.pszToken+1,
     663          578297 :                          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               4 :                                 sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue,
     673               8 :                                 sContext.pszToken );
     674                 :                     }
     675                 : 
     676          578297 :                     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          578297 :                     sContext.nStackSize--;
     687                 :                 }
     688                 :             }
     689                 :         }
     690                 : 
     691                 : /* -------------------------------------------------------------------- */
     692                 : /*      Add an attribute to a token.                                    */
     693                 : /* -------------------------------------------------------------------- */
     694         2982201 :         else if( sContext.eTokenType == TToken )
     695                 :         {
     696                 :             CPLXMLNode *psAttr;
     697                 : 
     698         1727270 :             psAttr = _CPLCreateXMLNode(NULL, CXT_Attribute, sContext.pszToken);
     699         1727270 :             if (!psAttr) break;
     700         1727270 :             AttachNode( &sContext, psAttr );
     701                 :             
     702         1727270 :             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         1727270 :             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         1727270 :             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         1727270 :             if (!_CPLCreateXMLNode( psAttr, CXT_Text, sContext.pszToken )) break;
     729                 :         }
     730                 : 
     731                 : /* -------------------------------------------------------------------- */
     732                 : /*      Close the start section of an element.                          */
     733                 : /* -------------------------------------------------------------------- */
     734         1254931 :         else if( sContext.eTokenType == TClose )
     735                 :         {
     736          578297 :             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          676634 :         else if( sContext.eTokenType == TSlashClose )
     750                 :         {
     751          466058 :             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          466058 :             sContext.nStackSize--;
     760                 :         }
     761                 : 
     762                 : /* -------------------------------------------------------------------- */
     763                 : /*      Close the start section of a <?...?> element, and pop it        */
     764                 : /*      immediately.                                                    */
     765                 : /* -------------------------------------------------------------------- */
     766          210576 :         else if( sContext.eTokenType == TQuestionClose )
     767                 :         {
     768             733 :             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             733 :             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             733 :             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          209843 :         else if( sContext.eTokenType == TComment )
     792                 :         {
     793                 :             CPLXMLNode *psValue;
     794                 : 
     795            5568 :             psValue = _CPLCreateXMLNode(NULL, CXT_Comment, sContext.pszToken);
     796            5568 :             if (!psValue) break;
     797            5568 :             AttachNode( &sContext, psValue );
     798                 :         }
     799                 : 
     800                 : /* -------------------------------------------------------------------- */
     801                 : /*      Handle literals.  They are returned without processing.         */
     802                 : /* -------------------------------------------------------------------- */
     803          204275 :         else if( sContext.eTokenType == TLiteral )
     804                 :         {
     805                 :             CPLXMLNode *psValue;
     806                 : 
     807               4 :             psValue = _CPLCreateXMLNode(NULL, CXT_Literal, sContext.pszToken);
     808               4 :             if (!psValue) break;
     809               4 :             AttachNode( &sContext, psValue );
     810                 :         }
     811                 : 
     812                 : /* -------------------------------------------------------------------- */
     813                 : /*      Add a text value node as a child of the current element.        */
     814                 : /* -------------------------------------------------------------------- */
     815          408542 :         else if( sContext.eTokenType == TString && !sContext.bInElement )
     816                 :         {
     817                 :             CPLXMLNode *psValue;
     818                 : 
     819          204271 :             psValue = _CPLCreateXMLNode(NULL, CXT_Text, sContext.pszToken);
     820          204271 :             if (!psValue) break;
     821          204271 :             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            9384 :     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            9384 :     CPLFree( sContext.pszToken );
     850            9384 :     if( sContext.papsStack != NULL )
     851            9382 :         CPLFree( sContext.papsStack );
     852                 : 
     853            9384 :     if( CPLGetLastErrorType() == CE_Failure )
     854                 :     {
     855               0 :         CPLDestroyXMLNode( sContext.psFirstNode );
     856               0 :         sContext.psFirstNode = NULL;
     857               0 :         sContext.psLastNode = NULL;
     858                 :     }
     859                 : 
     860            9384 :     return sContext.psFirstNode;
     861                 : }
     862                 : 
     863                 : /************************************************************************/
     864                 : /*                            _GrowBuffer()                             */
     865                 : /************************************************************************/
     866                 : 
     867          133742 : static void _GrowBuffer( size_t nNeeded, 
     868                 :                          char **ppszText, unsigned int *pnMaxLength )
     869                 : 
     870                 : {
     871          133742 :     if( nNeeded+1 >= *pnMaxLength )
     872                 :     {
     873            3286 :         *pnMaxLength = MAX(*pnMaxLength * 2,nNeeded+1);
     874            3286 :         *ppszText = (char *) CPLRealloc(*ppszText, *pnMaxLength);
     875                 :     }
     876          133742 : }
     877                 : 
     878                 : /************************************************************************/
     879                 : /*                        CPLSerializeXMLNode()                         */
     880                 : /************************************************************************/
     881                 : 
     882                 : static void
     883           79244 : CPLSerializeXMLNode( CPLXMLNode *psNode, int nIndent, 
     884                 :                      char **ppszText, unsigned int *pnLength, 
     885                 :                      unsigned int *pnMaxLength )
     886                 : 
     887                 : {
     888           79244 :     if( psNode == NULL )
     889               0 :         return;
     890                 :     
     891                 : /* -------------------------------------------------------------------- */
     892                 : /*      Ensure the buffer is plenty large to hold this additional       */
     893                 : /*      string.                                                         */
     894                 : /* -------------------------------------------------------------------- */
     895           79244 :     *pnLength += strlen(*ppszText + *pnLength);
     896                 :     _GrowBuffer( strlen(psNode->pszValue) + *pnLength + 40 + nIndent, 
     897           79244 :                  ppszText, pnMaxLength );
     898                 :     
     899                 : /* -------------------------------------------------------------------- */
     900                 : /*      Text is just directly emitted.                                  */
     901                 : /* -------------------------------------------------------------------- */
     902           79244 :     if( psNode->eType == CXT_Text )
     903                 :     {
     904           32153 :         char *pszEscaped = CPLEscapeString( psNode->pszValue, -1, CPLES_XML );
     905                 : 
     906           32153 :         CPLAssert( psNode->psChild == NULL );
     907                 : 
     908                 :         /* Escaped text might be bigger than expected. */
     909                 :         _GrowBuffer( strlen(pszEscaped) + *pnLength,
     910           32153 :                      ppszText, pnMaxLength );
     911           32153 :         strcat( *ppszText + *pnLength, pszEscaped );
     912                 : 
     913           32153 :         CPLFree( pszEscaped );
     914                 :     }
     915                 : 
     916                 : /* -------------------------------------------------------------------- */
     917                 : /*      Attributes require a little formatting.                         */
     918                 : /* -------------------------------------------------------------------- */
     919           47091 :     else if( psNode->eType == CXT_Attribute )
     920                 :     {
     921                 :         CPLAssert( psNode->psChild != NULL 
     922           20369 :                    && psNode->psChild->eType == CXT_Text );
     923                 : 
     924           20369 :         sprintf( *ppszText + *pnLength, " %s=\"", psNode->pszValue );
     925                 :         CPLSerializeXMLNode( psNode->psChild, 0, ppszText, 
     926           20369 :                              pnLength, pnMaxLength );
     927           20369 :         strcat( *ppszText + *pnLength, "\"" );
     928                 :     }
     929                 : 
     930                 : /* -------------------------------------------------------------------- */
     931                 : /*      Handle comment output.                                          */
     932                 : /* -------------------------------------------------------------------- */
     933           26722 :     else if( psNode->eType == CXT_Comment )
     934                 :     {
     935                 :         int     i;
     936                 : 
     937               9 :         CPLAssert( psNode->psChild == NULL );
     938                 : 
     939              27 :         for( i = 0; i < nIndent; i++ )
     940              18 :             (*ppszText)[(*pnLength)++] = ' ';
     941                 : 
     942                 :         sprintf( *ppszText + *pnLength, "<!--%s-->\n", 
     943               9 :                  psNode->pszValue );
     944                 :     }
     945                 : 
     946                 : /* -------------------------------------------------------------------- */
     947                 : /*      Handle literal output (like <!DOCTYPE...>)                      */
     948                 : /* -------------------------------------------------------------------- */
     949           26713 :     else if( psNode->eType == CXT_Literal )
     950                 :     {
     951                 :         int     i;
     952                 : 
     953               0 :         CPLAssert( psNode->psChild == NULL );
     954                 : 
     955               0 :         for( i = 0; i < nIndent; i++ )
     956               0 :             (*ppszText)[(*pnLength)++] = ' ';
     957                 : 
     958               0 :         strcpy( *ppszText + *pnLength, psNode->pszValue );
     959               0 :         strcat( *ppszText + *pnLength, "\n" );
     960                 :     }
     961                 : 
     962                 : /* -------------------------------------------------------------------- */
     963                 : /*      Elements actually have to deal with general children, and       */
     964                 : /*      various formatting issues.                                      */
     965                 : /* -------------------------------------------------------------------- */
     966           26713 :     else if( psNode->eType == CXT_Element )
     967                 :     {
     968           26713 :         int             bHasNonAttributeChildren = FALSE;
     969                 :         CPLXMLNode      *psChild;
     970                 :         
     971           26713 :         memset( *ppszText + *pnLength, ' ', nIndent );
     972           26713 :         *pnLength += nIndent;
     973           26713 :         (*ppszText)[*pnLength] = '\0';
     974                 : 
     975           26713 :         sprintf( *ppszText + *pnLength, "<%s", psNode->pszValue );
     976                 : 
     977                 :         /* Serialize *all* the attribute children, regardless of order */
     978           84362 :         for( psChild = psNode->psChild; 
     979                 :              psChild != NULL; 
     980                 :              psChild = psChild->psNext )
     981                 :         {
     982           57649 :             if( psChild->eType == CXT_Attribute )
     983                 :                 CPLSerializeXMLNode( psChild, 0, ppszText, pnLength, 
     984           20369 :                                      pnMaxLength );
     985                 :             else
     986           37280 :                 bHasNonAttributeChildren = TRUE;
     987                 :         }
     988                 :         
     989           26713 :         if( !bHasNonAttributeChildren )
     990                 :         {
     991            4368 :             if( psNode->pszValue[0] == '?' )
     992              26 :                 strcat( *ppszText + *pnLength, "?>\n" );
     993                 :             else
     994            4342 :                 strcat( *ppszText + *pnLength, " />\n" );
     995                 :         }
     996                 :         else
     997                 :         {
     998           22345 :             int         bJustText = TRUE;
     999                 : 
    1000           22345 :             strcat( *ppszText + *pnLength, ">" );
    1001                 : 
    1002           70749 :             for( psChild = psNode->psChild; 
    1003                 :                  psChild != NULL; 
    1004                 :                  psChild = psChild->psNext )
    1005                 :             {
    1006           48404 :                 if( psChild->eType == CXT_Attribute )
    1007           11124 :                     continue;
    1008                 : 
    1009           37280 :                 if( psChild->eType != CXT_Text && bJustText )
    1010                 :                 {
    1011           10561 :                     bJustText = FALSE;
    1012           10561 :                     strcat( *ppszText + *pnLength, "\n" );
    1013                 :                 }
    1014                 : 
    1015                 :                 CPLSerializeXMLNode( psChild, nIndent + 2, ppszText, pnLength, 
    1016           37280 :                                      pnMaxLength );
    1017                 :             }
    1018                 :         
    1019           22345 :             *pnLength += strlen(*ppszText + *pnLength);
    1020                 :             _GrowBuffer( strlen(psNode->pszValue) + *pnLength + 40 + nIndent, 
    1021           22345 :                          ppszText, pnMaxLength );
    1022                 : 
    1023           22345 :             if( !bJustText )
    1024                 :             {
    1025           10561 :                 memset( *ppszText + *pnLength, ' ', nIndent );
    1026           10561 :                 *pnLength += nIndent;
    1027           10561 :                 (*ppszText)[*pnLength] = '\0';
    1028                 :             }
    1029                 : 
    1030           22345 :             *pnLength += strlen(*ppszText + *pnLength);
    1031           22345 :             sprintf( *ppszText + *pnLength, "</%s>\n", psNode->pszValue );
    1032                 :         }
    1033                 :     }
    1034                 : }
    1035                 :                                 
    1036                 : /************************************************************************/
    1037                 : /*                        CPLSerializeXMLTree()                         */
    1038                 : /************************************************************************/
    1039                 : 
    1040                 : /**
    1041                 :  * \brief Convert tree into string document.
    1042                 :  *
    1043                 :  * This function converts a CPLXMLNode tree representation of a document
    1044                 :  * into a flat string representation.  White space indentation is used
    1045                 :  * visually preserve the tree structure of the document.  The returned 
    1046                 :  * document becomes owned by the caller and should be freed with CPLFree()
    1047                 :  * when no longer needed.
    1048                 :  *
    1049                 :  * @param psNode
    1050                 :  *
    1051                 :  * @return the document on success or NULL on failure. 
    1052                 :  */
    1053                 : 
    1054            1201 : char *CPLSerializeXMLTree( CPLXMLNode *psNode )
    1055                 : 
    1056                 : {
    1057            1201 :     unsigned int nMaxLength = 100, nLength = 0;
    1058            1201 :     char *pszText = NULL;
    1059                 :     CPLXMLNode *psThis;
    1060                 : 
    1061            1201 :     pszText = (char *) CPLMalloc(nMaxLength);
    1062            1201 :     pszText[0] = '\0';
    1063                 : 
    1064            2427 :     for( psThis = psNode; psThis != NULL; psThis = psThis->psNext )
    1065            1226 :         CPLSerializeXMLNode( psThis, 0, &pszText, &nLength, &nMaxLength );
    1066                 : 
    1067            1201 :     return pszText;
    1068                 : }
    1069                 : 
    1070                 : /************************************************************************/
    1071                 : /*                          CPLCreateXMLNode()                          */
    1072                 : /************************************************************************/
    1073                 : 
    1074                 : /**
    1075                 :  * \brief Create an document tree item.
    1076                 :  *
    1077                 :  * Create a single CPLXMLNode object with the desired value and type, and
    1078                 :  * attach it as a child of the indicated parent.  
    1079                 :  *
    1080                 :  * @param poParent the parent to which this node should be attached as a 
    1081                 :  * child.  May be NULL to keep as free standing. 
    1082                 :  * @param eType the type of the newly created node
    1083                 :  * @param pszText the value of the newly created node
    1084                 :  *
    1085                 :  * @return the newly created node, now owned by the caller (or parent node).
    1086                 :  */
    1087                 : 
    1088          104650 : CPLXMLNode *CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, 
    1089                 :                               const char *pszText )
    1090                 : 
    1091                 : {
    1092                 :     CPLXMLNode  *psNode;
    1093                 : 
    1094                 : /* -------------------------------------------------------------------- */
    1095                 : /*      Create new node.                                                */
    1096                 : /* -------------------------------------------------------------------- */
    1097          104650 :     psNode = (CPLXMLNode *) CPLCalloc(sizeof(CPLXMLNode),1);
    1098                 :     
    1099          104650 :     psNode->eType = eType;
    1100          104650 :     psNode->pszValue = CPLStrdup( pszText );
    1101                 : 
    1102                 : /* -------------------------------------------------------------------- */
    1103                 : /*      Attach to parent, if provided.                                  */
    1104                 : /* -------------------------------------------------------------------- */
    1105          104650 :     if( poParent != NULL )
    1106                 :     {
    1107           45635 :         if( poParent->psChild == NULL )
    1108           31642 :             poParent->psChild = psNode;
    1109                 :         else
    1110                 :         {
    1111           13993 :             CPLXMLNode  *psLink = poParent->psChild;
    1112                 : 
    1113           53513 :             while( psLink->psNext != NULL )
    1114           25527 :                 psLink = psLink->psNext;
    1115                 : 
    1116           13993 :             psLink->psNext = psNode;
    1117                 :         }
    1118                 :     }
    1119                 :     
    1120          104650 :     return psNode;
    1121                 : }
    1122                 : 
    1123                 : /************************************************************************/
    1124                 : /*                         _CPLCreateXMLNode()                          */
    1125                 : /************************************************************************/
    1126                 : 
    1127                 : /* Same as CPLCreateXMLNode() but can return NULL in case of out-of-memory */
    1128                 : /* situation */
    1129                 : 
    1130         4709471 : static CPLXMLNode *_CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, 
    1131                 :                                const char *pszText )
    1132                 : 
    1133                 : {
    1134                 :     CPLXMLNode  *psNode;
    1135                 : 
    1136                 : /* -------------------------------------------------------------------- */
    1137                 : /*      Create new node.                                                */
    1138                 : /* -------------------------------------------------------------------- */
    1139         4709471 :     psNode = (CPLXMLNode *) VSICalloc(sizeof(CPLXMLNode),1);
    1140         4709471 :     if (psNode == NULL)
    1141                 :     {
    1142               0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate CPLXMLNode");
    1143               0 :         return NULL;
    1144                 :     }
    1145                 :     
    1146         4709471 :     psNode->eType = eType;
    1147         4709471 :     psNode->pszValue = VSIStrdup( pszText );
    1148         4709471 :     if (psNode->pszValue == NULL)
    1149                 :     {
    1150               0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate psNode->pszValue");
    1151               0 :         VSIFree(psNode);
    1152               0 :         return NULL;
    1153                 :     }
    1154                 : 
    1155                 : /* -------------------------------------------------------------------- */
    1156                 : /*      Attach to parent, if provided.                                  */
    1157                 : /* -------------------------------------------------------------------- */
    1158         4709471 :     if( poParent != NULL )
    1159                 :     {
    1160         1727270 :         if( poParent->psChild == NULL )
    1161         1727270 :             poParent->psChild = psNode;
    1162                 :         else
    1163                 :         {
    1164               0 :             CPLXMLNode  *psLink = poParent->psChild;
    1165                 : 
    1166               0 :             while( psLink->psNext != NULL )
    1167               0 :                 psLink = psLink->psNext;
    1168                 : 
    1169               0 :             psLink->psNext = psNode;
    1170                 :         }
    1171                 :     }
    1172                 :     
    1173         4709471 :     return psNode;
    1174                 : }
    1175                 : 
    1176                 : /************************************************************************/
    1177                 : /*                         CPLDestroyXMLNode()                          */
    1178                 : /************************************************************************/
    1179                 : 
    1180                 : /**
    1181                 :  * \brief Destroy a tree. 
    1182                 :  *
    1183                 :  * This function frees resources associated with a CPLXMLNode and all its
    1184                 :  * children nodes.  
    1185                 :  *
    1186                 :  * @param psNode the tree to free.
    1187                 :  */
    1188                 : 
    1189           20939 : void CPLDestroyXMLNode( CPLXMLNode *psNode )
    1190                 : 
    1191                 : {
    1192         4865011 :     while(psNode != NULL)
    1193                 :     {
    1194         4823133 :         if( psNode->pszValue != NULL )
    1195         4823133 :             CPLFree( psNode->pszValue );
    1196                 : 
    1197         4823133 :         if( psNode->psChild != NULL )
    1198                 :         {
    1199         2837741 :             CPLXMLNode* psNext = psNode->psNext;
    1200         2837741 :             psNode->psNext = psNode->psChild;
    1201                 :             /* Move the child and its siblings as the next */
    1202                 :             /* siblings of the current node */
    1203         2837741 :             if (psNext != NULL)
    1204                 :             {
    1205         2767229 :                 CPLXMLNode* psIter = psNode->psChild;
    1206         7338092 :                 while(psIter->psNext != NULL)
    1207         1803634 :                     psIter = psIter->psNext;
    1208         2767229 :                 psIter->psNext = psNext;
    1209                 :             }
    1210                 :         }
    1211                 : 
    1212         4823133 :         CPLXMLNode* psNext = psNode->psNext;
    1213                 : 
    1214         4823133 :         CPLFree( psNode );
    1215                 : 
    1216         4823133 :         psNode = psNext;
    1217                 :     }
    1218           20939 : }
    1219                 : 
    1220                 : /************************************************************************/
    1221                 : /*                           CPLSearchXMLNode()                         */
    1222                 : /************************************************************************/
    1223                 : 
    1224                 : /**
    1225                 :  * \brief Search for a node in document.
    1226                 :  *
    1227                 :  * Searches the children (and potentially siblings) of the documented
    1228                 :  * passed in for the named element or attribute.  To search following
    1229                 :  * siblings as well as children, prefix the pszElement name with an equal
    1230                 :  * sign.  This function does an in-order traversal of the document tree.
    1231                 :  * So it will first match against the current node, then it's first child,
    1232                 :  * that childs first child, and so on. 
    1233                 :  *
    1234                 :  * Use CPLGetXMLNode() to find a specific child, or along a specific
    1235                 :  * node path. 
    1236                 :  *
    1237                 :  * @param psRoot the subtree to search.  This should be a node of type
    1238                 :  * CXT_Element.  NULL is safe. 
    1239                 :  *
    1240                 :  * @param pszElement the name of the element or attribute to search for.
    1241                 :  *
    1242                 :  * @return The matching node or NULL on failure. 
    1243                 :  */
    1244                 : 
    1245            2700 : CPLXMLNode *CPLSearchXMLNode( CPLXMLNode *psRoot, const char *pszElement )
    1246                 : 
    1247                 : {
    1248            2700 :     int         bSideSearch = FALSE;
    1249                 :     CPLXMLNode *psChild, *psResult;
    1250                 : 
    1251            2700 :     if( psRoot == NULL || pszElement == NULL )
    1252               2 :         return NULL;
    1253                 : 
    1254            2698 :     if( *pszElement == '=' )
    1255                 :     {
    1256              79 :         bSideSearch = TRUE;
    1257              79 :         pszElement++;
    1258                 :     }
    1259                 : 
    1260                 : /* -------------------------------------------------------------------- */
    1261                 : /*      Does this node match?                                           */
    1262                 : /* -------------------------------------------------------------------- */
    1263            2698 :     if( (psRoot->eType == CXT_Element 
    1264                 :          || psRoot->eType == CXT_Attribute)
    1265                 :         && EQUAL(pszElement,psRoot->pszValue) )
    1266               5 :         return psRoot;
    1267                 : 
    1268                 : /* -------------------------------------------------------------------- */
    1269                 : /*      Search children.                                                */
    1270                 : /* -------------------------------------------------------------------- */
    1271            6348 :     for( psChild = psRoot->psChild; psChild != NULL; psChild = psChild->psNext)
    1272                 :     {
    1273            3691 :         if( (psChild->eType == CXT_Element 
    1274                 :              || psChild->eType == CXT_Attribute)
    1275                 :             && EQUAL(pszElement,psChild->pszValue) )
    1276              15 :             return psChild;
    1277                 : 
    1278            3676 :         if( psChild->psChild != NULL )
    1279                 :         {
    1280            1881 :             psResult = CPLSearchXMLNode( psChild, pszElement );
    1281            1881 :             if( psResult != NULL )
    1282              21 :                 return psResult;
    1283                 :         }
    1284                 :     }
    1285                 : 
    1286                 : /* -------------------------------------------------------------------- */
    1287                 : /*      Search siblings if we are in side search mode.                  */
    1288                 : /* -------------------------------------------------------------------- */
    1289            2657 :     if( bSideSearch )
    1290                 :     {
    1291             795 :         for( psRoot = psRoot->psNext; psRoot != NULL; psRoot = psRoot->psNext )
    1292                 :         {
    1293             728 :             psResult = CPLSearchXMLNode( psRoot, pszElement );
    1294             728 :             if( psResult != NULL )
    1295              10 :                 return psResult;
    1296                 :         }
    1297                 :     }
    1298                 :     
    1299            2647 :     return NULL;
    1300                 : }
    1301                 : 
    1302                 : /************************************************************************/
    1303                 : /*                           CPLGetXMLNode()                            */
    1304                 : /************************************************************************/
    1305                 : 
    1306                 : /**
    1307                 :  * \brief Find node by path.
    1308                 :  *
    1309                 :  * Searches the document or subdocument indicated by psRoot for an element 
    1310                 :  * (or attribute) with the given path.  The path should consist of a set of
    1311                 :  * element names separated by dots, not including the name of the root 
    1312                 :  * element (psRoot).  If the requested element is not found NULL is returned.
    1313                 :  *
    1314                 :  * Attribute names may only appear as the last item in the path. 
    1315                 :  *
    1316                 :  * The search is done from the root nodes children, but all intermediate
    1317                 :  * nodes in the path must be specified.  Seaching for "name" would only find
    1318                 :  * a name element or attribute if it is a direct child of the root, not at any
    1319                 :  * level in the subdocument. 
    1320                 :  *
    1321                 :  * If the pszPath is prefixed by "=" then the search will begin with the
    1322                 :  * root node, and it's siblings, instead of the root nodes children.  This
    1323                 :  * is particularly useful when searching within a whole document which is
    1324                 :  * often prefixed by one or more "junk" nodes like the <?xml> declaration.
    1325                 :  *
    1326                 :  * @param psRoot the subtree in which to search.  This should be a node of 
    1327                 :  * type CXT_Element.  NULL is safe. 
    1328                 :  *
    1329                 :  * @param pszPath the list of element names in the path (dot separated). 
    1330                 :  *
    1331                 :  * @return the requested element node, or NULL if not found. 
    1332                 :  */
    1333                 : 
    1334          106930 : CPLXMLNode *CPLGetXMLNode( CPLXMLNode *psRoot, const char *pszPath )
    1335                 : 
    1336                 : {
    1337                 :     char        *apszTokens[2];
    1338                 :     char        **papszTokens;
    1339          106930 :     int         iToken = 0;
    1340          106930 :     int         bSideSearch = FALSE;
    1341                 : 
    1342          106930 :     if( psRoot == NULL || pszPath == NULL )
    1343              12 :         return NULL;
    1344                 : 
    1345          106918 :     if( *pszPath == '=' )
    1346                 :     {
    1347             987 :         bSideSearch = TRUE;
    1348             987 :         pszPath++;
    1349                 :     }
    1350                 : 
    1351                 :     /* Slight optimization : avoid using CSLTokenizeStringComplex that */
    1352                 :     /* does memory allocations when it is not really necessary */
    1353          106918 :     if (strchr(pszPath, '.'))
    1354            1041 :         papszTokens = CSLTokenizeStringComplex( pszPath, ".", FALSE, FALSE );
    1355                 :     else
    1356                 :     {
    1357          105877 :         apszTokens[0] = (char*) pszPath;
    1358          105877 :         apszTokens[1] = NULL;
    1359          105877 :         papszTokens = apszTokens;
    1360                 :     }
    1361                 : 
    1362          280376 :     while( papszTokens[iToken] != NULL && psRoot != NULL )
    1363                 :     {
    1364                 :         CPLXMLNode *psChild;
    1365                 : 
    1366          108075 :         if( bSideSearch )
    1367                 :         {
    1368             987 :             psChild = psRoot;
    1369             987 :             bSideSearch = FALSE;
    1370                 :         }
    1371                 :         else
    1372          107088 :             psChild = psRoot->psChild;
    1373                 : 
    1374          598135 :         for( ; psChild != NULL; psChild = psChild->psNext ) 
    1375                 :         {
    1376         1110611 :             if( psChild->eType != CXT_Text 
    1377          554011 :                 && EQUAL(papszTokens[iToken],psChild->pszValue) )
    1378           66540 :                 break;
    1379                 :         }
    1380                 : 
    1381          108075 :         if( psChild == NULL )
    1382                 :         {
    1383           41535 :             psRoot = NULL;
    1384           41535 :             break;
    1385                 :         }
    1386                 : 
    1387           66540 :         psRoot = psChild;
    1388           66540 :         iToken++;
    1389                 :     }
    1390                 : 
    1391          106918 :     if (papszTokens != apszTokens)
    1392            1041 :         CSLDestroy( papszTokens );
    1393          106918 :     return psRoot;
    1394                 : }
    1395                 : 
    1396                 : /************************************************************************/
    1397                 : /*                           CPLGetXMLValue()                           */
    1398                 : /************************************************************************/
    1399                 : 
    1400                 : /**
    1401                 :  * \brief Fetch element/attribute value. 
    1402                 :  *
    1403                 :  * Searches the document for the element/attribute value associated with
    1404                 :  * the path.  The corresponding node is internally found with CPLGetXMLNode()
    1405                 :  * (see there for details on path handling).  Once found, the value is 
    1406                 :  * considered to be the first CXT_Text child of the node.
    1407                 :  *
    1408                 :  * If the attribute/element search fails, or if the found node has not
    1409                 :  * value then the passed default value is returned. 
    1410                 :  *
    1411                 :  * The returned value points to memory within the document tree, and should
    1412                 :  * not be altered or freed. 
    1413                 :  *
    1414                 :  * @param psRoot the subtree in which to search.  This should be a node of 
    1415                 :  * type CXT_Element.  NULL is safe. 
    1416                 :  *
    1417                 :  * @param pszPath the list of element names in the path (dot separated).  An
    1418                 :  * empty path means get the value of the psRoot node.
    1419                 :  *
    1420                 :  * @param pszDefault the value to return if a corresponding value is not
    1421                 :  * found, may be NULL.
    1422                 :  *
    1423                 :  * @return the requested value or pszDefault if not found.
    1424                 :  */
    1425                 : 
    1426           98101 : const char *CPLGetXMLValue( CPLXMLNode *psRoot, const char *pszPath, 
    1427                 :                             const char *pszDefault )
    1428                 : 
    1429                 : {
    1430                 :     CPLXMLNode  *psTarget;
    1431                 : 
    1432           99762 :     if( pszPath == NULL || *pszPath == '\0' )
    1433            1661 :         psTarget  = psRoot;
    1434                 :     else
    1435           96440 :         psTarget = CPLGetXMLNode( psRoot, pszPath );
    1436                 : 
    1437           98101 :     if( psTarget == NULL )
    1438           35981 :         return pszDefault;
    1439                 : 
    1440           62120 :     if( psTarget->eType == CXT_Attribute )
    1441                 :     {
    1442                 :         CPLAssert( psTarget->psChild != NULL 
    1443           46921 :                    && psTarget->psChild->eType == CXT_Text );
    1444                 : 
    1445           46921 :         return psTarget->psChild->pszValue;
    1446                 :     }
    1447                 : 
    1448           15199 :     if( psTarget->eType == CXT_Element )
    1449                 :     {
    1450                 :         // Find first non-attribute child, and verify it is a single text 
    1451                 :         // with no siblings
    1452                 : 
    1453           15199 :         psTarget = psTarget->psChild;
    1454                 : 
    1455           31990 :         while( psTarget != NULL && psTarget->eType == CXT_Attribute )
    1456            1592 :             psTarget = psTarget->psNext;
    1457                 : 
    1458           15199 :         if( psTarget != NULL 
    1459                 :             && psTarget->eType == CXT_Text
    1460                 :             && psTarget->psNext == NULL )
    1461           14835 :             return psTarget->pszValue;
    1462                 :     }
    1463                 : 
    1464             364 :     return pszDefault;
    1465                 : }
    1466                 : 
    1467                 : /************************************************************************/
    1468                 : /*                           CPLAddXMLChild()                           */
    1469                 : /************************************************************************/
    1470                 : 
    1471                 : /**
    1472                 :  * \brief Add child node to parent. 
    1473                 :  *
    1474                 :  * The passed child is added to the list of children of the indicated
    1475                 :  * parent.  Normally the child is added at the end of the parents child
    1476                 :  * list, but attributes (CXT_Attribute) will be inserted after any other
    1477                 :  * attributes but before any other element type.  Ownership of the child
    1478                 :  * node is effectively assumed by the parent node.   If the child has
    1479                 :  * siblings (it's psNext is not NULL) they will be trimmed, but if the child
    1480                 :  * has children they are carried with it. 
    1481                 :  *
    1482                 :  * @param psParent the node to attach the child to.  May not be NULL.
    1483                 :  *
    1484                 :  * @param psChild the child to add to the parent.  May not be NULL.  Should 
    1485                 :  * not be a child of any other parent. 
    1486                 :  */
    1487                 : 
    1488            2972 : void CPLAddXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild )
    1489                 : 
    1490                 : {
    1491                 :     CPLXMLNode *psSib;
    1492                 : 
    1493            2972 :     if( psParent->psChild == NULL )
    1494                 :     {
    1495             769 :         psParent->psChild = psChild;
    1496             769 :         return;
    1497                 :     }
    1498                 : 
    1499                 :     // Insert at head of list if first child is not attribute.
    1500            2203 :     if( psChild->eType == CXT_Attribute 
    1501                 :         && psParent->psChild->eType != CXT_Attribute )
    1502                 :     {
    1503               0 :         psChild->psNext = psParent->psChild;
    1504               0 :         psParent->psChild = psChild;
    1505               0 :         return;
    1506                 :     }
    1507                 : 
    1508                 :     // Search for end of list.
    1509            5494 :     for( psSib = psParent->psChild; 
    1510                 :          psSib->psNext != NULL; 
    1511                 :          psSib = psSib->psNext ) 
    1512                 :     {
    1513                 :         // Insert attributes if the next node is not an attribute.
    1514            3291 :         if( psChild->eType == CXT_Attribute 
    1515                 :             && psSib->psNext != NULL 
    1516                 :             && psSib->psNext->eType != CXT_Attribute )
    1517                 :         {
    1518               0 :             psChild->psNext = psSib->psNext;
    1519               0 :             psSib->psNext = psChild;
    1520               0 :             return;
    1521                 :         }
    1522                 :     }
    1523                 : 
    1524            2203 :     psSib->psNext = psChild;
    1525                 : }
    1526                 : 
    1527                 : /************************************************************************/
    1528                 : /*                        CPLRemoveXMLChild()                           */
    1529                 : /************************************************************************/
    1530                 : 
    1531                 : /**
    1532                 :  * \brief Remove child node from parent. 
    1533                 :  *
    1534                 :  * The passed child is removed from the child list of the passed parent,
    1535                 :  * but the child is not destroyed.  The child retains ownership of it's
    1536                 :  * own children, but is cleanly removed from the child list of the parent.
    1537                 :  *
    1538                 :  * @param psParent the node to the child is attached to.
    1539                 :  *
    1540                 :  * @param psChild the child to remove.
    1541                 :  *
    1542                 :  * @return TRUE on success or FALSE if the child was not found.
    1543                 :  */
    1544                 : 
    1545            8997 : int CPLRemoveXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild )
    1546                 : 
    1547                 : {
    1548            8997 :     CPLXMLNode *psLast = NULL, *psThis;
    1549                 : 
    1550            8997 :     if( psParent == NULL )
    1551               0 :         return FALSE;
    1552                 : 
    1553           20069 :     for( psThis = psParent->psChild; 
    1554                 :          psThis != NULL; 
    1555                 :          psLast = psThis, psThis = psThis->psNext )
    1556                 :     {
    1557           18244 :         if( psThis == psChild )
    1558                 :         {
    1559            7172 :             if( psLast == NULL )
    1560            2270 :                 psParent->psChild = psThis->psNext;
    1561                 :             else 
    1562            4902 :                 psLast->psNext = psThis->psNext;
    1563                 : 
    1564            7172 :             psThis->psNext = NULL;
    1565            7172 :             return TRUE;
    1566                 :         }
    1567                 :     }
    1568                 : 
    1569            1825 :     return FALSE;
    1570                 : }
    1571                 : 
    1572                 : /************************************************************************/
    1573                 : /*                          CPLAddXMLSibling()                          */
    1574                 : /************************************************************************/
    1575                 : 
    1576                 : /**
    1577                 :  * \brief Add new sibling.
    1578                 :  *
    1579                 :  * The passed psNewSibling is added to the end of siblings of the 
    1580                 :  * psOlderSibling node.  That is, it is added to the end of the psNext
    1581                 :  * chain.  There is no special handling if psNewSibling is an attribute. 
    1582                 :  * If this is required, use CPLAddXMLChild(). 
    1583                 :  *
    1584                 :  * @param psOlderSibling the node to attach the sibling after.
    1585                 :  *
    1586                 :  * @param psNewSibling the node to add at the end of psOlderSiblings psNext 
    1587                 :  * chain.
    1588                 :  */
    1589                 : 
    1590             238 : void CPLAddXMLSibling( CPLXMLNode *psOlderSibling, CPLXMLNode *psNewSibling )
    1591                 : 
    1592                 : {
    1593             238 :     if( psOlderSibling == NULL )
    1594               0 :         return;
    1595                 : 
    1596             478 :     while( psOlderSibling->psNext != NULL )
    1597               2 :         psOlderSibling = psOlderSibling->psNext;
    1598                 : 
    1599             238 :     psOlderSibling->psNext = psNewSibling;
    1600                 : }
    1601                 : 
    1602                 : /************************************************************************/
    1603                 : /*                    CPLCreateXMLElementAndValue()                     */
    1604                 : /************************************************************************/
    1605                 : 
    1606                 : /**
    1607                 :  * \brief Create an element and text value.
    1608                 :  *
    1609                 :  * This is function is a convenient short form for:
    1610                 :  *
    1611                 :  * \code
    1612                 :  *     CPLXMLNode *psTextNode;
    1613                 :  *     CPLXMLNode *psElementNode;
    1614                 :  *
    1615                 :  *     psElementNode = CPLCreateXMLNode( psParent, CXT_Element, pszName );
    1616                 :  *     psTextNode = CPLCreateXMLNode( psElementNode, CXT_Text, pszValue );
    1617                 :  * 
    1618                 :  *     return psElementNode;
    1619                 :  * \endcode
    1620                 :  *
    1621                 :  * It creates a CXT_Element node, with a CXT_Text child, and
    1622                 :  * attaches the element to the passed parent.
    1623                 :  *
    1624                 :  * @param psParent the parent node to which the resulting node should
    1625                 :  * be attached.  May be NULL to keep as freestanding. 
    1626                 :  *
    1627                 :  * @param pszName the element name to create.
    1628                 :  * @param pszValue the text to attach to the element. Must not be NULL. 
    1629                 :  *
    1630                 :  * @return the pointer to the new element node.
    1631                 :  */
    1632                 : 
    1633            2958 : CPLXMLNode *CPLCreateXMLElementAndValue( CPLXMLNode *psParent, 
    1634                 :                                          const char *pszName, 
    1635                 :                                          const char *pszValue )
    1636                 : 
    1637                 : {
    1638                 :     CPLXMLNode *psElementNode;
    1639                 : 
    1640            2958 :     psElementNode = CPLCreateXMLNode( psParent, CXT_Element, pszName );
    1641            2958 :     CPLCreateXMLNode( psElementNode, CXT_Text, pszValue );
    1642                 : 
    1643            2958 :     return psElementNode;
    1644                 : }
    1645                 : 
    1646                 : /************************************************************************/
    1647                 : /*                          CPLCloneXMLTree()                           */
    1648                 : /************************************************************************/
    1649                 : 
    1650                 : /**
    1651                 :  * \brief Copy tree.
    1652                 :  *
    1653                 :  * Creates a deep copy of a CPLXMLNode tree.  
    1654                 :  *
    1655                 :  * @param psTree the tree to duplicate. 
    1656                 :  *
    1657                 :  * @return a copy of the whole tree. 
    1658                 :  */
    1659                 : 
    1660           26846 : CPLXMLNode *CPLCloneXMLTree( CPLXMLNode *psTree )
    1661                 : 
    1662                 : {
    1663           26846 :     CPLXMLNode *psPrevious = NULL;
    1664           26846 :     CPLXMLNode *psReturn = NULL;
    1665                 : 
    1666           98570 :     while( psTree != NULL )
    1667                 :     {
    1668                 :         CPLXMLNode *psCopy;
    1669                 : 
    1670           44878 :         psCopy = CPLCreateXMLNode( NULL, psTree->eType, psTree->pszValue );
    1671           44878 :         if( psReturn == NULL )
    1672           26846 :             psReturn = psCopy;
    1673           44878 :         if( psPrevious != NULL )
    1674           18032 :             psPrevious->psNext = psCopy;
    1675                 : 
    1676           44878 :         if( psTree->psChild != NULL )
    1677           26214 :             psCopy->psChild = CPLCloneXMLTree( psTree->psChild );
    1678                 : 
    1679           44878 :         psPrevious = psCopy;
    1680           44878 :         psTree = psTree->psNext;
    1681                 :     }
    1682                 : 
    1683           26846 :     return psReturn;
    1684                 : }
    1685                 : 
    1686                 : /************************************************************************/
    1687                 : /*                           CPLSetXMLValue()                           */
    1688                 : /************************************************************************/
    1689                 : 
    1690                 : /**
    1691                 :  * \brief Set element value by path. 
    1692                 :  *
    1693                 :  * Find (or create) the target element or attribute specified in the
    1694                 :  * path, and assign it the indicated value. 
    1695                 :  *
    1696                 :  * Any path elements that do not already exist will be created.  The target
    1697                 :  * nodes value (the first CXT_Text child) will be replaced with the provided
    1698                 :  * value.  
    1699                 :  *
    1700                 :  * If the target node is an attribute instead of an element, the name
    1701                 :  * should be prefixed with a #.
    1702                 :  *
    1703                 :  * Example:
    1704                 :  *   CPLSetXMLValue( "Citation.Id.Description", "DOQ dataset" );
    1705                 :  *   CPLSetXMLValue( "Citation.Id.Description.#name", "doq" );
    1706                 :  *
    1707                 :  * @param psRoot the subdocument to be updated. 
    1708                 :  *
    1709                 :  * @param pszPath the dot seperated path to the target element/attribute.
    1710                 :  *
    1711                 :  * @param pszValue the text value to assign. 
    1712                 :  *
    1713                 :  * @return TRUE on success.
    1714                 :  */
    1715                 : 
    1716           11640 : int CPLSetXMLValue( CPLXMLNode *psRoot,  const char *pszPath,
    1717                 :                     const char *pszValue )
    1718                 : 
    1719                 : {
    1720                 :     char        **papszTokens;
    1721           11640 :     int         iToken = 0;
    1722                 : 
    1723           11640 :     papszTokens = CSLTokenizeStringComplex( pszPath, ".", FALSE, FALSE );
    1724                 : 
    1725           37262 :     while( papszTokens[iToken] != NULL && psRoot != NULL )
    1726                 :     {
    1727                 :         CPLXMLNode *psChild;
    1728           13982 :         int        bIsAttribute = FALSE;
    1729           13982 :         const char *pszName = papszTokens[iToken];
    1730                 : 
    1731           13982 :         if( pszName[0] == '#' )
    1732                 :         {
    1733            9851 :             bIsAttribute = TRUE;
    1734            9851 :             pszName++;
    1735                 :         }
    1736                 : 
    1737           13982 :         if( psRoot->eType != CXT_Element )
    1738               0 :             return FALSE;
    1739                 : 
    1740           29231 :         for( psChild = psRoot->psChild; psChild != NULL; 
    1741                 :              psChild = psChild->psNext ) 
    1742                 :         {
    1743           17880 :             if( psChild->eType != CXT_Text 
    1744                 :                 && EQUAL(pszName,psChild->pszValue) )
    1745            2631 :                 break;
    1746                 :         }
    1747                 : 
    1748           13982 :         if( psChild == NULL )
    1749                 :         {
    1750           11351 :             if( bIsAttribute )
    1751            9065 :                 psChild = CPLCreateXMLNode( psRoot, CXT_Attribute, pszName );
    1752                 :             else
    1753            2286 :                 psChild = CPLCreateXMLNode( psRoot, CXT_Element, pszName );
    1754                 :         }
    1755                 : 
    1756           13982 :         psRoot = psChild;
    1757           13982 :         iToken++;
    1758                 :     }
    1759                 : 
    1760           11640 :     CSLDestroy( papszTokens );
    1761                 : 
    1762                 : /* -------------------------------------------------------------------- */
    1763                 : /*      Find the "text" child if there is one.                          */
    1764                 : /* -------------------------------------------------------------------- */
    1765           11640 :     CPLXMLNode *psTextChild = psRoot->psChild;
    1766                 : 
    1767           23320 :     while( psTextChild != NULL && psTextChild->eType != CXT_Text )
    1768              40 :         psTextChild = psTextChild->psNext;
    1769                 : 
    1770                 : /* -------------------------------------------------------------------- */
    1771                 : /*      Now set a value node under this node.                           */
    1772                 : /* -------------------------------------------------------------------- */
    1773                 :     
    1774           11640 :     if( psTextChild == NULL )
    1775           10814 :         CPLCreateXMLNode( psRoot, CXT_Text, pszValue );
    1776                 :     else 
    1777                 :     {
    1778             826 :         CPLFree( psTextChild->pszValue );
    1779             826 :         psTextChild->pszValue = CPLStrdup( pszValue );
    1780                 :     }
    1781                 : 
    1782           11640 :     return TRUE;
    1783                 : }
    1784                 : 
    1785                 : /************************************************************************/
    1786                 : /*                        CPLStripXMLNamespace()                        */
    1787                 : /************************************************************************/
    1788                 : 
    1789                 : /**
    1790                 :  * \brief Strip indicated namespaces. 
    1791                 :  *
    1792                 :  * The subdocument (psRoot) is recursively examined, and any elements
    1793                 :  * with the indicated namespace prefix will have the namespace prefix
    1794                 :  * stripped from the element names.  If the passed namespace is NULL, then
    1795                 :  * all namespace prefixes will be stripped. 
    1796                 :  *
    1797                 :  * Nodes other than elements should remain unaffected.  The changes are
    1798                 :  * made "in place", and should not alter any node locations, only the 
    1799                 :  * pszValue field of affected nodes. 
    1800                 :  *
    1801                 :  * @param psRoot the document to operate on.
    1802                 :  * @param pszNamespace the name space prefix (not including colon), or NULL.
    1803                 :  * @param bRecurse TRUE to recurse over whole document, or FALSE to only
    1804                 :  * operate on the passed node.
    1805                 :  */
    1806                 : 
    1807           30650 : void CPLStripXMLNamespace( CPLXMLNode *psRoot, 
    1808                 :                            const char *pszNamespace, 
    1809                 :                            int bRecurse )
    1810                 : 
    1811                 : {
    1812           30650 :     size_t nNameSpaceLen = (pszNamespace) ? strlen(pszNamespace) : 0;
    1813                 : 
    1814          113583 :     while( psRoot != NULL )
    1815                 :     {
    1816                 : 
    1817           52283 :         if( psRoot->eType == CXT_Element || psRoot->eType == CXT_Attribute )
    1818                 :         {
    1819           30786 :             if( pszNamespace != NULL )
    1820                 :             {
    1821             246 :                 if( EQUALN(pszNamespace,psRoot->pszValue,nNameSpaceLen) 
    1822             120 :                     && psRoot->pszValue[nNameSpaceLen] == ':' )
    1823                 :                 {
    1824                 :                     memmove(psRoot->pszValue, psRoot->pszValue+nNameSpaceLen+1,
    1825             120 :                            strlen(psRoot->pszValue+nNameSpaceLen+1) + 1);
    1826                 :                 }
    1827                 :             }
    1828                 :             else
    1829                 :             {
    1830                 :                 const char *pszCheck;
    1831                 : 
    1832          213695 :                 for( pszCheck = psRoot->pszValue; *pszCheck != '\0'; pszCheck++ )
    1833                 :                 {
    1834          191457 :                     if( *pszCheck == ':' )
    1835                 :                     {
    1836            8422 :                         memmove(psRoot->pszValue, pszCheck + 1, strlen(pszCheck + 1) + 1);
    1837            8422 :                         break;
    1838                 :                     }
    1839                 :                 }
    1840                 :             }
    1841                 :         }
    1842                 : 
    1843           52283 :         if( bRecurse )
    1844                 :         {
    1845           52283 :             if( psRoot->psChild != NULL )
    1846           30491 :                 CPLStripXMLNamespace( psRoot->psChild, pszNamespace, 1 );
    1847                 : 
    1848           52283 :             psRoot = psRoot->psNext;
    1849                 :         }
    1850                 :         else
    1851               0 :             break;
    1852                 :     }
    1853           30650 : }
    1854                 : 
    1855                 : /************************************************************************/
    1856                 : /*                          CPLParseXMLFile()                           */
    1857                 : /************************************************************************/
    1858                 : 
    1859                 : /**
    1860                 :  * \brief Parse XML file into tree.
    1861                 :  *
    1862                 :  * The named file is opened, loaded into memory as a big string, and
    1863                 :  * parsed with CPLParseXMLString().  Errors in reading the file or parsing
    1864                 :  * the XML will be reported by CPLError(). 
    1865                 :  *
    1866                 :  * The "large file" API is used, so XML files can come from virtualized
    1867                 :  * files. 
    1868                 :  *
    1869                 :  * @param pszFilename the file to open. 
    1870                 :  *
    1871                 :  * @return NULL on failure, or the document tree on success.
    1872                 :  */
    1873                 : 
    1874            1264 : CPLXMLNode *CPLParseXMLFile( const char *pszFilename )
    1875                 : 
    1876                 : {
    1877                 :     VSILFILE       *fp;
    1878                 :     vsi_l_offset    nLen;
    1879                 :     char            *pszDoc;
    1880                 :     CPLXMLNode      *psTree;
    1881                 : 
    1882                 : /* -------------------------------------------------------------------- */
    1883                 : /*      Read the file.                                                  */
    1884                 : /* -------------------------------------------------------------------- */
    1885            1264 :     fp = VSIFOpenL( pszFilename, "rb" );
    1886            1264 :     if( fp == NULL )
    1887                 :     {
    1888                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
    1889               4 :                   "Failed to open %.500s to read.", pszFilename );
    1890               4 :         return NULL;
    1891                 :     }
    1892                 : 
    1893            1260 :     VSIFSeekL( fp, 0, SEEK_END );
    1894            1260 :     nLen = VSIFTellL( fp );
    1895            1260 :     VSIFSeekL( fp, 0, SEEK_SET );
    1896                 :     
    1897            1260 :     pszDoc = (char *) VSIMalloc((size_t)nLen + 1);
    1898            1260 :     if( pszDoc == NULL )
    1899                 :     {
    1900                 :         CPLError( CE_Failure, CPLE_OutOfMemory, 
    1901                 :                   "Out of memory allocating space for %d byte buffer in\n"
    1902                 :                   "CPLParseXMLFile(%.500s).", 
    1903               0 :                   (int)nLen+1, pszFilename );
    1904               0 :         VSIFCloseL( fp );
    1905               0 :         return NULL;
    1906                 :     }
    1907            1260 :     if( VSIFReadL( pszDoc, 1, (size_t)nLen, fp ) < nLen )
    1908                 :     {
    1909                 :         CPLError( CE_Failure, CPLE_FileIO, 
    1910                 :                   "VSIFRead() result short of expected %d bytes from %.500s.", 
    1911               2 :                   (int)nLen, pszFilename );
    1912               2 :         pszDoc[0] = '\0';
    1913                 :     }
    1914            1260 :     VSIFCloseL( fp );
    1915                 : 
    1916            1260 :     pszDoc[nLen] = '\0';
    1917                 : 
    1918                 : /* -------------------------------------------------------------------- */
    1919                 : /*      Parse it.                                                       */
    1920                 : /* -------------------------------------------------------------------- */
    1921            1260 :     psTree = CPLParseXMLString( pszDoc );
    1922            1260 :     CPLFree( pszDoc );
    1923                 : 
    1924            1260 :     return psTree;
    1925                 : }
    1926                 : 
    1927                 : /************************************************************************/
    1928                 : /*                     CPLSerializeXMLTreeToFile()                      */
    1929                 : /************************************************************************/
    1930                 : 
    1931                 : /**
    1932                 :  * \brief Write document tree to a file. 
    1933                 :  *
    1934                 :  * The passed document tree is converted into one big string (with 
    1935                 :  * CPLSerializeXMLTree()) and then written to the named file.  Errors writing
    1936                 :  * the file will be reported by CPLError().  The source document tree is
    1937                 :  * not altered.  If the output file already exists it will be overwritten. 
    1938                 :  *
    1939                 :  * @param psTree the document tree to write. 
    1940                 :  * @param pszFilename the name of the file to write to. 
    1941                 :  * @return TRUE on success, FALSE otherwise.
    1942                 :  */
    1943                 : 
    1944             776 : int CPLSerializeXMLTreeToFile( CPLXMLNode *psTree, const char *pszFilename )
    1945                 : 
    1946                 : {
    1947                 :     char    *pszDoc;
    1948                 :     VSILFILE    *fp;
    1949                 :     vsi_l_offset nLength;
    1950                 : 
    1951                 : /* -------------------------------------------------------------------- */
    1952                 : /*      Serialize document.                                             */
    1953                 : /* -------------------------------------------------------------------- */
    1954             776 :     pszDoc = CPLSerializeXMLTree( psTree );
    1955             776 :     if( pszDoc == NULL )
    1956               0 :         return FALSE;
    1957                 : 
    1958             776 :     nLength = strlen(pszDoc);
    1959                 : 
    1960                 : /* -------------------------------------------------------------------- */
    1961                 : /*      Create file.                                                    */
    1962                 : /* -------------------------------------------------------------------- */
    1963             776 :     fp = VSIFOpenL( pszFilename, "wt" );
    1964             776 :     if( fp == NULL )
    1965                 :     {
    1966                 :         CPLError( CE_Failure, CPLE_OpenFailed, 
    1967               3 :                   "Failed to open %.500s to write.", pszFilename );
    1968               3 :         CPLFree( pszDoc );
    1969               3 :         return FALSE;
    1970                 :     }
    1971                 : 
    1972                 : /* -------------------------------------------------------------------- */
    1973                 : /*      Write file.                                                     */
    1974                 : /* -------------------------------------------------------------------- */
    1975             773 :     if( VSIFWriteL( pszDoc, 1, (size_t)nLength, fp ) != nLength )
    1976                 :     {
    1977                 :         CPLError( CE_Failure, CPLE_FileIO, 
    1978                 :                   "Failed to write whole XML document (%.500s).",
    1979               0 :                   pszFilename );
    1980               0 :         VSIFCloseL( fp );
    1981               0 :         CPLFree( pszDoc );
    1982               0 :         return FALSE;
    1983                 :     }
    1984                 : 
    1985                 : /* -------------------------------------------------------------------- */
    1986                 : /*      Cleanup                                                         */
    1987                 : /* -------------------------------------------------------------------- */
    1988             773 :     VSIFCloseL( fp );
    1989             773 :     CPLFree( pszDoc );
    1990                 : 
    1991             773 :     return TRUE;
    1992                 : }
    1993                 : 
    1994                 : /************************************************************************/
    1995                 : /*                       CPLCleanXMLElementName()                       */
    1996                 : /************************************************************************/
    1997                 : 
    1998                 : /**
    1999                 :  * \brief Make string into safe XML token.
    2000                 :  *
    2001                 :  * Modififies a string in place to try and make it into a legal
    2002                 :  * XML token that can be used as an element name.   This is accomplished
    2003                 :  * by changing any characters not legal in a token into an underscore. 
    2004                 :  * 
    2005                 :  * NOTE: This function should implement the rules in section 2.3 of 
    2006                 :  * http://www.w3.org/TR/xml11/ but it doesn't yet do that properly.  We
    2007                 :  * only do a rough approximation of that.
    2008                 :  *
    2009                 :  * @param pszTarget the string to be adjusted.  It is altered in place. 
    2010                 :  */
    2011                 : 
    2012              31 : void CPLCleanXMLElementName( char *pszTarget )
    2013                 : {
    2014              31 :     if( pszTarget == NULL )
    2015               0 :         return;
    2016                 : 
    2017             229 :     for( ; *pszTarget != '\0'; pszTarget++ )
    2018                 :     {
    2019             198 :         if( (*((unsigned char *) pszTarget) & 0x80) || isalnum( *pszTarget )
    2020                 :             || *pszTarget == '_' || *pszTarget == '.' )
    2021                 :         {
    2022                 :             /* ok */
    2023                 :         }
    2024                 :         else
    2025                 :         {
    2026               0 :             *pszTarget = '_';
    2027                 :         }
    2028                 :     }
    2029                 : }

Generated by: LCOV version 1.7