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