Coverage Report

Created: 2024-09-06 07:53

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