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