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