Coverage Report

Created: 2026-06-30 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/xml-tree.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                             X   X  M   M  L                                 %
7
%                              X X   MM MM  L                                 %
8
%                               X    M M M  L                                 %
9
%                              X X   M   M  L                                 %
10
%                             X   X  M   M  LLLLL                             %
11
%                                                                             %
12
%                         TTTTT  RRRR   EEEEE  EEEEE                          %
13
%                           T    R   R  E      E                              %
14
%                           T    RRRR   EEE    EEE                            %
15
%                           T    R R    E      E                              %
16
%                           T    R  R   EEEEE  EEEEE                          %
17
%                                                                             %
18
%                                                                             %
19
%                              XML Tree Methods                               %
20
%                                                                             %
21
%                              Software Design                                %
22
%                                   Cristy                                    %
23
%                               December 2004                                 %
24
%                                                                             %
25
%                                                                             %
26
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
27
%  dedicated to making software imaging solutions freely available.           %
28
%                                                                             %
29
%  You may not use this file except in compliance with the License.  You may  %
30
%  obtain a copy of the License at                                            %
31
%                                                                             %
32
%    https://imagemagick.org/license/                                         %
33
%                                                                             %
34
%  Unless required by applicable law or agreed to in writing, software        %
35
%  distributed under the License is distributed on an "AS IS" BASIS,          %
36
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
37
%  See the License for the specific language governing permissions and        %
38
%  limitations under the License.                                             %
39
%                                                                             %
40
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41
%
42
%  This module implements the standard handy xml-tree methods for storing and
43
%  retrieving nodes and attributes from an XML string.
44
%
45
*/
46

47
/*
48
  Include declarations.
49
*/
50
#include "MagickCore/studio.h"
51
#include "MagickCore/blob.h"
52
#include "MagickCore/blob-private.h"
53
#include "MagickCore/exception.h"
54
#include "MagickCore/exception-private.h"
55
#include "MagickCore/image-private.h"
56
#include "MagickCore/log.h"
57
#include "MagickCore/memory_.h"
58
#include "MagickCore/memory-private.h"
59
#include "MagickCore/semaphore.h"
60
#include "MagickCore/string_.h"
61
#include "MagickCore/string-private.h"
62
#include "MagickCore/token-private.h"
63
#include "MagickCore/xml-tree.h"
64
#include "MagickCore/xml-tree-private.h"
65
#include "MagickCore/utility.h"
66
#include "MagickCore/utility-private.h"
67

68
/*
69
  Define declarations.
70
*/
71
0
#define NumberPredefinedEntities  10
72
0
#define XMLWhitespace "\t\r\n "
73

74
/*
75
  Typedef declarations.
76
*/
77
struct _XMLTreeInfo
78
{
79
  char
80
    *tag,
81
    **attributes,
82
    *content;
83
84
  size_t
85
    offset;
86
87
  XMLTreeInfo
88
    *parent,
89
    *next,
90
    *sibling,
91
    *ordered,
92
    *child;
93
94
  MagickBooleanType
95
    debug;
96
97
  SemaphoreInfo
98
    *semaphore;
99
100
  size_t
101
    signature;
102
};
103
104
typedef struct _XMLTreeRoot
105
  XMLTreeRoot;
106
107
struct _XMLTreeRoot
108
{
109
  struct _XMLTreeInfo
110
    root;
111
112
  XMLTreeInfo
113
    *node;
114
115
  MagickBooleanType
116
    standalone;
117
118
  char
119
    ***processing_instructions,
120
    **entities,
121
    ***attributes;
122
123
  MagickBooleanType
124
    debug;
125
126
  SemaphoreInfo
127
    *semaphore;
128
129
  size_t
130
    signature;
131
};
132

133
/*
134
  Global declarations.
135
*/
136
static char
137
  *sentinel[] = { (char *) NULL };
138

139
/*
140
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141
%                                                                             %
142
%                                                                             %
143
%                                                                             %
144
%   A d d C h i l d T o X M L T r e e                                         %
145
%                                                                             %
146
%                                                                             %
147
%                                                                             %
148
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149
%
150
%  AddChildToXMLTree() adds a child tag at an offset relative to the start of
151
%  the parent tag's character content.  Return the child tag.
152
%
153
%  The format of the AddChildToXMLTree method is:
154
%
155
%      XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag,
156
%        const size_t offset)
157
%
158
%  A description of each parameter follows:
159
%
160
%    o xml_info: the xml info.
161
%
162
%    o tag: the tag.
163
%
164
%    o offset: the tag offset.
165
%
166
*/
167
MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,
168
  const char *tag,const size_t offset)
169
0
{
170
0
  XMLTreeInfo
171
0
    *child;
172
173
0
  if (xml_info == (XMLTreeInfo *) NULL)
174
0
    return((XMLTreeInfo *) NULL);
175
0
  child=(XMLTreeInfo *) AcquireMagickMemory(sizeof(*child));
176
0
  if (child == (XMLTreeInfo *) NULL)
177
0
    return((XMLTreeInfo *) NULL);
178
0
  (void) memset(child,0,sizeof(*child));
179
0
  child->tag=ConstantString(tag);
180
0
  child->attributes=sentinel;
181
0
  child->content=ConstantString("");
182
0
  child->debug=IsEventLogging();
183
0
  child->signature=MagickCoreSignature;
184
0
  return(InsertTagIntoXMLTree(xml_info,child,offset));
185
0
}
186

187
/*
188
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189
%                                                                             %
190
%                                                                             %
191
%                                                                             %
192
%   A d d P a t h T o X M L T r e e                                           %
193
%                                                                             %
194
%                                                                             %
195
%                                                                             %
196
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197
%
198
%  AddPathToXMLTree() adds a child tag at an offset relative to the start of
199
%  the parent tag's character content.  This method returns the child tag.
200
%
201
%  The format of the AddPathToXMLTree method is:
202
%
203
%      XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path,
204
%        const size_t offset)
205
%
206
%  A description of each parameter follows:
207
%
208
%    o xml_info: the xml info.
209
%
210
%    o path: the path.
211
%
212
%    o offset: the tag offset.
213
%
214
*/
215
MagickPrivate XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,
216
  const char *path,const size_t offset)
217
0
{
218
0
  char
219
0
    **components,
220
0
    subnode[MagickPathExtent],
221
0
    tag[MagickPathExtent];
222
223
0
  size_t
224
0
    number_components;
225
226
0
  ssize_t
227
0
    i,
228
0
    j;
229
230
0
  XMLTreeInfo
231
0
    *child,
232
0
    *node;
233
234
0
  assert(xml_info != (XMLTreeInfo *) NULL);
235
0
  assert((xml_info->signature == MagickCoreSignature) ||
236
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
237
0
  if (IsEventLogging() != MagickFalse)
238
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
239
0
  node=xml_info;
240
0
  components=GetPathComponents(path,&number_components);
241
0
  if (components == (char **) NULL)
242
0
    return((XMLTreeInfo *) NULL);
243
0
  for (i=0; i < (ssize_t) number_components; i++)
244
0
  {
245
0
    GetPathComponent(components[i],SubimagePath,subnode);
246
0
    GetPathComponent(components[i],CanonicalPath,tag);
247
0
    child=GetXMLTreeChild(node,tag);
248
0
    if (child == (XMLTreeInfo *) NULL)
249
0
      child=AddChildToXMLTree(node,tag,offset);
250
0
    node=child;
251
0
    if (node == (XMLTreeInfo *) NULL)
252
0
      break;
253
0
    for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
254
0
    {
255
0
      node=GetXMLTreeOrdered(node);
256
0
      if (node == (XMLTreeInfo *) NULL)
257
0
        break;
258
0
    }
259
0
    if (node == (XMLTreeInfo *) NULL)
260
0
      break;
261
0
    components[i]=DestroyString(components[i]);
262
0
  }
263
0
  for ( ; i < (ssize_t) number_components; i++)
264
0
    components[i]=DestroyString(components[i]);
265
0
  components=(char **) RelinquishMagickMemory(components);
266
0
  return(node);
267
0
}
268

269
/*
270
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
271
%                                                                             %
272
%                                                                             %
273
%                                                                             %
274
%   C a n o n i c a l X M L C o n t e n t                                     %
275
%                                                                             %
276
%                                                                             %
277
%                                                                             %
278
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
279
%
280
%  CanonicalXMLContent() converts text to canonical XML content by converting
281
%  to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding
282
%  as base-64 as required.
283
%
284
%  The format of the CanonicalXMLContent method is:
285
%
286
%      char *CanonicalXMLContent(const char *content,
287
%        const MagickBooleanType pedantic)
288
%
289
%  A description of each parameter follows:
290
%
291
%    o content: the content.
292
%
293
%    o pedantic: if true, replace newlines and tabs with their respective
294
%      entities.
295
%
296
*/
297
MagickPrivate char *CanonicalXMLContent(const char *content,
298
  const MagickBooleanType pedantic)
299
0
{
300
0
  char
301
0
    *base64,
302
0
    *canonical_content;
303
304
0
  const unsigned char
305
0
    *p;
306
307
0
  size_t
308
0
    length;
309
310
0
  unsigned char
311
0
    *utf8;
312
313
0
  utf8=ConvertLatin1ToUTF8((const unsigned char *) content);
314
0
  if (utf8 == (unsigned char *) NULL)
315
0
    return((char *) NULL);
316
0
  for (p=utf8; *p != '\0'; p++)
317
0
    if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d))
318
0
      break;
319
0
  if (*p != '\0')
320
0
    {
321
      /*
322
        String is binary, base64-encode it.
323
      */
324
0
      base64=Base64Encode(utf8,strlen((char *) utf8),&length);
325
0
      utf8=(unsigned char *) RelinquishMagickMemory(utf8);
326
0
      if (base64 == (char *) NULL)
327
0
        return((char *) NULL);
328
0
      canonical_content=AcquireString("<base64>");
329
0
      (void) ConcatenateString(&canonical_content,base64);
330
0
      base64=DestroyString(base64);
331
0
      (void) ConcatenateString(&canonical_content,"</base64>");
332
0
      return(canonical_content);
333
0
    }
334
0
  canonical_content=SubstituteXMLEntities((const char *) utf8,pedantic);
335
0
  utf8=(unsigned char *) RelinquishMagickMemory(utf8);
336
0
  return(canonical_content);
337
0
}
338

339
/*
340
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341
%                                                                             %
342
%                                                                             %
343
%                                                                             %
344
%   D e s t r o y X M L T r e e                                               %
345
%                                                                             %
346
%                                                                             %
347
%                                                                             %
348
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349
%
350
%  DestroyXMLTree() destroys the xml-tree.
351
%
352
%  The format of the DestroyXMLTree method is:
353
%
354
%      XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
355
%
356
%  A description of each parameter follows:
357
%
358
%    o xml_info: the xml info.
359
%
360
*/
361
362
static XMLTreeInfo
363
  *DestroyXMLTree_(XMLTreeInfo *,const size_t);
364
365
static char **DestroyXMLTreeAttributes(char **attributes)
366
0
{
367
0
  ssize_t
368
0
    i;
369
370
  /*
371
    Destroy a tag attribute list.
372
  */
373
0
  if ((attributes == (char **) NULL) || (attributes == sentinel))
374
0
    return((char **) NULL);
375
0
  for (i=0; attributes[i] != (char *) NULL; i+=2)
376
0
  {
377
    /*
378
      Destroy attribute tag and value.
379
    */
380
0
    if (attributes[i] != (char *) NULL)
381
0
      attributes[i]=DestroyString(attributes[i]);
382
0
    if (attributes[i+1] != (char *) NULL)
383
0
      attributes[i+1]=DestroyString(attributes[i+1]);
384
0
  }
385
0
  attributes=(char **) RelinquishMagickMemory(attributes);
386
0
  return((char **) NULL);
387
0
}
388
389
static void DestroyXMLTreeChild(XMLTreeInfo *xml_info,
390
  const size_t depth)
391
0
{
392
0
  XMLTreeInfo
393
0
    *child,
394
0
    *node;
395
396
0
  child=xml_info->child;
397
0
  while (child != (XMLTreeInfo *) NULL)
398
0
  {
399
0
    node=child;
400
0
    child=node->child;
401
0
    node->child=(XMLTreeInfo *) NULL;
402
0
    (void) DestroyXMLTree_(node,depth+1);
403
0
  }
404
0
}
405
406
static void DestroyXMLTreeOrdered(XMLTreeInfo *xml_info,
407
  const size_t depth)
408
0
{
409
0
  XMLTreeInfo
410
0
    *node,
411
0
    *ordered;
412
413
0
  ordered=xml_info->ordered;
414
0
  while (ordered != (XMLTreeInfo *) NULL)
415
0
  {
416
0
    node=ordered;
417
0
    ordered=node->ordered;
418
0
    node->ordered=(XMLTreeInfo *) NULL;
419
0
    (void) DestroyXMLTree_(node,depth+1);
420
0
  }
421
0
}
422
423
static void DestroyXMLTreeRoot(XMLTreeInfo *xml_info)
424
0
{
425
0
  char
426
0
    **attributes;
427
428
0
  ssize_t
429
0
    i,
430
0
    j;
431
432
0
  XMLTreeRoot
433
0
    *root;
434
435
0
  assert(xml_info != (XMLTreeInfo *) NULL);
436
0
  assert((xml_info->signature == MagickCoreSignature) ||
437
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
438
0
  if (IsEventLogging() != MagickFalse)
439
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
440
0
  if (xml_info->parent != (XMLTreeInfo *) NULL)
441
0
    return;
442
  /*
443
    Free root tag allocations.
444
  */
445
0
  root=(XMLTreeRoot *) xml_info;
446
0
  for (i=NumberPredefinedEntities; root->entities[i] != (char *) NULL; i+=2)
447
0
    root->entities[i+1]=DestroyString(root->entities[i+1]);
448
0
  root->entities=(char **) RelinquishMagickMemory(root->entities);
449
0
  for (i=0; root->attributes[i] != (char **) NULL; i++)
450
0
  {
451
0
    attributes=root->attributes[i];
452
0
    if (attributes[0] != (char *) NULL)
453
0
      attributes[0]=DestroyString(attributes[0]);
454
0
    for (j=1; attributes[j] != (char *) NULL; j+=3)
455
0
    {
456
0
      if (attributes[j] != (char *) NULL)
457
0
        attributes[j]=DestroyString(attributes[j]);
458
0
      if (attributes[j+1] != (char *) NULL)
459
0
        attributes[j+1]=DestroyString(attributes[j+1]);
460
0
      if (attributes[j+2] != (char *) NULL)
461
0
        attributes[j+2]=DestroyString(attributes[j+2]);
462
0
    }
463
0
    attributes=(char **) RelinquishMagickMemory(attributes);
464
0
  }
465
0
  if (root->attributes[0] != (char **) NULL)
466
0
    root->attributes=(char ***) RelinquishMagickMemory(root->attributes);
467
0
  if (root->processing_instructions[0] != (char **) NULL)
468
0
    {
469
0
      for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
470
0
      {
471
0
        for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++)
472
0
          root->processing_instructions[i][j]=DestroyString(
473
0
            root->processing_instructions[i][j]);
474
0
        root->processing_instructions[i][j+1]=DestroyString(
475
0
          root->processing_instructions[i][j+1]);
476
0
        root->processing_instructions[i]=(char **) RelinquishMagickMemory(
477
0
          root->processing_instructions[i]);
478
0
      }
479
0
      root->processing_instructions=(char ***) RelinquishMagickMemory(
480
0
        root->processing_instructions);
481
0
    }
482
0
}
483
484
static XMLTreeInfo *DestroyXMLTree_(XMLTreeInfo *xml_info,
485
  const size_t depth)
486
0
{
487
0
  assert(xml_info != (XMLTreeInfo *) NULL);
488
0
  assert((xml_info->signature == MagickCoreSignature) ||
489
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
490
0
  if (IsEventLogging() != MagickFalse)
491
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
492
0
  if (depth > MagickMaxRecursionDepth)
493
0
    ThrowFatalException(ResourceLimitFatalError,
494
0
      "MemoryAllocationFailed");
495
0
  DestroyXMLTreeChild(xml_info,depth+1);
496
0
  DestroyXMLTreeOrdered(xml_info,depth+1);
497
0
  DestroyXMLTreeRoot(xml_info);
498
0
  xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes);
499
0
  xml_info->content=DestroyString(xml_info->content);
500
0
  xml_info->tag=DestroyString(xml_info->tag);
501
0
  xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info);
502
0
  return((XMLTreeInfo *) NULL);
503
0
}
504
505
MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
506
0
{
507
0
  return(DestroyXMLTree_(xml_info,0));
508
0
}
509

510
/*
511
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512
%                                                                             %
513
%                                                                             %
514
%                                                                             %
515
%   F i l e T o X M L                                                         %
516
%                                                                             %
517
%                                                                             %
518
%                                                                             %
519
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
520
%
521
%  FileToXML() returns the contents of a file as a XML string.
522
%
523
%  The format of the FileToXML method is:
524
%
525
%      char *FileToXML(const char *filename,const size_t extent)
526
%
527
%  A description of each parameter follows:
528
%
529
%    o filename: the filename.
530
%
531
%    o extent: Maximum length of the string.
532
%
533
*/
534
MagickPrivate char *FileToXML(const char *filename,const size_t extent)
535
0
{
536
0
  char
537
0
    *xml;
538
539
0
  int
540
0
    file;
541
542
0
  MagickOffsetType
543
0
    offset;
544
545
0
  size_t
546
0
    i,
547
0
    length;
548
549
0
  ssize_t
550
0
    count;
551
552
0
  void
553
0
    *map;
554
555
0
  assert(filename != (const char *) NULL);
556
0
  length=0;
557
0
  file=fileno(stdin);
558
0
  if (LocaleCompare(filename,"-") != 0)
559
0
    file=open_utf8(filename,O_RDONLY | O_BINARY,0);
560
0
  if (file == -1)
561
0
    return((char *) NULL);
562
0
  offset=(MagickOffsetType) lseek(file,0,SEEK_END);
563
0
  count=0;
564
0
  if ((file == fileno(stdin)) || (offset < 0) ||
565
0
      (offset != (MagickOffsetType) ((ssize_t) offset)))
566
0
    {
567
0
      size_t
568
0
        quantum;
569
570
0
      struct stat
571
0
        file_stats;
572
573
      /*
574
        Stream is not seekable.
575
      */
576
0
      offset=(MagickOffsetType) lseek(file,0,SEEK_SET);
577
0
      quantum=(size_t) MagickMaxBufferExtent;
578
0
      if ((fstat(file,&file_stats) == 0) && (file_stats.st_size > 0))
579
0
        quantum=(size_t) MagickMin(file_stats.st_size,MagickMaxBufferExtent);
580
0
      xml=(char *) AcquireQuantumMemory(quantum,sizeof(*xml));
581
0
      for (i=0; xml != (char *) NULL; i+=(size_t) count)
582
0
      {
583
0
        count=read(file,xml+i,quantum);
584
0
        if (count <= 0)
585
0
          {
586
0
            count=0;
587
0
            if (errno != EINTR)
588
0
              break;
589
0
          }
590
0
        if (~((size_t) i) < (quantum+1))
591
0
          {
592
0
            xml=(char *) RelinquishMagickMemory(xml);
593
0
            break;
594
0
          }
595
0
        xml=(char *) ResizeQuantumMemory(xml,i+quantum+1,sizeof(*xml));
596
0
        if ((i+(size_t) count) >= extent)
597
0
          break;
598
0
      }
599
0
      if (LocaleCompare(filename,"-") != 0)
600
0
        file=close_utf8(file);
601
0
      if (xml == (char *) NULL)
602
0
        return((char *) NULL);
603
0
      if (file == -1)
604
0
        {
605
0
          xml=(char *) RelinquishMagickMemory(xml);
606
0
          return((char *) NULL);
607
0
        }
608
0
      length=MagickMin(i+(size_t) count,extent);
609
0
      xml[length]='\0';
610
0
      return(xml);
611
0
    }
612
0
  length=(size_t) MagickMin(offset,(MagickOffsetType) extent);
613
0
  xml=(char *) NULL;
614
0
  if (~length >= (MagickPathExtent-1))
615
0
    xml=(char *) AcquireQuantumMemory(length+MagickPathExtent,sizeof(*xml));
616
0
  if (xml == (char *) NULL)
617
0
    {
618
0
      file=close_utf8(file);
619
0
      return((char *) NULL);
620
0
    }
621
0
  map=MapBlob(file,ReadMode,0,length);
622
0
  if (map != (char *) NULL)
623
0
    {
624
0
      (void) memcpy(xml,map,length);
625
0
      (void) UnmapBlob(map,length);
626
0
    }
627
0
  else
628
0
    {
629
0
      (void) lseek(file,0,SEEK_SET);
630
0
      for (i=0; i < length; i+=(size_t) count)
631
0
      {
632
0
        count=read(file,xml+i,(size_t) MagickMin(length-i,(size_t)
633
0
          MagickMaxBufferExtent));
634
0
        if (count <= 0)
635
0
          {
636
0
            count=0;
637
0
            if (errno != EINTR)
638
0
              break;
639
0
          }
640
0
      }
641
0
      if (i < length)
642
0
        {
643
0
          file=close_utf8(file)-1;
644
0
          xml=(char *) RelinquishMagickMemory(xml);
645
0
          return((char *) NULL);
646
0
        }
647
0
    }
648
0
  xml[length]='\0';
649
0
  if (LocaleCompare(filename,"-") != 0)
650
0
    file=close_utf8(file);
651
0
  if (file == -1)
652
0
    xml=(char *) RelinquishMagickMemory(xml);
653
0
  return(xml);
654
0
}
655

656
/*
657
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
658
%                                                                             %
659
%                                                                             %
660
%                                                                             %
661
%   G e t N e x t X M L T r e e T a g                                         %
662
%                                                                             %
663
%                                                                             %
664
%                                                                             %
665
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
666
%
667
%  GetNextXMLTreeTag() returns the next tag or NULL if not found.
668
%
669
%  The format of the GetNextXMLTreeTag method is:
670
%
671
%      XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
672
%
673
%  A description of each parameter follows:
674
%
675
%    o xml_info: the xml info.
676
%
677
*/
678
MagickExport XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
679
0
{
680
0
  assert(xml_info != (XMLTreeInfo *) NULL);
681
0
  assert((xml_info->signature == MagickCoreSignature) ||
682
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
683
0
  if (IsEventLogging() != MagickFalse)
684
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
685
0
  return(xml_info->next);
686
0
}
687

688
/*
689
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
690
%                                                                             %
691
%                                                                             %
692
%                                                                             %
693
%   G e t X M L T r e e A t t r i b u t e                                     %
694
%                                                                             %
695
%                                                                             %
696
%                                                                             %
697
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698
%
699
%  GetXMLTreeAttribute() returns the value of the attribute tag with the
700
%  specified tag if found, otherwise NULL.
701
%
702
%  The format of the GetXMLTreeAttribute method is:
703
%
704
%      const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag)
705
%
706
%  A description of each parameter follows:
707
%
708
%    o xml_info: the xml info.
709
%
710
%    o tag: the attribute tag.
711
%
712
*/
713
MagickExport const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,
714
  const char *tag)
715
0
{
716
0
  ssize_t
717
0
    i,
718
0
    j;
719
720
0
  XMLTreeRoot
721
0
    *root;
722
723
0
  assert(xml_info != (XMLTreeInfo *) NULL);
724
0
  assert((xml_info->signature == MagickCoreSignature) ||
725
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
726
0
  if (IsEventLogging() != MagickFalse)
727
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
728
0
  if (xml_info->attributes == (char **) NULL)
729
0
    return((const char *) NULL);
730
0
  i=0;
731
0
  while ((xml_info->attributes[i] != (char *) NULL) &&
732
0
         (strcmp(xml_info->attributes[i],tag) != 0))
733
0
    i+=2;
734
0
  if (xml_info->attributes[i] != (char *) NULL)
735
0
    return(xml_info->attributes[i+1]);
736
0
  root=(XMLTreeRoot*) xml_info;
737
0
  while (root->root.parent != (XMLTreeInfo *) NULL)
738
0
    root=(XMLTreeRoot *) root->root.parent;
739
0
  i=0;
740
0
  while ((root->attributes[i] != (char **) NULL) &&
741
0
         (strcmp(root->attributes[i][0],xml_info->tag) != 0))
742
0
    i++;
743
0
  if (root->attributes[i] == (char **) NULL)
744
0
    return((const char *) NULL);
745
0
  j=1;
746
0
  while ((root->attributes[i][j] != (char *) NULL) &&
747
0
         (strcmp(root->attributes[i][j],tag) != 0))
748
0
    j+=3;
749
0
  if (root->attributes[i][j] == (char *) NULL)
750
0
    return((const char *) NULL);
751
0
  return(root->attributes[i][j+1]);
752
0
}
753

754
/*
755
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756
%                                                                             %
757
%                                                                             %
758
%                                                                             %
759
%   G e t X M L T r e e A t t r i b u t e s                                   %
760
%                                                                             %
761
%                                                                             %
762
%                                                                             %
763
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
764
%
765
%  GetXMLTreeAttributes() injects all attributes associated with the current
766
%  tag in the specified splay-tree.
767
%
768
%  The format of the GetXMLTreeAttributes method is:
769
%
770
%      MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
771
%        SplayTreeInfo *attributes)
772
%
773
%  A description of each parameter follows:
774
%
775
%    o xml_info: the xml info.
776
%
777
%    o attributes: the attribute splay-tree.
778
%
779
*/
780
MagickPrivate MagickBooleanType GetXMLTreeAttributes(
781
  const XMLTreeInfo *xml_info,SplayTreeInfo *attributes)
782
0
{
783
0
  ssize_t
784
0
    i;
785
786
0
  assert(xml_info != (XMLTreeInfo *) NULL);
787
0
  assert((xml_info->signature == MagickCoreSignature) ||
788
0
         (((const XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
789
0
  assert(attributes != (SplayTreeInfo *) NULL);
790
0
  if (IsEventLogging() != MagickFalse)
791
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
792
0
  if (xml_info->attributes == (char **) NULL)
793
0
    return(MagickTrue);
794
0
  i=0;
795
0
  while (xml_info->attributes[i] != (char *) NULL)
796
0
  {
797
0
     (void) AddValueToSplayTree(attributes,
798
0
       ConstantString(xml_info->attributes[i]),
799
0
       ConstantString(xml_info->attributes[i+1]));
800
0
    i+=2;
801
0
  }
802
0
  return(MagickTrue);
803
0
}
804

805
/*
806
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807
%                                                                             %
808
%                                                                             %
809
%                                                                             %
810
%   G e t X M L T r e e C h i l d                                             %
811
%                                                                             %
812
%                                                                             %
813
%                                                                             %
814
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
815
%
816
%  GetXMLTreeChild() returns the first child tag with the specified tag if
817
%  found, otherwise NULL.
818
%
819
%  The format of the GetXMLTreeChild method is:
820
%
821
%      XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
822
%
823
%  A description of each parameter follows:
824
%
825
%    o xml_info: the xml info.
826
%
827
*/
828
MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
829
0
{
830
0
  XMLTreeInfo
831
0
    *child;
832
833
0
  assert(xml_info != (XMLTreeInfo *) NULL);
834
0
  assert((xml_info->signature == MagickCoreSignature) ||
835
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
836
0
  if (IsEventLogging() != MagickFalse)
837
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
838
0
  child=xml_info->child;
839
0
  if (tag != (const char *) NULL)
840
0
    while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0))
841
0
      child=child->sibling;
842
0
  return(child);
843
0
}
844

845
/*
846
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847
%                                                                             %
848
%                                                                             %
849
%                                                                             %
850
%   G e t X M L T r e e C o n t e n t                                         %
851
%                                                                             %
852
%                                                                             %
853
%                                                                             %
854
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855
%
856
%  GetXMLTreeContent() returns any content associated with specified
857
%  xml-tree node.
858
%
859
%  The format of the GetXMLTreeContent method is:
860
%
861
%      const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
862
%
863
%  A description of each parameter follows:
864
%
865
%    o xml_info: the xml info.
866
%
867
*/
868
MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
869
0
{
870
0
  assert(xml_info != (XMLTreeInfo *) NULL);
871
0
  assert((xml_info->signature == MagickCoreSignature) ||
872
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
873
0
  if (IsEventLogging() != MagickFalse)
874
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
875
0
  return(xml_info->content);
876
0
}
877

878
/*
879
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
880
%                                                                             %
881
%                                                                             %
882
%                                                                             %
883
%   G e t X M L T r e e O r d e r e d                                         %
884
%                                                                             %
885
%                                                                             %
886
%                                                                             %
887
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
888
%
889
%  GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL.
890
%
891
%  The format of the GetXMLTreeOrdered method is:
892
%
893
%      XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
894
%
895
%  A description of each parameter follows:
896
%
897
%    o xml_info: the xml info.
898
%
899
*/
900
MagickPrivate XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
901
0
{
902
0
  assert(xml_info != (XMLTreeInfo *) NULL);
903
0
  assert((xml_info->signature == MagickCoreSignature) ||
904
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
905
0
  if (IsEventLogging() != MagickFalse)
906
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
907
0
  return(xml_info->ordered);
908
0
}
909

910
/*
911
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
912
%                                                                             %
913
%                                                                             %
914
%                                                                             %
915
%   G e t X M L T r e e P a t h                                               %
916
%                                                                             %
917
%                                                                             %
918
%                                                                             %
919
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
920
%
921
%  GetXMLTreePath() traverses the XML-tree as defined by the specified path
922
%  and returns the node if found, otherwise NULL.
923
%
924
%  The format of the GetXMLTreePath method is:
925
%
926
%      XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
927
%
928
%  A description of each parameter follows:
929
%
930
%    o xml_info: the xml info.
931
%
932
%    o path: the path (e.g. property/elapsed-time).
933
%
934
*/
935
MagickPrivate XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,
936
  const char *path)
937
0
{
938
0
  char
939
0
    **components,
940
0
    subnode[MagickPathExtent],
941
0
    tag[MagickPathExtent];
942
943
0
  size_t
944
0
    number_components;
945
946
0
  ssize_t
947
0
    i,
948
0
    j;
949
950
0
  XMLTreeInfo
951
0
    *node;
952
953
0
  assert(xml_info != (XMLTreeInfo *) NULL);
954
0
  assert((xml_info->signature == MagickCoreSignature) ||
955
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
956
0
  if (IsEventLogging() != MagickFalse)
957
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
958
0
  node=xml_info;
959
0
  components=GetPathComponents(path,&number_components);
960
0
  if (components == (char **) NULL)
961
0
    return((XMLTreeInfo *) NULL);
962
0
  for (i=0; i < (ssize_t) number_components; i++)
963
0
  {
964
0
    GetPathComponent(components[i],SubimagePath,subnode);
965
0
    GetPathComponent(components[i],CanonicalPath,tag);
966
0
    node=GetXMLTreeChild(node,tag);
967
0
    if (node == (XMLTreeInfo *) NULL)
968
0
      break;
969
0
    for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
970
0
    {
971
0
      node=GetXMLTreeOrdered(node);
972
0
      if (node == (XMLTreeInfo *) NULL)
973
0
        break;
974
0
    }
975
0
    if (node == (XMLTreeInfo *) NULL)
976
0
      break;
977
0
    components[i]=DestroyString(components[i]);
978
0
  }
979
0
  for ( ; i < (ssize_t) number_components; i++)
980
0
    components[i]=DestroyString(components[i]);
981
0
  components=(char **) RelinquishMagickMemory(components);
982
0
  return(node);
983
0
}
984

985
/*
986
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
987
%                                                                             %
988
%                                                                             %
989
%                                                                             %
990
%   G e t X M L T r e e P r o c e s s i n g I n s t r u c t i o n s           %
991
%                                                                             %
992
%                                                                             %
993
%                                                                             %
994
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
995
%
996
%  GetXMLTreeProcessingInstructions() returns a null terminated array of
997
%  processing instructions for the given target.
998
%
999
%  The format of the GetXMLTreeProcessingInstructions method is:
1000
%
1001
%      const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info,
1002
%        const char *target)
1003
%
1004
%  A description of each parameter follows:
1005
%
1006
%    o xml_info: the xml info.
1007
%
1008
*/
1009
MagickPrivate const char **GetXMLTreeProcessingInstructions(
1010
  XMLTreeInfo *xml_info,const char *target)
1011
0
{
1012
0
  ssize_t
1013
0
    i;
1014
1015
0
  XMLTreeRoot
1016
0
    *root;
1017
1018
0
  assert(xml_info != (XMLTreeInfo *) NULL);
1019
0
  assert((xml_info->signature == MagickCoreSignature) ||
1020
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
1021
0
  if (IsEventLogging() != MagickFalse)
1022
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1023
0
  root=(XMLTreeRoot *) xml_info;
1024
0
  while (root->root.parent != (XMLTreeInfo *) NULL)
1025
0
    root=(XMLTreeRoot *) root->root.parent;
1026
0
  i=0;
1027
0
  while ((root->processing_instructions[i] != (char **) NULL) &&
1028
0
         (strcmp(root->processing_instructions[i][0],target) != 0))
1029
0
    i++;
1030
0
  if (root->processing_instructions[i] == (char **) NULL)
1031
0
    return((const char **) sentinel);
1032
0
  return((const char **) (root->processing_instructions[i]+1));
1033
0
}
1034

1035
/*
1036
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1037
%                                                                             %
1038
%                                                                             %
1039
%                                                                             %
1040
%   G e t X M L T r e e S i b l i n g                                         %
1041
%                                                                             %
1042
%                                                                             %
1043
%                                                                             %
1044
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1045
%
1046
%  GetXMLTreeSibling() returns the node sibling if found, otherwise NULL.
1047
%
1048
%  The format of the GetXMLTreeSibling method is:
1049
%
1050
%      XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
1051
%
1052
%  A description of each parameter follows:
1053
%
1054
%    o xml_info: the xml info.
1055
%
1056
*/
1057
MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
1058
0
{
1059
0
  assert(xml_info != (XMLTreeInfo *) NULL);
1060
0
  assert((xml_info->signature == MagickCoreSignature) ||
1061
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
1062
0
  if (IsEventLogging() != MagickFalse)
1063
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1064
0
  return(xml_info->sibling);
1065
0
}
1066

1067
/*
1068
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069
%                                                                             %
1070
%                                                                             %
1071
%                                                                             %
1072
%   G e t X M L T r e e T a g                                                 %
1073
%                                                                             %
1074
%                                                                             %
1075
%                                                                             %
1076
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1077
%
1078
%  GetXMLTreeTag() returns the tag associated with specified xml-tree node.
1079
%
1080
%  The format of the GetXMLTreeTag method is:
1081
%
1082
%      const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
1083
%
1084
%  A description of each parameter follows:
1085
%
1086
%    o xml_info: the xml info.
1087
%
1088
*/
1089
MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
1090
0
{
1091
0
  assert(xml_info != (XMLTreeInfo *) NULL);
1092
0
  assert((xml_info->signature == MagickCoreSignature) ||
1093
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
1094
0
  if (IsEventLogging() != MagickFalse)
1095
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1096
0
  return(xml_info->tag);
1097
0
}
1098

1099
/*
1100
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1101
%                                                                             %
1102
%                                                                             %
1103
%                                                                             %
1104
%   I n s e r t I n t o T a g X M L T r e e                                   %
1105
%                                                                             %
1106
%                                                                             %
1107
%                                                                             %
1108
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1109
%
1110
%  InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of
1111
%  the parent tag's character content.  This method returns the child tag.
1112
%
1113
%  The format of the InsertTagIntoXMLTree method is:
1114
%
1115
%      XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1116
%        XMLTreeInfo *child,const size_t offset)
1117
%
1118
%  A description of each parameter follows:
1119
%
1120
%    o xml_info: the xml info.
1121
%
1122
%    o child: the child tag.
1123
%
1124
%    o offset: the tag offset.
1125
%
1126
*/
1127
MagickPrivate XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1128
  XMLTreeInfo *child,const size_t offset)
1129
0
{
1130
0
  XMLTreeInfo
1131
0
    *head,
1132
0
    *node,
1133
0
    *previous;
1134
1135
0
  child->ordered=(XMLTreeInfo *) NULL;
1136
0
  child->sibling=(XMLTreeInfo *) NULL;
1137
0
  child->next=(XMLTreeInfo *) NULL;
1138
0
  child->offset=offset;
1139
0
  child->parent=xml_info;
1140
0
  if (xml_info->child == (XMLTreeInfo *) NULL)
1141
0
    {
1142
0
      xml_info->child=child;
1143
0
      return(child);
1144
0
    }
1145
0
  head=xml_info->child;
1146
0
  if (head->offset > offset)
1147
0
    {
1148
0
      child->ordered=head;
1149
0
      xml_info->child=child;
1150
0
    }
1151
0
  else
1152
0
    {
1153
0
      node=head;
1154
0
      while ((node->ordered != (XMLTreeInfo *) NULL) &&
1155
0
             (node->ordered->offset <= offset))
1156
0
        node=node->ordered;
1157
0
      child->ordered=node->ordered;
1158
0
      node->ordered=child;
1159
0
    }
1160
0
  previous=(XMLTreeInfo *) NULL;
1161
0
  node=head;
1162
0
  while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0))
1163
0
  {
1164
0
    previous=node;
1165
0
    node=node->sibling;
1166
0
  }
1167
0
  if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1168
0
    {
1169
0
      while ((node->next != (XMLTreeInfo *) NULL) &&
1170
0
             (node->next->offset <= offset))
1171
0
        node=node->next;
1172
0
      child->next=node->next;
1173
0
      node->next=child;
1174
0
    }
1175
0
  else
1176
0
    {
1177
0
      if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL))
1178
0
        previous->sibling=node->sibling;
1179
0
      child->next=node;
1180
0
      previous=(XMLTreeInfo *) NULL;
1181
0
      node=head;
1182
0
      while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1183
0
      {
1184
0
        previous=node;
1185
0
        node=node->sibling;
1186
0
      }
1187
0
      child->sibling=node;
1188
0
      if (previous != (XMLTreeInfo *) NULL)
1189
0
        previous->sibling=child;
1190
0
    }
1191
0
  return(child);
1192
0
}
1193

1194
/*
1195
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1196
%                                                                             %
1197
%                                                                             %
1198
%                                                                             %
1199
%   N e w X M L T r e e                                                       %
1200
%                                                                             %
1201
%                                                                             %
1202
%                                                                             %
1203
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1204
%
1205
%  NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified
1206
%  XML string.
1207
%
1208
%  The format of the NewXMLTree method is:
1209
%
1210
%      XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1211
%
1212
%  A description of each parameter follows:
1213
%
1214
%    o xml:  A null-terminated XML string.
1215
%
1216
%    o exception: return any errors or warnings in this structure.
1217
%
1218
*/
1219
1220
static char *ConvertUTF16ToUTF8(const char *content,size_t *length)
1221
0
{
1222
0
  char
1223
0
    *utf8;
1224
1225
0
  int
1226
0
    bits,
1227
0
    byte,
1228
0
    c,
1229
0
    encoding;
1230
1231
0
  size_t
1232
0
    extent;
1233
1234
0
  ssize_t
1235
0
    i,
1236
0
    j;
1237
1238
0
  utf8=(char *) AcquireQuantumMemory(*length+1,sizeof(*utf8));
1239
0
  if (utf8 == (char *) NULL)
1240
0
    return((char *) NULL);
1241
0
  encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1;
1242
0
  if (encoding == -1)
1243
0
    {
1244
      /*
1245
        Already UTF-8.
1246
      */
1247
0
      (void) memcpy(utf8,content,*length*sizeof(*utf8));
1248
0
      utf8[*length]='\0';
1249
0
      return(utf8);
1250
0
    }
1251
0
  j=0;
1252
0
  extent=(*length);
1253
0
  for (i=2; i < (ssize_t) (*length-1); i+=2)
1254
0
  {
1255
0
    c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) :
1256
0
      ((content[i+1] & 0xff) << 8) | (content[i] & 0xff);
1257
0
    if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (ssize_t) (*length-1)))
1258
0
      {
1259
0
        byte=(encoding != 0) ? ((content[i] & 0xff) << 8) |
1260
0
          (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) |
1261
0
          (content[i] & 0xff);
1262
0
        c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000;
1263
0
      }
1264
0
    if ((size_t) (j+MagickPathExtent) > extent)
1265
0
      {
1266
0
        extent=(size_t) j+MagickPathExtent;
1267
0
        utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8));
1268
0
        if (utf8 == (char *) NULL)
1269
0
          return(utf8);
1270
0
      }
1271
0
    if (c < 0x80)
1272
0
      {
1273
0
        utf8[j]=(char) c;
1274
0
        j++;
1275
0
        continue;
1276
0
      }
1277
    /*
1278
      Multi-byte UTF-8 sequence.
1279
    */
1280
0
    byte=c;
1281
0
    for (bits=0; byte != 0; byte/=2)
1282
0
      bits++;
1283
0
    bits=(bits-2)/5;
1284
0
    utf8[j++]=(char) ((0xFF << (7-bits)) | (c >> (6*bits)));
1285
0
    while (bits != 0)
1286
0
    {
1287
0
      bits--;
1288
0
      utf8[j]=(char) (0x80 | ((c >> (6*bits)) & 0x3f));
1289
0
      j++;
1290
0
    }
1291
0
  }
1292
0
  *length=(size_t) j;
1293
0
  utf8=(char *) ResizeQuantumMemory(utf8,(*length+1),sizeof(*utf8));
1294
0
  if (utf8 != (char *) NULL)
1295
0
    utf8[*length]='\0';
1296
0
  return(utf8);
1297
0
}
1298
1299
static char *ParseEntities(char *xml,char **entities,int state)
1300
0
{
1301
0
  char
1302
0
    *entity,
1303
0
    *p,
1304
0
    *q;
1305
1306
0
  int
1307
0
    byte,
1308
0
    c;
1309
1310
0
  size_t
1311
0
    extent,
1312
0
    length;
1313
1314
0
  ssize_t
1315
0
    i,
1316
0
    offset;
1317
1318
  /*
1319
    Normalize line endings.
1320
  */
1321
0
  p=xml;
1322
0
  q=xml;
1323
0
  for ( ; *xml != '\0'; xml++)
1324
0
    while (*xml == '\r')
1325
0
    {
1326
0
      *(xml++)='\n';
1327
0
      if (*xml == '\n')
1328
0
        (void) memmove(xml,xml+1,strlen(xml));
1329
0
    }
1330
0
  for (xml=p; ; )
1331
0
  {
1332
0
    while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') ||
1333
0
           (state != '%')) && (isspace((int) ((unsigned char) *xml)) == 0))
1334
0
      xml++;
1335
0
    if (*xml == '\0')
1336
0
      break;
1337
    /*
1338
      States include:
1339
        '&' for general entity decoding
1340
        '%' for parameter entity decoding
1341
        'c' for CDATA sections
1342
        ' ' for attributes normalization
1343
        '*' for non-CDATA attributes normalization
1344
    */
1345
0
    if ((state != 'c') && (strncmp(xml,"&#",2) == 0))
1346
0
      {
1347
        /*
1348
          Character reference.
1349
        */
1350
0
        if (xml[2] != 'x')
1351
0
          c=strtol(xml+2,&entity,10);  /* base 10 */
1352
0
        else
1353
0
          c=strtol(xml+3,&entity,16);  /* base 16 */
1354
0
        if ((c == 0) || (*entity != ';'))
1355
0
          {
1356
            /*
1357
              Not a character reference.
1358
            */
1359
0
            xml++;
1360
0
            continue;
1361
0
          }
1362
0
        if (c < 0x80)
1363
0
          *(xml++)=(char) c;
1364
0
        else
1365
0
          {
1366
            /*
1367
              Multi-byte UTF-8 sequence.
1368
            */
1369
0
            byte=c;
1370
0
            for (i=0; byte != 0; byte/=2)
1371
0
              i++;
1372
0
            i=(i-2)/5;
1373
0
            *xml=(char) ((0xFF << (7-i)) | (c >> (6*i)));
1374
0
            xml++;
1375
0
            while (i != 0)
1376
0
            {
1377
0
              i--;
1378
0
              *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F));
1379
0
              xml++;
1380
0
            }
1381
0
          }
1382
0
        (void) memmove(xml,strchr(xml,';')+1,strlen(strchr(xml,';')));
1383
0
      }
1384
0
    else
1385
0
      if (((*xml == '&') && ((state == '&') || (state == ' ') ||
1386
0
          (state == '*'))) || ((state == '%') && (*xml == '%')))
1387
0
        {
1388
          /*
1389
            Find entity in the list.
1390
          */
1391
0
          i=0;
1392
0
          while ((entities[i] != (char *) NULL) &&
1393
0
                 (strncmp(xml+1,entities[i],strlen(entities[i])) != 0))
1394
0
            i+=2;
1395
0
          if (entities[i++] == (char *) NULL)
1396
0
            xml++;
1397
0
          else
1398
0
            if (entities[i] != (char *) NULL)
1399
0
              {
1400
                /*
1401
                  Found a match.
1402
                */
1403
0
                length=strlen(entities[i]);
1404
0
                entity=strchr(xml,';');
1405
0
                if ((entity != (char *) NULL) &&
1406
0
                    ((length-1L) >= (size_t) (entity-xml)))
1407
0
                  {
1408
0
                    offset=(ssize_t) (xml-p);
1409
0
                    extent=((size_t) offset+length+strlen(entity));
1410
0
                    if (p != q)
1411
0
                      {
1412
0
                        p=(char *) ResizeQuantumMemory(p,extent+1,sizeof(*p));
1413
0
                        if (p != (char *) NULL)
1414
0
                          p[extent]='\0';
1415
0
                      }
1416
0
                    else
1417
0
                      {
1418
0
                        char
1419
0
                          *extent_xml;
1420
1421
0
                        extent_xml=(char *) AcquireQuantumMemory(extent+1,
1422
0
                          sizeof(*extent_xml));
1423
0
                        if (extent_xml != (char *) NULL)
1424
0
                          {
1425
0
                            memset(extent_xml,0,extent*sizeof(*extent_xml));
1426
0
                            (void) CopyMagickString(extent_xml,p,extent*
1427
0
                              sizeof(*extent_xml));
1428
0
                          }
1429
0
                        p=extent_xml;
1430
0
                      }
1431
0
                    if (p == (char *) NULL)
1432
0
                      ThrowFatalException(ResourceLimitFatalError,
1433
0
                        "MemoryAllocationFailed");
1434
0
                    xml=p+offset;
1435
0
                    entity=strchr(xml,';');
1436
0
                  }
1437
0
                if (entity != (char *) NULL)
1438
0
                  (void) memmove(xml+length,entity+1,strlen(entity));
1439
0
                (void) memcpy(xml,entities[i],length);
1440
0
              }
1441
0
        }
1442
0
      else
1443
0
        if (((state == ' ') || (state == '*')) &&
1444
0
            (isspace((int) ((unsigned char) *xml)) != 0))
1445
0
          *(xml++)=' ';
1446
0
        else
1447
0
          xml++;
1448
0
  }
1449
0
  if (state == '*')
1450
0
    {
1451
      /*
1452
        Normalize spaces for non-CDATA attributes.
1453
      */
1454
0
      for (xml=p; *xml != '\0'; xml++)
1455
0
      {
1456
0
        char
1457
0
          accept[] = " ";
1458
1459
0
        i=(ssize_t) strspn(xml,accept);
1460
0
        if (i != 0)
1461
0
          (void) memmove(xml,xml+i,strlen(xml+i)+1);
1462
0
        while ((*xml != '\0') && (*xml != ' '))
1463
0
          xml++;
1464
0
        if (*xml == '\0')
1465
0
          break;
1466
0
      }
1467
0
      xml--;
1468
0
      if ((xml >= p) && (*xml == ' '))
1469
0
        *xml='\0';
1470
0
    }
1471
0
  return(p == q ? ConstantString(p) : p);
1472
0
}
1473
1474
static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
1475
  const size_t length,const char state)
1476
0
{
1477
0
  XMLTreeInfo
1478
0
    *xml_info;
1479
1480
0
  xml_info=root->node;
1481
0
  if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
1482
0
      (length == 0))
1483
0
    return;
1484
0
  xml[length]='\0';
1485
0
  xml=ParseEntities(xml,root->entities,state);
1486
0
  if ((xml_info->content != (char *) NULL) && (*xml_info->content != '\0'))
1487
0
    {
1488
0
      (void) ConcatenateString(&xml_info->content,xml);
1489
0
      xml=DestroyString(xml);
1490
0
    }
1491
0
  else
1492
0
    {
1493
0
      if (xml_info->content != (char *) NULL)
1494
0
        xml_info->content=DestroyString(xml_info->content);
1495
0
      xml_info->content=xml;
1496
0
    }
1497
0
}
1498
1499
static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
1500
  ExceptionInfo *exception)
1501
0
{
1502
0
  if ((root->node == (XMLTreeInfo *) NULL) ||
1503
0
      (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
1504
0
    {
1505
0
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1506
0
        "ParseError","unexpected closing tag </%s>",tag);
1507
0
      return(&root->root);
1508
0
    }
1509
0
  root->node=root->node->parent;
1510
0
  return((XMLTreeInfo *) NULL);
1511
0
}
1512
1513
static MagickBooleanType ValidateEntities(char *tag,char *xml,
1514
  const size_t depth,char **entities)
1515
0
{
1516
0
  ssize_t
1517
0
    i;
1518
1519
  /*
1520
    Check for circular entity references.
1521
  */
1522
0
  if (depth > MagickMaxRecursionDepth)
1523
0
    return(MagickFalse);
1524
0
  for ( ; ; xml++)
1525
0
  {
1526
0
    while ((*xml != '\0') && (*xml != '&'))
1527
0
      xml++;
1528
0
    if (*xml == '\0')
1529
0
      return(MagickTrue);
1530
0
    if (strncmp(xml+1,tag,strlen(tag)) == 0)
1531
0
      return(MagickFalse);
1532
0
    i=0;
1533
0
    while ((entities[i] != (char *) NULL) &&
1534
0
           (strncmp(entities[i],xml+1,strlen(entities[i])) == 0))
1535
0
      i+=2;
1536
0
    if ((entities[i] != (char *) NULL) &&
1537
0
        (ValidateEntities(tag,entities[i+1],depth+1,entities) == 0))
1538
0
      return(MagickFalse);
1539
0
  }
1540
0
}
1541
1542
static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
1543
  size_t length)
1544
0
{
1545
0
  char
1546
0
    *target;
1547
1548
0
  ssize_t
1549
0
    i,
1550
0
    j;
1551
1552
0
  target=xml;
1553
0
  xml[length]='\0';
1554
0
  xml+=strcspn(xml,XMLWhitespace);
1555
0
  if (*xml != '\0')
1556
0
    {
1557
0
      *xml='\0';
1558
0
      xml+=strspn(xml+1,XMLWhitespace)+1;
1559
0
    }
1560
0
  if (strcmp(target,"xml") == 0)
1561
0
    {
1562
0
      xml=strstr(xml,"standalone");
1563
0
      if ((xml != (char *) NULL) &&
1564
0
          (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
1565
0
        root->standalone=MagickTrue;
1566
0
      return;
1567
0
    }
1568
0
  if (root->processing_instructions[0] == (char **) NULL)
1569
0
    {
1570
0
      root->processing_instructions=(char ***) AcquireCriticalMemory(sizeof(
1571
0
        *root->processing_instructions));
1572
0
      *root->processing_instructions=(char **) NULL;
1573
0
    }
1574
0
  i=0;
1575
0
  while ((root->processing_instructions[i] != (char **) NULL) &&
1576
0
         (strcmp(target,root->processing_instructions[i][0]) != 0))
1577
0
    i++;
1578
0
  if (root->processing_instructions[i] == (char **) NULL)
1579
0
    {
1580
0
      root->processing_instructions=(char ***) ResizeQuantumMemory(
1581
0
        root->processing_instructions,(size_t) (i+2),
1582
0
        sizeof(*root->processing_instructions));
1583
0
      if (root->processing_instructions == (char ***) NULL)
1584
0
        ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1585
0
      root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
1586
0
        sizeof(**root->processing_instructions));
1587
0
      if (root->processing_instructions[i] == (char **) NULL)
1588
0
        ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1589
0
      root->processing_instructions[i+1]=(char **) NULL;
1590
0
      root->processing_instructions[i][0]=ConstantString(target);
1591
0
      root->processing_instructions[i][1]=(char *)
1592
0
        root->processing_instructions[i+1];
1593
0
      root->processing_instructions[i+1]=(char **) NULL;
1594
0
      root->processing_instructions[i][2]=ConstantString("");
1595
0
    }
1596
0
  j=1;
1597
0
  while (root->processing_instructions[i][j] != (char *) NULL)
1598
0
    j++;
1599
0
  root->processing_instructions[i]=(char **) ResizeQuantumMemory(
1600
0
    root->processing_instructions[i],(size_t) (j+3),
1601
0
    sizeof(**root->processing_instructions));
1602
0
  if (root->processing_instructions[i] == (char **) NULL)
1603
0
    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1604
0
  root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
1605
0
    root->processing_instructions[i][j+1],(size_t) (j+1),
1606
0
    sizeof(***root->processing_instructions));
1607
0
  if (root->processing_instructions[i][j+2] == (char *) NULL)
1608
0
    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1609
0
  (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
1610
0
    root->root.tag != (char *) NULL ? ">" : "<",2);
1611
0
  root->processing_instructions[i][j]=ConstantString(xml);
1612
0
  root->processing_instructions[i][j+1]=(char *) NULL;
1613
0
}
1614
1615
static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
1616
  size_t length,ExceptionInfo *exception)
1617
0
{
1618
0
  char
1619
0
    *c,
1620
0
    **entities,
1621
0
    *n,
1622
0
    **predefined_entities,
1623
0
    q,
1624
0
    *t,
1625
0
    *v;
1626
1627
0
  ssize_t
1628
0
    i,
1629
0
    j;
1630
1631
0
  n=(char *) NULL;
1632
0
  predefined_entities=(char **) AcquireMagickMemory(sizeof(sentinel));
1633
0
  if (predefined_entities == (char **) NULL)
1634
0
    ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
1635
0
  (void) memcpy(predefined_entities,sentinel,sizeof(sentinel));
1636
0
  for (xml[length]='\0'; xml != (char *) NULL; )
1637
0
  {
1638
0
    while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
1639
0
      xml++;
1640
0
    if (*xml == '\0')
1641
0
      break;
1642
0
    if ((strlen(xml) > 9) && (strncmp(xml,"<!ENTITY",8) == 0))
1643
0
      {
1644
        /*
1645
          Parse entity definitions.
1646
        */
1647
0
        if (strspn(xml+8,XMLWhitespace) == 0)
1648
0
          break;
1649
0
        xml+=strspn(xml+8,XMLWhitespace)+8;
1650
0
        c=xml;
1651
0
        n=xml+strspn(xml,XMLWhitespace "%");
1652
0
        if ((isalpha((int) ((unsigned char) *n)) == 0) && (*n != '_'))
1653
0
          break;
1654
0
        xml=n+strcspn(n,XMLWhitespace);
1655
0
        if (*xml == '\0')
1656
0
          break;
1657
0
        *xml=';';
1658
0
        v=xml+strspn(xml+1,XMLWhitespace)+1;
1659
0
        q=(*v);
1660
0
        v++;
1661
0
        if ((q != '"') && (q != '\''))
1662
0
          {
1663
            /*
1664
              Skip externals.
1665
            */
1666
0
            xml=strchr(xml,'>');
1667
0
            continue;
1668
0
          }
1669
0
        entities=(*c == '%') ? predefined_entities : root->entities;
1670
0
        for (i=0; entities[i] != (char *) NULL; i++) ;
1671
0
        entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
1672
0
          sizeof(*entities));
1673
0
        if (entities == (char **) NULL)
1674
0
          ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1675
0
        if (*c == '%')
1676
0
          predefined_entities=entities;
1677
0
        else
1678
0
          root->entities=entities;
1679
0
        xml++;
1680
0
        *xml='\0';
1681
0
        xml=strchr(v,q);
1682
0
        if (xml != (char *) NULL)
1683
0
          {
1684
0
            *xml='\0';
1685
0
            xml++;
1686
0
          }
1687
0
        entities[i+1]=ParseEntities(v,predefined_entities,'%');
1688
0
        entities[i+2]=(char *) NULL;
1689
0
        if (ValidateEntities(n,entities[i+1],0,entities) != MagickFalse)
1690
0
          entities[i]=n;
1691
0
        else
1692
0
          {
1693
0
            if (entities[i+1] != v)
1694
0
              entities[i+1]=DestroyString(entities[i+1]);
1695
0
            (void) ThrowMagickException(exception,GetMagickModule(),
1696
0
              OptionWarning,"ParseError","circular entity declaration &%s",n);
1697
0
            predefined_entities=(char **) RelinquishMagickMemory(
1698
0
              predefined_entities);
1699
0
            return(MagickFalse);
1700
0
          }
1701
0
        }
1702
0
      else
1703
0
       if (strncmp(xml,"<!ATTLIST",9) == 0)
1704
0
         {
1705
            /*
1706
              Parse default attributes.
1707
            */
1708
0
            t=xml+strspn(xml+9,XMLWhitespace)+9;
1709
0
            if (*t == '\0')
1710
0
              {
1711
0
                (void) ThrowMagickException(exception,GetMagickModule(),
1712
0
                  OptionWarning,"ParseError","unclosed <!ATTLIST");
1713
0
                predefined_entities=(char **) RelinquishMagickMemory(
1714
0
                  predefined_entities);
1715
0
                return(MagickFalse);
1716
0
              }
1717
0
            xml=t+strcspn(t,XMLWhitespace ">");
1718
0
            if (*xml == '>')
1719
0
              continue;
1720
0
            *xml='\0';
1721
0
            i=0;
1722
0
            while ((root->attributes[i] != (char **) NULL) &&
1723
0
                   (n != (char *) NULL) &&
1724
0
                   (strcmp(n,root->attributes[i][0]) != 0))
1725
0
              i++;
1726
0
            while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1727
0
                   (*n != '>'))
1728
0
            {
1729
0
              xml=n+strcspn(n,XMLWhitespace);
1730
0
              if (*xml != '\0')
1731
0
                *xml='\0';
1732
0
              else
1733
0
                {
1734
0
                  (void) ThrowMagickException(exception,GetMagickModule(),
1735
0
                    OptionWarning,"ParseError","malformed <!ATTLIST");
1736
0
                  predefined_entities=(char **) RelinquishMagickMemory(
1737
0
                    predefined_entities);
1738
0
                  return(MagickFalse);
1739
0
                }
1740
0
              xml+=strspn(xml+1,XMLWhitespace)+1;
1741
0
              c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1742
0
              if (strncmp(xml,"NOTATION",8) == 0)
1743
0
                xml+=strspn(xml+8,XMLWhitespace)+8;
1744
0
              xml=(*xml == '(') ? strchr(xml,')') : xml+
1745
0
                strcspn(xml,XMLWhitespace);
1746
0
              if (xml == (char *) NULL)
1747
0
                {
1748
0
                  (void) ThrowMagickException(exception,GetMagickModule(),
1749
0
                    OptionWarning,"ParseError","malformed <!ATTLIST");
1750
0
                  predefined_entities=(char **) RelinquishMagickMemory(
1751
0
                    predefined_entities);
1752
0
                  return(MagickFalse);
1753
0
                }
1754
0
              xml+=strspn(xml,XMLWhitespace ")");
1755
0
              if (strncmp(xml,"#FIXED",6) == 0)
1756
0
                xml+=strspn(xml+6,XMLWhitespace)+6;
1757
0
              if (*xml == '#')
1758
0
                {
1759
0
                  xml+=strcspn(xml,XMLWhitespace ">")-1;
1760
0
                  if (*c == ' ')
1761
0
                    continue;
1762
0
                  v=(char *) NULL;
1763
0
                }
1764
0
              else
1765
0
                if (((*xml == '"') || (*xml == '\''))  &&
1766
0
                    ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1767
0
                  *xml='\0';
1768
0
                else
1769
0
                  {
1770
0
                    (void) ThrowMagickException(exception,GetMagickModule(),
1771
0
                      OptionWarning,"ParseError","malformed <!ATTLIST");
1772
0
                    predefined_entities=(char **) RelinquishMagickMemory(
1773
0
                      predefined_entities);
1774
0
                    return(MagickFalse);
1775
0
                  }
1776
0
              if (root->attributes[i] == (char **) NULL)
1777
0
                {
1778
                  /*
1779
                    New attribute tag.
1780
                  */
1781
0
                  if (i == 0)
1782
0
                    root->attributes=(char ***) AcquireQuantumMemory(2,
1783
0
                      sizeof(*root->attributes));
1784
0
                  else
1785
0
                    root->attributes=(char ***) ResizeQuantumMemory(
1786
0
                      root->attributes,(size_t) (i+2),
1787
0
                      sizeof(*root->attributes));
1788
0
                  if (root->attributes == (char ***) NULL)
1789
0
                    ThrowFatalException(ResourceLimitFatalError,
1790
0
                      "MemoryAllocationFailed");
1791
0
                  root->attributes[i]=(char **) AcquireQuantumMemory(2,
1792
0
                    sizeof(**root->attributes));
1793
0
                  if (root->attributes[i] == (char **) NULL)
1794
0
                    ThrowFatalException(ResourceLimitFatalError,
1795
0
                      "MemoryAllocationFailed");
1796
0
                  root->attributes[i][0]=ConstantString(t);
1797
0
                  root->attributes[i][1]=(char *) NULL;
1798
0
                  root->attributes[i+1]=(char **) NULL;
1799
0
                }
1800
0
              for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1801
0
              root->attributes[i]=(char **) ResizeQuantumMemory(
1802
0
                root->attributes[i],(size_t) (j+4),sizeof(**root->attributes));
1803
0
              if (root->attributes[i] == (char **) NULL)
1804
0
                ThrowFatalException(ResourceLimitFatalError,
1805
0
                  "MemoryAllocationFailed");
1806
0
              root->attributes[i][j+3]=(char *) NULL;
1807
0
              root->attributes[i][j+2]=ConstantString(c);
1808
0
              root->attributes[i][j+1]=(char *) NULL;
1809
0
              if (v != (char *) NULL)
1810
0
                root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1811
0
              root->attributes[i][j]=ConstantString(n);
1812
0
            }
1813
0
        }
1814
0
      else
1815
0
        if (strncmp(xml, "<!--", 4) == 0)
1816
0
          xml=strstr(xml+4,"-->");
1817
0
        else
1818
0
          if (strncmp(xml,"<?", 2) == 0)
1819
0
            {
1820
0
              c=xml+2;
1821
0
              xml=strstr(c,"?>");
1822
0
              if (xml != (char *) NULL)
1823
0
                {
1824
0
                  ParseProcessingInstructions(root,c,(size_t) (xml-c));
1825
0
                  xml++;
1826
0
                }
1827
0
            }
1828
0
           else
1829
0
             if (*xml == '<')
1830
0
               xml=strchr(xml,'>');
1831
0
             else
1832
0
               if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1833
0
                 break;
1834
0
    }
1835
0
  predefined_entities=(char **) RelinquishMagickMemory(predefined_entities);
1836
0
  return(MagickTrue);
1837
0
}
1838
1839
static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1840
0
{
1841
0
  XMLTreeInfo
1842
0
    *xml_info;
1843
1844
0
  xml_info=root->node;
1845
0
  if (xml_info->tag == (char *) NULL)
1846
0
    xml_info->tag=ConstantString(tag);
1847
0
  else
1848
0
    xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
1849
0
  if (xml_info != (XMLTreeInfo *) NULL)
1850
0
    xml_info->attributes=attributes;
1851
0
  root->node=xml_info;
1852
0
}
1853
1854
static const char
1855
  *ignore_tags[3] =
1856
  {
1857
    "rdf:Bag",
1858
    "rdf:Seq",
1859
    (const char *) NULL
1860
  };
1861
1862
static inline MagickBooleanType IsSkipTag(const char *tag)
1863
0
{
1864
0
  ssize_t
1865
0
    i;
1866
1867
0
  i=0;
1868
0
  while (ignore_tags[i] != (const char *) NULL)
1869
0
  {
1870
0
    if (LocaleCompare(tag,ignore_tags[i]) == 0)
1871
0
      return(MagickTrue);
1872
0
    i++;
1873
0
  }
1874
0
  return(MagickFalse);
1875
0
}
1876
1877
MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1878
0
{
1879
0
  char
1880
0
    **attribute,
1881
0
    **attributes,
1882
0
    *p,
1883
0
    *tag,
1884
0
    *utf8;
1885
1886
0
  int
1887
0
    c,
1888
0
    terminal;
1889
1890
0
  MagickBooleanType
1891
0
    status;
1892
1893
0
  size_t
1894
0
    ignore_depth,
1895
0
    length;
1896
1897
0
  ssize_t
1898
0
    i,
1899
0
    j,
1900
0
    l;
1901
1902
0
  XMLTreeRoot
1903
0
    *root;
1904
1905
  /*
1906
    Convert xml-string to UTF8.
1907
  */
1908
0
  if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1909
0
    {
1910
0
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1911
0
        "ParseError","root tag missing");
1912
0
      return((XMLTreeInfo *) NULL);
1913
0
    }
1914
0
  root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1915
0
  length=strlen(xml);
1916
0
  utf8=ConvertUTF16ToUTF8(xml,&length);
1917
0
  if (utf8 == (char *) NULL)
1918
0
    {
1919
0
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1920
0
        "ParseError","UTF16 to UTF8 failed");
1921
0
      return((XMLTreeInfo *) NULL);
1922
0
    }
1923
0
  if (length == 0)
1924
0
    {
1925
0
      utf8=DestroyString(utf8);
1926
0
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1927
0
        "ParseError","root tag missing");
1928
0
      return((XMLTreeInfo *) NULL);
1929
0
    }
1930
0
  terminal=utf8[length-1];
1931
0
  utf8[length-1]='\0';
1932
0
  p=utf8;
1933
0
  while ((*p != '\0') && (*p != '<'))
1934
0
    p++;
1935
0
  if (*p == '\0')
1936
0
    {
1937
0
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1938
0
        "ParseError","root tag missing");
1939
0
      utf8=DestroyString(utf8);
1940
0
      return((XMLTreeInfo *) NULL);
1941
0
    }
1942
0
  attribute=(char **) NULL;
1943
0
  l=0;
1944
0
  ignore_depth=0;
1945
0
  for (p++; ; p++)
1946
0
  {
1947
0
    attributes=(char **) sentinel;
1948
0
    tag=p;
1949
0
    c=(*p);
1950
0
    if ((isalpha((int) ((unsigned char) *p)) != 0) || (*p == '_') ||
1951
0
        (*p == ':') || (c < '\0'))
1952
0
      {
1953
        /*
1954
          Tag.
1955
        */
1956
0
        if (root->node == (XMLTreeInfo *) NULL)
1957
0
          {
1958
0
            (void) ThrowMagickException(exception,GetMagickModule(),
1959
0
              OptionWarning,"ParseError","root tag missing");
1960
0
            utf8=DestroyString(utf8);
1961
0
            return(&root->root);
1962
0
          }
1963
0
        p+=(ptrdiff_t) strcspn(p,XMLWhitespace "/>");
1964
0
        while (isspace((int) ((unsigned char) *p)) != 0)
1965
0
          *p++='\0';
1966
0
        if (((isalpha((int) ((unsigned char) *p)) != 0) || (*p == '_')) &&
1967
0
            (ignore_depth == 0))
1968
0
          {
1969
0
            if ((*p != '\0') && (*p != '/') && (*p != '>'))
1970
0
              {
1971
                /*
1972
                  Find tag in default attributes list.
1973
                */
1974
0
                i=0;
1975
0
                while ((root->attributes[i] != (char **) NULL) &&
1976
0
                       (strcmp(root->attributes[i][0],tag) != 0))
1977
0
                  i++;
1978
0
                attribute=root->attributes[i];
1979
0
              }
1980
0
            for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
1981
0
            {
1982
              /*
1983
                Attribute.
1984
              */
1985
0
              if (l == 0)
1986
0
                attributes=(char **) AcquireQuantumMemory(4,
1987
0
                  sizeof(*attributes));
1988
0
              else
1989
0
                attributes=(char **) ResizeQuantumMemory(attributes,(size_t)
1990
0
                  (l+4),sizeof(*attributes));
1991
0
              if (attributes == (char **) NULL)
1992
0
                {
1993
0
                  (void) ThrowMagickException(exception,GetMagickModule(),
1994
0
                    ResourceLimitError,"MemoryAllocationFailed","`%s'","");
1995
0
                  utf8=DestroyString(utf8);
1996
0
                  return(&root->root);
1997
0
                }
1998
0
              attributes[l+2]=(char *) NULL;
1999
0
              attributes[l+1]=(char *) NULL;
2000
0
              attributes[l]=p;
2001
0
              p+=(ptrdiff_t) strcspn(p,XMLWhitespace "=/>");
2002
0
              if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
2003
0
                attributes[l]=ConstantString("");
2004
0
              else
2005
0
                {
2006
0
                  *p++='\0';
2007
0
                  p+=(ptrdiff_t) strspn(p,XMLWhitespace "=");
2008
0
                  c=(*p);
2009
0
                  if ((c == '"') || (c == '\''))
2010
0
                    {
2011
                      /*
2012
                        Attributes value.
2013
                      */
2014
0
                      p++;
2015
0
                      attributes[l+1]=p;
2016
0
                      while ((*p != '\0') && (*p != c))
2017
0
                        p++;
2018
0
                      if (*p != '\0')
2019
0
                        *p++='\0';
2020
0
                      else
2021
0
                        {
2022
0
                          attributes[l]=ConstantString("");
2023
0
                          attributes[l+1]=ConstantString("");
2024
0
                          (void) DestroyXMLTreeAttributes(attributes);
2025
0
                          (void) ThrowMagickException(exception,
2026
0
                            GetMagickModule(),OptionWarning,"ParseError",
2027
0
                            "missing %c",c);
2028
0
                          utf8=DestroyString(utf8);
2029
0
                          return(&root->root);
2030
0
                        }
2031
0
                      j=1;
2032
0
                      while ((attribute != (char **) NULL) &&
2033
0
                             (attribute[j] != (char *) NULL) &&
2034
0
                             (strcmp(attribute[j],attributes[l]) != 0))
2035
0
                        j+=3;
2036
0
                      attributes[l+1]=ParseEntities(attributes[l+1],
2037
0
                        root->entities,(attribute != (char **) NULL) &&
2038
0
                        (attribute[j] != (char *) NULL) ? *attribute[j+2] :
2039
0
                        ' ');
2040
0
                    }
2041
0
                  attributes[l]=ConstantString(attributes[l]);
2042
0
                }
2043
0
              while (isspace((int) ((unsigned char) *p)) != 0)
2044
0
                p++;
2045
0
            }
2046
0
          }
2047
0
        else
2048
0
          {
2049
0
            while ((*p != '\0') && (*p != '/') && (*p != '>'))
2050
0
              p++;
2051
0
          }
2052
0
        if (*p == '/')
2053
0
          {
2054
            /*
2055
              Self closing tag.
2056
            */
2057
0
            *p++='\0';
2058
0
            if (((*p != '\0') && (*p != '>')) ||
2059
0
                ((*p == '\0') && (terminal != '>')))
2060
0
              {
2061
0
                if (l != 0)
2062
0
                  (void) DestroyXMLTreeAttributes(attributes);
2063
0
                (void) ThrowMagickException(exception,GetMagickModule(),
2064
0
                  OptionWarning,"ParseError","missing >");
2065
0
                utf8=DestroyString(utf8);
2066
0
                return(&root->root);
2067
0
              }
2068
0
            if ((ignore_depth != 0) || (IsSkipTag(tag) != MagickFalse))
2069
0
              (void) DestroyXMLTreeAttributes(attributes);
2070
0
            else
2071
0
              {
2072
0
                ParseOpenTag(root,tag,attributes);
2073
0
                (void) ParseCloseTag(root,tag,exception);
2074
0
              }
2075
0
          }
2076
0
        else
2077
0
          {
2078
0
            c=(*p);
2079
0
            if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
2080
0
              {
2081
0
                *p='\0';
2082
0
                if ((ignore_depth == 0) && (IsSkipTag(tag) == MagickFalse))
2083
0
                  ParseOpenTag(root,tag,attributes);
2084
0
                else
2085
0
                  {
2086
0
                    ignore_depth++;
2087
0
                    (void) DestroyXMLTreeAttributes(attributes);
2088
0
                  }
2089
0
                *p=(char) c;
2090
0
              }
2091
0
            else
2092
0
              {
2093
0
                if (l != 0)
2094
0
                  (void) DestroyXMLTreeAttributes(attributes);
2095
0
                (void) ThrowMagickException(exception,GetMagickModule(),
2096
0
                  OptionWarning,"ParseError","missing >");
2097
0
                utf8=DestroyString(utf8);
2098
0
                return(&root->root);
2099
0
              }
2100
0
          }
2101
0
      }
2102
0
    else
2103
0
      if (*p == '/')
2104
0
        {
2105
          /*
2106
            Close tag.
2107
          */
2108
0
          tag=p+1;
2109
0
          p+=(ptrdiff_t) strcspn(tag,XMLWhitespace ">")+1;
2110
0
          c=(*p);
2111
0
          if ((c == '\0') && (terminal != '>'))
2112
0
            {
2113
0
              (void) ThrowMagickException(exception,GetMagickModule(),
2114
0
                OptionWarning,"ParseError","missing >");
2115
0
              utf8=DestroyString(utf8);
2116
0
              return(&root->root);
2117
0
            }
2118
0
          *p='\0';
2119
0
          if ((ignore_depth == 0) &&
2120
0
              (ParseCloseTag(root,tag,exception) != (XMLTreeInfo *) NULL))
2121
0
            {
2122
0
              utf8=DestroyString(utf8);
2123
0
              return(&root->root);
2124
0
            }
2125
0
          if (ignore_depth > 0)
2126
0
            ignore_depth--;
2127
0
          *p=(char) c;
2128
0
          if (isspace((int) ((unsigned char) *p)) != 0)
2129
0
            p+=(ptrdiff_t) strspn(p,XMLWhitespace);
2130
0
        }
2131
0
      else
2132
0
        if (strncmp(p,"!--",3) == 0)
2133
0
          {
2134
            /*
2135
              Comment.
2136
            */
2137
0
            p=strstr(p+3,"--");
2138
0
            if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
2139
0
                ((*p == '\0') && (terminal != '>')))
2140
0
              {
2141
0
                (void) ThrowMagickException(exception,GetMagickModule(),
2142
0
                  OptionWarning,"ParseError","unclosed <!--");
2143
0
                utf8=DestroyString(utf8);
2144
0
                return(&root->root);
2145
0
              }
2146
0
          }
2147
0
        else
2148
0
          if (strncmp(p,"![CDATA[",8) == 0)
2149
0
            {
2150
              /*
2151
                Cdata.
2152
              */
2153
0
              p=strstr(p,"]]>");
2154
0
              if (p != (char *) NULL)
2155
0
                {
2156
0
                  p+=(ptrdiff_t) 2;
2157
0
                  if (ignore_depth == 0)
2158
0
                    ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
2159
0
                }
2160
0
              else
2161
0
                {
2162
0
                  (void) ThrowMagickException(exception,GetMagickModule(),
2163
0
                    OptionWarning,"ParseError","unclosed <![CDATA[");
2164
0
                  utf8=DestroyString(utf8);
2165
0
                  return(&root->root);
2166
0
                }
2167
0
            }
2168
0
          else
2169
0
            if (strncmp(p,"!DOCTYPE",8) == 0)
2170
0
              {
2171
                /*
2172
                  DTD.
2173
                */
2174
0
                for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
2175
0
                     ((l != 0) && ((*p != ']') ||
2176
0
                     (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
2177
0
                  l=(ssize_t) ((*p == '[') ? 1 : l))
2178
0
                p+=(ptrdiff_t) strcspn(p+1,"[]>")+1;
2179
0
                if ((*p == '\0') && (terminal != '>'))
2180
0
                  {
2181
0
                    (void) ThrowMagickException(exception,GetMagickModule(),
2182
0
                      OptionWarning,"ParseError","unclosed <!DOCTYPE");
2183
0
                    utf8=DestroyString(utf8);
2184
0
                    return(&root->root);
2185
0
                  }
2186
0
                if (l != 0)
2187
0
                  tag=strchr(tag,'[')+1;
2188
0
                if (l != 0)
2189
0
                  {
2190
0
                    status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
2191
0
                      exception);
2192
0
                    if (status == MagickFalse)
2193
0
                      {
2194
0
                        utf8=DestroyString(utf8);
2195
0
                        return(&root->root);
2196
0
                      }
2197
0
                    p++;
2198
0
                  }
2199
0
              }
2200
0
            else
2201
0
              if (*p == '?')
2202
0
                {
2203
                  /*
2204
                    Processing instructions.
2205
                  */
2206
0
                  do
2207
0
                  {
2208
0
                    p=strchr(p,'?');
2209
0
                    if (p == (char *) NULL)
2210
0
                      break;
2211
0
                    p++;
2212
0
                  } while ((*p != '\0') && (*p != '>'));
2213
0
                  if ((p == (char *) NULL) || ((*p == '\0') &&
2214
0
                      (terminal != '>')))
2215
0
                    {
2216
0
                      (void) ThrowMagickException(exception,GetMagickModule(),
2217
0
                        OptionWarning,"ParseError","unclosed <?");
2218
0
                      utf8=DestroyString(utf8);
2219
0
                      return(&root->root);
2220
0
                    }
2221
0
                  ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2222
0
                }
2223
0
              else
2224
0
                {
2225
0
                  (void) ThrowMagickException(exception,GetMagickModule(),
2226
0
                    OptionWarning,"ParseError","unexpected <");
2227
0
                  utf8=DestroyString(utf8);
2228
0
                  return(&root->root);
2229
0
                }
2230
0
     if ((p == (char *) NULL) || (*p == '\0'))
2231
0
       break;
2232
0
     *p++='\0';
2233
0
     tag=p;
2234
0
     if ((*p != '\0') && (*p != '<'))
2235
0
       {
2236
        /*
2237
          Tag character content.
2238
        */
2239
0
        while ((*p != '\0') && (*p != '<'))
2240
0
          p++;
2241
0
        if (*p == '\0')
2242
0
          break;
2243
0
        if (ignore_depth == 0)
2244
0
          ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
2245
0
      }
2246
0
    else
2247
0
      if (*p == '\0')
2248
0
        break;
2249
0
  }
2250
0
  utf8=DestroyString(utf8);
2251
0
  if (root->node == (XMLTreeInfo *) NULL)
2252
0
    return(&root->root);
2253
0
  if (root->node->tag == (char *) NULL)
2254
0
    {
2255
0
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2256
0
        "ParseError","root tag missing");
2257
0
      return(&root->root);
2258
0
    }
2259
0
  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2260
0
    "ParseError","unclosed tag: '%s'",root->node->tag);
2261
0
  return(&root->root);
2262
0
}
2263

2264
/*
2265
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2266
%                                                                             %
2267
%                                                                             %
2268
%                                                                             %
2269
%   N e w X M L T r e e T a g                                                 %
2270
%                                                                             %
2271
%                                                                             %
2272
%                                                                             %
2273
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2274
%
2275
%  NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2276
%
2277
%  The format of the NewXMLTreeTag method is:
2278
%
2279
%      XMLTreeInfo *NewXMLTreeTag(const char *tag)
2280
%
2281
%  A description of each parameter follows:
2282
%
2283
%    o tag: the tag.
2284
%
2285
*/
2286
MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2287
0
{
2288
0
  static const char
2289
0
    *predefined_entities[NumberPredefinedEntities+1] =
2290
0
    {
2291
0
      "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
2292
0
      "apos;", "&#39;", "amp;", "&#38;", (char *) NULL
2293
0
    };
2294
2295
0
  XMLTreeRoot
2296
0
    *root;
2297
2298
0
  root=(XMLTreeRoot *) AcquireCriticalMemory(sizeof(*root));
2299
0
  (void) memset(root,0,sizeof(*root));
2300
0
  root->root.tag=(char *) NULL;
2301
0
  if (tag != (char *) NULL)
2302
0
    root->root.tag=ConstantString(tag);
2303
0
  root->node=(&root->root);
2304
0
  root->root.content=ConstantString("");
2305
0
  root->entities=(char **) AcquireCriticalMemory(sizeof(predefined_entities));
2306
0
  (void) memcpy(root->entities,predefined_entities,sizeof(predefined_entities));
2307
0
  root->root.attributes=sentinel;
2308
0
  root->attributes=(char ***) root->root.attributes;
2309
0
  root->processing_instructions=(char ***) root->root.attributes;
2310
0
  root->debug=IsEventLogging();
2311
0
  root->signature=MagickCoreSignature;
2312
0
  return(&root->root);
2313
0
}
2314

2315
/*
2316
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2317
%                                                                             %
2318
%                                                                             %
2319
%                                                                             %
2320
%   P r u n e T a g F r o m X M L T r e e                                     %
2321
%                                                                             %
2322
%                                                                             %
2323
%                                                                             %
2324
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2325
%
2326
%  PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
2327
%  subtags.
2328
%
2329
%  The format of the PruneTagFromXMLTree method is:
2330
%
2331
%      XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2332
%
2333
%  A description of each parameter follows:
2334
%
2335
%    o xml_info: the xml info.
2336
%
2337
*/
2338
MagickPrivate XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2339
0
{
2340
0
  XMLTreeInfo
2341
0
    *node;
2342
2343
0
  assert(xml_info != (XMLTreeInfo *) NULL);
2344
0
  assert((xml_info->signature == MagickCoreSignature) ||
2345
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2346
0
  if (IsEventLogging() != MagickFalse)
2347
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2348
0
  if (xml_info->next != (XMLTreeInfo *) NULL)
2349
0
    xml_info->next->sibling=xml_info->sibling;
2350
0
  if (xml_info->parent != (XMLTreeInfo *) NULL)
2351
0
    {
2352
0
      node=xml_info->parent->child;
2353
0
      if (node == xml_info)
2354
0
        xml_info->parent->child=xml_info->ordered;
2355
0
      else
2356
0
        {
2357
0
          while (node->ordered != xml_info)
2358
0
            node=node->ordered;
2359
0
          node->ordered=node->ordered->ordered;
2360
0
          node=xml_info->parent->child;
2361
0
          if (strcmp(node->tag,xml_info->tag) != 0)
2362
0
            {
2363
0
              while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2364
0
                node=node->sibling;
2365
0
              if (node->sibling != xml_info)
2366
0
                node=node->sibling;
2367
0
              else
2368
0
                node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2369
0
                  xml_info->next : node->sibling->sibling;
2370
0
            }
2371
0
          while ((node->next != (XMLTreeInfo *) NULL) &&
2372
0
                 (node->next != xml_info))
2373
0
            node=node->next;
2374
0
          if (node->next != (XMLTreeInfo *) NULL)
2375
0
            node->next=node->next->next;
2376
0
        }
2377
0
    }
2378
0
  xml_info->ordered=(XMLTreeInfo *) NULL;
2379
0
  xml_info->sibling=(XMLTreeInfo *) NULL;
2380
0
  xml_info->next=(XMLTreeInfo *) NULL;
2381
0
  return(xml_info);
2382
0
}
2383

2384
/*
2385
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2386
%                                                                             %
2387
%                                                                             %
2388
%                                                                             %
2389
%   S e t X M L T r e e A t t r i b u t e                                     %
2390
%                                                                             %
2391
%                                                                             %
2392
%                                                                             %
2393
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2394
%
2395
%  SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2396
%  found.  A value of NULL removes the specified attribute.
2397
%
2398
%  The format of the SetXMLTreeAttribute method is:
2399
%
2400
%      XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2401
%        const char *value)
2402
%
2403
%  A description of each parameter follows:
2404
%
2405
%    o xml_info: the xml info.
2406
%
2407
%    o tag:  The attribute tag.
2408
%
2409
%    o value:  The attribute value.
2410
%
2411
*/
2412
MagickPrivate XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
2413
  const char *tag,const char *value)
2414
0
{
2415
0
  ssize_t
2416
0
    i,
2417
0
    j;
2418
2419
0
  assert(xml_info != (XMLTreeInfo *) NULL);
2420
0
  assert((xml_info->signature == MagickCoreSignature) ||
2421
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2422
0
  if (IsEventLogging() != MagickFalse)
2423
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2424
0
  i=0;
2425
0
  while ((xml_info->attributes[i] != (char *) NULL) &&
2426
0
         (strcmp(xml_info->attributes[i],tag) != 0))
2427
0
    i+=2;
2428
0
  if (xml_info->attributes[i] == (char *) NULL)
2429
0
    {
2430
      /*
2431
        Add new attribute tag.
2432
      */
2433
0
      if (value == (const char *) NULL)
2434
0
        return(xml_info);
2435
0
      if (xml_info->attributes != sentinel)
2436
0
        xml_info->attributes=(char **) ResizeQuantumMemory(
2437
0
          xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2438
0
      else
2439
0
        {
2440
0
          xml_info->attributes=(char **) AcquireQuantumMemory(4,
2441
0
            sizeof(*xml_info->attributes));
2442
0
          if (xml_info->attributes != (char **) NULL)
2443
0
            xml_info->attributes[1]=ConstantString("");
2444
0
        }
2445
0
      if (xml_info->attributes == (char **) NULL)
2446
0
        ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2447
0
      xml_info->attributes[i]=ConstantString(tag);
2448
0
      xml_info->attributes[i+2]=(char *) NULL;
2449
0
      (void) strlen(xml_info->attributes[i+1]);
2450
0
    }
2451
  /*
2452
    Add new value to an existing attribute.
2453
  */
2454
0
  for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2455
0
  if (xml_info->attributes[i+1] != (char *) NULL)
2456
0
    xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2457
0
  if (value != (const char *) NULL)
2458
0
    {
2459
0
      xml_info->attributes[i+1]=ConstantString(value);
2460
0
      return(xml_info);
2461
0
    }
2462
0
  if (xml_info->attributes[i] != (char *) NULL)
2463
0
    xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2464
0
  (void) memmove(xml_info->attributes+i,xml_info->attributes+i+2,(size_t)
2465
0
    (j-i)*sizeof(*xml_info->attributes));
2466
0
  xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2467
0
    (size_t) (j+2),sizeof(*xml_info->attributes));
2468
0
  if (xml_info->attributes == (char **) NULL)
2469
0
    ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2470
0
  j-=2;
2471
0
  (void) memmove(xml_info->attributes[j+1]+(i/2),xml_info->attributes[j+1]+
2472
0
    (i/2)+1,(size_t) (((j+2)/2)-(i/2))*sizeof(**xml_info->attributes));
2473
0
  return(xml_info);
2474
0
}
2475

2476
/*
2477
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2478
%                                                                             %
2479
%                                                                             %
2480
%                                                                             %
2481
%   S e t X M L T r e e C o n t e n t                                         %
2482
%                                                                             %
2483
%                                                                             %
2484
%                                                                             %
2485
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2486
%
2487
%  SetXMLTreeContent() sets the character content for the given tag and
2488
%  returns the tag.
2489
%
2490
%  The format of the SetXMLTreeContent method is:
2491
%
2492
%      XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2493
%        const char *content)
2494
%
2495
%  A description of each parameter follows:
2496
%
2497
%    o xml_info: the xml info.
2498
%
2499
%    o content:  The content.
2500
%
2501
*/
2502
MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2503
  const char *content)
2504
0
{
2505
0
  assert(xml_info != (XMLTreeInfo *) NULL);
2506
0
  assert((xml_info->signature == MagickCoreSignature) ||
2507
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2508
0
  if (IsEventLogging() != MagickFalse)
2509
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2510
0
  if (xml_info->content != (char *) NULL)
2511
0
    xml_info->content=DestroyString(xml_info->content);
2512
0
  xml_info->content=(char *) ConstantString(content);
2513
0
  return(xml_info);
2514
0
}
2515

2516
/*
2517
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2518
%                                                                             %
2519
%                                                                             %
2520
%                                                                             %
2521
%   X M L T r e e I n f o T o X M L                                           %
2522
%                                                                             %
2523
%                                                                             %
2524
%                                                                             %
2525
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2526
%
2527
%  XMLTreeInfoToXML() converts an xml-tree to an XML string.
2528
%
2529
%  The format of the XMLTreeInfoToXML method is:
2530
%
2531
%      char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2532
%
2533
%  A description of each parameter follows:
2534
%
2535
%    o xml_info: the xml info.
2536
%
2537
*/
2538
2539
static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2540
  char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2541
0
{
2542
0
  char
2543
0
    *canonical_content;
2544
2545
0
  if (offset < 0)
2546
0
    canonical_content=CanonicalXMLContent(source,pedantic);
2547
0
  else
2548
0
    {
2549
0
      char
2550
0
        *content;
2551
2552
0
      content=AcquireString(source);
2553
0
      content[offset]='\0';
2554
0
      canonical_content=CanonicalXMLContent(content,pedantic);
2555
0
      content=DestroyString(content);
2556
0
    }
2557
0
  if (canonical_content == (char *) NULL)
2558
0
    return(*destination);
2559
0
  if ((*length+strlen(canonical_content)+MagickPathExtent) > *extent)
2560
0
    {
2561
0
      *extent=(*length)+strlen(canonical_content)+MagickPathExtent;
2562
0
      *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2563
0
        sizeof(**destination));
2564
0
      if (*destination == (char *) NULL)
2565
0
        return(*destination);
2566
0
    }
2567
0
  *length+=(size_t) FormatLocaleString(*destination+(*length),*extent,"%s",
2568
0
    canonical_content);
2569
0
  canonical_content=DestroyString(canonical_content);
2570
0
  return(*destination);
2571
0
}
2572
2573
static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2574
  size_t *extent,size_t start,char ***attributes)
2575
0
{
2576
0
  char
2577
0
    *content;
2578
2579
0
  const char
2580
0
    *attribute;
2581
2582
0
  size_t
2583
0
    offset;
2584
2585
0
  ssize_t
2586
0
    i,
2587
0
    j;
2588
2589
0
  content=(char *) "";
2590
0
  if (xml_info->parent != (XMLTreeInfo *) NULL)
2591
0
    content=xml_info->parent->content;
2592
0
  offset=0;
2593
0
  *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2594
0
    start),source,length,extent,MagickFalse);
2595
0
  if ((*length+strlen(xml_info->tag)+MagickPathExtent) > *extent)
2596
0
    {
2597
0
      *extent=(*length)+strlen(xml_info->tag)+MagickPathExtent;
2598
0
      *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2599
0
      if (*source == (char *) NULL)
2600
0
        return(*source);
2601
0
    }
2602
0
  *length+=(size_t) FormatLocaleString(*source+(*length),*extent,
2603
0
    "<%s",xml_info->tag);
2604
0
  for (i=0; xml_info->attributes[i]; i+=2)
2605
0
  {
2606
0
    attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2607
0
    if (attribute != xml_info->attributes[i+1])
2608
0
      continue;
2609
0
    if ((*length+strlen(xml_info->attributes[i])+MagickPathExtent) > *extent)
2610
0
      {
2611
0
        *extent=(*length)+strlen(xml_info->attributes[i])+MagickPathExtent;
2612
0
        *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2613
0
        if (*source == (char *) NULL)
2614
0
          return((char *) NULL);
2615
0
      }
2616
0
    *length+=(size_t) FormatLocaleString(*source+(*length),*extent," %s=\"",
2617
0
      xml_info->attributes[i]);
2618
0
    (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2619
0
      extent,MagickTrue);
2620
0
    *length+=(size_t) FormatLocaleString(*source+(*length),*extent,"\"");
2621
0
  }
2622
0
  i=0;
2623
0
  while ((attributes[i] != (char **) NULL) &&
2624
0
         (strcmp(attributes[i][0],xml_info->tag) != 0))
2625
0
    i++;
2626
0
  j=1;
2627
0
  while ((attributes[i] != (char **) NULL) &&
2628
0
         (attributes[i][j] != (char *) NULL))
2629
0
  {
2630
0
    if ((attributes[i][j+1] == (char *) NULL) ||
2631
0
        (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2632
0
      {
2633
0
        j+=3;
2634
0
        continue;
2635
0
      }
2636
0
    if ((*length+strlen(attributes[i][j])+MagickPathExtent) > *extent)
2637
0
      {
2638
0
        *extent=(*length)+strlen(attributes[i][j])+MagickPathExtent;
2639
0
        *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2640
0
        if (*source == (char *) NULL)
2641
0
          return((char *) NULL);
2642
0
      }
2643
0
    *length+=(size_t) FormatLocaleString(*source+(*length),*extent," %s=\"",
2644
0
      attributes[i][j]);
2645
0
    (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2646
0
      MagickTrue);
2647
0
    *length+=(size_t) FormatLocaleString(*source+(*length),*extent,"\"");
2648
0
    j+=3;
2649
0
  }
2650
0
  *length+=(size_t) FormatLocaleString(*source+(*length),*extent,
2651
0
    *xml_info->content ? ">" : "/>");
2652
0
  if (xml_info->child != (XMLTreeInfo *) NULL)
2653
0
    *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2654
0
  else
2655
0
    *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2656
0
      MagickFalse);
2657
0
  if ((*length+strlen(xml_info->tag)+MagickPathExtent) > *extent)
2658
0
    {
2659
0
      *extent=(*length)+strlen(xml_info->tag)+MagickPathExtent;
2660
0
      *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2661
0
      if (*source == (char *) NULL)
2662
0
        return((char *) NULL);
2663
0
    }
2664
0
  if (*xml_info->content != '\0')
2665
0
    *length+=(size_t) FormatLocaleString(*source+(*length),*extent,"</%s>",
2666
0
      xml_info->tag);
2667
0
  while ((offset < xml_info->offset) && (content[offset] != '\0'))
2668
0
    offset++;
2669
0
  if (xml_info->ordered != (XMLTreeInfo *) NULL)
2670
0
    content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2671
0
      attributes);
2672
0
  else
2673
0
    content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2674
0
      MagickFalse);
2675
0
  return(content);
2676
0
}
2677
2678
MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2679
0
{
2680
0
  char
2681
0
    *p,
2682
0
    *q,
2683
0
    *xml;
2684
2685
0
  size_t
2686
0
    extent,
2687
0
    length;
2688
2689
0
  ssize_t
2690
0
    i,
2691
0
    j,
2692
0
    k;
2693
2694
0
  XMLTreeInfo
2695
0
    *ordered,
2696
0
    *parent;
2697
2698
0
  XMLTreeRoot
2699
0
    *root;
2700
2701
0
  assert(xml_info != (XMLTreeInfo *) NULL);
2702
0
  assert((xml_info->signature == MagickCoreSignature) ||
2703
0
         (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2704
0
  if (IsEventLogging() != MagickFalse)
2705
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2706
0
  if (xml_info->tag == (char *) NULL)
2707
0
    return((char *) NULL);
2708
0
  xml=AcquireString((char *) NULL);
2709
0
  length=0;
2710
0
  extent=MagickPathExtent;
2711
0
  root=(XMLTreeRoot *) xml_info;
2712
0
  while (root->root.parent != (XMLTreeInfo *) NULL)
2713
0
    root=(XMLTreeRoot *) root->root.parent;
2714
0
  parent=xml_info->parent;
2715
0
  if (parent == (XMLTreeInfo *) NULL)
2716
0
    for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2717
0
    {
2718
      /*
2719
        Pre-root processing instructions.
2720
      */
2721
0
      for (k=2; root->processing_instructions[i][k-1]; k++) ;
2722
0
      p=root->processing_instructions[i][1];
2723
0
      for (j=1; p != (char *) NULL; j++)
2724
0
      {
2725
0
        if (root->processing_instructions[i][k][j-1] == '>')
2726
0
          {
2727
0
            p=root->processing_instructions[i][j];
2728
0
            continue;
2729
0
          }
2730
0
        q=root->processing_instructions[i][0];
2731
0
        if ((length+strlen(p)+strlen(q)+MagickPathExtent) > extent)
2732
0
          {
2733
0
            extent=length+strlen(p)+strlen(q)+MagickPathExtent;
2734
0
            xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2735
0
            if (xml == (char *) NULL)
2736
0
              return(xml);
2737
0
          }
2738
0
        length+=(size_t) FormatLocaleString(xml+length,extent,"<?%s%s%s?>\n",q,
2739
0
          *p != '\0' ? " " : "",p);
2740
0
        p=root->processing_instructions[i][j];
2741
0
      }
2742
0
    }
2743
0
  ordered=xml_info->ordered;
2744
0
  xml_info->parent=(XMLTreeInfo *) NULL;
2745
0
  xml_info->ordered=(XMLTreeInfo *) NULL;
2746
0
  xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2747
0
  xml_info->parent=parent;
2748
0
  xml_info->ordered=ordered;
2749
0
  if (parent == (XMLTreeInfo *) NULL)
2750
0
    for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2751
0
    {
2752
      /*
2753
        Post-root processing instructions.
2754
      */
2755
0
      for (k=2; root->processing_instructions[i][k-1]; k++) ;
2756
0
      p=root->processing_instructions[i][1];
2757
0
      for (j=1; p != (char *) NULL; j++)
2758
0
      {
2759
0
        if (root->processing_instructions[i][k][j-1] == '<')
2760
0
          {
2761
0
            p=root->processing_instructions[i][j];
2762
0
            continue;
2763
0
          }
2764
0
        q=root->processing_instructions[i][0];
2765
0
        if ((length+strlen(p)+strlen(q)+MagickPathExtent) > extent)
2766
0
          {
2767
0
            extent=length+strlen(p)+strlen(q)+MagickPathExtent;
2768
0
            xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2769
0
            if (xml == (char *) NULL)
2770
0
              return(xml);
2771
0
          }
2772
0
        length+=(size_t) FormatLocaleString(xml+length,extent,"\n<?%s%s%s?>",q,
2773
0
          *p != '\0' ? " " : "",p);
2774
0
        p=root->processing_instructions[i][j];
2775
0
      }
2776
0
    }
2777
0
  return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
2778
0
}