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