Coverage Report

Created: 2024-02-04 06:19

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