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