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