• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
xmlEntitiesErrMemory(const char * extra)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
xmlEntitiesErr(xmlParserErrors code,const char * msg)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
xmlFreeEntity(xmlEntityPtr entity)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
xmlCreateEntity(xmlDictPtr dict,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)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
xmlAddEntity(xmlDtdPtr dtd,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)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
xmlGetPredefinedEntity(const xmlChar * name)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
xmlAddDtdEntity(xmlDocPtr doc,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)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
xmlAddDocEntity(xmlDocPtr doc,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)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
xmlNewEntity(xmlDocPtr doc,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)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
xmlGetEntityFromTable(xmlEntitiesTablePtr table,const xmlChar * name)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
xmlGetParameterEntity(xmlDocPtr doc,const xmlChar * name)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
xmlGetDtdEntity(xmlDocPtr doc,const xmlChar * name)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
xmlGetDocEntity(xmlDocPtr doc,const xmlChar * name)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 *
xmlEncodeEntitiesReentrant(xmlDocPtr doc,const xmlChar * input)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 *
xmlEncodeSpecialChars(xmlDocPtr doc ATTRIBUTE_UNUSED,const xmlChar * input)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
xmlCreateEntitiesTable(void)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
xmlFreeEntityWrapper(xmlEntityPtr entity,const xmlChar * name ATTRIBUTE_UNUSED)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
xmlFreeEntitiesTable(xmlEntitiesTablePtr table)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
xmlCopyEntity(xmlEntityPtr ent)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
xmlCopyEntitiesTable(xmlEntitiesTablePtr table)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
xmlDumpEntityContent(xmlBufferPtr buf,const xmlChar * content)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
xmlDumpEntityDecl(xmlBufferPtr buf,xmlEntityPtr ent)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
xmlDumpEntityDeclScan(xmlEntityPtr ent,xmlBufferPtr buf)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
xmlDumpEntitiesTable(xmlBufferPtr buf,xmlEntitiesTablePtr table)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"
1023