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