source: trunk/third/libxml2/entities.c @ 19097

Revision 19097, 26.2 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19096, which included commits to RCS files with non-trunk default branches.
Line 
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
27struct xmlPredefinedEntityValue {
28    const char *name;
29    const char *value;
30};
31static 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 */
43static xmlHashTablePtr xmlPredefinedEntities = NULL;
44
45/*
46 * xmlFreeEntity : clean-up an entity record.
47 */
48static 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 */
72static xmlEntityPtr
73xmlAddEntity(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 */
147void 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 */
177void 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 */
192xmlEntityPtr
193xmlGetPredefinedEntity(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 */
212xmlEntityPtr
213xmlAddDtdEntity(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 */
261xmlEntityPtr
262xmlAddDocEntity(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 */
308static xmlEntityPtr
309xmlGetEntityFromTable(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 */
323xmlEntityPtr
324xmlGetParameterEntity(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 */
354xmlEntityPtr
355xmlGetDtdEntity(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 */
378xmlEntityPtr
379xmlGetDocEntity(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 */
418static int static_buffer_size = 0;
419static xmlChar *static_buffer = NULL;
420
421static 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 */
449const xmlChar *
450xmlEncodeEntities(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(*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 */
585xmlChar *
586xmlEncodeEntitiesReentrant(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        } else if (*cur == '"') {
636            *out++ = '&';
637            *out++ = 'q';
638            *out++ = 'u';
639            *out++ = 'o';
640            *out++ = 't';
641            *out++ = ';';
642#if 0
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(*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 */
768xmlChar *
769xmlEncodeSpecialChars(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 {
826            /*
827             * Works because on UTF-8, all extended sequences cannot
828             * result in bytes in the ASCII range.
829             */
830            *out++ = *cur;
831        }
832        cur++;
833    }
834    *out++ = 0;
835    return(buffer);
836}
837
838/**
839 * xmlCreateEntitiesTable:
840 *
841 * create and initialize an empty entities hash table.
842 *
843 * Returns the xmlEntitiesTablePtr just created or NULL in case of error.
844 */
845xmlEntitiesTablePtr
846xmlCreateEntitiesTable(void) {
847    return((xmlEntitiesTablePtr) xmlHashCreate(0));
848}
849
850/**
851 * xmlFreeEntityWrapper:
852 * @entity:  An entity
853 * @name:  its name
854 *
855 * Deallocate the memory used by an entities in the hash table.
856 */
857static void
858xmlFreeEntityWrapper(xmlEntityPtr entity,
859                       const xmlChar *name ATTRIBUTE_UNUSED) {
860    if (entity != NULL)
861        xmlFreeEntity(entity);
862}
863
864/**
865 * xmlFreeEntitiesTable:
866 * @table:  An entity table
867 *
868 * Deallocate the memory used by an entities hash table.
869 */
870void
871xmlFreeEntitiesTable(xmlEntitiesTablePtr table) {
872    xmlHashFree(table, (xmlHashDeallocator) xmlFreeEntityWrapper);
873}
874
875/**
876 * xmlCopyEntity:
877 * @ent:  An entity
878 *
879 * Build a copy of an entity
880 *
881 * Returns the new xmlEntitiesPtr or NULL in case of error.
882 */
883static xmlEntityPtr
884xmlCopyEntity(xmlEntityPtr ent) {
885    xmlEntityPtr cur;
886
887    cur = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
888    if (cur == NULL) {
889        xmlGenericError(xmlGenericErrorContext,
890                "xmlCopyEntity: out of memory !\n");
891        return(NULL);
892    }
893    memset(cur, 0, sizeof(xmlEntity));
894    cur->type = XML_ENTITY_DECL;
895
896    cur->etype = ent->etype;
897    if (ent->name != NULL)
898        cur->name = xmlStrdup(ent->name);
899    if (ent->ExternalID != NULL)
900        cur->ExternalID = xmlStrdup(ent->ExternalID);
901    if (ent->SystemID != NULL)
902        cur->SystemID = xmlStrdup(ent->SystemID);
903    if (ent->content != NULL)
904        cur->content = xmlStrdup(ent->content);
905    if (ent->orig != NULL)
906        cur->orig = xmlStrdup(ent->orig);
907    if (ent->URI != NULL)
908        cur->URI = xmlStrdup(ent->URI);
909    return(cur);
910}
911
912/**
913 * xmlCopyEntitiesTable:
914 * @table:  An entity table
915 *
916 * Build a copy of an entity table.
917 *
918 * Returns the new xmlEntitiesTablePtr or NULL in case of error.
919 */
920xmlEntitiesTablePtr
921xmlCopyEntitiesTable(xmlEntitiesTablePtr table) {
922    return(xmlHashCopy(table, (xmlHashCopier) xmlCopyEntity));
923}
924
925/**
926 * xmlDumpEntityDecl:
927 * @buf:  An XML buffer.
928 * @ent:  An entity table
929 *
930 * This will dump the content of the entity table as an XML DTD definition
931 */
932void
933xmlDumpEntityDecl(xmlBufferPtr buf, xmlEntityPtr ent) {
934    switch (ent->etype) {
935        case XML_INTERNAL_GENERAL_ENTITY:
936            xmlBufferWriteChar(buf, "<!ENTITY ");
937            xmlBufferWriteCHAR(buf, ent->name);
938            xmlBufferWriteChar(buf, " ");
939            if (ent->orig != NULL)
940                xmlBufferWriteQuotedString(buf, ent->orig);
941            else
942                xmlBufferWriteQuotedString(buf, ent->content);
943            xmlBufferWriteChar(buf, ">\n");
944            break;
945        case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
946            xmlBufferWriteChar(buf, "<!ENTITY ");
947            xmlBufferWriteCHAR(buf, ent->name);
948            if (ent->ExternalID != NULL) {
949                 xmlBufferWriteChar(buf, " PUBLIC ");
950                 xmlBufferWriteQuotedString(buf, ent->ExternalID);
951                 xmlBufferWriteChar(buf, " ");
952                 xmlBufferWriteQuotedString(buf, ent->SystemID);
953            } else {
954                 xmlBufferWriteChar(buf, " SYSTEM ");
955                 xmlBufferWriteQuotedString(buf, ent->SystemID);
956            }
957            xmlBufferWriteChar(buf, ">\n");
958            break;
959        case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
960            xmlBufferWriteChar(buf, "<!ENTITY ");
961            xmlBufferWriteCHAR(buf, ent->name);
962            if (ent->ExternalID != NULL) {
963                 xmlBufferWriteChar(buf, " PUBLIC ");
964                 xmlBufferWriteQuotedString(buf, ent->ExternalID);
965                 xmlBufferWriteChar(buf, " ");
966                 xmlBufferWriteQuotedString(buf, ent->SystemID);
967            } else {
968                 xmlBufferWriteChar(buf, " SYSTEM ");
969                 xmlBufferWriteQuotedString(buf, ent->SystemID);
970            }
971            if (ent->content != NULL) { /* Should be true ! */
972                xmlBufferWriteChar(buf, " NDATA ");
973                if (ent->orig != NULL)
974                    xmlBufferWriteCHAR(buf, ent->orig);
975                else
976                    xmlBufferWriteCHAR(buf, ent->content);
977            }
978            xmlBufferWriteChar(buf, ">\n");
979            break;
980        case XML_INTERNAL_PARAMETER_ENTITY:
981            xmlBufferWriteChar(buf, "<!ENTITY % ");
982            xmlBufferWriteCHAR(buf, ent->name);
983            xmlBufferWriteChar(buf, " ");
984            if (ent->orig == NULL)
985                xmlBufferWriteQuotedString(buf, ent->content);
986            else
987                xmlBufferWriteQuotedString(buf, ent->orig);
988            xmlBufferWriteChar(buf, ">\n");
989            break;
990        case XML_EXTERNAL_PARAMETER_ENTITY:
991            xmlBufferWriteChar(buf, "<!ENTITY % ");
992            xmlBufferWriteCHAR(buf, ent->name);
993            if (ent->ExternalID != NULL) {
994                 xmlBufferWriteChar(buf, " PUBLIC ");
995                 xmlBufferWriteQuotedString(buf, ent->ExternalID);
996                 xmlBufferWriteChar(buf, " ");
997                 xmlBufferWriteQuotedString(buf, ent->SystemID);
998            } else {
999                 xmlBufferWriteChar(buf, " SYSTEM ");
1000                 xmlBufferWriteQuotedString(buf, ent->SystemID);
1001            }
1002            xmlBufferWriteChar(buf, ">\n");
1003            break;
1004        default:
1005            xmlGenericError(xmlGenericErrorContext,
1006                "xmlDumpEntitiesDecl: internal: unknown type %d\n",
1007                    ent->etype);
1008    }
1009}
1010
1011/**
1012 * xmlDumpEntitiesTable:
1013 * @buf:  An XML buffer.
1014 * @table:  An entity table
1015 *
1016 * This will dump the content of the entity table as an XML DTD definition
1017 */
1018void
1019xmlDumpEntitiesTable(xmlBufferPtr buf, xmlEntitiesTablePtr table) {
1020    xmlHashScan(table, (xmlHashScanner)xmlDumpEntityDecl, buf);
1021}
Note: See TracBrowser for help on using the repository browser.