blob: 26848326cf549c2937cec5b22e1050a311c6eed3 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002 * entities.c : implementation for the XML entities handling
Owen Taylor3473f882001-02-23 17:55:21 +00003 *
4 * See Copyright for the status of this software.
5 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00006 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00007 */
8
Daniel Veillard34ce8be2002-03-18 19:37:11 +00009#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000010#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000011
Owen Taylor3473f882001-02-23 17:55:21 +000012#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>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000021#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000022
Owen Taylor3473f882001-02-23 17:55:21 +000023/*
24 * The XML predefined entities.
25 */
26
Daniel Veillardd3a2e4c2003-09-30 13:38:04 +000027static xmlEntity xmlEntityLt = {
28 NULL, XML_ENTITY_DECL, BAD_CAST "lt",
29 NULL, NULL, NULL, NULL, NULL, NULL,
30 BAD_CAST "<", BAD_CAST "<", 1,
31 XML_INTERNAL_PREDEFINED_ENTITY,
32 NULL, NULL, NULL, NULL, 0
Owen Taylor3473f882001-02-23 17:55:21 +000033};
Daniel Veillardd3a2e4c2003-09-30 13:38:04 +000034static xmlEntity xmlEntityGt = {
35 NULL, XML_ENTITY_DECL, BAD_CAST "gt",
36 NULL, NULL, NULL, NULL, NULL, NULL,
37 BAD_CAST ">", BAD_CAST ">", 1,
38 XML_INTERNAL_PREDEFINED_ENTITY,
39 NULL, NULL, NULL, NULL, 0
Owen Taylor3473f882001-02-23 17:55:21 +000040};
Daniel Veillardd3a2e4c2003-09-30 13:38:04 +000041static xmlEntity xmlEntityAmp = {
42 NULL, XML_ENTITY_DECL, BAD_CAST "amp",
43 NULL, NULL, NULL, NULL, NULL, NULL,
44 BAD_CAST "&", BAD_CAST "&", 1,
45 XML_INTERNAL_PREDEFINED_ENTITY,
46 NULL, NULL, NULL, NULL, 0
47};
48static xmlEntity xmlEntityQuot = {
49 NULL, XML_ENTITY_DECL, BAD_CAST "quot",
50 NULL, NULL, NULL, NULL, NULL, NULL,
51 BAD_CAST "\"", BAD_CAST "\"", 1,
52 XML_INTERNAL_PREDEFINED_ENTITY,
53 NULL, NULL, NULL, NULL, 0
54};
55static xmlEntity xmlEntityApos = {
56 NULL, XML_ENTITY_DECL, BAD_CAST "apos",
57 NULL, NULL, NULL, NULL, NULL, NULL,
58 BAD_CAST "'", BAD_CAST "'", 1,
59 XML_INTERNAL_PREDEFINED_ENTITY,
60 NULL, NULL, NULL, NULL, 0
61};
Owen Taylor3473f882001-02-23 17:55:21 +000062
63/*
64 * xmlFreeEntity : clean-up an entity record.
65 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +000066static void xmlFreeEntity(xmlEntityPtr entity) {
Owen Taylor3473f882001-02-23 17:55:21 +000067 if (entity == NULL) return;
68
Daniel Veillard2d84a892002-12-30 00:01:08 +000069 if ((entity->children) && (entity->owner == 1) &&
Daniel Veillard22090732001-07-16 00:06:07 +000070 (entity == (xmlEntityPtr) entity->children->parent))
Owen Taylor3473f882001-02-23 17:55:21 +000071 xmlFreeNodeList(entity->children);
72 if (entity->name != NULL)
73 xmlFree((char *) entity->name);
74 if (entity->ExternalID != NULL)
75 xmlFree((char *) entity->ExternalID);
76 if (entity->SystemID != NULL)
77 xmlFree((char *) entity->SystemID);
78 if (entity->URI != NULL)
79 xmlFree((char *) entity->URI);
80 if (entity->content != NULL)
81 xmlFree((char *) entity->content);
82 if (entity->orig != NULL)
83 xmlFree((char *) entity->orig);
Owen Taylor3473f882001-02-23 17:55:21 +000084 xmlFree(entity);
85}
86
87/*
88 * xmlAddEntity : register a new entity for an entities table.
89 */
90static xmlEntityPtr
91xmlAddEntity(xmlDtdPtr dtd, const xmlChar *name, int type,
92 const xmlChar *ExternalID, const xmlChar *SystemID,
93 const xmlChar *content) {
94 xmlEntitiesTablePtr table = NULL;
95 xmlEntityPtr ret;
96
97 if (name == NULL)
98 return(NULL);
99 switch (type) {
100 case XML_INTERNAL_GENERAL_ENTITY:
101 case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
102 case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
103 if (dtd->entities == NULL)
104 dtd->entities = xmlHashCreate(0);
105 table = dtd->entities;
106 break;
107 case XML_INTERNAL_PARAMETER_ENTITY:
108 case XML_EXTERNAL_PARAMETER_ENTITY:
109 if (dtd->pentities == NULL)
110 dtd->pentities = xmlHashCreate(0);
111 table = dtd->pentities;
112 break;
113 case XML_INTERNAL_PREDEFINED_ENTITY:
Daniel Veillardd3a2e4c2003-09-30 13:38:04 +0000114 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000115 }
116 if (table == NULL)
117 return(NULL);
118 ret = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
119 if (ret == NULL) {
120 xmlGenericError(xmlGenericErrorContext,
121 "xmlAddEntity: out of memory\n");
122 return(NULL);
123 }
124 memset(ret, 0, sizeof(xmlEntity));
125 ret->type = XML_ENTITY_DECL;
126
127 /*
128 * fill the structure.
129 */
130 ret->name = xmlStrdup(name);
131 ret->etype = (xmlEntityType) type;
132 if (ExternalID != NULL)
133 ret->ExternalID = xmlStrdup(ExternalID);
134 if (SystemID != NULL)
135 ret->SystemID = xmlStrdup(SystemID);
136 if (content != NULL) {
137 ret->length = xmlStrlen(content);
138 ret->content = xmlStrndup(content, ret->length);
139 } else {
140 ret->length = 0;
141 ret->content = NULL;
142 }
143 ret->URI = NULL; /* to be computed by the layer knowing
144 the defining entity */
145 ret->orig = NULL;
Daniel Veillard2d84a892002-12-30 00:01:08 +0000146 ret->owner = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000147
148 if (xmlHashAddEntry(table, name, ret)) {
149 /*
150 * entity was already defined at another level.
151 */
152 xmlFreeEntity(ret);
153 return(NULL);
154 }
155 return(ret);
156}
157
158/**
Owen Taylor3473f882001-02-23 17:55:21 +0000159 * xmlGetPredefinedEntity:
160 * @name: the entity name
161 *
162 * Check whether this name is an predefined entity.
163 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000164 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000165 */
166xmlEntityPtr
167xmlGetPredefinedEntity(const xmlChar *name) {
Daniel Veillardd3a2e4c2003-09-30 13:38:04 +0000168 if (name == NULL) return(NULL);
169 switch (name[0]) {
170 case 'l':
171 if (xmlStrEqual(name, BAD_CAST "lt"))
172 return(&xmlEntityLt);
173 break;
174 case 'g':
175 if (xmlStrEqual(name, BAD_CAST "gt"))
176 return(&xmlEntityGt);
177 break;
178 case 'a':
179 if (xmlStrEqual(name, BAD_CAST "amp"))
180 return(&xmlEntityAmp);
181 if (xmlStrEqual(name, BAD_CAST "apos"))
182 return(&xmlEntityApos);
183 break;
184 case 'q':
185 if (xmlStrEqual(name, BAD_CAST "quot"))
186 return(&xmlEntityQuot);
187 break;
188 default:
189 break;
190 }
191 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000192}
193
194/**
195 * xmlAddDtdEntity:
196 * @doc: the document
197 * @name: the entity name
198 * @type: the entity type XML_xxx_yyy_ENTITY
199 * @ExternalID: the entity external ID if available
200 * @SystemID: the entity system ID if available
201 * @content: the entity content
202 *
203 * Register a new entity for this document DTD external subset.
204 *
205 * Returns a pointer to the entity or NULL in case of error
206 */
207xmlEntityPtr
208xmlAddDtdEntity(xmlDocPtr doc, const xmlChar *name, int type,
209 const xmlChar *ExternalID, const xmlChar *SystemID,
210 const xmlChar *content) {
211 xmlEntityPtr ret;
212 xmlDtdPtr dtd;
213
214 if (doc == NULL) {
215 xmlGenericError(xmlGenericErrorContext,
216 "xmlAddDtdEntity: doc == NULL !\n");
217 return(NULL);
218 }
219 if (doc->extSubset == NULL) {
220 xmlGenericError(xmlGenericErrorContext,
221 "xmlAddDtdEntity: document without external subset !\n");
222 return(NULL);
223 }
224 dtd = doc->extSubset;
225 ret = xmlAddEntity(dtd, name, type, ExternalID, SystemID, content);
226 if (ret == NULL) return(NULL);
227
228 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000229 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +0000230 */
231 ret->parent = dtd;
232 ret->doc = dtd->doc;
233 if (dtd->last == NULL) {
234 dtd->children = dtd->last = (xmlNodePtr) ret;
235 } else {
236 dtd->last->next = (xmlNodePtr) ret;
237 ret->prev = dtd->last;
238 dtd->last = (xmlNodePtr) ret;
239 }
240 return(ret);
241}
242
243/**
244 * xmlAddDocEntity:
245 * @doc: the document
246 * @name: the entity name
247 * @type: the entity type XML_xxx_yyy_ENTITY
248 * @ExternalID: the entity external ID if available
249 * @SystemID: the entity system ID if available
250 * @content: the entity content
251 *
252 * Register a new entity for this document.
253 *
254 * Returns a pointer to the entity or NULL in case of error
255 */
256xmlEntityPtr
257xmlAddDocEntity(xmlDocPtr doc, const xmlChar *name, int type,
258 const xmlChar *ExternalID, const xmlChar *SystemID,
259 const xmlChar *content) {
260 xmlEntityPtr ret;
261 xmlDtdPtr dtd;
262
263 if (doc == NULL) {
264 xmlGenericError(xmlGenericErrorContext,
265 "xmlAddDocEntity: document is NULL !\n");
266 return(NULL);
267 }
268 if (doc->intSubset == NULL) {
269 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000270 "xmlAddDocEntity: document without internal subset !\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000271 return(NULL);
272 }
273 dtd = doc->intSubset;
274 ret = xmlAddEntity(dtd, name, type, ExternalID, SystemID, content);
275 if (ret == NULL) return(NULL);
276
277 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000278 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +0000279 */
280 ret->parent = dtd;
281 ret->doc = dtd->doc;
282 if (dtd->last == NULL) {
283 dtd->children = dtd->last = (xmlNodePtr) ret;
284 } else {
285 dtd->last->next = (xmlNodePtr) ret;
286 ret->prev = dtd->last;
287 dtd->last = (xmlNodePtr) ret;
288 }
289 return(ret);
290}
291
292/**
293 * xmlGetEntityFromTable:
294 * @table: an entity table
295 * @name: the entity name
296 * @parameter: look for parameter entities
297 *
298 * Do an entity lookup in the table.
299 * returns the corresponding parameter entity, if found.
300 *
301 * Returns A pointer to the entity structure or NULL if not found.
302 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000303static xmlEntityPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000304xmlGetEntityFromTable(xmlEntitiesTablePtr table, const xmlChar *name) {
305 return((xmlEntityPtr) xmlHashLookup(table, name));
306}
307
308/**
309 * xmlGetParameterEntity:
310 * @doc: the document referencing the entity
311 * @name: the entity name
312 *
313 * Do an entity lookup in the internal and external subsets and
314 * returns the corresponding parameter entity, if found.
315 *
316 * Returns A pointer to the entity structure or NULL if not found.
317 */
318xmlEntityPtr
319xmlGetParameterEntity(xmlDocPtr doc, const xmlChar *name) {
320 xmlEntitiesTablePtr table;
321 xmlEntityPtr ret;
322
Daniel Veillard36065812002-01-24 15:02:46 +0000323 if (doc == NULL)
324 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000325 if ((doc->intSubset != NULL) && (doc->intSubset->pentities != NULL)) {
326 table = (xmlEntitiesTablePtr) doc->intSubset->pentities;
327 ret = xmlGetEntityFromTable(table, name);
328 if (ret != NULL)
329 return(ret);
330 }
331 if ((doc->extSubset != NULL) && (doc->extSubset->pentities != NULL)) {
332 table = (xmlEntitiesTablePtr) doc->extSubset->pentities;
333 return(xmlGetEntityFromTable(table, name));
334 }
335 return(NULL);
336}
337
338/**
339 * xmlGetDtdEntity:
340 * @doc: the document referencing the entity
341 * @name: the entity name
342 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000343 * Do an entity lookup in the DTD entity hash table and
Owen Taylor3473f882001-02-23 17:55:21 +0000344 * returns the corresponding entity, if found.
Daniel Veillard36065812002-01-24 15:02:46 +0000345 * Note: the first argument is the document node, not the DTD node.
Owen Taylor3473f882001-02-23 17:55:21 +0000346 *
347 * Returns A pointer to the entity structure or NULL if not found.
348 */
349xmlEntityPtr
350xmlGetDtdEntity(xmlDocPtr doc, const xmlChar *name) {
351 xmlEntitiesTablePtr table;
352
Daniel Veillard36065812002-01-24 15:02:46 +0000353 if (doc == NULL)
354 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000355 if ((doc->extSubset != NULL) && (doc->extSubset->entities != NULL)) {
356 table = (xmlEntitiesTablePtr) doc->extSubset->entities;
357 return(xmlGetEntityFromTable(table, name));
358 }
359 return(NULL);
360}
361
362/**
363 * xmlGetDocEntity:
364 * @doc: the document referencing the entity
365 * @name: the entity name
366 *
367 * Do an entity lookup in the document entity hash table and
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000368 * returns the corresponding entity, otherwise a lookup is done
Owen Taylor3473f882001-02-23 17:55:21 +0000369 * in the predefined entities too.
370 *
371 * Returns A pointer to the entity structure or NULL if not found.
372 */
373xmlEntityPtr
374xmlGetDocEntity(xmlDocPtr doc, const xmlChar *name) {
375 xmlEntityPtr cur;
376 xmlEntitiesTablePtr table;
377
378 if (doc != NULL) {
379 if ((doc->intSubset != NULL) && (doc->intSubset->entities != NULL)) {
380 table = (xmlEntitiesTablePtr) doc->intSubset->entities;
381 cur = xmlGetEntityFromTable(table, name);
382 if (cur != NULL)
383 return(cur);
384 }
Daniel Veillard28757702002-02-18 11:19:30 +0000385 if (doc->standalone != 1) {
386 if ((doc->extSubset != NULL) &&
387 (doc->extSubset->entities != NULL)) {
388 table = (xmlEntitiesTablePtr) doc->extSubset->entities;
389 cur = xmlGetEntityFromTable(table, name);
390 if (cur != NULL)
391 return(cur);
392 }
Owen Taylor3473f882001-02-23 17:55:21 +0000393 }
394 }
Daniel Veillardd3a2e4c2003-09-30 13:38:04 +0000395 return(xmlGetPredefinedEntity(name));
Owen Taylor3473f882001-02-23 17:55:21 +0000396}
397
398/*
399 * [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
400 * | [#x10000-#x10FFFF]
401 * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
402 */
403#define IS_CHAR(c) \
404 (((c) == 0x09) || ((c) == 0x0a) || ((c) == 0x0d) || \
405 (((c) >= 0x20) && ((c) != 0xFFFE) && ((c) != 0xFFFF)))
406
407/*
Owen Taylor3473f882001-02-23 17:55:21 +0000408 * Macro used to grow the current buffer.
409 */
410#define growBufferReentrant() { \
411 buffer_size *= 2; \
412 buffer = (xmlChar *) \
413 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
414 if (buffer == NULL) { \
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000415 xmlGenericError(xmlGenericErrorContext, "realloc failed\n"); \
Owen Taylor3473f882001-02-23 17:55:21 +0000416 return(NULL); \
417 } \
418}
419
420
421/**
422 * xmlEncodeEntitiesReentrant:
423 * @doc: the document containing the string
424 * @input: A string to convert to XML.
425 *
426 * Do a global encoding of a string, replacing the predefined entities
427 * and non ASCII values with their entities and CharRef counterparts.
428 * Contrary to xmlEncodeEntities, this routine is reentrant, and result
429 * must be deallocated.
430 *
431 * Returns A newly allocated string with the substitution done.
432 */
433xmlChar *
434xmlEncodeEntitiesReentrant(xmlDocPtr doc, const xmlChar *input) {
435 const xmlChar *cur = input;
436 xmlChar *buffer = NULL;
437 xmlChar *out = NULL;
438 int buffer_size = 0;
439 int html = 0;
440
441 if (input == NULL) return(NULL);
442 if (doc != NULL)
443 html = (doc->type == XML_HTML_DOCUMENT_NODE);
444
445 /*
446 * allocate an translation buffer.
447 */
448 buffer_size = 1000;
449 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
450 if (buffer == NULL) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000451 xmlGenericError(xmlGenericErrorContext, "malloc failed\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000452 return(NULL);
453 }
454 out = buffer;
455
456 while (*cur != '\0') {
457 if (out - buffer > buffer_size - 100) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000458 int indx = out - buffer;
Owen Taylor3473f882001-02-23 17:55:21 +0000459
460 growBufferReentrant();
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000461 out = &buffer[indx];
Owen Taylor3473f882001-02-23 17:55:21 +0000462 }
463
464 /*
465 * By default one have to encode at least '<', '>', '"' and '&' !
466 */
467 if (*cur == '<') {
468 *out++ = '&';
469 *out++ = 'l';
470 *out++ = 't';
471 *out++ = ';';
472 } else if (*cur == '>') {
473 *out++ = '&';
474 *out++ = 'g';
475 *out++ = 't';
476 *out++ = ';';
477 } else if (*cur == '&') {
478 *out++ = '&';
479 *out++ = 'a';
480 *out++ = 'm';
481 *out++ = 'p';
482 *out++ = ';';
Owen Taylor3473f882001-02-23 17:55:21 +0000483 } else if (((*cur >= 0x20) && (*cur < 0x80)) ||
Daniel Veillard0046c0f2003-02-23 13:52:30 +0000484 (*cur == '\n') || (*cur == '\t') || ((html) && (*cur == '\r'))) {
Owen Taylor3473f882001-02-23 17:55:21 +0000485 /*
486 * default case, just copy !
487 */
488 *out++ = *cur;
489 } else if (*cur >= 0x80) {
Daniel Veillard122376b2001-04-24 12:12:30 +0000490 if (((doc != NULL) && (doc->encoding != NULL)) || (html)) {
Owen Taylor3473f882001-02-23 17:55:21 +0000491 /*
492 * Bjørn Reese <br@sseusa.com> provided the patch
493 xmlChar xc;
494 xc = (*cur & 0x3F) << 6;
495 if (cur[1] != 0) {
496 xc += *(++cur) & 0x3F;
497 *out++ = xc;
498 } else
499 */
500 *out++ = *cur;
501 } else {
502 /*
503 * We assume we have UTF-8 input.
504 */
Daniel Veillardb2517d82003-10-01 19:13:56 +0000505 char buf[11], *ptr;
Owen Taylor3473f882001-02-23 17:55:21 +0000506 int val = 0, l = 1;
507
508 if (*cur < 0xC0) {
509 xmlGenericError(xmlGenericErrorContext,
510 "xmlEncodeEntitiesReentrant : input not UTF-8\n");
Daniel Veillard122376b2001-04-24 12:12:30 +0000511 if (doc != NULL)
512 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
Owen Taylor3473f882001-02-23 17:55:21 +0000513 snprintf(buf, sizeof(buf), "&#%d;", *cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000514 buf[sizeof(buf) - 1] = 0;
515 ptr = buf;
516 while (*ptr != 0) *out++ = *ptr++;
Daniel Veillard05c13a22001-09-09 08:38:09 +0000517 cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000518 continue;
519 } else if (*cur < 0xE0) {
520 val = (cur[0]) & 0x1F;
521 val <<= 6;
522 val |= (cur[1]) & 0x3F;
523 l = 2;
524 } else if (*cur < 0xF0) {
525 val = (cur[0]) & 0x0F;
526 val <<= 6;
527 val |= (cur[1]) & 0x3F;
528 val <<= 6;
529 val |= (cur[2]) & 0x3F;
530 l = 3;
531 } else if (*cur < 0xF8) {
532 val = (cur[0]) & 0x07;
533 val <<= 6;
534 val |= (cur[1]) & 0x3F;
535 val <<= 6;
536 val |= (cur[2]) & 0x3F;
537 val <<= 6;
538 val |= (cur[3]) & 0x3F;
539 l = 4;
540 }
541 if ((l == 1) || (!IS_CHAR(val))) {
542 xmlGenericError(xmlGenericErrorContext,
543 "xmlEncodeEntitiesReentrant : char out of range\n");
Daniel Veillard122376b2001-04-24 12:12:30 +0000544 if (doc != NULL)
545 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
Owen Taylor3473f882001-02-23 17:55:21 +0000546 snprintf(buf, sizeof(buf), "&#%d;", *cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000547 buf[sizeof(buf) - 1] = 0;
548 ptr = buf;
549 while (*ptr != 0) *out++ = *ptr++;
550 cur++;
551 continue;
552 }
553 /*
554 * We could do multiple things here. Just save as a char ref
555 */
Daniel Veillard16698282001-09-14 10:29:27 +0000556 if (html)
557 snprintf(buf, sizeof(buf), "&#%d;", val);
558 else
559 snprintf(buf, sizeof(buf), "&#x%X;", val);
Owen Taylor3473f882001-02-23 17:55:21 +0000560 buf[sizeof(buf) - 1] = 0;
561 ptr = buf;
562 while (*ptr != 0) *out++ = *ptr++;
563 cur += l;
564 continue;
565 }
Daniel Veillard34ba3872003-07-15 13:34:05 +0000566 } else if (IS_CHAR((unsigned int) *cur)) {
Daniel Veillardb2517d82003-10-01 19:13:56 +0000567 char buf[11], *ptr;
Owen Taylor3473f882001-02-23 17:55:21 +0000568
Owen Taylor3473f882001-02-23 17:55:21 +0000569 snprintf(buf, sizeof(buf), "&#%d;", *cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000570 buf[sizeof(buf) - 1] = 0;
571 ptr = buf;
572 while (*ptr != 0) *out++ = *ptr++;
573 }
Owen Taylor3473f882001-02-23 17:55:21 +0000574 cur++;
575 }
576 *out++ = 0;
577 return(buffer);
578}
579
580/**
581 * xmlEncodeSpecialChars:
582 * @doc: the document containing the string
583 * @input: A string to convert to XML.
584 *
585 * Do a global encoding of a string, replacing the predefined entities
586 * this routine is reentrant, and result must be deallocated.
587 *
588 * Returns A newly allocated string with the substitution done.
589 */
590xmlChar *
Daniel Veillard9ee35f32003-09-28 00:19:54 +0000591xmlEncodeSpecialChars(xmlDocPtr doc ATTRIBUTE_UNUSED, const xmlChar *input) {
Owen Taylor3473f882001-02-23 17:55:21 +0000592 const xmlChar *cur = input;
593 xmlChar *buffer = NULL;
594 xmlChar *out = NULL;
595 int buffer_size = 0;
William M. Brack899e64a2003-09-26 18:03:42 +0000596 if (input == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000597
598 /*
599 * allocate an translation buffer.
600 */
601 buffer_size = 1000;
602 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
603 if (buffer == NULL) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000604 xmlGenericError(xmlGenericErrorContext, "malloc failed\n");
Owen Taylor3473f882001-02-23 17:55:21 +0000605 return(NULL);
606 }
607 out = buffer;
608
609 while (*cur != '\0') {
610 if (out - buffer > buffer_size - 10) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000611 int indx = out - buffer;
Owen Taylor3473f882001-02-23 17:55:21 +0000612
613 growBufferReentrant();
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000614 out = &buffer[indx];
Owen Taylor3473f882001-02-23 17:55:21 +0000615 }
616
617 /*
618 * By default one have to encode at least '<', '>', '"' and '&' !
619 */
620 if (*cur == '<') {
621 *out++ = '&';
622 *out++ = 'l';
623 *out++ = 't';
624 *out++ = ';';
625 } else if (*cur == '>') {
626 *out++ = '&';
627 *out++ = 'g';
628 *out++ = 't';
629 *out++ = ';';
630 } else if (*cur == '&') {
631 *out++ = '&';
632 *out++ = 'a';
633 *out++ = 'm';
634 *out++ = 'p';
635 *out++ = ';';
636 } else if (*cur == '"') {
637 *out++ = '&';
638 *out++ = 'q';
639 *out++ = 'u';
640 *out++ = 'o';
641 *out++ = 't';
642 *out++ = ';';
Daniel Veillard19ab45b2003-02-26 15:49:03 +0000643 } else if (*cur == '\r') {
644 *out++ = '&';
645 *out++ = '#';
646 *out++ = '1';
647 *out++ = '3';
648 *out++ = ';';
Owen Taylor3473f882001-02-23 17:55:21 +0000649 } else {
650 /*
651 * Works because on UTF-8, all extended sequences cannot
652 * result in bytes in the ASCII range.
653 */
654 *out++ = *cur;
655 }
656 cur++;
657 }
658 *out++ = 0;
659 return(buffer);
660}
661
662/**
663 * xmlCreateEntitiesTable:
664 *
665 * create and initialize an empty entities hash table.
666 *
667 * Returns the xmlEntitiesTablePtr just created or NULL in case of error.
668 */
669xmlEntitiesTablePtr
670xmlCreateEntitiesTable(void) {
671 return((xmlEntitiesTablePtr) xmlHashCreate(0));
672}
673
674/**
Daniel Veillard2d84a892002-12-30 00:01:08 +0000675 * xmlFreeEntityWrapper:
676 * @entity: An entity
677 * @name: its name
678 *
679 * Deallocate the memory used by an entities in the hash table.
680 */
681static void
682xmlFreeEntityWrapper(xmlEntityPtr entity,
683 const xmlChar *name ATTRIBUTE_UNUSED) {
684 if (entity != NULL)
685 xmlFreeEntity(entity);
686}
687
688/**
Owen Taylor3473f882001-02-23 17:55:21 +0000689 * xmlFreeEntitiesTable:
690 * @table: An entity table
691 *
692 * Deallocate the memory used by an entities hash table.
693 */
694void
695xmlFreeEntitiesTable(xmlEntitiesTablePtr table) {
Daniel Veillard2d84a892002-12-30 00:01:08 +0000696 xmlHashFree(table, (xmlHashDeallocator) xmlFreeEntityWrapper);
Owen Taylor3473f882001-02-23 17:55:21 +0000697}
698
Daniel Veillard652327a2003-09-29 18:02:38 +0000699#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +0000700/**
701 * xmlCopyEntity:
702 * @ent: An entity
703 *
704 * Build a copy of an entity
705 *
706 * Returns the new xmlEntitiesPtr or NULL in case of error.
707 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000708static xmlEntityPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000709xmlCopyEntity(xmlEntityPtr ent) {
710 xmlEntityPtr cur;
711
712 cur = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
713 if (cur == NULL) {
714 xmlGenericError(xmlGenericErrorContext,
715 "xmlCopyEntity: out of memory !\n");
716 return(NULL);
717 }
718 memset(cur, 0, sizeof(xmlEntity));
Daniel Veillard845cce42002-01-09 11:51:37 +0000719 cur->type = XML_ENTITY_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +0000720
721 cur->etype = ent->etype;
722 if (ent->name != NULL)
723 cur->name = xmlStrdup(ent->name);
724 if (ent->ExternalID != NULL)
725 cur->ExternalID = xmlStrdup(ent->ExternalID);
726 if (ent->SystemID != NULL)
727 cur->SystemID = xmlStrdup(ent->SystemID);
728 if (ent->content != NULL)
729 cur->content = xmlStrdup(ent->content);
730 if (ent->orig != NULL)
731 cur->orig = xmlStrdup(ent->orig);
Daniel Veillard8ee9c8f2002-01-26 21:42:58 +0000732 if (ent->URI != NULL)
733 cur->URI = xmlStrdup(ent->URI);
Owen Taylor3473f882001-02-23 17:55:21 +0000734 return(cur);
735}
736
737/**
738 * xmlCopyEntitiesTable:
739 * @table: An entity table
740 *
741 * Build a copy of an entity table.
742 *
743 * Returns the new xmlEntitiesTablePtr or NULL in case of error.
744 */
745xmlEntitiesTablePtr
746xmlCopyEntitiesTable(xmlEntitiesTablePtr table) {
747 return(xmlHashCopy(table, (xmlHashCopier) xmlCopyEntity));
748}
Daniel Veillard652327a2003-09-29 18:02:38 +0000749#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +0000750
Daniel Veillarda9cce9c2003-09-29 13:20:24 +0000751#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +0000752/**
753 * xmlDumpEntityDecl:
754 * @buf: An XML buffer.
755 * @ent: An entity table
756 *
757 * This will dump the content of the entity table as an XML DTD definition
758 */
759void
760xmlDumpEntityDecl(xmlBufferPtr buf, xmlEntityPtr ent) {
761 switch (ent->etype) {
762 case XML_INTERNAL_GENERAL_ENTITY:
763 xmlBufferWriteChar(buf, "<!ENTITY ");
764 xmlBufferWriteCHAR(buf, ent->name);
765 xmlBufferWriteChar(buf, " ");
766 if (ent->orig != NULL)
767 xmlBufferWriteQuotedString(buf, ent->orig);
768 else
769 xmlBufferWriteQuotedString(buf, ent->content);
770 xmlBufferWriteChar(buf, ">\n");
771 break;
772 case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
773 xmlBufferWriteChar(buf, "<!ENTITY ");
774 xmlBufferWriteCHAR(buf, ent->name);
775 if (ent->ExternalID != NULL) {
776 xmlBufferWriteChar(buf, " PUBLIC ");
777 xmlBufferWriteQuotedString(buf, ent->ExternalID);
778 xmlBufferWriteChar(buf, " ");
779 xmlBufferWriteQuotedString(buf, ent->SystemID);
780 } else {
781 xmlBufferWriteChar(buf, " SYSTEM ");
782 xmlBufferWriteQuotedString(buf, ent->SystemID);
783 }
784 xmlBufferWriteChar(buf, ">\n");
785 break;
786 case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
787 xmlBufferWriteChar(buf, "<!ENTITY ");
788 xmlBufferWriteCHAR(buf, ent->name);
789 if (ent->ExternalID != NULL) {
790 xmlBufferWriteChar(buf, " PUBLIC ");
791 xmlBufferWriteQuotedString(buf, ent->ExternalID);
792 xmlBufferWriteChar(buf, " ");
793 xmlBufferWriteQuotedString(buf, ent->SystemID);
794 } else {
795 xmlBufferWriteChar(buf, " SYSTEM ");
796 xmlBufferWriteQuotedString(buf, ent->SystemID);
797 }
798 if (ent->content != NULL) { /* Should be true ! */
799 xmlBufferWriteChar(buf, " NDATA ");
800 if (ent->orig != NULL)
801 xmlBufferWriteCHAR(buf, ent->orig);
802 else
803 xmlBufferWriteCHAR(buf, ent->content);
804 }
805 xmlBufferWriteChar(buf, ">\n");
806 break;
807 case XML_INTERNAL_PARAMETER_ENTITY:
808 xmlBufferWriteChar(buf, "<!ENTITY % ");
809 xmlBufferWriteCHAR(buf, ent->name);
810 xmlBufferWriteChar(buf, " ");
811 if (ent->orig == NULL)
812 xmlBufferWriteQuotedString(buf, ent->content);
813 else
814 xmlBufferWriteQuotedString(buf, ent->orig);
815 xmlBufferWriteChar(buf, ">\n");
816 break;
817 case XML_EXTERNAL_PARAMETER_ENTITY:
818 xmlBufferWriteChar(buf, "<!ENTITY % ");
819 xmlBufferWriteCHAR(buf, ent->name);
820 if (ent->ExternalID != NULL) {
821 xmlBufferWriteChar(buf, " PUBLIC ");
822 xmlBufferWriteQuotedString(buf, ent->ExternalID);
823 xmlBufferWriteChar(buf, " ");
824 xmlBufferWriteQuotedString(buf, ent->SystemID);
825 } else {
826 xmlBufferWriteChar(buf, " SYSTEM ");
827 xmlBufferWriteQuotedString(buf, ent->SystemID);
828 }
829 xmlBufferWriteChar(buf, ">\n");
830 break;
831 default:
832 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000833 "xmlDumpEntitiesDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +0000834 ent->etype);
835 }
836}
837
838/**
839 * xmlDumpEntitiesTable:
840 * @buf: An XML buffer.
841 * @table: An entity table
842 *
843 * This will dump the content of the entity table as an XML DTD definition
844 */
845void
846xmlDumpEntitiesTable(xmlBufferPtr buf, xmlEntitiesTablePtr table) {
847 xmlHashScan(table, (xmlHashScanner)xmlDumpEntityDecl, buf);
848}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +0000849#endif /* LIBXML_OUTPUT_ENABLED */