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