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