Coverage Report

Created: 2023-12-13 20:02

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