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