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