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