Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members   Related Pages  

cpl_minixml.cpp

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

Generated at Sat Dec 21 14:01:57 2002 for GDAL by doxygen1.2.3-20001105 written by Dimitri van Heesch, © 1997-2000