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

Generated at Thu Mar 28 09:47:28 2002 for GDAL by doxygen1.2.3-20001105 written by Dimitri van Heesch, © 1997-2000