Coverage Report

Created: 2026-03-07 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/entities.c
Line
Count
Source
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
#include <libxml/xmlsave.h>
28
29
#include "private/entities.h"
30
#include "private/error.h"
31
32
#ifndef SIZE_MAX
33
  #define SIZE_MAX ((size_t) -1)
34
#endif
35
36
/*
37
 * The XML predefined entities.
38
 */
39
40
static xmlEntity xmlEntityLt = {
41
    NULL, XML_ENTITY_DECL, BAD_CAST "lt",
42
    NULL, NULL, NULL, NULL, NULL, NULL,
43
    BAD_CAST "<", BAD_CAST "<", 1,
44
    XML_INTERNAL_PREDEFINED_ENTITY,
45
    NULL, NULL, NULL, NULL, 0, 0, 0
46
};
47
static xmlEntity xmlEntityGt = {
48
    NULL, XML_ENTITY_DECL, BAD_CAST "gt",
49
    NULL, NULL, NULL, NULL, NULL, NULL,
50
    BAD_CAST ">", BAD_CAST ">", 1,
51
    XML_INTERNAL_PREDEFINED_ENTITY,
52
    NULL, NULL, NULL, NULL, 0, 0, 0
53
};
54
static xmlEntity xmlEntityAmp = {
55
    NULL, XML_ENTITY_DECL, BAD_CAST "amp",
56
    NULL, NULL, NULL, NULL, NULL, NULL,
57
    BAD_CAST "&", BAD_CAST "&", 1,
58
    XML_INTERNAL_PREDEFINED_ENTITY,
59
    NULL, NULL, NULL, NULL, 0, 0, 0
60
};
61
static xmlEntity xmlEntityQuot = {
62
    NULL, XML_ENTITY_DECL, BAD_CAST "quot",
63
    NULL, NULL, NULL, NULL, NULL, NULL,
64
    BAD_CAST "\"", BAD_CAST "\"", 1,
65
    XML_INTERNAL_PREDEFINED_ENTITY,
66
    NULL, NULL, NULL, NULL, 0, 0, 0
67
};
68
static xmlEntity xmlEntityApos = {
69
    NULL, XML_ENTITY_DECL, BAD_CAST "apos",
70
    NULL, NULL, NULL, NULL, NULL, NULL,
71
    BAD_CAST "'", BAD_CAST "'", 1,
72
    XML_INTERNAL_PREDEFINED_ENTITY,
73
    NULL, NULL, NULL, NULL, 0, 0, 0
74
};
75
76
/*
77
 * xmlFreeEntity:
78
 * @entity:  an entity
79
 *
80
 * Frees the entity.
81
 */
82
void
83
xmlFreeEntity(xmlEntityPtr entity)
84
10.2k
{
85
10.2k
    xmlDictPtr dict = NULL;
86
87
10.2k
    if (entity == NULL)
88
0
        return;
89
90
10.2k
    if (entity->doc != NULL)
91
10.2k
        dict = entity->doc->dict;
92
93
94
10.2k
    if ((entity->children) &&
95
51
        (entity == (xmlEntityPtr) entity->children->parent))
96
51
        xmlFreeNodeList(entity->children);
97
10.2k
    if ((entity->name != NULL) &&
98
10.2k
        ((dict == NULL) || (!xmlDictOwns(dict, entity->name))))
99
0
        xmlFree((char *) entity->name);
100
10.2k
    if (entity->ExternalID != NULL)
101
208
        xmlFree((char *) entity->ExternalID);
102
10.2k
    if (entity->SystemID != NULL)
103
207
        xmlFree((char *) entity->SystemID);
104
10.2k
    if (entity->URI != NULL)
105
3
        xmlFree((char *) entity->URI);
106
10.2k
    if (entity->content != NULL)
107
8.96k
        xmlFree((char *) entity->content);
108
10.2k
    if (entity->orig != NULL)
109
414
        xmlFree((char *) entity->orig);
110
10.2k
    xmlFree(entity);
111
10.2k
}
112
113
/*
114
 * xmlCreateEntity:
115
 *
116
 * internal routine doing the entity node structures allocations
117
 */
118
static xmlEntityPtr
119
xmlCreateEntity(xmlDocPtr doc, const xmlChar *name, int type,
120
          const xmlChar *ExternalID, const xmlChar *SystemID,
121
10.2k
          const xmlChar *content) {
122
10.2k
    xmlEntityPtr ret;
123
124
10.2k
    ret = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
125
10.2k
    if (ret == NULL)
126
0
  return(NULL);
127
10.2k
    memset(ret, 0, sizeof(xmlEntity));
128
10.2k
    ret->doc = doc;
129
10.2k
    ret->type = XML_ENTITY_DECL;
130
131
    /*
132
     * fill the structure.
133
     */
134
10.2k
    ret->etype = (xmlEntityType) type;
135
10.2k
    if ((doc == NULL) || (doc->dict == NULL))
136
0
  ret->name = xmlStrdup(name);
137
10.2k
    else
138
10.2k
        ret->name = xmlDictLookup(doc->dict, name, -1);
139
10.2k
    if (ret->name == NULL)
140
0
        goto error;
141
10.2k
    if (ExternalID != NULL) {
142
208
        ret->ExternalID = xmlStrdup(ExternalID);
143
208
        if (ret->ExternalID == NULL)
144
0
            goto error;
145
208
    }
146
10.2k
    if (SystemID != NULL) {
147
207
        ret->SystemID = xmlStrdup(SystemID);
148
207
        if (ret->SystemID == NULL)
149
0
            goto error;
150
207
    }
151
10.2k
    if (content != NULL) {
152
8.96k
        ret->length = xmlStrlen(content);
153
8.96k
  ret->content = xmlStrndup(content, ret->length);
154
8.96k
        if (ret->content == NULL)
155
0
            goto error;
156
8.96k
     } else {
157
1.28k
        ret->length = 0;
158
1.28k
        ret->content = NULL;
159
1.28k
    }
160
10.2k
    ret->URI = NULL; /* to be computed by the layer knowing
161
      the defining entity */
162
10.2k
    ret->orig = NULL;
163
164
10.2k
    return(ret);
165
166
0
error:
167
0
    xmlFreeEntity(ret);
168
0
    return(NULL);
169
10.2k
}
170
171
/**
172
 * xmlAddEntity:
173
 * @doc:  the document
174
 * @extSubset:  add to the external or internal subset
175
 * @name:  the entity name
176
 * @type:  the entity type XML_xxx_yyy_ENTITY
177
 * @ExternalID:  the entity external ID if available
178
 * @SystemID:  the entity system ID if available
179
 * @content:  the entity content
180
 * @out:  pointer to resulting entity (optional)
181
 *
182
 * Register a new entity for this document.
183
 *
184
 * Available since 2.13.0.
185
 *
186
 * Returns an xmlParserErrors error code.
187
 */
188
int
189
xmlAddEntity(xmlDocPtr doc, int extSubset, const xmlChar *name, int type,
190
    const xmlChar *ExternalID, const xmlChar *SystemID,
191
16.7k
    const xmlChar *content, xmlEntityPtr *out) {
192
16.7k
    xmlDtdPtr dtd;
193
16.7k
    xmlDictPtr dict = NULL;
194
16.7k
    xmlEntitiesTablePtr table = NULL;
195
16.7k
    xmlEntityPtr ret, predef;
196
16.7k
    int res;
197
198
16.7k
    if (out != NULL)
199
16.7k
        *out = NULL;
200
16.7k
    if ((doc == NULL) || (name == NULL))
201
0
  return(XML_ERR_ARGUMENT);
202
16.7k
    dict = doc->dict;
203
204
16.7k
    if (extSubset)
205
0
        dtd = doc->extSubset;
206
16.7k
    else
207
16.7k
        dtd = doc->intSubset;
208
16.7k
    if (dtd == NULL)
209
0
        return(XML_DTD_NO_DTD);
210
211
16.7k
    switch (type) {
212
10.0k
        case XML_INTERNAL_GENERAL_ENTITY:
213
11.9k
        case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
214
11.9k
        case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
215
11.9k
            predef = xmlGetPredefinedEntity(name);
216
11.9k
            if (predef != NULL) {
217
8.40k
                int valid = 0;
218
219
                /* 4.6 Predefined Entities */
220
8.40k
                if ((type == XML_INTERNAL_GENERAL_ENTITY) &&
221
7.56k
                    (content != NULL)) {
222
7.56k
                    int c = predef->content[0];
223
224
7.56k
                    if (((content[0] == c) && (content[1] == 0)) &&
225
0
                        ((c == '>') || (c == '\'') || (c == '"'))) {
226
0
                        valid = 1;
227
7.56k
                    } else if ((content[0] == '&') && (content[1] == '#')) {
228
4.70k
                        if (content[2] == 'x') {
229
4.51k
                            xmlChar *hex = BAD_CAST "0123456789ABCDEF";
230
4.51k
                            xmlChar ref[] = "00;";
231
232
4.51k
                            ref[0] = hex[c / 16 % 16];
233
4.51k
                            ref[1] = hex[c % 16];
234
4.51k
                            if (xmlStrcasecmp(&content[3], ref) == 0)
235
1.87k
                                valid = 1;
236
4.51k
                        } else {
237
188
                            xmlChar ref[] = "00;";
238
239
188
                            ref[0] = '0' + c / 10 % 10;
240
188
                            ref[1] = '0' + c % 10;
241
188
                            if (xmlStrEqual(&content[2], ref))
242
0
                                valid = 1;
243
188
                        }
244
4.70k
                    }
245
7.56k
                }
246
8.40k
                if (!valid)
247
6.52k
                    return(XML_ERR_REDECL_PREDEF_ENTITY);
248
8.40k
            }
249
5.45k
      if (dtd->entities == NULL) {
250
203
    dtd->entities = xmlHashCreateDict(0, dict);
251
203
                if (dtd->entities == NULL)
252
0
                    return(XML_ERR_NO_MEMORY);
253
203
            }
254
5.45k
      table = dtd->entities;
255
5.45k
      break;
256
4.59k
        case XML_INTERNAL_PARAMETER_ENTITY:
257
4.79k
        case XML_EXTERNAL_PARAMETER_ENTITY:
258
4.79k
      if (dtd->pentities == NULL) {
259
55
    dtd->pentities = xmlHashCreateDict(0, dict);
260
55
                if (dtd->pentities == NULL)
261
0
                    return(XML_ERR_NO_MEMORY);
262
55
            }
263
4.79k
      table = dtd->pentities;
264
4.79k
      break;
265
0
        default:
266
0
      return(XML_ERR_ARGUMENT);
267
16.7k
    }
268
10.2k
    ret = xmlCreateEntity(dtd->doc, name, type, ExternalID, SystemID, content);
269
10.2k
    if (ret == NULL)
270
0
        return(XML_ERR_NO_MEMORY);
271
272
10.2k
    res = xmlHashAdd(table, name, ret);
273
10.2k
    if (res < 0) {
274
0
        xmlFreeEntity(ret);
275
0
        return(XML_ERR_NO_MEMORY);
276
10.2k
    } else if (res == 0) {
277
  /*
278
   * entity was already defined at another level.
279
   */
280
9.83k
        xmlFreeEntity(ret);
281
9.83k
  return(XML_WAR_ENTITY_REDEFINED);
282
9.83k
    }
283
284
    /*
285
     * Link it to the DTD
286
     */
287
423
    ret->parent = dtd;
288
423
    ret->doc = dtd->doc;
289
423
    if (dtd->last == NULL) {
290
87
  dtd->children = dtd->last = (xmlNodePtr) ret;
291
336
    } else {
292
336
  dtd->last->next = (xmlNodePtr) ret;
293
336
  ret->prev = dtd->last;
294
336
  dtd->last = (xmlNodePtr) ret;
295
336
    }
296
297
423
    if (out != NULL)
298
423
        *out = ret;
299
423
    return(0);
300
10.2k
}
301
302
/**
303
 * xmlGetPredefinedEntity:
304
 * @name:  the entity name
305
 *
306
 * Check whether this name is an predefined entity.
307
 *
308
 * Returns NULL if not, otherwise the entity
309
 */
310
xmlEntityPtr
311
157k
xmlGetPredefinedEntity(const xmlChar *name) {
312
157k
    if (name == NULL) return(NULL);
313
157k
    switch (name[0]) {
314
1.96k
        case 'l':
315
1.96k
      if (xmlStrEqual(name, BAD_CAST "lt"))
316
872
          return(&xmlEntityLt);
317
1.09k
      break;
318
4.04k
        case 'g':
319
4.04k
      if (xmlStrEqual(name, BAD_CAST "gt"))
320
4.03k
          return(&xmlEntityGt);
321
11
      break;
322
20.2k
        case 'a':
323
20.2k
      if (xmlStrEqual(name, BAD_CAST "amp"))
324
10.5k
          return(&xmlEntityAmp);
325
9.70k
      if (xmlStrEqual(name, BAD_CAST "apos"))
326
2.08k
          return(&xmlEntityApos);
327
7.62k
      break;
328
7.62k
        case 'q':
329
4.91k
      if (xmlStrEqual(name, BAD_CAST "quot"))
330
4.52k
          return(&xmlEntityQuot);
331
395
      break;
332
126k
  default:
333
126k
      break;
334
157k
    }
335
135k
    return(NULL);
336
157k
}
337
338
/**
339
 * xmlAddDtdEntity:
340
 * @doc:  the document
341
 * @name:  the entity name
342
 * @type:  the entity type XML_xxx_yyy_ENTITY
343
 * @ExternalID:  the entity external ID if available
344
 * @SystemID:  the entity system ID if available
345
 * @content:  the entity content
346
 *
347
 * Register a new entity for this document DTD external subset.
348
 *
349
 * Returns a pointer to the entity or NULL in case of error
350
 */
351
xmlEntityPtr
352
xmlAddDtdEntity(xmlDocPtr doc, const xmlChar *name, int type,
353
          const xmlChar *ExternalID, const xmlChar *SystemID,
354
0
    const xmlChar *content) {
355
0
    xmlEntityPtr ret;
356
357
0
    xmlAddEntity(doc, 1, name, type, ExternalID, SystemID, content, &ret);
358
0
    return(ret);
359
0
}
360
361
/**
362
 * xmlAddDocEntity:
363
 * @doc:  the document
364
 * @name:  the entity name
365
 * @type:  the entity type XML_xxx_yyy_ENTITY
366
 * @ExternalID:  the entity external ID if available
367
 * @SystemID:  the entity system ID if available
368
 * @content:  the entity content
369
 *
370
 * Register a new entity for this document.
371
 *
372
 * Returns a pointer to the entity or NULL in case of error
373
 */
374
xmlEntityPtr
375
xmlAddDocEntity(xmlDocPtr doc, const xmlChar *name, int type,
376
          const xmlChar *ExternalID, const xmlChar *SystemID,
377
0
          const xmlChar *content) {
378
0
    xmlEntityPtr ret;
379
380
0
    xmlAddEntity(doc, 0, name, type, ExternalID, SystemID, content, &ret);
381
0
    return(ret);
382
0
}
383
384
/**
385
 * xmlNewEntity:
386
 * @doc:  the document
387
 * @name:  the entity name
388
 * @type:  the entity type XML_xxx_yyy_ENTITY
389
 * @ExternalID:  the entity external ID if available
390
 * @SystemID:  the entity system ID if available
391
 * @content:  the entity content
392
 *
393
 * Create a new entity, this differs from xmlAddDocEntity() that if
394
 * the document is NULL or has no internal subset defined, then an
395
 * unlinked entity structure will be returned, it is then the responsibility
396
 * of the caller to link it to the document later or free it when not needed
397
 * anymore.
398
 *
399
 * Returns a pointer to the entity or NULL in case of error
400
 */
401
xmlEntityPtr
402
xmlNewEntity(xmlDocPtr doc, const xmlChar *name, int type,
403
       const xmlChar *ExternalID, const xmlChar *SystemID,
404
0
       const xmlChar *content) {
405
0
    if ((doc != NULL) && (doc->intSubset != NULL)) {
406
0
  return(xmlAddDocEntity(doc, name, type, ExternalID, SystemID, content));
407
0
    }
408
0
    if (name == NULL)
409
0
        return(NULL);
410
0
    return(xmlCreateEntity(doc, name, type, ExternalID, SystemID, content));
411
0
}
412
413
/**
414
 * xmlGetEntityFromTable:
415
 * @table:  an entity table
416
 * @name:  the entity name
417
 * @parameter:  look for parameter entities
418
 *
419
 * Do an entity lookup in the table.
420
 * returns the corresponding parameter entity, if found.
421
 *
422
 * Returns A pointer to the entity structure or NULL if not found.
423
 */
424
static xmlEntityPtr
425
68.2k
xmlGetEntityFromTable(xmlEntitiesTablePtr table, const xmlChar *name) {
426
68.2k
    return((xmlEntityPtr) xmlHashLookup(table, name));
427
68.2k
}
428
429
/**
430
 * xmlGetParameterEntity:
431
 * @doc:  the document referencing the entity
432
 * @name:  the entity name
433
 *
434
 * Do an entity lookup in the internal and external subsets and
435
 * returns the corresponding parameter entity, if found.
436
 *
437
 * Returns A pointer to the entity structure or NULL if not found.
438
 */
439
xmlEntityPtr
440
8.32k
xmlGetParameterEntity(xmlDocPtr doc, const xmlChar *name) {
441
8.32k
    xmlEntitiesTablePtr table;
442
8.32k
    xmlEntityPtr ret;
443
444
8.32k
    if (doc == NULL)
445
0
  return(NULL);
446
8.32k
    if ((doc->intSubset != NULL) && (doc->intSubset->pentities != NULL)) {
447
8.30k
  table = (xmlEntitiesTablePtr) doc->intSubset->pentities;
448
8.30k
  ret = xmlGetEntityFromTable(table, name);
449
8.30k
  if (ret != NULL)
450
7.07k
      return(ret);
451
8.30k
    }
452
1.25k
    if ((doc->extSubset != NULL) && (doc->extSubset->pentities != NULL)) {
453
0
  table = (xmlEntitiesTablePtr) doc->extSubset->pentities;
454
0
  return(xmlGetEntityFromTable(table, name));
455
0
    }
456
1.25k
    return(NULL);
457
1.25k
}
458
459
/**
460
 * xmlGetDtdEntity:
461
 * @doc:  the document referencing the entity
462
 * @name:  the entity name
463
 *
464
 * Do an entity lookup in the DTD entity hash table and
465
 * returns the corresponding entity, if found.
466
 * Note: the first argument is the document node, not the DTD node.
467
 *
468
 * Returns A pointer to the entity structure or NULL if not found.
469
 */
470
xmlEntityPtr
471
0
xmlGetDtdEntity(xmlDocPtr doc, const xmlChar *name) {
472
0
    xmlEntitiesTablePtr table;
473
474
0
    if (doc == NULL)
475
0
  return(NULL);
476
0
    if ((doc->extSubset != NULL) && (doc->extSubset->entities != NULL)) {
477
0
  table = (xmlEntitiesTablePtr) doc->extSubset->entities;
478
0
  return(xmlGetEntityFromTable(table, name));
479
0
    }
480
0
    return(NULL);
481
0
}
482
483
/**
484
 * xmlGetDocEntity:
485
 * @doc:  the document referencing the entity
486
 * @name:  the entity name
487
 *
488
 * Do an entity lookup in the document entity hash table and
489
 * returns the corresponding entity, otherwise a lookup is done
490
 * in the predefined entities too.
491
 *
492
 * Returns A pointer to the entity structure or NULL if not found.
493
 */
494
xmlEntityPtr
495
84.5k
xmlGetDocEntity(const xmlDoc *doc, const xmlChar *name) {
496
84.5k
    xmlEntityPtr cur;
497
84.5k
    xmlEntitiesTablePtr table;
498
499
84.5k
    if (doc != NULL) {
500
84.5k
  if ((doc->intSubset != NULL) && (doc->intSubset->entities != NULL)) {
501
59.9k
      table = (xmlEntitiesTablePtr) doc->intSubset->entities;
502
59.9k
      cur = xmlGetEntityFromTable(table, name);
503
59.9k
      if (cur != NULL)
504
53.1k
    return(cur);
505
59.9k
  }
506
31.4k
  if (doc->standalone != 1) {
507
31.4k
      if ((doc->extSubset != NULL) &&
508
0
    (doc->extSubset->entities != NULL)) {
509
0
    table = (xmlEntitiesTablePtr) doc->extSubset->entities;
510
0
    cur = xmlGetEntityFromTable(table, name);
511
0
    if (cur != NULL)
512
0
        return(cur);
513
0
      }
514
31.4k
  }
515
31.4k
    }
516
31.4k
    return(xmlGetPredefinedEntity(name));
517
84.5k
}
518
519
/*
520
 * xmlSerializeHexCharRef:
521
 * @buf:  a char buffer
522
 * @val:  a codepoint
523
 *
524
 * Serializes a hex char ref like &#xA0;
525
 *
526
 * Writes at most 9 bytes. Does not include a terminating zero byte.
527
 *
528
 * Returns the number of bytes written.
529
 */
530
int
531
0
xmlSerializeHexCharRef(char *buf, int val) {
532
0
    char *out = buf;
533
0
    int shift = 0, bits;
534
535
0
    *out++ = '&';
536
0
    *out++ = '#';
537
0
    *out++ = 'x';
538
539
0
    bits = val;
540
0
    if (bits & 0xFF0000) {
541
0
        shift = 16;
542
0
        bits &= 0xFF0000;
543
0
    } else if (bits & 0x00FF00) {
544
0
        shift = 8;
545
0
        bits &= 0x00FF00;
546
0
    }
547
0
    if (bits & 0xF0F0F0) {
548
0
        shift += 4;
549
0
    }
550
551
0
    do {
552
0
        int d = (val >> shift) & 0x0F;
553
554
0
        if (d < 10)
555
0
            *out++ = '0' + d;
556
0
        else
557
0
            *out++ = 'A' + (d - 10);
558
559
0
  shift -= 4;
560
0
    } while (shift >= 0);
561
562
0
    *out++ = ';';
563
564
0
    return(out - buf);
565
0
}
566
567
/*
568
 * xmlSerializeDecCharRef:
569
 * @buf:  a char buffer
570
 * @val:  a codepoint
571
 *
572
 * Serializes a decimal char ref like &#38;
573
 *
574
 * Writes at most 10 bytes. Does not include a terminating zero byte.
575
 *
576
 * Returns the number of bytes written.
577
 */
578
int
579
0
xmlSerializeDecCharRef(char *buf, int val) {
580
0
    char *out = buf;
581
0
    int len, i;
582
583
0
    *out++ = '&';
584
0
    *out++ = '#';
585
586
0
    if (val < 100) {
587
0
        len = (val < 10) ? 1 : 2;
588
0
    } else if (val < 10000) {
589
0
        len = (val < 1000) ? 3 : 4;
590
0
    } else if (val < 1000000) {
591
0
        len = (val < 100000) ? 5 : 6;
592
0
    } else {
593
0
        len = 7;
594
0
    }
595
596
0
    for (i = len - 1; i >= 0; i--) {
597
0
        out[i] = '0' + val % 10;
598
0
        val /= 10;
599
0
    }
600
601
0
    out[len] = ';';
602
603
0
    return(len + 3);
604
0
}
605
606
static const char xmlEscapeSafe[128] = {
607
    0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
608
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
609
    1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
610
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
611
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
612
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
613
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
614
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
615
};
616
617
/*
618
 * xmlEscapeText:
619
 * @text:  input text
620
 * @flags:  XML_ESCAPE flags
621
 *
622
 * Escapes certain characters with char refs.
623
 *
624
 * XML_ESCAPE_ATTR: for attribute content.
625
 * XML_ESCAPE_NON_ASCII: escape non-ASCII chars.
626
 * XML_ESCAPE_HTML: for HTML content.
627
 * XML_ESCAPE_QUOT: escape double quotes.
628
 * XML_ESCAPE_ALLOW_INVALID: allow invalid characters.
629
 *
630
 * Returns an escaped string or NULL if a memory allocation failed.
631
 */
632
xmlChar *
633
0
xmlEscapeText(const xmlChar *text, int flags) {
634
0
    const xmlChar *cur;
635
0
    xmlChar *buffer;
636
0
    xmlChar *out;
637
0
    const xmlChar *unescaped;
638
0
    size_t size = 50;
639
640
0
    buffer = xmlMalloc(size + 1);
641
0
    if (buffer == NULL)
642
0
        return(NULL);
643
0
    out = buffer;
644
645
0
    cur = text;
646
0
    unescaped = cur;
647
648
0
    while (*cur != '\0') {
649
0
        char buf[12];
650
0
  const xmlChar *end;
651
0
        const xmlChar *repl;
652
0
        size_t used;
653
0
        size_t replSize;
654
0
        size_t unescapedSize;
655
0
        size_t totalSize;
656
0
        int chunkSize = 1;
657
0
        int c;
658
659
        /* accelerator */
660
0
  while (1) {
661
0
            c = *cur;
662
663
0
            if (c < 0x80) {
664
0
                if (!xmlEscapeSafe[*cur])
665
0
                    break;
666
0
            } else {
667
0
               if (flags & XML_ESCAPE_NON_ASCII)
668
0
                   break;
669
0
            }
670
0
            cur += 1;
671
0
        }
672
673
0
        if (c == 0) {
674
0
            chunkSize = 0;
675
0
            repl = BAD_CAST "";
676
0
            replSize = 0;
677
0
        } else if (c == '<') {
678
      /*
679
       * Special handling of server side include in HTML attributes
680
       */
681
0
      if ((flags & XML_ESCAPE_HTML) && (flags & XML_ESCAPE_ATTR) &&
682
0
          (cur[1] == '!') && (cur[2] == '-') && (cur[3] == '-') &&
683
0
          ((end = xmlStrstr(cur, BAD_CAST "-->")) != NULL)) {
684
0
                chunkSize = (end - cur) + 3;
685
0
                repl = cur;
686
0
                replSize = chunkSize;
687
0
      } else {
688
0
                repl = BAD_CAST "&lt;";
689
0
                replSize = 4;
690
0
            }
691
0
  } else if (c == '>') {
692
0
            repl = BAD_CAST "&gt;";
693
0
            replSize = 4;
694
0
  } else if (c == '&') {
695
      /*
696
       * Special handling of &{...} construct from HTML 4, see
697
       * http://www.w3.org/TR/html401/appendix/notes.html#h-B.7.1
698
       */
699
0
      if ((flags & XML_ESCAPE_HTML) && (flags & XML_ESCAPE_ATTR) &&
700
0
                (cur[1] == '{') && (end = xmlStrchr(cur, '}'))) {
701
0
                chunkSize = (end - cur) + 1;
702
0
                repl = cur;
703
0
                replSize = chunkSize;
704
0
      } else {
705
0
                repl = BAD_CAST "&amp;";
706
0
                replSize = 5;
707
0
            }
708
0
  } else if ((flags & XML_ESCAPE_QUOT) && (c == '"')) {
709
0
            repl = BAD_CAST "&quot;";
710
0
            replSize = 6;
711
0
  } else if (((flags & XML_ESCAPE_HTML) == 0) && (c == '\r')) {
712
0
      repl = BAD_CAST "&#13;";
713
0
            replSize = 5;
714
0
  } else if ((flags & XML_ESCAPE_NON_ASCII) && (c >= 0x80)) {
715
0
            int val;
716
717
0
            chunkSize = 4;
718
0
            val = xmlGetUTF8Char(cur, &chunkSize);
719
0
            if (val < 0) {
720
0
                val = 0xFFFD;
721
0
                chunkSize = 1;
722
0
            } else if (((flags & XML_ESCAPE_ALLOW_INVALID) == 0) &&
723
0
                       (!IS_CHAR(val))) {
724
0
                val = 0xFFFD;
725
0
            }
726
727
0
            replSize = xmlSerializeHexCharRef(buf, val);
728
0
            repl = BAD_CAST buf;
729
0
  } else if ((flags & (XML_ESCAPE_ALLOW_INVALID | XML_ESCAPE_HTML)) ||
730
0
                   (c >= 0x20) ||
731
0
             (c == '\n') || (c == '\t') || (c == '\r')) {
732
      /* default case, just copy */
733
0
            cur += 1;
734
0
            if (*cur != 0)
735
0
                continue;
736
737
0
            chunkSize = 0;
738
0
            repl = BAD_CAST "";
739
0
            replSize = 0;
740
0
  } else {
741
            /* ignore */
742
0
            repl = BAD_CAST "";
743
0
            replSize = 0;
744
0
        }
745
746
0
        used = out - buffer;
747
0
        unescapedSize = cur - unescaped;
748
0
        totalSize = unescapedSize + replSize;
749
750
0
  cur += chunkSize;
751
752
0
        if (totalSize > size - used) {
753
0
            xmlChar *tmp;
754
0
            int newSize;
755
756
0
            if ((size > (SIZE_MAX - 1) / 2) ||
757
0
                (totalSize > (SIZE_MAX - 1) / 2 - size)) {
758
0
                xmlFree(buffer);
759
0
                return(NULL);
760
0
            }
761
0
            newSize = size + totalSize;
762
0
            if (*cur != 0)
763
0
                newSize *= 2;
764
0
            tmp = xmlRealloc(buffer, newSize + 1);
765
0
            if (tmp == NULL) {
766
0
                xmlFree(buffer);
767
0
                return(NULL);
768
0
            }
769
0
            buffer = tmp;
770
0
            size = newSize;
771
0
            out = buffer + used;
772
0
        }
773
774
0
        memcpy(out, unescaped, unescapedSize);
775
0
        out += unescapedSize;
776
0
        memcpy(out, repl, replSize);
777
0
        out += replSize;
778
779
0
        unescaped = cur;
780
0
    }
781
782
0
    *out = 0;
783
0
    return(buffer);
784
0
}
785
786
/**
787
 * xmlEncodeEntitiesInternal:
788
 * @doc:  the document containing the string
789
 * @input:  A string to convert to XML.
790
 * @flags:  XML_ESCAPE flags
791
 *
792
 * Do a global encoding of a string, replacing the predefined entities
793
 * and non ASCII values with their entities and CharRef counterparts.
794
 * Contrary to xmlEncodeEntities, this routine is reentrant, and result
795
 * must be deallocated.
796
 *
797
 * Returns A newly allocated string with the substitution done.
798
 */
799
xmlChar *
800
xmlEncodeEntitiesInternal(xmlDocPtr doc, const xmlChar *input,
801
0
                          unsigned flags) {
802
0
    if (input == NULL)
803
0
        return(NULL);
804
805
0
    if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE))
806
0
        flags |= XML_ESCAPE_HTML;
807
0
    else if ((doc == NULL) || (doc->encoding == NULL))
808
0
        flags |= XML_ESCAPE_NON_ASCII;
809
810
0
    return(xmlEscapeText(input, flags));
811
0
}
812
813
/**
814
 * xmlEncodeEntitiesReentrant:
815
 * @doc:  the document containing the string
816
 * @input:  A string to convert to XML.
817
 *
818
 * Do a global encoding of a string, replacing the predefined entities
819
 * and non ASCII values with their entities and CharRef counterparts.
820
 * Contrary to xmlEncodeEntities, this routine is reentrant, and result
821
 * must be deallocated.
822
 *
823
 * This escapes '<', '>', '&' and '\r'. If the document has no encoding,
824
 * non-ASCII codepoints are escaped. There is some special handling for
825
 * HTML documents.
826
 *
827
 * Returns A newly allocated string with the substitution done.
828
 */
829
xmlChar *
830
0
xmlEncodeEntitiesReentrant(xmlDocPtr doc, const xmlChar *input) {
831
0
    return xmlEncodeEntitiesInternal(doc, input, 0);
832
0
}
833
834
/**
835
 * xmlEncodeSpecialChars:
836
 * @doc:  unused
837
 * @input:  A string to convert to XML.
838
 *
839
 * Do a global encoding of a string, replacing the predefined entities
840
 * this routine is reentrant, and result must be deallocated.
841
 *
842
 * This escapes '<', '>', '&', '"' and '\r' chars.
843
 *
844
 * Returns A newly allocated string with the substitution done.
845
 */
846
xmlChar *
847
xmlEncodeSpecialChars(const xmlDoc *doc ATTRIBUTE_UNUSED,
848
0
                      const xmlChar *input) {
849
0
    if (input == NULL)
850
0
        return(NULL);
851
852
0
    return(xmlEscapeText(input, XML_ESCAPE_QUOT | XML_ESCAPE_ALLOW_INVALID));
853
0
}
854
855
/**
856
 * xmlCreateEntitiesTable:
857
 *
858
 * create and initialize an empty entities hash table.
859
 * This really doesn't make sense and should be deprecated
860
 *
861
 * Returns the xmlEntitiesTablePtr just created or NULL in case of error.
862
 */
863
xmlEntitiesTablePtr
864
0
xmlCreateEntitiesTable(void) {
865
0
    return((xmlEntitiesTablePtr) xmlHashCreate(0));
866
0
}
867
868
/**
869
 * xmlFreeEntityWrapper:
870
 * @entity:  An entity
871
 * @name:  its name
872
 *
873
 * Deallocate the memory used by an entities in the hash table.
874
 */
875
static void
876
423
xmlFreeEntityWrapper(void *entity, const xmlChar *name ATTRIBUTE_UNUSED) {
877
423
    if (entity != NULL)
878
423
  xmlFreeEntity((xmlEntityPtr) entity);
879
423
}
880
881
/**
882
 * xmlFreeEntitiesTable:
883
 * @table:  An entity table
884
 *
885
 * Deallocate the memory used by an entities hash table.
886
 */
887
void
888
258
xmlFreeEntitiesTable(xmlEntitiesTablePtr table) {
889
258
    xmlHashFree(table, xmlFreeEntityWrapper);
890
258
}
891
892
/**
893
 * xmlCopyEntity:
894
 * @ent:  An entity
895
 *
896
 * Build a copy of an entity
897
 *
898
 * Returns the new xmlEntitiesPtr or NULL in case of error.
899
 */
900
static void *
901
0
xmlCopyEntity(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
902
0
    xmlEntityPtr ent = (xmlEntityPtr) payload;
903
0
    xmlEntityPtr cur;
904
905
0
    cur = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
906
0
    if (cur == NULL)
907
0
  return(NULL);
908
0
    memset(cur, 0, sizeof(xmlEntity));
909
0
    cur->type = XML_ENTITY_DECL;
910
911
0
    cur->etype = ent->etype;
912
0
    if (ent->name != NULL) {
913
0
  cur->name = xmlStrdup(ent->name);
914
0
        if (cur->name == NULL)
915
0
            goto error;
916
0
    }
917
0
    if (ent->ExternalID != NULL) {
918
0
  cur->ExternalID = xmlStrdup(ent->ExternalID);
919
0
        if (cur->ExternalID == NULL)
920
0
            goto error;
921
0
    }
922
0
    if (ent->SystemID != NULL) {
923
0
  cur->SystemID = xmlStrdup(ent->SystemID);
924
0
        if (cur->SystemID == NULL)
925
0
            goto error;
926
0
    }
927
0
    if (ent->content != NULL) {
928
0
  cur->content = xmlStrdup(ent->content);
929
0
        if (cur->content == NULL)
930
0
            goto error;
931
0
    }
932
0
    if (ent->orig != NULL) {
933
0
  cur->orig = xmlStrdup(ent->orig);
934
0
        if (cur->orig == NULL)
935
0
            goto error;
936
0
    }
937
0
    if (ent->URI != NULL) {
938
0
  cur->URI = xmlStrdup(ent->URI);
939
0
        if (cur->URI == NULL)
940
0
            goto error;
941
0
    }
942
0
    return(cur);
943
944
0
error:
945
0
    xmlFreeEntity(cur);
946
0
    return(NULL);
947
0
}
948
949
/**
950
 * xmlCopyEntitiesTable:
951
 * @table:  An entity table
952
 *
953
 * Build a copy of an entity table.
954
 *
955
 * Returns the new xmlEntitiesTablePtr or NULL in case of error.
956
 */
957
xmlEntitiesTablePtr
958
0
xmlCopyEntitiesTable(xmlEntitiesTablePtr table) {
959
0
    return(xmlHashCopySafe(table, xmlCopyEntity, xmlFreeEntityWrapper));
960
0
}
961
962
#ifdef LIBXML_OUTPUT_ENABLED
963
964
/**
965
 * xmlDumpEntityDecl:
966
 * @buf:  An XML buffer.
967
 * @ent:  An entity table
968
 *
969
 * This will dump the content of the entity table as an XML DTD definition
970
 */
971
void
972
0
xmlDumpEntityDecl(xmlBufferPtr buf, xmlEntityPtr ent) {
973
0
    xmlSaveCtxtPtr save;
974
975
0
    if ((buf == NULL) || (ent == NULL))
976
0
        return;
977
978
0
    save = xmlSaveToBuffer(buf, NULL, 0);
979
0
    xmlSaveTree(save, (xmlNodePtr) ent);
980
0
    if (xmlSaveFinish(save) != XML_ERR_OK)
981
0
        xmlFree(xmlBufferDetach(buf));
982
0
}
983
984
/**
985
 * xmlDumpEntityDeclScan:
986
 * @ent:  An entity table
987
 * @buf:  An XML buffer.
988
 *
989
 * When using the hash table scan function, arguments need to be reversed
990
 */
991
static void
992
xmlDumpEntityDeclScan(void *ent, void *save,
993
0
                      const xmlChar *name ATTRIBUTE_UNUSED) {
994
0
    xmlSaveTree(save, ent);
995
0
}
996
997
/**
998
 * xmlDumpEntitiesTable:
999
 * @buf:  An XML buffer.
1000
 * @table:  An entity table
1001
 *
1002
 * This will dump the content of the entity table as an XML DTD definition
1003
 */
1004
void
1005
0
xmlDumpEntitiesTable(xmlBufferPtr buf, xmlEntitiesTablePtr table) {
1006
0
    xmlSaveCtxtPtr save;
1007
1008
0
    if ((buf == NULL) || (table == NULL))
1009
0
        return;
1010
1011
0
    save = xmlSaveToBuffer(buf, NULL, 0);
1012
0
    xmlHashScan(table, xmlDumpEntityDeclScan, save);
1013
0
    if (xmlSaveFinish(save) != XML_ERR_OK)
1014
0
        xmlFree(xmlBufferDetach(buf));
1015
0
}
1016
#endif /* LIBXML_OUTPUT_ENABLED */