Coverage Report

Created: 2025-07-23 08:13

/src/fontconfig/subprojects/libxml2-2.12.6/entities.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * entities.c : implementation for the XML entities handling
3
 *
4
 * See Copyright for the status of this software.
5
 *
6
 * daniel@veillard.com
7
 */
8
9
/* To avoid EBCDIC trouble when parsing on zOS */
10
#if defined(__MVS__)
11
#pragma convert("ISO8859-1")
12
#endif
13
14
#define IN_LIBXML
15
#include "libxml.h"
16
17
#include <string.h>
18
#include <stdlib.h>
19
20
#include <libxml/xmlmemory.h>
21
#include <libxml/hash.h>
22
#include <libxml/entities.h>
23
#include <libxml/parser.h>
24
#include <libxml/parserInternals.h>
25
#include <libxml/xmlerror.h>
26
#include <libxml/dict.h>
27
28
#include "private/entities.h"
29
#include "private/error.h"
30
31
/*
32
 * The XML predefined entities.
33
 */
34
35
static xmlEntity xmlEntityLt = {
36
    NULL, XML_ENTITY_DECL, BAD_CAST "lt",
37
    NULL, NULL, NULL, NULL, NULL, NULL,
38
    BAD_CAST "<", BAD_CAST "<", 1,
39
    XML_INTERNAL_PREDEFINED_ENTITY,
40
    NULL, NULL, NULL, NULL, 0, 0, 0
41
};
42
static xmlEntity xmlEntityGt = {
43
    NULL, XML_ENTITY_DECL, BAD_CAST "gt",
44
    NULL, NULL, NULL, NULL, NULL, NULL,
45
    BAD_CAST ">", BAD_CAST ">", 1,
46
    XML_INTERNAL_PREDEFINED_ENTITY,
47
    NULL, NULL, NULL, NULL, 0, 0, 0
48
};
49
static xmlEntity xmlEntityAmp = {
50
    NULL, XML_ENTITY_DECL, BAD_CAST "amp",
51
    NULL, NULL, NULL, NULL, NULL, NULL,
52
    BAD_CAST "&", BAD_CAST "&", 1,
53
    XML_INTERNAL_PREDEFINED_ENTITY,
54
    NULL, NULL, NULL, NULL, 0, 0, 0
55
};
56
static xmlEntity xmlEntityQuot = {
57
    NULL, XML_ENTITY_DECL, BAD_CAST "quot",
58
    NULL, NULL, NULL, NULL, NULL, NULL,
59
    BAD_CAST "\"", BAD_CAST "\"", 1,
60
    XML_INTERNAL_PREDEFINED_ENTITY,
61
    NULL, NULL, NULL, NULL, 0, 0, 0
62
};
63
static xmlEntity xmlEntityApos = {
64
    NULL, XML_ENTITY_DECL, BAD_CAST "apos",
65
    NULL, NULL, NULL, NULL, NULL, NULL,
66
    BAD_CAST "'", BAD_CAST "'", 1,
67
    XML_INTERNAL_PREDEFINED_ENTITY,
68
    NULL, NULL, NULL, NULL, 0, 0, 0
69
};
70
71
/**
72
 * xmlEntitiesErrMemory:
73
 * @extra:  extra information
74
 *
75
 * Handle an out of memory condition
76
 */
77
static void
78
xmlEntitiesErrMemory(const char *extra)
79
0
{
80
0
    __xmlSimpleError(XML_FROM_TREE, XML_ERR_NO_MEMORY, NULL, NULL, extra);
81
0
}
82
83
/**
84
 * xmlEntitiesErr:
85
 * @code:  the error code
86
 * @msg:  the message
87
 *
88
 * Raise an error.
89
 */
90
static void LIBXML_ATTR_FORMAT(2,0)
91
xmlEntitiesErr(xmlParserErrors code, const char *msg)
92
0
{
93
0
    __xmlSimpleError(XML_FROM_TREE, code, NULL, msg, NULL);
94
0
}
95
96
/**
97
 * xmlEntitiesWarn:
98
 * @code:  the error code
99
 * @msg:  the message
100
 *
101
 * Raise a warning.
102
 */
103
static void LIBXML_ATTR_FORMAT(2,0)
104
xmlEntitiesWarn(xmlParserErrors code, const char *msg, const xmlChar *str1)
105
0
{
106
0
    __xmlRaiseError(NULL, NULL, NULL,
107
0
                NULL, NULL, XML_FROM_TREE, code,
108
0
                XML_ERR_WARNING, NULL, 0,
109
0
                (const char *)str1, NULL, NULL, 0, 0,
110
0
                msg, (const char *)str1, NULL);
111
0
}
112
113
/*
114
 * xmlFreeEntity : clean-up an entity record.
115
 */
116
void
117
xmlFreeEntity(xmlEntityPtr entity)
118
0
{
119
0
    xmlDictPtr dict = NULL;
120
121
0
    if (entity == NULL)
122
0
        return;
123
124
0
    if (entity->doc != NULL)
125
0
        dict = entity->doc->dict;
126
127
128
0
    if ((entity->children) && (entity->owner == 1) &&
129
0
        (entity == (xmlEntityPtr) entity->children->parent))
130
0
        xmlFreeNodeList(entity->children);
131
0
    if ((entity->name != NULL) &&
132
0
        ((dict == NULL) || (!xmlDictOwns(dict, entity->name))))
133
0
        xmlFree((char *) entity->name);
134
0
    if (entity->ExternalID != NULL)
135
0
        xmlFree((char *) entity->ExternalID);
136
0
    if (entity->SystemID != NULL)
137
0
        xmlFree((char *) entity->SystemID);
138
0
    if (entity->URI != NULL)
139
0
        xmlFree((char *) entity->URI);
140
0
    if (entity->content != NULL)
141
0
        xmlFree((char *) entity->content);
142
0
    if (entity->orig != NULL)
143
0
        xmlFree((char *) entity->orig);
144
0
    xmlFree(entity);
145
0
}
146
147
/*
148
 * xmlCreateEntity:
149
 *
150
 * internal routine doing the entity node structures allocations
151
 */
152
static xmlEntityPtr
153
xmlCreateEntity(xmlDictPtr dict, const xmlChar *name, int type,
154
          const xmlChar *ExternalID, const xmlChar *SystemID,
155
0
          const xmlChar *content) {
156
0
    xmlEntityPtr ret;
157
158
0
    ret = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
159
0
    if (ret == NULL) {
160
0
        xmlEntitiesErrMemory("xmlCreateEntity: malloc failed");
161
0
  return(NULL);
162
0
    }
163
0
    memset(ret, 0, sizeof(xmlEntity));
164
0
    ret->type = XML_ENTITY_DECL;
165
166
    /*
167
     * fill the structure.
168
     */
169
0
    ret->etype = (xmlEntityType) type;
170
0
    if (dict == NULL) {
171
0
  ret->name = xmlStrdup(name);
172
0
  if (ExternalID != NULL)
173
0
      ret->ExternalID = xmlStrdup(ExternalID);
174
0
  if (SystemID != NULL)
175
0
      ret->SystemID = xmlStrdup(SystemID);
176
0
    } else {
177
0
        ret->name = xmlDictLookup(dict, name, -1);
178
0
  ret->ExternalID = xmlStrdup(ExternalID);
179
0
  ret->SystemID = xmlStrdup(SystemID);
180
0
    }
181
0
    if (content != NULL) {
182
0
        ret->length = xmlStrlen(content);
183
0
  ret->content = xmlStrndup(content, ret->length);
184
0
     } else {
185
0
        ret->length = 0;
186
0
        ret->content = NULL;
187
0
    }
188
0
    ret->URI = NULL; /* to be computed by the layer knowing
189
      the defining entity */
190
0
    ret->orig = NULL;
191
0
    ret->owner = 0;
192
193
0
    return(ret);
194
0
}
195
196
/*
197
 * xmlAddEntity : register a new entity for an entities table.
198
 */
199
static xmlEntityPtr
200
xmlAddEntity(xmlDtdPtr dtd, const xmlChar *name, int type,
201
    const xmlChar *ExternalID, const xmlChar *SystemID,
202
0
    const xmlChar *content) {
203
0
    xmlDictPtr dict = NULL;
204
0
    xmlEntitiesTablePtr table = NULL;
205
0
    xmlEntityPtr ret, predef;
206
207
0
    if (name == NULL)
208
0
  return(NULL);
209
0
    if (dtd == NULL)
210
0
  return(NULL);
211
0
    if (dtd->doc != NULL)
212
0
        dict = dtd->doc->dict;
213
214
0
    switch (type) {
215
0
        case XML_INTERNAL_GENERAL_ENTITY:
216
0
        case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
217
0
        case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
218
0
            predef = xmlGetPredefinedEntity(name);
219
0
            if (predef != NULL) {
220
0
                int valid = 0;
221
222
                /* 4.6 Predefined Entities */
223
0
                if ((type == XML_INTERNAL_GENERAL_ENTITY) &&
224
0
                    (content != NULL)) {
225
0
                    int c = predef->content[0];
226
227
0
                    if (((content[0] == c) && (content[1] == 0)) &&
228
0
                        ((c == '>') || (c == '\'') || (c == '"'))) {
229
0
                        valid = 1;
230
0
                    } else if ((content[0] == '&') && (content[1] == '#')) {
231
0
                        if (content[2] == 'x') {
232
0
                            xmlChar *hex = BAD_CAST "0123456789ABCDEF";
233
0
                            xmlChar ref[] = "00;";
234
235
0
                            ref[0] = hex[c / 16 % 16];
236
0
                            ref[1] = hex[c % 16];
237
0
                            if (xmlStrcasecmp(&content[3], ref) == 0)
238
0
                                valid = 1;
239
0
                        } else {
240
0
                            xmlChar ref[] = "00;";
241
242
0
                            ref[0] = '0' + c / 10 % 10;
243
0
                            ref[1] = '0' + c % 10;
244
0
                            if (xmlStrEqual(&content[2], ref))
245
0
                                valid = 1;
246
0
                        }
247
0
                    }
248
0
                }
249
0
                if (!valid) {
250
0
                    xmlEntitiesWarn(XML_ERR_ENTITY_PROCESSING,
251
0
                            "xmlAddEntity: invalid redeclaration of predefined"
252
0
                            " entity '%s'", name);
253
0
                    return(NULL);
254
0
                }
255
0
            }
256
0
      if (dtd->entities == NULL)
257
0
    dtd->entities = xmlHashCreateDict(0, dict);
258
0
      table = dtd->entities;
259
0
      break;
260
0
        case XML_INTERNAL_PARAMETER_ENTITY:
261
0
        case XML_EXTERNAL_PARAMETER_ENTITY:
262
0
      if (dtd->pentities == NULL)
263
0
    dtd->pentities = xmlHashCreateDict(0, dict);
264
0
      table = dtd->pentities;
265
0
      break;
266
0
        case XML_INTERNAL_PREDEFINED_ENTITY:
267
0
      return(NULL);
268
0
    }
269
0
    if (table == NULL)
270
0
  return(NULL);
271
0
    ret = xmlCreateEntity(dict, name, type, ExternalID, SystemID, content);
272
0
    if (ret == NULL)
273
0
        return(NULL);
274
0
    ret->doc = dtd->doc;
275
276
0
    if (xmlHashAddEntry(table, name, ret)) {
277
  /*
278
   * entity was already defined at another level.
279
   */
280
0
        xmlFreeEntity(ret);
281
0
  return(NULL);
282
0
    }
283
0
    return(ret);
284
0
}
285
286
/**
287
 * xmlGetPredefinedEntity:
288
 * @name:  the entity name
289
 *
290
 * Check whether this name is an predefined entity.
291
 *
292
 * Returns NULL if not, otherwise the entity
293
 */
294
xmlEntityPtr
295
0
xmlGetPredefinedEntity(const xmlChar *name) {
296
0
    if (name == NULL) return(NULL);
297
0
    switch (name[0]) {
298
0
        case 'l':
299
0
      if (xmlStrEqual(name, BAD_CAST "lt"))
300
0
          return(&xmlEntityLt);
301
0
      break;
302
0
        case 'g':
303
0
      if (xmlStrEqual(name, BAD_CAST "gt"))
304
0
          return(&xmlEntityGt);
305
0
      break;
306
0
        case 'a':
307
0
      if (xmlStrEqual(name, BAD_CAST "amp"))
308
0
          return(&xmlEntityAmp);
309
0
      if (xmlStrEqual(name, BAD_CAST "apos"))
310
0
          return(&xmlEntityApos);
311
0
      break;
312
0
        case 'q':
313
0
      if (xmlStrEqual(name, BAD_CAST "quot"))
314
0
          return(&xmlEntityQuot);
315
0
      break;
316
0
  default:
317
0
      break;
318
0
    }
319
0
    return(NULL);
320
0
}
321
322
/**
323
 * xmlAddDtdEntity:
324
 * @doc:  the document
325
 * @name:  the entity name
326
 * @type:  the entity type XML_xxx_yyy_ENTITY
327
 * @ExternalID:  the entity external ID if available
328
 * @SystemID:  the entity system ID if available
329
 * @content:  the entity content
330
 *
331
 * Register a new entity for this document DTD external subset.
332
 *
333
 * Returns a pointer to the entity or NULL in case of error
334
 */
335
xmlEntityPtr
336
xmlAddDtdEntity(xmlDocPtr doc, const xmlChar *name, int type,
337
          const xmlChar *ExternalID, const xmlChar *SystemID,
338
0
    const xmlChar *content) {
339
0
    xmlEntityPtr ret;
340
0
    xmlDtdPtr dtd;
341
342
0
    if (doc == NULL) {
343
0
  xmlEntitiesErr(XML_DTD_NO_DOC,
344
0
          "xmlAddDtdEntity: document is NULL");
345
0
  return(NULL);
346
0
    }
347
0
    if (doc->extSubset == NULL) {
348
0
  xmlEntitiesErr(XML_DTD_NO_DTD,
349
0
          "xmlAddDtdEntity: document without external subset");
350
0
  return(NULL);
351
0
    }
352
0
    dtd = doc->extSubset;
353
0
    ret = xmlAddEntity(dtd, name, type, ExternalID, SystemID, content);
354
0
    if (ret == NULL) return(NULL);
355
356
    /*
357
     * Link it to the DTD
358
     */
359
0
    ret->parent = dtd;
360
0
    ret->doc = dtd->doc;
361
0
    if (dtd->last == NULL) {
362
0
  dtd->children = dtd->last = (xmlNodePtr) ret;
363
0
    } else {
364
0
        dtd->last->next = (xmlNodePtr) ret;
365
0
  ret->prev = dtd->last;
366
0
  dtd->last = (xmlNodePtr) ret;
367
0
    }
368
0
    return(ret);
369
0
}
370
371
/**
372
 * xmlAddDocEntity:
373
 * @doc:  the document
374
 * @name:  the entity name
375
 * @type:  the entity type XML_xxx_yyy_ENTITY
376
 * @ExternalID:  the entity external ID if available
377
 * @SystemID:  the entity system ID if available
378
 * @content:  the entity content
379
 *
380
 * Register a new entity for this document.
381
 *
382
 * Returns a pointer to the entity or NULL in case of error
383
 */
384
xmlEntityPtr
385
xmlAddDocEntity(xmlDocPtr doc, const xmlChar *name, int type,
386
          const xmlChar *ExternalID, const xmlChar *SystemID,
387
0
          const xmlChar *content) {
388
0
    xmlEntityPtr ret;
389
0
    xmlDtdPtr dtd;
390
391
0
    if (doc == NULL) {
392
0
  xmlEntitiesErr(XML_DTD_NO_DOC,
393
0
          "xmlAddDocEntity: document is NULL");
394
0
  return(NULL);
395
0
    }
396
0
    if (doc->intSubset == NULL) {
397
0
  xmlEntitiesErr(XML_DTD_NO_DTD,
398
0
          "xmlAddDocEntity: document without internal subset");
399
0
  return(NULL);
400
0
    }
401
0
    dtd = doc->intSubset;
402
0
    ret = xmlAddEntity(dtd, name, type, ExternalID, SystemID, content);
403
0
    if (ret == NULL) return(NULL);
404
405
    /*
406
     * Link it to the DTD
407
     */
408
0
    ret->parent = dtd;
409
0
    ret->doc = dtd->doc;
410
0
    if (dtd->last == NULL) {
411
0
  dtd->children = dtd->last = (xmlNodePtr) ret;
412
0
    } else {
413
0
  dtd->last->next = (xmlNodePtr) ret;
414
0
  ret->prev = dtd->last;
415
0
  dtd->last = (xmlNodePtr) ret;
416
0
    }
417
0
    return(ret);
418
0
}
419
420
/**
421
 * xmlNewEntity:
422
 * @doc:  the document
423
 * @name:  the entity name
424
 * @type:  the entity type XML_xxx_yyy_ENTITY
425
 * @ExternalID:  the entity external ID if available
426
 * @SystemID:  the entity system ID if available
427
 * @content:  the entity content
428
 *
429
 * Create a new entity, this differs from xmlAddDocEntity() that if
430
 * the document is NULL or has no internal subset defined, then an
431
 * unlinked entity structure will be returned, it is then the responsibility
432
 * of the caller to link it to the document later or free it when not needed
433
 * anymore.
434
 *
435
 * Returns a pointer to the entity or NULL in case of error
436
 */
437
xmlEntityPtr
438
xmlNewEntity(xmlDocPtr doc, const xmlChar *name, int type,
439
       const xmlChar *ExternalID, const xmlChar *SystemID,
440
0
       const xmlChar *content) {
441
0
    xmlEntityPtr ret;
442
0
    xmlDictPtr dict;
443
444
0
    if ((doc != NULL) && (doc->intSubset != NULL)) {
445
0
  return(xmlAddDocEntity(doc, name, type, ExternalID, SystemID, content));
446
0
    }
447
0
    if (doc != NULL)
448
0
        dict = doc->dict;
449
0
    else
450
0
        dict = NULL;
451
0
    ret = xmlCreateEntity(dict, name, type, ExternalID, SystemID, content);
452
0
    if (ret == NULL)
453
0
        return(NULL);
454
0
    ret->doc = doc;
455
0
    return(ret);
456
0
}
457
458
/**
459
 * xmlGetEntityFromTable:
460
 * @table:  an entity table
461
 * @name:  the entity name
462
 * @parameter:  look for parameter entities
463
 *
464
 * Do an entity lookup in the table.
465
 * returns the corresponding parameter entity, if found.
466
 *
467
 * Returns A pointer to the entity structure or NULL if not found.
468
 */
469
static xmlEntityPtr
470
0
xmlGetEntityFromTable(xmlEntitiesTablePtr table, const xmlChar *name) {
471
0
    return((xmlEntityPtr) xmlHashLookup(table, name));
472
0
}
473
474
/**
475
 * xmlGetParameterEntity:
476
 * @doc:  the document referencing the entity
477
 * @name:  the entity name
478
 *
479
 * Do an entity lookup in the internal and external subsets and
480
 * returns the corresponding parameter entity, if found.
481
 *
482
 * Returns A pointer to the entity structure or NULL if not found.
483
 */
484
xmlEntityPtr
485
0
xmlGetParameterEntity(xmlDocPtr doc, const xmlChar *name) {
486
0
    xmlEntitiesTablePtr table;
487
0
    xmlEntityPtr ret;
488
489
0
    if (doc == NULL)
490
0
  return(NULL);
491
0
    if ((doc->intSubset != NULL) && (doc->intSubset->pentities != NULL)) {
492
0
  table = (xmlEntitiesTablePtr) doc->intSubset->pentities;
493
0
  ret = xmlGetEntityFromTable(table, name);
494
0
  if (ret != NULL)
495
0
      return(ret);
496
0
    }
497
0
    if ((doc->extSubset != NULL) && (doc->extSubset->pentities != NULL)) {
498
0
  table = (xmlEntitiesTablePtr) doc->extSubset->pentities;
499
0
  return(xmlGetEntityFromTable(table, name));
500
0
    }
501
0
    return(NULL);
502
0
}
503
504
/**
505
 * xmlGetDtdEntity:
506
 * @doc:  the document referencing the entity
507
 * @name:  the entity name
508
 *
509
 * Do an entity lookup in the DTD entity hash table and
510
 * returns the corresponding entity, if found.
511
 * Note: the first argument is the document node, not the DTD node.
512
 *
513
 * Returns A pointer to the entity structure or NULL if not found.
514
 */
515
xmlEntityPtr
516
0
xmlGetDtdEntity(xmlDocPtr doc, const xmlChar *name) {
517
0
    xmlEntitiesTablePtr table;
518
519
0
    if (doc == NULL)
520
0
  return(NULL);
521
0
    if ((doc->extSubset != NULL) && (doc->extSubset->entities != NULL)) {
522
0
  table = (xmlEntitiesTablePtr) doc->extSubset->entities;
523
0
  return(xmlGetEntityFromTable(table, name));
524
0
    }
525
0
    return(NULL);
526
0
}
527
528
/**
529
 * xmlGetDocEntity:
530
 * @doc:  the document referencing the entity
531
 * @name:  the entity name
532
 *
533
 * Do an entity lookup in the document entity hash table and
534
 * returns the corresponding entity, otherwise a lookup is done
535
 * in the predefined entities too.
536
 *
537
 * Returns A pointer to the entity structure or NULL if not found.
538
 */
539
xmlEntityPtr
540
0
xmlGetDocEntity(const xmlDoc *doc, const xmlChar *name) {
541
0
    xmlEntityPtr cur;
542
0
    xmlEntitiesTablePtr table;
543
544
0
    if (doc != NULL) {
545
0
  if ((doc->intSubset != NULL) && (doc->intSubset->entities != NULL)) {
546
0
      table = (xmlEntitiesTablePtr) doc->intSubset->entities;
547
0
      cur = xmlGetEntityFromTable(table, name);
548
0
      if (cur != NULL)
549
0
    return(cur);
550
0
  }
551
0
  if (doc->standalone != 1) {
552
0
      if ((doc->extSubset != NULL) &&
553
0
    (doc->extSubset->entities != NULL)) {
554
0
    table = (xmlEntitiesTablePtr) doc->extSubset->entities;
555
0
    cur = xmlGetEntityFromTable(table, name);
556
0
    if (cur != NULL)
557
0
        return(cur);
558
0
      }
559
0
  }
560
0
    }
561
0
    return(xmlGetPredefinedEntity(name));
562
0
}
563
564
/*
565
 * Macro used to grow the current buffer.
566
 */
567
0
#define growBufferReentrant() {           \
568
0
    xmlChar *tmp;                                                       \
569
0
    size_t new_size = buffer_size * 2;                                  \
570
0
    if (new_size < buffer_size) goto mem_error;                         \
571
0
    tmp = (xmlChar *) xmlRealloc(buffer, new_size);                 \
572
0
    if (tmp == NULL) goto mem_error;                                    \
573
0
    buffer = tmp;             \
574
0
    buffer_size = new_size;           \
575
0
}
576
577
/**
578
 * xmlEncodeEntitiesInternal:
579
 * @doc:  the document containing the string
580
 * @input:  A string to convert to XML.
581
 * @attr: are we handling an attribute value
582
 *
583
 * Do a global encoding of a string, replacing the predefined entities
584
 * and non ASCII values with their entities and CharRef counterparts.
585
 * Contrary to xmlEncodeEntities, this routine is reentrant, and result
586
 * must be deallocated.
587
 *
588
 * Returns A newly allocated string with the substitution done.
589
 */
590
static xmlChar *
591
0
xmlEncodeEntitiesInternal(xmlDocPtr doc, const xmlChar *input, int attr) {
592
0
    const xmlChar *cur = input;
593
0
    xmlChar *buffer = NULL;
594
0
    xmlChar *out = NULL;
595
0
    size_t buffer_size = 0;
596
0
    int html = 0;
597
598
0
    if (input == NULL) return(NULL);
599
0
    if (doc != NULL)
600
0
        html = (doc->type == XML_HTML_DOCUMENT_NODE);
601
602
    /*
603
     * allocate an translation buffer.
604
     */
605
0
    buffer_size = 1000;
606
0
    buffer = (xmlChar *) xmlMalloc(buffer_size);
607
0
    if (buffer == NULL) {
608
0
        xmlEntitiesErrMemory("xmlEncodeEntities: malloc failed");
609
0
  return(NULL);
610
0
    }
611
0
    out = buffer;
612
613
0
    while (*cur != '\0') {
614
0
        size_t indx = out - buffer;
615
0
        if (indx + 100 > buffer_size) {
616
617
0
      growBufferReentrant();
618
0
      out = &buffer[indx];
619
0
  }
620
621
  /*
622
   * By default one have to encode at least '<', '>', '"' and '&' !
623
   */
624
0
  if (*cur == '<') {
625
0
      const xmlChar *end;
626
627
      /*
628
       * Special handling of server side include in HTML attributes
629
       */
630
0
      if (html && attr &&
631
0
          (cur[1] == '!') && (cur[2] == '-') && (cur[3] == '-') &&
632
0
          ((end = xmlStrstr(cur, BAD_CAST "-->")) != NULL)) {
633
0
          while (cur != end) {
634
0
        *out++ = *cur++;
635
0
        indx = out - buffer;
636
0
        if (indx + 100 > buffer_size) {
637
0
      growBufferReentrant();
638
0
      out = &buffer[indx];
639
0
        }
640
0
    }
641
0
    *out++ = *cur++;
642
0
    *out++ = *cur++;
643
0
    *out++ = *cur++;
644
0
    continue;
645
0
      }
646
0
      *out++ = '&';
647
0
      *out++ = 'l';
648
0
      *out++ = 't';
649
0
      *out++ = ';';
650
0
  } else if (*cur == '>') {
651
0
      *out++ = '&';
652
0
      *out++ = 'g';
653
0
      *out++ = 't';
654
0
      *out++ = ';';
655
0
  } else if (*cur == '&') {
656
      /*
657
       * Special handling of &{...} construct from HTML 4, see
658
       * http://www.w3.org/TR/html401/appendix/notes.html#h-B.7.1
659
       */
660
0
      if (html && attr && (cur[1] == '{') &&
661
0
          (strchr((const char *) cur, '}'))) {
662
0
          while (*cur != '}') {
663
0
        *out++ = *cur++;
664
0
        indx = out - buffer;
665
0
        if (indx + 100 > buffer_size) {
666
0
      growBufferReentrant();
667
0
      out = &buffer[indx];
668
0
        }
669
0
    }
670
0
    *out++ = *cur++;
671
0
    continue;
672
0
      }
673
0
      *out++ = '&';
674
0
      *out++ = 'a';
675
0
      *out++ = 'm';
676
0
      *out++ = 'p';
677
0
      *out++ = ';';
678
0
  } else if (((*cur >= 0x20) && (*cur < 0x80)) ||
679
0
      (*cur == '\n') || (*cur == '\t') || ((html) && (*cur == '\r'))) {
680
      /*
681
       * default case, just copy !
682
       */
683
0
      *out++ = *cur;
684
0
  } else if (*cur >= 0x80) {
685
0
      if (((doc != NULL) && (doc->encoding != NULL)) || (html)) {
686
    /*
687
     * Bjørn Reese <br@sseusa.com> provided the patch
688
          xmlChar xc;
689
          xc = (*cur & 0x3F) << 6;
690
          if (cur[1] != 0) {
691
        xc += *(++cur) & 0x3F;
692
        *out++ = xc;
693
          } else
694
     */
695
0
    *out++ = *cur;
696
0
      } else {
697
    /*
698
     * We assume we have UTF-8 input.
699
     * It must match either:
700
     *   110xxxxx 10xxxxxx
701
     *   1110xxxx 10xxxxxx 10xxxxxx
702
     *   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
703
     * That is:
704
     *   cur[0] is 11xxxxxx
705
     *   cur[1] is 10xxxxxx
706
     *   cur[2] is 10xxxxxx if cur[0] is 111xxxxx
707
     *   cur[3] is 10xxxxxx if cur[0] is 1111xxxx
708
     *   cur[0] is not 11111xxx
709
     */
710
0
    char buf[11], *ptr;
711
0
    int val = 0, l = 1;
712
713
0
    if (((cur[0] & 0xC0) != 0xC0) ||
714
0
        ((cur[1] & 0xC0) != 0x80) ||
715
0
        (((cur[0] & 0xE0) == 0xE0) && ((cur[2] & 0xC0) != 0x80)) ||
716
0
        (((cur[0] & 0xF0) == 0xF0) && ((cur[3] & 0xC0) != 0x80)) ||
717
0
        (((cur[0] & 0xF8) == 0xF8))) {
718
0
        xmlEntitiesErr(XML_CHECK_NOT_UTF8,
719
0
          "xmlEncodeEntities: input not UTF-8");
720
0
        snprintf(buf, sizeof(buf), "&#%d;", *cur);
721
0
        buf[sizeof(buf) - 1] = 0;
722
0
        ptr = buf;
723
0
        while (*ptr != 0) *out++ = *ptr++;
724
0
        cur++;
725
0
        continue;
726
0
    } else if (*cur < 0xE0) {
727
0
                    val = (cur[0]) & 0x1F;
728
0
        val <<= 6;
729
0
        val |= (cur[1]) & 0x3F;
730
0
        l = 2;
731
0
    } else if (*cur < 0xF0) {
732
0
                    val = (cur[0]) & 0x0F;
733
0
        val <<= 6;
734
0
        val |= (cur[1]) & 0x3F;
735
0
        val <<= 6;
736
0
        val |= (cur[2]) & 0x3F;
737
0
        l = 3;
738
0
    } else if (*cur < 0xF8) {
739
0
                    val = (cur[0]) & 0x07;
740
0
        val <<= 6;
741
0
        val |= (cur[1]) & 0x3F;
742
0
        val <<= 6;
743
0
        val |= (cur[2]) & 0x3F;
744
0
        val <<= 6;
745
0
        val |= (cur[3]) & 0x3F;
746
0
        l = 4;
747
0
    }
748
0
    if ((l == 1) || (!IS_CHAR(val))) {
749
0
        xmlEntitiesErr(XML_ERR_INVALID_CHAR,
750
0
      "xmlEncodeEntities: char out of range\n");
751
0
        snprintf(buf, sizeof(buf), "&#%d;", *cur);
752
0
        buf[sizeof(buf) - 1] = 0;
753
0
        ptr = buf;
754
0
        while (*ptr != 0) *out++ = *ptr++;
755
0
        cur++;
756
0
        continue;
757
0
    }
758
    /*
759
     * We could do multiple things here. Just save as a char ref
760
     */
761
0
    snprintf(buf, sizeof(buf), "&#x%X;", val);
762
0
    buf[sizeof(buf) - 1] = 0;
763
0
    ptr = buf;
764
0
    while (*ptr != 0) *out++ = *ptr++;
765
0
    cur += l;
766
0
    continue;
767
0
      }
768
0
  } else if (IS_BYTE_CHAR(*cur)) {
769
0
      char buf[11], *ptr;
770
771
0
      snprintf(buf, sizeof(buf), "&#%d;", *cur);
772
0
      buf[sizeof(buf) - 1] = 0;
773
0
            ptr = buf;
774
0
      while (*ptr != 0) *out++ = *ptr++;
775
0
  }
776
0
  cur++;
777
0
    }
778
0
    *out = 0;
779
0
    return(buffer);
780
781
0
mem_error:
782
0
    xmlEntitiesErrMemory("xmlEncodeEntities: realloc failed");
783
0
    xmlFree(buffer);
784
0
    return(NULL);
785
0
}
786
787
/**
788
 * xmlEncodeAttributeEntities:
789
 * @doc:  the document containing the string
790
 * @input:  A string to convert to XML.
791
 *
792
 * Do a global encoding of a string, replacing the predefined entities
793
 * and non ASCII values with their entities and CharRef counterparts for
794
 * attribute values.
795
 *
796
 * Returns A newly allocated string with the substitution done.
797
 */
798
xmlChar *
799
0
xmlEncodeAttributeEntities(xmlDocPtr doc, const xmlChar *input) {
800
0
    return xmlEncodeEntitiesInternal(doc, input, 1);
801
0
}
802
803
/**
804
 * xmlEncodeEntitiesReentrant:
805
 * @doc:  the document containing the string
806
 * @input:  A string to convert to XML.
807
 *
808
 * Do a global encoding of a string, replacing the predefined entities
809
 * and non ASCII values with their entities and CharRef counterparts.
810
 * Contrary to xmlEncodeEntities, this routine is reentrant, and result
811
 * must be deallocated.
812
 *
813
 * Returns A newly allocated string with the substitution done.
814
 */
815
xmlChar *
816
0
xmlEncodeEntitiesReentrant(xmlDocPtr doc, const xmlChar *input) {
817
0
    return xmlEncodeEntitiesInternal(doc, input, 0);
818
0
}
819
820
/**
821
 * xmlEncodeSpecialChars:
822
 * @doc:  the document containing the string
823
 * @input:  A string to convert to XML.
824
 *
825
 * Do a global encoding of a string, replacing the predefined entities
826
 * this routine is reentrant, and result must be deallocated.
827
 *
828
 * Returns A newly allocated string with the substitution done.
829
 */
830
xmlChar *
831
0
xmlEncodeSpecialChars(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlChar *input) {
832
0
    const xmlChar *cur = input;
833
0
    xmlChar *buffer = NULL;
834
0
    xmlChar *out = NULL;
835
0
    size_t buffer_size = 0;
836
0
    if (input == NULL) return(NULL);
837
838
    /*
839
     * allocate an translation buffer.
840
     */
841
0
    buffer_size = 1000;
842
0
    buffer = (xmlChar *) xmlMalloc(buffer_size);
843
0
    if (buffer == NULL) {
844
0
        xmlEntitiesErrMemory("xmlEncodeSpecialChars: malloc failed");
845
0
  return(NULL);
846
0
    }
847
0
    out = buffer;
848
849
0
    while (*cur != '\0') {
850
0
        size_t indx = out - buffer;
851
0
        if (indx + 10 > buffer_size) {
852
853
0
      growBufferReentrant();
854
0
      out = &buffer[indx];
855
0
  }
856
857
  /*
858
   * By default one have to encode at least '<', '>', '"' and '&' !
859
   */
860
0
  if (*cur == '<') {
861
0
      *out++ = '&';
862
0
      *out++ = 'l';
863
0
      *out++ = 't';
864
0
      *out++ = ';';
865
0
  } else if (*cur == '>') {
866
0
      *out++ = '&';
867
0
      *out++ = 'g';
868
0
      *out++ = 't';
869
0
      *out++ = ';';
870
0
  } else if (*cur == '&') {
871
0
      *out++ = '&';
872
0
      *out++ = 'a';
873
0
      *out++ = 'm';
874
0
      *out++ = 'p';
875
0
      *out++ = ';';
876
0
  } else if (*cur == '"') {
877
0
      *out++ = '&';
878
0
      *out++ = 'q';
879
0
      *out++ = 'u';
880
0
      *out++ = 'o';
881
0
      *out++ = 't';
882
0
      *out++ = ';';
883
0
  } else if (*cur == '\r') {
884
0
      *out++ = '&';
885
0
      *out++ = '#';
886
0
      *out++ = '1';
887
0
      *out++ = '3';
888
0
      *out++ = ';';
889
0
  } else {
890
      /*
891
       * Works because on UTF-8, all extended sequences cannot
892
       * result in bytes in the ASCII range.
893
       */
894
0
      *out++ = *cur;
895
0
  }
896
0
  cur++;
897
0
    }
898
0
    *out = 0;
899
0
    return(buffer);
900
901
0
mem_error:
902
0
    xmlEntitiesErrMemory("xmlEncodeSpecialChars: realloc failed");
903
0
    xmlFree(buffer);
904
0
    return(NULL);
905
0
}
906
907
/**
908
 * xmlCreateEntitiesTable:
909
 *
910
 * create and initialize an empty entities hash table.
911
 * This really doesn't make sense and should be deprecated
912
 *
913
 * Returns the xmlEntitiesTablePtr just created or NULL in case of error.
914
 */
915
xmlEntitiesTablePtr
916
0
xmlCreateEntitiesTable(void) {
917
0
    return((xmlEntitiesTablePtr) xmlHashCreate(0));
918
0
}
919
920
/**
921
 * xmlFreeEntityWrapper:
922
 * @entity:  An entity
923
 * @name:  its name
924
 *
925
 * Deallocate the memory used by an entities in the hash table.
926
 */
927
static void
928
0
xmlFreeEntityWrapper(void *entity, const xmlChar *name ATTRIBUTE_UNUSED) {
929
0
    if (entity != NULL)
930
0
  xmlFreeEntity((xmlEntityPtr) entity);
931
0
}
932
933
/**
934
 * xmlFreeEntitiesTable:
935
 * @table:  An entity table
936
 *
937
 * Deallocate the memory used by an entities hash table.
938
 */
939
void
940
0
xmlFreeEntitiesTable(xmlEntitiesTablePtr table) {
941
0
    xmlHashFree(table, xmlFreeEntityWrapper);
942
0
}
943
944
#ifdef LIBXML_TREE_ENABLED
945
/**
946
 * xmlCopyEntity:
947
 * @ent:  An entity
948
 *
949
 * Build a copy of an entity
950
 *
951
 * Returns the new xmlEntitiesPtr or NULL in case of error.
952
 */
953
static void *
954
0
xmlCopyEntity(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
955
0
    xmlEntityPtr ent = (xmlEntityPtr) payload;
956
0
    xmlEntityPtr cur;
957
958
0
    cur = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
959
0
    if (cur == NULL) {
960
0
        xmlEntitiesErrMemory("xmlCopyEntity:: malloc failed");
961
0
  return(NULL);
962
0
    }
963
0
    memset(cur, 0, sizeof(xmlEntity));
964
0
    cur->type = XML_ENTITY_DECL;
965
966
0
    cur->etype = ent->etype;
967
0
    if (ent->name != NULL)
968
0
  cur->name = xmlStrdup(ent->name);
969
0
    if (ent->ExternalID != NULL)
970
0
  cur->ExternalID = xmlStrdup(ent->ExternalID);
971
0
    if (ent->SystemID != NULL)
972
0
  cur->SystemID = xmlStrdup(ent->SystemID);
973
0
    if (ent->content != NULL)
974
0
  cur->content = xmlStrdup(ent->content);
975
0
    if (ent->orig != NULL)
976
0
  cur->orig = xmlStrdup(ent->orig);
977
0
    if (ent->URI != NULL)
978
0
  cur->URI = xmlStrdup(ent->URI);
979
0
    return(cur);
980
0
}
981
982
/**
983
 * xmlCopyEntitiesTable:
984
 * @table:  An entity table
985
 *
986
 * Build a copy of an entity table.
987
 *
988
 * Returns the new xmlEntitiesTablePtr or NULL in case of error.
989
 */
990
xmlEntitiesTablePtr
991
0
xmlCopyEntitiesTable(xmlEntitiesTablePtr table) {
992
0
    return(xmlHashCopy(table, xmlCopyEntity));
993
0
}
994
#endif /* LIBXML_TREE_ENABLED */
995
996
#ifdef LIBXML_OUTPUT_ENABLED
997
998
/**
999
 * xmlDumpEntityContent:
1000
 * @buf:  An XML buffer.
1001
 * @content:  The entity content.
1002
 *
1003
 * This will dump the quoted string value, taking care of the special
1004
 * treatment required by %
1005
 */
1006
static void
1007
0
xmlDumpEntityContent(xmlBufferPtr buf, const xmlChar *content) {
1008
0
    if (xmlStrchr(content, '%')) {
1009
0
        const xmlChar * base, *cur;
1010
1011
0
  xmlBufferCCat(buf, "\"");
1012
0
  base = cur = content;
1013
0
  while (*cur != 0) {
1014
0
      if (*cur == '"') {
1015
0
    if (base != cur)
1016
0
        xmlBufferAdd(buf, base, cur - base);
1017
0
    xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1018
0
    cur++;
1019
0
    base = cur;
1020
0
      } else if (*cur == '%') {
1021
0
    if (base != cur)
1022
0
        xmlBufferAdd(buf, base, cur - base);
1023
0
    xmlBufferAdd(buf, BAD_CAST "&#x25;", 6);
1024
0
    cur++;
1025
0
    base = cur;
1026
0
      } else {
1027
0
    cur++;
1028
0
      }
1029
0
  }
1030
0
  if (base != cur)
1031
0
      xmlBufferAdd(buf, base, cur - base);
1032
0
  xmlBufferCCat(buf, "\"");
1033
0
    } else {
1034
0
        xmlBufferWriteQuotedString(buf, content);
1035
0
    }
1036
0
}
1037
1038
/**
1039
 * xmlDumpEntityDecl:
1040
 * @buf:  An XML buffer.
1041
 * @ent:  An entity table
1042
 *
1043
 * This will dump the content of the entity table as an XML DTD definition
1044
 */
1045
void
1046
0
xmlDumpEntityDecl(xmlBufferPtr buf, xmlEntityPtr ent) {
1047
0
    if ((buf == NULL) || (ent == NULL)) return;
1048
0
    switch (ent->etype) {
1049
0
  case XML_INTERNAL_GENERAL_ENTITY:
1050
0
      xmlBufferWriteChar(buf, "<!ENTITY ");
1051
0
      xmlBufferWriteCHAR(buf, ent->name);
1052
0
      xmlBufferWriteChar(buf, " ");
1053
0
      if (ent->orig != NULL)
1054
0
    xmlBufferWriteQuotedString(buf, ent->orig);
1055
0
      else
1056
0
    xmlDumpEntityContent(buf, ent->content);
1057
0
      xmlBufferWriteChar(buf, ">\n");
1058
0
      break;
1059
0
  case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
1060
0
      xmlBufferWriteChar(buf, "<!ENTITY ");
1061
0
      xmlBufferWriteCHAR(buf, ent->name);
1062
0
      if (ent->ExternalID != NULL) {
1063
0
     xmlBufferWriteChar(buf, " PUBLIC ");
1064
0
     xmlBufferWriteQuotedString(buf, ent->ExternalID);
1065
0
     xmlBufferWriteChar(buf, " ");
1066
0
     xmlBufferWriteQuotedString(buf, ent->SystemID);
1067
0
      } else {
1068
0
     xmlBufferWriteChar(buf, " SYSTEM ");
1069
0
     xmlBufferWriteQuotedString(buf, ent->SystemID);
1070
0
      }
1071
0
      xmlBufferWriteChar(buf, ">\n");
1072
0
      break;
1073
0
  case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
1074
0
      xmlBufferWriteChar(buf, "<!ENTITY ");
1075
0
      xmlBufferWriteCHAR(buf, ent->name);
1076
0
      if (ent->ExternalID != NULL) {
1077
0
     xmlBufferWriteChar(buf, " PUBLIC ");
1078
0
     xmlBufferWriteQuotedString(buf, ent->ExternalID);
1079
0
     xmlBufferWriteChar(buf, " ");
1080
0
     xmlBufferWriteQuotedString(buf, ent->SystemID);
1081
0
      } else {
1082
0
     xmlBufferWriteChar(buf, " SYSTEM ");
1083
0
     xmlBufferWriteQuotedString(buf, ent->SystemID);
1084
0
      }
1085
0
      if (ent->content != NULL) { /* Should be true ! */
1086
0
    xmlBufferWriteChar(buf, " NDATA ");
1087
0
    if (ent->orig != NULL)
1088
0
        xmlBufferWriteCHAR(buf, ent->orig);
1089
0
    else
1090
0
        xmlBufferWriteCHAR(buf, ent->content);
1091
0
      }
1092
0
      xmlBufferWriteChar(buf, ">\n");
1093
0
      break;
1094
0
  case XML_INTERNAL_PARAMETER_ENTITY:
1095
0
      xmlBufferWriteChar(buf, "<!ENTITY % ");
1096
0
      xmlBufferWriteCHAR(buf, ent->name);
1097
0
      xmlBufferWriteChar(buf, " ");
1098
0
      if (ent->orig == NULL)
1099
0
    xmlDumpEntityContent(buf, ent->content);
1100
0
      else
1101
0
    xmlBufferWriteQuotedString(buf, ent->orig);
1102
0
      xmlBufferWriteChar(buf, ">\n");
1103
0
      break;
1104
0
  case XML_EXTERNAL_PARAMETER_ENTITY:
1105
0
      xmlBufferWriteChar(buf, "<!ENTITY % ");
1106
0
      xmlBufferWriteCHAR(buf, ent->name);
1107
0
      if (ent->ExternalID != NULL) {
1108
0
     xmlBufferWriteChar(buf, " PUBLIC ");
1109
0
     xmlBufferWriteQuotedString(buf, ent->ExternalID);
1110
0
     xmlBufferWriteChar(buf, " ");
1111
0
     xmlBufferWriteQuotedString(buf, ent->SystemID);
1112
0
      } else {
1113
0
     xmlBufferWriteChar(buf, " SYSTEM ");
1114
0
     xmlBufferWriteQuotedString(buf, ent->SystemID);
1115
0
      }
1116
0
      xmlBufferWriteChar(buf, ">\n");
1117
0
      break;
1118
0
  default:
1119
0
      xmlEntitiesErr(XML_DTD_UNKNOWN_ENTITY,
1120
0
    "xmlDumpEntitiesDecl: internal: unknown type entity type");
1121
0
    }
1122
0
}
1123
1124
/**
1125
 * xmlDumpEntityDeclScan:
1126
 * @ent:  An entity table
1127
 * @buf:  An XML buffer.
1128
 *
1129
 * When using the hash table scan function, arguments need to be reversed
1130
 */
1131
static void
1132
xmlDumpEntityDeclScan(void *ent, void *buf,
1133
0
                      const xmlChar *name ATTRIBUTE_UNUSED) {
1134
0
    xmlDumpEntityDecl((xmlBufferPtr) buf, (xmlEntityPtr) ent);
1135
0
}
1136
1137
/**
1138
 * xmlDumpEntitiesTable:
1139
 * @buf:  An XML buffer.
1140
 * @table:  An entity table
1141
 *
1142
 * This will dump the content of the entity table as an XML DTD definition
1143
 */
1144
void
1145
0
xmlDumpEntitiesTable(xmlBufferPtr buf, xmlEntitiesTablePtr table) {
1146
0
    xmlHashScan(table, xmlDumpEntityDeclScan, buf);
1147
0
}
1148
#endif /* LIBXML_OUTPUT_ENABLED */