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