1 /* 2 * xmlsave.c: Implemetation of the document serializer 3 * 4 * See Copyright for the status of this software. 5 * 6 * daniel (at) veillard.com 7 */ 8 9 #define IN_LIBXML 10 #include "libxml.h" 11 12 #include <string.h> 13 #include <libxml/xmlmemory.h> 14 #include <libxml/parserInternals.h> 15 #include <libxml/tree.h> 16 #include <libxml/xmlsave.h> 17 18 #define MAX_INDENT 60 19 20 #include <libxml/HTMLtree.h> 21 22 /************************************************************************ 23 * * 24 * XHTML detection * 25 * * 26 ************************************************************************/ 27 #define XHTML_STRICT_PUBLIC_ID BAD_CAST \ 28 "-//W3C//DTD XHTML 1.0 Strict//EN" 29 #define XHTML_STRICT_SYSTEM_ID BAD_CAST \ 30 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" 31 #define XHTML_FRAME_PUBLIC_ID BAD_CAST \ 32 "-//W3C//DTD XHTML 1.0 Frameset//EN" 33 #define XHTML_FRAME_SYSTEM_ID BAD_CAST \ 34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd" 35 #define XHTML_TRANS_PUBLIC_ID BAD_CAST \ 36 "-//W3C//DTD XHTML 1.0 Transitional//EN" 37 #define XHTML_TRANS_SYSTEM_ID BAD_CAST \ 38 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" 39 40 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml" 41 /** 42 * xmlIsXHTML: 43 * @systemID: the system identifier 44 * @publicID: the public identifier 45 * 46 * Try to find if the document correspond to an XHTML DTD 47 * 48 * Returns 1 if true, 0 if not and -1 in case of error 49 */ 50 int 51 xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) { 52 if ((systemID == NULL) && (publicID == NULL)) 53 return(-1); 54 if (publicID != NULL) { 55 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1); 56 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1); 57 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1); 58 } 59 if (systemID != NULL) { 60 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1); 61 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1); 62 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1); 63 } 64 return(0); 65 } 66 67 #ifdef LIBXML_OUTPUT_ENABLED 68 69 #define TODO \ 70 xmlGenericError(xmlGenericErrorContext, \ 71 "Unimplemented block at %s:%d\n", \ 72 __FILE__, __LINE__); 73 74 struct _xmlSaveCtxt { 75 void *_private; 76 int type; 77 int fd; 78 const xmlChar *filename; 79 const xmlChar *encoding; 80 xmlCharEncodingHandlerPtr handler; 81 xmlOutputBufferPtr buf; 82 xmlDocPtr doc; 83 int options; 84 int level; 85 int format; 86 char indent[MAX_INDENT + 1]; /* array for indenting output */ 87 int indent_nr; 88 int indent_size; 89 xmlCharEncodingOutputFunc escape; /* used for element content */ 90 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */ 91 }; 92 93 /************************************************************************ 94 * * 95 * Output error handlers * 96 * * 97 ************************************************************************/ 98 /** 99 * xmlSaveErrMemory: 100 * @extra: extra informations 101 * 102 * Handle an out of memory condition 103 */ 104 static void 105 xmlSaveErrMemory(const char *extra) 106 { 107 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra); 108 } 109 110 /** 111 * xmlSaveErr: 112 * @code: the error number 113 * @node: the location of the error. 114 * @extra: extra informations 115 * 116 * Handle an out of memory condition 117 */ 118 static void 119 xmlSaveErr(int code, xmlNodePtr node, const char *extra) 120 { 121 const char *msg = NULL; 122 123 switch(code) { 124 case XML_SAVE_NOT_UTF8: 125 msg = "string is not in UTF-8\n"; 126 break; 127 case XML_SAVE_CHAR_INVALID: 128 msg = "invalid character value\n"; 129 break; 130 case XML_SAVE_UNKNOWN_ENCODING: 131 msg = "unknown encoding %s\n"; 132 break; 133 case XML_SAVE_NO_DOCTYPE: 134 msg = "document has no DOCTYPE\n"; 135 break; 136 default: 137 msg = "unexpected error number\n"; 138 } 139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra); 140 } 141 142 /************************************************************************ 143 * * 144 * Special escaping routines * 145 * * 146 ************************************************************************/ 147 static unsigned char * 148 xmlSerializeHexCharRef(unsigned char *out, int val) { 149 unsigned char *ptr; 150 151 *out++ = '&'; 152 *out++ = '#'; 153 *out++ = 'x'; 154 if (val < 0x10) ptr = out; 155 else if (val < 0x100) ptr = out + 1; 156 else if (val < 0x1000) ptr = out + 2; 157 else if (val < 0x10000) ptr = out + 3; 158 else if (val < 0x100000) ptr = out + 4; 159 else ptr = out + 5; 160 out = ptr + 1; 161 while (val > 0) { 162 switch (val & 0xF) { 163 case 0: *ptr-- = '0'; break; 164 case 1: *ptr-- = '1'; break; 165 case 2: *ptr-- = '2'; break; 166 case 3: *ptr-- = '3'; break; 167 case 4: *ptr-- = '4'; break; 168 case 5: *ptr-- = '5'; break; 169 case 6: *ptr-- = '6'; break; 170 case 7: *ptr-- = '7'; break; 171 case 8: *ptr-- = '8'; break; 172 case 9: *ptr-- = '9'; break; 173 case 0xA: *ptr-- = 'A'; break; 174 case 0xB: *ptr-- = 'B'; break; 175 case 0xC: *ptr-- = 'C'; break; 176 case 0xD: *ptr-- = 'D'; break; 177 case 0xE: *ptr-- = 'E'; break; 178 case 0xF: *ptr-- = 'F'; break; 179 default: *ptr-- = '0'; break; 180 } 181 val >>= 4; 182 } 183 *out++ = ';'; 184 *out = 0; 185 return(out); 186 } 187 188 /** 189 * xmlEscapeEntities: 190 * @out: a pointer to an array of bytes to store the result 191 * @outlen: the length of @out 192 * @in: a pointer to an array of unescaped UTF-8 bytes 193 * @inlen: the length of @in 194 * 195 * Take a block of UTF-8 chars in and escape them. Used when there is no 196 * encoding specified. 197 * 198 * Returns 0 if success, or -1 otherwise 199 * The value of @inlen after return is the number of octets consumed 200 * if the return value is positive, else unpredictable. 201 * The value of @outlen after return is the number of octets consumed. 202 */ 203 static int 204 xmlEscapeEntities(unsigned char* out, int *outlen, 205 const xmlChar* in, int *inlen) { 206 unsigned char* outstart = out; 207 const unsigned char* base = in; 208 unsigned char* outend = out + *outlen; 209 const unsigned char* inend; 210 int val; 211 212 inend = in + (*inlen); 213 214 while ((in < inend) && (out < outend)) { 215 if (*in == '<') { 216 if (outend - out < 4) break; 217 *out++ = '&'; 218 *out++ = 'l'; 219 *out++ = 't'; 220 *out++ = ';'; 221 in++; 222 continue; 223 } else if (*in == '>') { 224 if (outend - out < 4) break; 225 *out++ = '&'; 226 *out++ = 'g'; 227 *out++ = 't'; 228 *out++ = ';'; 229 in++; 230 continue; 231 } else if (*in == '&') { 232 if (outend - out < 5) break; 233 *out++ = '&'; 234 *out++ = 'a'; 235 *out++ = 'm'; 236 *out++ = 'p'; 237 *out++ = ';'; 238 in++; 239 continue; 240 } else if (((*in >= 0x20) && (*in < 0x80)) || 241 (*in == '\n') || (*in == '\t')) { 242 /* 243 * default case, just copy ! 244 */ 245 *out++ = *in++; 246 continue; 247 } else if (*in >= 0x80) { 248 /* 249 * We assume we have UTF-8 input. 250 */ 251 if (outend - out < 10) break; 252 253 if (*in < 0xC0) { 254 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL); 255 in++; 256 goto error; 257 } else if (*in < 0xE0) { 258 if (inend - in < 2) break; 259 val = (in[0]) & 0x1F; 260 val <<= 6; 261 val |= (in[1]) & 0x3F; 262 in += 2; 263 } else if (*in < 0xF0) { 264 if (inend - in < 3) break; 265 val = (in[0]) & 0x0F; 266 val <<= 6; 267 val |= (in[1]) & 0x3F; 268 val <<= 6; 269 val |= (in[2]) & 0x3F; 270 in += 3; 271 } else if (*in < 0xF8) { 272 if (inend - in < 4) break; 273 val = (in[0]) & 0x07; 274 val <<= 6; 275 val |= (in[1]) & 0x3F; 276 val <<= 6; 277 val |= (in[2]) & 0x3F; 278 val <<= 6; 279 val |= (in[3]) & 0x3F; 280 in += 4; 281 } else { 282 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL); 283 in++; 284 goto error; 285 } 286 if (!IS_CHAR(val)) { 287 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL); 288 in++; 289 goto error; 290 } 291 292 /* 293 * We could do multiple things here. Just save as a char ref 294 */ 295 out = xmlSerializeHexCharRef(out, val); 296 } else if (IS_BYTE_CHAR(*in)) { 297 if (outend - out < 6) break; 298 out = xmlSerializeHexCharRef(out, *in++); 299 } else { 300 xmlGenericError(xmlGenericErrorContext, 301 "xmlEscapeEntities : char out of range\n"); 302 in++; 303 goto error; 304 } 305 } 306 *outlen = out - outstart; 307 *inlen = in - base; 308 return(0); 309 error: 310 *outlen = out - outstart; 311 *inlen = in - base; 312 return(-1); 313 } 314 315 /************************************************************************ 316 * * 317 * Allocation and deallocation * 318 * * 319 ************************************************************************/ 320 /** 321 * xmlSaveCtxtInit: 322 * @ctxt: the saving context 323 * 324 * Initialize a saving context 325 */ 326 static void 327 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt) 328 { 329 int i; 330 int len; 331 332 if (ctxt == NULL) return; 333 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL)) 334 ctxt->escape = xmlEscapeEntities; 335 len = xmlStrlen((xmlChar *)xmlTreeIndentString); 336 if ((xmlTreeIndentString == NULL) || (len == 0)) { 337 memset(&ctxt->indent[0], 0, MAX_INDENT + 1); 338 } else { 339 ctxt->indent_size = len; 340 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size; 341 for (i = 0;i < ctxt->indent_nr;i++) 342 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString, 343 ctxt->indent_size); 344 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0; 345 } 346 347 if (xmlSaveNoEmptyTags) { 348 ctxt->options |= XML_SAVE_NO_EMPTY; 349 } 350 } 351 352 /** 353 * xmlFreeSaveCtxt: 354 * 355 * Free a saving context, destroying the ouptut in any remaining buffer 356 */ 357 static void 358 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt) 359 { 360 if (ctxt == NULL) return; 361 if (ctxt->encoding != NULL) 362 xmlFree((char *) ctxt->encoding); 363 if (ctxt->buf != NULL) 364 xmlOutputBufferClose(ctxt->buf); 365 xmlFree(ctxt); 366 } 367 368 /** 369 * xmlNewSaveCtxt: 370 * 371 * Create a new saving context 372 * 373 * Returns the new structure or NULL in case of error 374 */ 375 static xmlSaveCtxtPtr 376 xmlNewSaveCtxt(const char *encoding, int options) 377 { 378 xmlSaveCtxtPtr ret; 379 380 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt)); 381 if (ret == NULL) { 382 xmlSaveErrMemory("creating saving context"); 383 return ( NULL ); 384 } 385 memset(ret, 0, sizeof(xmlSaveCtxt)); 386 387 if (encoding != NULL) { 388 ret->handler = xmlFindCharEncodingHandler(encoding); 389 if (ret->handler == NULL) { 390 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding); 391 xmlFreeSaveCtxt(ret); 392 return(NULL); 393 } 394 ret->encoding = xmlStrdup((const xmlChar *)encoding); 395 ret->escape = NULL; 396 } 397 xmlSaveCtxtInit(ret); 398 399 /* 400 * Use the options 401 */ 402 403 /* Re-check this option as it may already have been set */ 404 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) { 405 options |= XML_SAVE_NO_EMPTY; 406 } 407 408 ret->options = options; 409 if (options & XML_SAVE_FORMAT) 410 ret->format = 1; 411 412 return(ret); 413 } 414 415 /************************************************************************ 416 * * 417 * Dumping XML tree content to a simple buffer * 418 * * 419 ************************************************************************/ 420 /** 421 * xmlAttrSerializeContent: 422 * @buf: the XML buffer output 423 * @doc: the document 424 * @attr: the attribute pointer 425 * 426 * Serialize the attribute in the buffer 427 */ 428 static void 429 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr) 430 { 431 xmlNodePtr children; 432 433 children = attr->children; 434 while (children != NULL) { 435 switch (children->type) { 436 case XML_TEXT_NODE: 437 xmlAttrSerializeTxtContent(buf->buffer, attr->doc, 438 attr, children->content); 439 break; 440 case XML_ENTITY_REF_NODE: 441 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1); 442 xmlBufferAdd(buf->buffer, children->name, 443 xmlStrlen(children->name)); 444 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1); 445 break; 446 default: 447 /* should not happen unless we have a badly built tree */ 448 break; 449 } 450 children = children->next; 451 } 452 } 453 454 /************************************************************************ 455 * * 456 * Dumping XML tree content to an I/O output buffer * 457 * * 458 ************************************************************************/ 459 460 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) { 461 xmlOutputBufferPtr buf = ctxt->buf; 462 463 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) { 464 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding); 465 if (buf->encoder == NULL) { 466 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, 467 (const char *)encoding); 468 return(-1); 469 } 470 buf->conv = xmlBufferCreate(); 471 if (buf->conv == NULL) { 472 xmlCharEncCloseFunc(buf->encoder); 473 xmlSaveErrMemory("creating encoding buffer"); 474 return(-1); 475 } 476 /* 477 * initialize the state, e.g. if outputting a BOM 478 */ 479 xmlCharEncOutFunc(buf->encoder, buf->conv, NULL); 480 } 481 return(0); 482 } 483 484 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) { 485 xmlOutputBufferPtr buf = ctxt->buf; 486 xmlOutputBufferFlush(buf); 487 xmlCharEncCloseFunc(buf->encoder); 488 xmlBufferFree(buf->conv); 489 buf->encoder = NULL; 490 buf->conv = NULL; 491 return(0); 492 } 493 494 #ifdef LIBXML_HTML_ENABLED 495 static void 496 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur); 497 #endif 498 static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur); 499 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur); 500 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur); 501 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur); 502 503 /** 504 * xmlNsDumpOutput: 505 * @buf: the XML buffer output 506 * @cur: a namespace 507 * 508 * Dump a local Namespace definition. 509 * Should be called in the context of attributes dumps. 510 */ 511 static void 512 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) { 513 if ((cur == NULL) || (buf == NULL)) return; 514 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) { 515 if (xmlStrEqual(cur->prefix, BAD_CAST "xml")) 516 return; 517 518 /* Within the context of an element attributes */ 519 if (cur->prefix != NULL) { 520 xmlOutputBufferWrite(buf, 7, " xmlns:"); 521 xmlOutputBufferWriteString(buf, (const char *)cur->prefix); 522 } else 523 xmlOutputBufferWrite(buf, 6, " xmlns"); 524 xmlOutputBufferWrite(buf, 1, "="); 525 xmlBufferWriteQuotedString(buf->buffer, cur->href); 526 } 527 } 528 529 /** 530 * xmlNsListDumpOutput: 531 * @buf: the XML buffer output 532 * @cur: the first namespace 533 * 534 * Dump a list of local Namespace definitions. 535 * Should be called in the context of attributes dumps. 536 */ 537 void 538 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) { 539 while (cur != NULL) { 540 xmlNsDumpOutput(buf, cur); 541 cur = cur->next; 542 } 543 } 544 545 /** 546 * xmlDtdDumpOutput: 547 * @buf: the XML buffer output 548 * @dtd: the pointer to the DTD 549 * 550 * Dump the XML document DTD, if any. 551 */ 552 static void 553 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) { 554 xmlOutputBufferPtr buf; 555 int format, level; 556 xmlDocPtr doc; 557 558 if (dtd == NULL) return; 559 if ((ctxt == NULL) || (ctxt->buf == NULL)) 560 return; 561 buf = ctxt->buf; 562 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE "); 563 xmlOutputBufferWriteString(buf, (const char *)dtd->name); 564 if (dtd->ExternalID != NULL) { 565 xmlOutputBufferWrite(buf, 8, " PUBLIC "); 566 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID); 567 xmlOutputBufferWrite(buf, 1, " "); 568 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID); 569 } else if (dtd->SystemID != NULL) { 570 xmlOutputBufferWrite(buf, 8, " SYSTEM "); 571 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID); 572 } 573 if ((dtd->entities == NULL) && (dtd->elements == NULL) && 574 (dtd->attributes == NULL) && (dtd->notations == NULL) && 575 (dtd->pentities == NULL)) { 576 xmlOutputBufferWrite(buf, 1, ">"); 577 return; 578 } 579 xmlOutputBufferWrite(buf, 3, " [\n"); 580 /* 581 * Dump the notations first they are not in the DTD children list 582 * Do this only on a standalone DTD or on the internal subset though. 583 */ 584 if ((dtd->notations != NULL) && ((dtd->doc == NULL) || 585 (dtd->doc->intSubset == dtd))) { 586 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations); 587 } 588 format = ctxt->format; 589 level = ctxt->level; 590 doc = ctxt->doc; 591 ctxt->format = 0; 592 ctxt->level = -1; 593 ctxt->doc = dtd->doc; 594 xmlNodeListDumpOutput(ctxt, dtd->children); 595 ctxt->format = format; 596 ctxt->level = level; 597 ctxt->doc = doc; 598 xmlOutputBufferWrite(buf, 2, "]>"); 599 } 600 601 /** 602 * xmlAttrDumpOutput: 603 * @buf: the XML buffer output 604 * @cur: the attribute pointer 605 * 606 * Dump an XML attribute 607 */ 608 static void 609 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) { 610 xmlOutputBufferPtr buf; 611 612 if (cur == NULL) return; 613 buf = ctxt->buf; 614 if (buf == NULL) return; 615 xmlOutputBufferWrite(buf, 1, " "); 616 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { 617 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); 618 xmlOutputBufferWrite(buf, 1, ":"); 619 } 620 xmlOutputBufferWriteString(buf, (const char *)cur->name); 621 xmlOutputBufferWrite(buf, 2, "=\""); 622 xmlAttrSerializeContent(buf, cur); 623 xmlOutputBufferWrite(buf, 1, "\""); 624 } 625 626 /** 627 * xmlAttrListDumpOutput: 628 * @buf: the XML buffer output 629 * @doc: the document 630 * @cur: the first attribute pointer 631 * @encoding: an optional encoding string 632 * 633 * Dump a list of XML attributes 634 */ 635 static void 636 xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) { 637 if (cur == NULL) return; 638 while (cur != NULL) { 639 xmlAttrDumpOutput(ctxt, cur); 640 cur = cur->next; 641 } 642 } 643 644 645 646 /** 647 * xmlNodeListDumpOutput: 648 * @cur: the first node 649 * 650 * Dump an XML node list, recursive behaviour, children are printed too. 651 */ 652 static void 653 xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { 654 xmlOutputBufferPtr buf; 655 656 if (cur == NULL) return; 657 buf = ctxt->buf; 658 while (cur != NULL) { 659 if ((ctxt->format) && (xmlIndentTreeOutput) && 660 ((cur->type == XML_ELEMENT_NODE) || 661 (cur->type == XML_COMMENT_NODE) || 662 (cur->type == XML_PI_NODE))) 663 xmlOutputBufferWrite(buf, ctxt->indent_size * 664 (ctxt->level > ctxt->indent_nr ? 665 ctxt->indent_nr : ctxt->level), 666 ctxt->indent); 667 xmlNodeDumpOutputInternal(ctxt, cur); 668 if (ctxt->format) { 669 xmlOutputBufferWrite(buf, 1, "\n"); 670 } 671 cur = cur->next; 672 } 673 } 674 675 #ifdef LIBXML_HTML_ENABLED 676 /** 677 * xmlNodeDumpOutputInternal: 678 * @cur: the current node 679 * 680 * Dump an HTML node, recursive behaviour, children are printed too. 681 */ 682 static int 683 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { 684 const xmlChar *oldenc = NULL; 685 const xmlChar *oldctxtenc = ctxt->encoding; 686 const xmlChar *encoding = ctxt->encoding; 687 xmlOutputBufferPtr buf = ctxt->buf; 688 int switched_encoding = 0; 689 xmlDocPtr doc; 690 691 xmlInitParser(); 692 693 doc = cur->doc; { 694 if (doc != NULL) 695 oldenc = doc->encoding; 696 if (ctxt->encoding != NULL) { 697 doc->encoding = BAD_CAST ctxt->encoding; 698 } else if (doc->encoding != NULL) { 699 encoding = doc->encoding; 700 } 701 } 702 703 if ((encoding != NULL) && (doc != NULL)) 704 htmlSetMetaEncoding(doc, (const xmlChar *) encoding); 705 if ((encoding == NULL) && (doc != NULL)) 706 encoding = htmlGetMetaEncoding(doc); 707 if (encoding == NULL) 708 encoding = BAD_CAST "HTML"; 709 if ((encoding != NULL) && (oldctxtenc == NULL) && 710 (buf->encoder == NULL) && (buf->conv == NULL)) { 711 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) { 712 doc->encoding = oldenc; 713 return(-1); 714 } 715 switched_encoding = 1; 716 } 717 if (ctxt->options & XML_SAVE_FORMAT) 718 htmlNodeDumpFormatOutput(buf, doc, cur, 719 (const char *)encoding, 1); 720 else 721 htmlNodeDumpFormatOutput(buf, doc, cur, 722 (const char *)encoding, 0); 723 /* 724 * Restore the state of the saving context at the end of the document 725 */ 726 if ((switched_encoding) && (oldctxtenc == NULL)) { 727 xmlSaveClearEncoding(ctxt); 728 } 729 if (doc != NULL) 730 doc->encoding = oldenc; 731 return(0); 732 } 733 #endif 734 735 /** 736 * xmlNodeDumpOutputInternal: 737 * @cur: the current node 738 * 739 * Dump an XML node, recursive behaviour, children are printed too. 740 */ 741 static void 742 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { 743 int format; 744 xmlNodePtr tmp; 745 xmlChar *start, *end; 746 xmlOutputBufferPtr buf; 747 748 if (cur == NULL) return; 749 buf = ctxt->buf; 750 if (cur->type == XML_XINCLUDE_START) 751 return; 752 if (cur->type == XML_XINCLUDE_END) 753 return; 754 if ((cur->type == XML_DOCUMENT_NODE) || 755 (cur->type == XML_HTML_DOCUMENT_NODE)) { 756 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur); 757 return; 758 } 759 #ifdef LIBXML_HTML_ENABLED 760 if (ctxt->options & XML_SAVE_XHTML) { 761 xhtmlNodeDumpOutput(ctxt, cur); 762 return; 763 } 764 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) && 765 (cur->doc->type == XML_HTML_DOCUMENT_NODE) && 766 ((ctxt->options & XML_SAVE_AS_XML) == 0)) || 767 (ctxt->options & XML_SAVE_AS_HTML)) { 768 htmlNodeDumpOutputInternal(ctxt, cur); 769 return; 770 } 771 #endif 772 if (cur->type == XML_DTD_NODE) { 773 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur); 774 return; 775 } 776 if (cur->type == XML_DOCUMENT_FRAG_NODE) { 777 xmlNodeListDumpOutput(ctxt, cur->children); 778 return; 779 } 780 if (cur->type == XML_ELEMENT_DECL) { 781 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur); 782 return; 783 } 784 if (cur->type == XML_ATTRIBUTE_DECL) { 785 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur); 786 return; 787 } 788 if (cur->type == XML_ENTITY_DECL) { 789 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur); 790 return; 791 } 792 if (cur->type == XML_TEXT_NODE) { 793 if (cur->content != NULL) { 794 if (cur->name != xmlStringTextNoenc) { 795 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape); 796 } else { 797 /* 798 * Disable escaping, needed for XSLT 799 */ 800 xmlOutputBufferWriteString(buf, (const char *) cur->content); 801 } 802 } 803 804 return; 805 } 806 if (cur->type == XML_PI_NODE) { 807 if (cur->content != NULL) { 808 xmlOutputBufferWrite(buf, 2, "<?"); 809 xmlOutputBufferWriteString(buf, (const char *)cur->name); 810 if (cur->content != NULL) { 811 xmlOutputBufferWrite(buf, 1, " "); 812 xmlOutputBufferWriteString(buf, (const char *)cur->content); 813 } 814 xmlOutputBufferWrite(buf, 2, "?>"); 815 } else { 816 xmlOutputBufferWrite(buf, 2, "<?"); 817 xmlOutputBufferWriteString(buf, (const char *)cur->name); 818 xmlOutputBufferWrite(buf, 2, "?>"); 819 } 820 return; 821 } 822 if (cur->type == XML_COMMENT_NODE) { 823 if (cur->content != NULL) { 824 xmlOutputBufferWrite(buf, 4, "<!--"); 825 xmlOutputBufferWriteString(buf, (const char *)cur->content); 826 xmlOutputBufferWrite(buf, 3, "-->"); 827 } 828 return; 829 } 830 if (cur->type == XML_ENTITY_REF_NODE) { 831 xmlOutputBufferWrite(buf, 1, "&"); 832 xmlOutputBufferWriteString(buf, (const char *)cur->name); 833 xmlOutputBufferWrite(buf, 1, ";"); 834 return; 835 } 836 if (cur->type == XML_CDATA_SECTION_NODE) { 837 if (cur->content == NULL || *cur->content == '\0') { 838 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>"); 839 } else { 840 start = end = cur->content; 841 while (*end != '\0') { 842 if ((*end == ']') && (*(end + 1) == ']') && 843 (*(end + 2) == '>')) { 844 end = end + 2; 845 xmlOutputBufferWrite(buf, 9, "<![CDATA["); 846 xmlOutputBufferWrite(buf, end - start, (const char *)start); 847 xmlOutputBufferWrite(buf, 3, "]]>"); 848 start = end; 849 } 850 end++; 851 } 852 if (start != end) { 853 xmlOutputBufferWrite(buf, 9, "<![CDATA["); 854 xmlOutputBufferWriteString(buf, (const char *)start); 855 xmlOutputBufferWrite(buf, 3, "]]>"); 856 } 857 } 858 return; 859 } 860 if (cur->type == XML_ATTRIBUTE_NODE) { 861 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur); 862 return; 863 } 864 if (cur->type == XML_NAMESPACE_DECL) { 865 xmlNsDumpOutput(buf, (xmlNsPtr) cur); 866 return; 867 } 868 869 format = ctxt->format; 870 if (format == 1) { 871 tmp = cur->children; 872 while (tmp != NULL) { 873 if ((tmp->type == XML_TEXT_NODE) || 874 (tmp->type == XML_CDATA_SECTION_NODE) || 875 (tmp->type == XML_ENTITY_REF_NODE)) { 876 ctxt->format = 0; 877 break; 878 } 879 tmp = tmp->next; 880 } 881 } 882 xmlOutputBufferWrite(buf, 1, "<"); 883 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { 884 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); 885 xmlOutputBufferWrite(buf, 1, ":"); 886 } 887 888 xmlOutputBufferWriteString(buf, (const char *)cur->name); 889 if (cur->nsDef) 890 xmlNsListDumpOutput(buf, cur->nsDef); 891 if (cur->properties != NULL) 892 xmlAttrListDumpOutput(ctxt, cur->properties); 893 894 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) && 895 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) { 896 xmlOutputBufferWrite(buf, 2, "/>"); 897 ctxt->format = format; 898 return; 899 } 900 xmlOutputBufferWrite(buf, 1, ">"); 901 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) { 902 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape); 903 } 904 if (cur->children != NULL) { 905 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n"); 906 if (ctxt->level >= 0) ctxt->level++; 907 xmlNodeListDumpOutput(ctxt, cur->children); 908 if (ctxt->level > 0) ctxt->level--; 909 if ((xmlIndentTreeOutput) && (ctxt->format)) 910 xmlOutputBufferWrite(buf, ctxt->indent_size * 911 (ctxt->level > ctxt->indent_nr ? 912 ctxt->indent_nr : ctxt->level), 913 ctxt->indent); 914 } 915 xmlOutputBufferWrite(buf, 2, "</"); 916 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { 917 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); 918 xmlOutputBufferWrite(buf, 1, ":"); 919 } 920 921 xmlOutputBufferWriteString(buf, (const char *)cur->name); 922 xmlOutputBufferWrite(buf, 1, ">"); 923 ctxt->format = format; 924 } 925 926 /** 927 * xmlDocContentDumpOutput: 928 * @cur: the document 929 * 930 * Dump an XML document. 931 */ 932 static int 933 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) { 934 #ifdef LIBXML_HTML_ENABLED 935 xmlDtdPtr dtd; 936 int is_xhtml = 0; 937 #endif 938 const xmlChar *oldenc = cur->encoding; 939 const xmlChar *oldctxtenc = ctxt->encoding; 940 const xmlChar *encoding = ctxt->encoding; 941 xmlCharEncodingOutputFunc oldescape = ctxt->escape; 942 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr; 943 xmlOutputBufferPtr buf = ctxt->buf; 944 xmlCharEncoding enc; 945 int switched_encoding = 0; 946 947 xmlInitParser(); 948 949 if ((cur->type != XML_HTML_DOCUMENT_NODE) && 950 (cur->type != XML_DOCUMENT_NODE)) 951 return(-1); 952 953 if (ctxt->encoding != NULL) { 954 cur->encoding = BAD_CAST ctxt->encoding; 955 } else if (cur->encoding != NULL) { 956 encoding = cur->encoding; 957 } else if (cur->charset != XML_CHAR_ENCODING_UTF8) { 958 encoding = (const xmlChar *) 959 xmlGetCharEncodingName((xmlCharEncoding) cur->charset); 960 } 961 962 if (((cur->type == XML_HTML_DOCUMENT_NODE) && 963 ((ctxt->options & XML_SAVE_AS_XML) == 0) && 964 ((ctxt->options & XML_SAVE_XHTML) == 0)) || 965 (ctxt->options & XML_SAVE_AS_HTML)) { 966 #ifdef LIBXML_HTML_ENABLED 967 if (encoding != NULL) 968 htmlSetMetaEncoding(cur, (const xmlChar *) encoding); 969 if (encoding == NULL) 970 encoding = htmlGetMetaEncoding(cur); 971 if (encoding == NULL) 972 encoding = BAD_CAST "HTML"; 973 if ((encoding != NULL) && (oldctxtenc == NULL) && 974 (buf->encoder == NULL) && (buf->conv == NULL)) { 975 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) { 976 cur->encoding = oldenc; 977 return(-1); 978 } 979 switched_encoding = 1; 980 } 981 if (ctxt->options & XML_SAVE_FORMAT) 982 htmlDocContentDumpFormatOutput(buf, cur, 983 (const char *)encoding, 1); 984 else 985 htmlDocContentDumpFormatOutput(buf, cur, 986 (const char *)encoding, 0); 987 if (ctxt->encoding != NULL) 988 cur->encoding = oldenc; 989 return(0); 990 #else 991 return(-1); 992 #endif 993 } else if ((cur->type == XML_DOCUMENT_NODE) || 994 (ctxt->options & XML_SAVE_AS_XML) || 995 (ctxt->options & XML_SAVE_XHTML)) { 996 enc = xmlParseCharEncoding((const char*) encoding); 997 if ((encoding != NULL) && (oldctxtenc == NULL) && 998 (buf->encoder == NULL) && (buf->conv == NULL) && 999 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) { 1000 if ((enc != XML_CHAR_ENCODING_UTF8) && 1001 (enc != XML_CHAR_ENCODING_NONE) && 1002 (enc != XML_CHAR_ENCODING_ASCII)) { 1003 /* 1004 * we need to switch to this encoding but just for this 1005 * document since we output the XMLDecl the conversion 1006 * must be done to not generate not well formed documents. 1007 */ 1008 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) { 1009 cur->encoding = oldenc; 1010 return(-1); 1011 } 1012 switched_encoding = 1; 1013 } 1014 if (ctxt->escape == xmlEscapeEntities) 1015 ctxt->escape = NULL; 1016 if (ctxt->escapeAttr == xmlEscapeEntities) 1017 ctxt->escapeAttr = NULL; 1018 } 1019 1020 1021 /* 1022 * Save the XML declaration 1023 */ 1024 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) { 1025 xmlOutputBufferWrite(buf, 14, "<?xml version="); 1026 if (cur->version != NULL) 1027 xmlBufferWriteQuotedString(buf->buffer, cur->version); 1028 else 1029 xmlOutputBufferWrite(buf, 5, "\"1.0\""); 1030 if (encoding != NULL) { 1031 xmlOutputBufferWrite(buf, 10, " encoding="); 1032 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding); 1033 } 1034 switch (cur->standalone) { 1035 case 0: 1036 xmlOutputBufferWrite(buf, 16, " standalone=\"no\""); 1037 break; 1038 case 1: 1039 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\""); 1040 break; 1041 } 1042 xmlOutputBufferWrite(buf, 3, "?>\n"); 1043 } 1044 1045 #ifdef LIBXML_HTML_ENABLED 1046 if (ctxt->options & XML_SAVE_XHTML) 1047 is_xhtml = 1; 1048 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) { 1049 dtd = xmlGetIntSubset(cur); 1050 if (dtd != NULL) { 1051 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID); 1052 if (is_xhtml < 0) is_xhtml = 0; 1053 } 1054 } 1055 #endif 1056 if (cur->children != NULL) { 1057 xmlNodePtr child = cur->children; 1058 1059 while (child != NULL) { 1060 ctxt->level = 0; 1061 #ifdef LIBXML_HTML_ENABLED 1062 if (is_xhtml) 1063 xhtmlNodeDumpOutput(ctxt, child); 1064 else 1065 #endif 1066 xmlNodeDumpOutputInternal(ctxt, child); 1067 xmlOutputBufferWrite(buf, 1, "\n"); 1068 child = child->next; 1069 } 1070 } 1071 } 1072 1073 /* 1074 * Restore the state of the saving context at the end of the document 1075 */ 1076 if ((switched_encoding) && (oldctxtenc == NULL)) { 1077 xmlSaveClearEncoding(ctxt); 1078 ctxt->escape = oldescape; 1079 ctxt->escapeAttr = oldescapeAttr; 1080 } 1081 cur->encoding = oldenc; 1082 return(0); 1083 } 1084 1085 #ifdef LIBXML_HTML_ENABLED 1086 /************************************************************************ 1087 * * 1088 * Functions specific to XHTML serialization * 1089 * * 1090 ************************************************************************/ 1091 1092 /** 1093 * xhtmlIsEmpty: 1094 * @node: the node 1095 * 1096 * Check if a node is an empty xhtml node 1097 * 1098 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error 1099 */ 1100 static int 1101 xhtmlIsEmpty(xmlNodePtr node) { 1102 if (node == NULL) 1103 return(-1); 1104 if (node->type != XML_ELEMENT_NODE) 1105 return(0); 1106 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME))) 1107 return(0); 1108 if (node->children != NULL) 1109 return(0); 1110 switch (node->name[0]) { 1111 case 'a': 1112 if (xmlStrEqual(node->name, BAD_CAST "area")) 1113 return(1); 1114 return(0); 1115 case 'b': 1116 if (xmlStrEqual(node->name, BAD_CAST "br")) 1117 return(1); 1118 if (xmlStrEqual(node->name, BAD_CAST "base")) 1119 return(1); 1120 if (xmlStrEqual(node->name, BAD_CAST "basefont")) 1121 return(1); 1122 return(0); 1123 case 'c': 1124 if (xmlStrEqual(node->name, BAD_CAST "col")) 1125 return(1); 1126 return(0); 1127 case 'f': 1128 if (xmlStrEqual(node->name, BAD_CAST "frame")) 1129 return(1); 1130 return(0); 1131 case 'h': 1132 if (xmlStrEqual(node->name, BAD_CAST "hr")) 1133 return(1); 1134 return(0); 1135 case 'i': 1136 if (xmlStrEqual(node->name, BAD_CAST "img")) 1137 return(1); 1138 if (xmlStrEqual(node->name, BAD_CAST "input")) 1139 return(1); 1140 if (xmlStrEqual(node->name, BAD_CAST "isindex")) 1141 return(1); 1142 return(0); 1143 case 'l': 1144 if (xmlStrEqual(node->name, BAD_CAST "link")) 1145 return(1); 1146 return(0); 1147 case 'm': 1148 if (xmlStrEqual(node->name, BAD_CAST "meta")) 1149 return(1); 1150 return(0); 1151 case 'p': 1152 if (xmlStrEqual(node->name, BAD_CAST "param")) 1153 return(1); 1154 return(0); 1155 } 1156 return(0); 1157 } 1158 1159 /** 1160 * xhtmlAttrListDumpOutput: 1161 * @cur: the first attribute pointer 1162 * 1163 * Dump a list of XML attributes 1164 */ 1165 static void 1166 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) { 1167 xmlAttrPtr xml_lang = NULL; 1168 xmlAttrPtr lang = NULL; 1169 xmlAttrPtr name = NULL; 1170 xmlAttrPtr id = NULL; 1171 xmlNodePtr parent; 1172 xmlOutputBufferPtr buf; 1173 1174 if (cur == NULL) return; 1175 buf = ctxt->buf; 1176 parent = cur->parent; 1177 while (cur != NULL) { 1178 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id"))) 1179 id = cur; 1180 else 1181 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name"))) 1182 name = cur; 1183 else 1184 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang"))) 1185 lang = cur; 1186 else 1187 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) && 1188 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml"))) 1189 xml_lang = cur; 1190 else if ((cur->ns == NULL) && 1191 ((cur->children == NULL) || 1192 (cur->children->content == NULL) || 1193 (cur->children->content[0] == 0)) && 1194 (htmlIsBooleanAttr(cur->name))) { 1195 if (cur->children != NULL) 1196 xmlFreeNode(cur->children); 1197 cur->children = xmlNewText(cur->name); 1198 if (cur->children != NULL) 1199 cur->children->parent = (xmlNodePtr) cur; 1200 } 1201 xmlAttrDumpOutput(ctxt, cur); 1202 cur = cur->next; 1203 } 1204 /* 1205 * C.8 1206 */ 1207 if ((name != NULL) && (id == NULL)) { 1208 if ((parent != NULL) && (parent->name != NULL) && 1209 ((xmlStrEqual(parent->name, BAD_CAST "a")) || 1210 (xmlStrEqual(parent->name, BAD_CAST "p")) || 1211 (xmlStrEqual(parent->name, BAD_CAST "div")) || 1212 (xmlStrEqual(parent->name, BAD_CAST "img")) || 1213 (xmlStrEqual(parent->name, BAD_CAST "map")) || 1214 (xmlStrEqual(parent->name, BAD_CAST "applet")) || 1215 (xmlStrEqual(parent->name, BAD_CAST "form")) || 1216 (xmlStrEqual(parent->name, BAD_CAST "frame")) || 1217 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) { 1218 xmlOutputBufferWrite(buf, 5, " id=\""); 1219 xmlAttrSerializeContent(buf, name); 1220 xmlOutputBufferWrite(buf, 1, "\""); 1221 } 1222 } 1223 /* 1224 * C.7. 1225 */ 1226 if ((lang != NULL) && (xml_lang == NULL)) { 1227 xmlOutputBufferWrite(buf, 11, " xml:lang=\""); 1228 xmlAttrSerializeContent(buf, lang); 1229 xmlOutputBufferWrite(buf, 1, "\""); 1230 } else 1231 if ((xml_lang != NULL) && (lang == NULL)) { 1232 xmlOutputBufferWrite(buf, 7, " lang=\""); 1233 xmlAttrSerializeContent(buf, xml_lang); 1234 xmlOutputBufferWrite(buf, 1, "\""); 1235 } 1236 } 1237 1238 /** 1239 * xhtmlNodeListDumpOutput: 1240 * @buf: the XML buffer output 1241 * @doc: the XHTML document 1242 * @cur: the first node 1243 * @level: the imbrication level for indenting 1244 * @format: is formatting allowed 1245 * @encoding: an optional encoding string 1246 * 1247 * Dump an XML node list, recursive behaviour, children are printed too. 1248 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1 1249 * or xmlKeepBlanksDefault(0) was called 1250 */ 1251 static void 1252 xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { 1253 xmlOutputBufferPtr buf; 1254 1255 if (cur == NULL) return; 1256 buf = ctxt->buf; 1257 while (cur != NULL) { 1258 if ((ctxt->format) && (xmlIndentTreeOutput) && 1259 (cur->type == XML_ELEMENT_NODE)) 1260 xmlOutputBufferWrite(buf, ctxt->indent_size * 1261 (ctxt->level > ctxt->indent_nr ? 1262 ctxt->indent_nr : ctxt->level), 1263 ctxt->indent); 1264 xhtmlNodeDumpOutput(ctxt, cur); 1265 if (ctxt->format) { 1266 xmlOutputBufferWrite(buf, 1, "\n"); 1267 } 1268 cur = cur->next; 1269 } 1270 } 1271 1272 /** 1273 * xhtmlNodeDumpOutput: 1274 * @buf: the XML buffer output 1275 * @doc: the XHTML document 1276 * @cur: the current node 1277 * @level: the imbrication level for indenting 1278 * @format: is formatting allowed 1279 * @encoding: an optional encoding string 1280 * 1281 * Dump an XHTML node, recursive behaviour, children are printed too. 1282 */ 1283 static void 1284 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { 1285 int format, addmeta = 0; 1286 xmlNodePtr tmp; 1287 xmlChar *start, *end; 1288 xmlOutputBufferPtr buf; 1289 1290 if (cur == NULL) return; 1291 if ((cur->type == XML_DOCUMENT_NODE) || 1292 (cur->type == XML_HTML_DOCUMENT_NODE)) { 1293 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur); 1294 return; 1295 } 1296 if (cur->type == XML_XINCLUDE_START) 1297 return; 1298 if (cur->type == XML_XINCLUDE_END) 1299 return; 1300 if (cur->type == XML_DTD_NODE) { 1301 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur); 1302 return; 1303 } 1304 if (cur->type == XML_DOCUMENT_FRAG_NODE) { 1305 xhtmlNodeListDumpOutput(ctxt, cur->children); 1306 return; 1307 } 1308 buf = ctxt->buf; 1309 if (cur->type == XML_ELEMENT_DECL) { 1310 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur); 1311 return; 1312 } 1313 if (cur->type == XML_ATTRIBUTE_DECL) { 1314 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur); 1315 return; 1316 } 1317 if (cur->type == XML_ENTITY_DECL) { 1318 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur); 1319 return; 1320 } 1321 if (cur->type == XML_TEXT_NODE) { 1322 if (cur->content != NULL) { 1323 if ((cur->name == xmlStringText) || 1324 (cur->name != xmlStringTextNoenc)) { 1325 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape); 1326 } else { 1327 /* 1328 * Disable escaping, needed for XSLT 1329 */ 1330 xmlOutputBufferWriteString(buf, (const char *) cur->content); 1331 } 1332 } 1333 1334 return; 1335 } 1336 if (cur->type == XML_PI_NODE) { 1337 if (cur->content != NULL) { 1338 xmlOutputBufferWrite(buf, 2, "<?"); 1339 xmlOutputBufferWriteString(buf, (const char *)cur->name); 1340 if (cur->content != NULL) { 1341 xmlOutputBufferWrite(buf, 1, " "); 1342 xmlOutputBufferWriteString(buf, (const char *)cur->content); 1343 } 1344 xmlOutputBufferWrite(buf, 2, "?>"); 1345 } else { 1346 xmlOutputBufferWrite(buf, 2, "<?"); 1347 xmlOutputBufferWriteString(buf, (const char *)cur->name); 1348 xmlOutputBufferWrite(buf, 2, "?>"); 1349 } 1350 return; 1351 } 1352 if (cur->type == XML_COMMENT_NODE) { 1353 if (cur->content != NULL) { 1354 xmlOutputBufferWrite(buf, 4, "<!--"); 1355 xmlOutputBufferWriteString(buf, (const char *)cur->content); 1356 xmlOutputBufferWrite(buf, 3, "-->"); 1357 } 1358 return; 1359 } 1360 if (cur->type == XML_ENTITY_REF_NODE) { 1361 xmlOutputBufferWrite(buf, 1, "&"); 1362 xmlOutputBufferWriteString(buf, (const char *)cur->name); 1363 xmlOutputBufferWrite(buf, 1, ";"); 1364 return; 1365 } 1366 if (cur->type == XML_CDATA_SECTION_NODE) { 1367 if (cur->content == NULL || *cur->content == '\0') { 1368 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>"); 1369 } else { 1370 start = end = cur->content; 1371 while (*end != '\0') { 1372 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') { 1373 end = end + 2; 1374 xmlOutputBufferWrite(buf, 9, "<![CDATA["); 1375 xmlOutputBufferWrite(buf, end - start, (const char *)start); 1376 xmlOutputBufferWrite(buf, 3, "]]>"); 1377 start = end; 1378 } 1379 end++; 1380 } 1381 if (start != end) { 1382 xmlOutputBufferWrite(buf, 9, "<![CDATA["); 1383 xmlOutputBufferWriteString(buf, (const char *)start); 1384 xmlOutputBufferWrite(buf, 3, "]]>"); 1385 } 1386 } 1387 return; 1388 } 1389 if (cur->type == XML_ATTRIBUTE_NODE) { 1390 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur); 1391 return; 1392 } 1393 1394 format = ctxt->format; 1395 if (format == 1) { 1396 tmp = cur->children; 1397 while (tmp != NULL) { 1398 if ((tmp->type == XML_TEXT_NODE) || 1399 (tmp->type == XML_ENTITY_REF_NODE)) { 1400 format = 0; 1401 break; 1402 } 1403 tmp = tmp->next; 1404 } 1405 } 1406 xmlOutputBufferWrite(buf, 1, "<"); 1407 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { 1408 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); 1409 xmlOutputBufferWrite(buf, 1, ":"); 1410 } 1411 1412 xmlOutputBufferWriteString(buf, (const char *)cur->name); 1413 if (cur->nsDef) 1414 xmlNsListDumpOutput(buf, cur->nsDef); 1415 if ((xmlStrEqual(cur->name, BAD_CAST "html") && 1416 (cur->ns == NULL) && (cur->nsDef == NULL))) { 1417 /* 1418 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/ 1419 */ 1420 xmlOutputBufferWriteString(buf, 1421 " xmlns=\"http://www.w3.org/1999/xhtml\""); 1422 } 1423 if (cur->properties != NULL) 1424 xhtmlAttrListDumpOutput(ctxt, cur->properties); 1425 1426 if ((cur->type == XML_ELEMENT_NODE) && 1427 (cur->parent != NULL) && 1428 (cur->parent->parent == (xmlNodePtr) cur->doc) && 1429 xmlStrEqual(cur->name, BAD_CAST"head") && 1430 xmlStrEqual(cur->parent->name, BAD_CAST"html")) { 1431 1432 tmp = cur->children; 1433 while (tmp != NULL) { 1434 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) { 1435 xmlChar *httpequiv; 1436 1437 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv"); 1438 if (httpequiv != NULL) { 1439 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) { 1440 xmlFree(httpequiv); 1441 break; 1442 } 1443 xmlFree(httpequiv); 1444 } 1445 } 1446 tmp = tmp->next; 1447 } 1448 if (tmp == NULL) 1449 addmeta = 1; 1450 } 1451 1452 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) { 1453 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) && 1454 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) { 1455 /* 1456 * C.2. Empty Elements 1457 */ 1458 xmlOutputBufferWrite(buf, 3, " />"); 1459 } else { 1460 if (addmeta == 1) { 1461 xmlOutputBufferWrite(buf, 1, ">"); 1462 if (ctxt->format) { 1463 xmlOutputBufferWrite(buf, 1, "\n"); 1464 if (xmlIndentTreeOutput) 1465 xmlOutputBufferWrite(buf, ctxt->indent_size * 1466 (ctxt->level + 1 > ctxt->indent_nr ? 1467 ctxt->indent_nr : ctxt->level + 1), ctxt->indent); 1468 } 1469 xmlOutputBufferWriteString(buf, 1470 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset="); 1471 if (ctxt->encoding) { 1472 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding); 1473 } else { 1474 xmlOutputBufferWrite(buf, 5, "UTF-8"); 1475 } 1476 xmlOutputBufferWrite(buf, 4, "\" />"); 1477 if (ctxt->format) 1478 xmlOutputBufferWrite(buf, 1, "\n"); 1479 } else { 1480 xmlOutputBufferWrite(buf, 1, ">"); 1481 } 1482 /* 1483 * C.3. Element Minimization and Empty Element Content 1484 */ 1485 xmlOutputBufferWrite(buf, 2, "</"); 1486 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { 1487 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); 1488 xmlOutputBufferWrite(buf, 1, ":"); 1489 } 1490 xmlOutputBufferWriteString(buf, (const char *)cur->name); 1491 xmlOutputBufferWrite(buf, 1, ">"); 1492 } 1493 return; 1494 } 1495 xmlOutputBufferWrite(buf, 1, ">"); 1496 if (addmeta == 1) { 1497 if (ctxt->format) { 1498 xmlOutputBufferWrite(buf, 1, "\n"); 1499 if (xmlIndentTreeOutput) 1500 xmlOutputBufferWrite(buf, ctxt->indent_size * 1501 (ctxt->level + 1 > ctxt->indent_nr ? 1502 ctxt->indent_nr : ctxt->level + 1), ctxt->indent); 1503 } 1504 xmlOutputBufferWriteString(buf, 1505 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset="); 1506 if (ctxt->encoding) { 1507 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding); 1508 } else { 1509 xmlOutputBufferWrite(buf, 5, "UTF-8"); 1510 } 1511 xmlOutputBufferWrite(buf, 4, "\" />"); 1512 } 1513 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) { 1514 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape); 1515 } 1516 1517 #if 0 1518 /* 1519 * This was removed due to problems with HTML processors. 1520 * See bug #345147. 1521 */ 1522 /* 1523 * 4.8. Script and Style elements 1524 */ 1525 if ((cur->type == XML_ELEMENT_NODE) && 1526 ((xmlStrEqual(cur->name, BAD_CAST "script")) || 1527 (xmlStrEqual(cur->name, BAD_CAST "style"))) && 1528 ((cur->ns == NULL) || 1529 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) { 1530 xmlNodePtr child = cur->children; 1531 1532 while (child != NULL) { 1533 if (child->type == XML_TEXT_NODE) { 1534 if ((xmlStrchr(child->content, '<') == NULL) && 1535 (xmlStrchr(child->content, '&') == NULL) && 1536 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) { 1537 /* Nothing to escape, so just output as is... */ 1538 /* FIXME: Should we do something about "--" also? */ 1539 int level = ctxt->level; 1540 int indent = ctxt->format; 1541 1542 ctxt->level = 0; 1543 ctxt->format = 0; 1544 xmlOutputBufferWriteString(buf, (const char *) child->content); 1545 /* (We cannot use xhtmlNodeDumpOutput() here because 1546 * we wish to leave '>' unescaped!) */ 1547 ctxt->level = level; 1548 ctxt->format = indent; 1549 } else { 1550 /* We must use a CDATA section. Unfortunately, 1551 * this will break CSS and JavaScript when read by 1552 * a browser in HTML4-compliant mode. :-( */ 1553 start = end = child->content; 1554 while (*end != '\0') { 1555 if (*end == ']' && 1556 *(end + 1) == ']' && 1557 *(end + 2) == '>') { 1558 end = end + 2; 1559 xmlOutputBufferWrite(buf, 9, "<![CDATA["); 1560 xmlOutputBufferWrite(buf, end - start, 1561 (const char *)start); 1562 xmlOutputBufferWrite(buf, 3, "]]>"); 1563 start = end; 1564 } 1565 end++; 1566 } 1567 if (start != end) { 1568 xmlOutputBufferWrite(buf, 9, "<![CDATA["); 1569 xmlOutputBufferWrite(buf, end - start, 1570 (const char *)start); 1571 xmlOutputBufferWrite(buf, 3, "]]>"); 1572 } 1573 } 1574 } else { 1575 int level = ctxt->level; 1576 int indent = ctxt->format; 1577 1578 ctxt->level = 0; 1579 ctxt->format = 0; 1580 xhtmlNodeDumpOutput(ctxt, child); 1581 ctxt->level = level; 1582 ctxt->format = indent; 1583 } 1584 child = child->next; 1585 } 1586 } 1587 #endif 1588 1589 if (cur->children != NULL) { 1590 int indent = ctxt->format; 1591 1592 if (format) xmlOutputBufferWrite(buf, 1, "\n"); 1593 if (ctxt->level >= 0) ctxt->level++; 1594 ctxt->format = format; 1595 xhtmlNodeListDumpOutput(ctxt, cur->children); 1596 if (ctxt->level > 0) ctxt->level--; 1597 ctxt->format = indent; 1598 if ((xmlIndentTreeOutput) && (format)) 1599 xmlOutputBufferWrite(buf, ctxt->indent_size * 1600 (ctxt->level > ctxt->indent_nr ? 1601 ctxt->indent_nr : ctxt->level), 1602 ctxt->indent); 1603 } 1604 xmlOutputBufferWrite(buf, 2, "</"); 1605 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { 1606 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); 1607 xmlOutputBufferWrite(buf, 1, ":"); 1608 } 1609 1610 xmlOutputBufferWriteString(buf, (const char *)cur->name); 1611 xmlOutputBufferWrite(buf, 1, ">"); 1612 } 1613 #endif 1614 1615 /************************************************************************ 1616 * * 1617 * Public entry points * 1618 * * 1619 ************************************************************************/ 1620 1621 /** 1622 * xmlSaveToFd: 1623 * @fd: a file descriptor number 1624 * @encoding: the encoding name to use or NULL 1625 * @options: a set of xmlSaveOptions 1626 * 1627 * Create a document saving context serializing to a file descriptor 1628 * with the encoding and the options given. 1629 * 1630 * Returns a new serialization context or NULL in case of error. 1631 */ 1632 xmlSaveCtxtPtr 1633 xmlSaveToFd(int fd, const char *encoding, int options) 1634 { 1635 xmlSaveCtxtPtr ret; 1636 1637 ret = xmlNewSaveCtxt(encoding, options); 1638 if (ret == NULL) return(NULL); 1639 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler); 1640 if (ret->buf == NULL) { 1641 xmlFreeSaveCtxt(ret); 1642 return(NULL); 1643 } 1644 return(ret); 1645 } 1646 1647 /** 1648 * xmlSaveToFilename: 1649 * @filename: a file name or an URL 1650 * @encoding: the encoding name to use or NULL 1651 * @options: a set of xmlSaveOptions 1652 * 1653 * Create a document saving context serializing to a filename or possibly 1654 * to an URL (but this is less reliable) with the encoding and the options 1655 * given. 1656 * 1657 * Returns a new serialization context or NULL in case of error. 1658 */ 1659 xmlSaveCtxtPtr 1660 xmlSaveToFilename(const char *filename, const char *encoding, int options) 1661 { 1662 xmlSaveCtxtPtr ret; 1663 int compression = 0; /* TODO handle compression option */ 1664 1665 ret = xmlNewSaveCtxt(encoding, options); 1666 if (ret == NULL) return(NULL); 1667 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler, 1668 compression); 1669 if (ret->buf == NULL) { 1670 xmlFreeSaveCtxt(ret); 1671 return(NULL); 1672 } 1673 return(ret); 1674 } 1675 1676 /** 1677 * xmlSaveToBuffer: 1678 * @buffer: a buffer 1679 * @encoding: the encoding name to use or NULL 1680 * @options: a set of xmlSaveOptions 1681 * 1682 * Create a document saving context serializing to a buffer 1683 * with the encoding and the options given 1684 * 1685 * Returns a new serialization context or NULL in case of error. 1686 */ 1687 1688 xmlSaveCtxtPtr 1689 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options) 1690 { 1691 xmlSaveCtxtPtr ret; 1692 xmlOutputBufferPtr out_buff; 1693 xmlCharEncodingHandlerPtr handler; 1694 1695 ret = xmlNewSaveCtxt(encoding, options); 1696 if (ret == NULL) return(NULL); 1697 1698 if (encoding != NULL) { 1699 handler = xmlFindCharEncodingHandler(encoding); 1700 if (handler == NULL) { 1701 xmlFree(ret); 1702 return(NULL); 1703 } 1704 } else 1705 handler = NULL; 1706 out_buff = xmlOutputBufferCreateBuffer(buffer, handler); 1707 if (out_buff == NULL) { 1708 xmlFree(ret); 1709 if (handler) xmlCharEncCloseFunc(handler); 1710 return(NULL); 1711 } 1712 1713 ret->buf = out_buff; 1714 return(ret); 1715 } 1716 1717 /** 1718 * xmlSaveToIO: 1719 * @iowrite: an I/O write function 1720 * @ioclose: an I/O close function 1721 * @ioctx: an I/O handler 1722 * @encoding: the encoding name to use or NULL 1723 * @options: a set of xmlSaveOptions 1724 * 1725 * Create a document saving context serializing to a file descriptor 1726 * with the encoding and the options given 1727 * 1728 * Returns a new serialization context or NULL in case of error. 1729 */ 1730 xmlSaveCtxtPtr 1731 xmlSaveToIO(xmlOutputWriteCallback iowrite, 1732 xmlOutputCloseCallback ioclose, 1733 void *ioctx, const char *encoding, int options) 1734 { 1735 xmlSaveCtxtPtr ret; 1736 1737 ret = xmlNewSaveCtxt(encoding, options); 1738 if (ret == NULL) return(NULL); 1739 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler); 1740 if (ret->buf == NULL) { 1741 xmlFreeSaveCtxt(ret); 1742 return(NULL); 1743 } 1744 return(ret); 1745 } 1746 1747 /** 1748 * xmlSaveDoc: 1749 * @ctxt: a document saving context 1750 * @doc: a document 1751 * 1752 * Save a full document to a saving context 1753 * TODO: The function is not fully implemented yet as it does not return the 1754 * byte count but 0 instead 1755 * 1756 * Returns the number of byte written or -1 in case of error 1757 */ 1758 long 1759 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc) 1760 { 1761 long ret = 0; 1762 1763 if ((ctxt == NULL) || (doc == NULL)) return(-1); 1764 if (xmlDocContentDumpOutput(ctxt, doc) < 0) 1765 return(-1); 1766 return(ret); 1767 } 1768 1769 /** 1770 * xmlSaveTree: 1771 * @ctxt: a document saving context 1772 * @node: the top node of the subtree to save 1773 * 1774 * Save a subtree starting at the node parameter to a saving context 1775 * TODO: The function is not fully implemented yet as it does not return the 1776 * byte count but 0 instead 1777 * 1778 * Returns the number of byte written or -1 in case of error 1779 */ 1780 long 1781 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node) 1782 { 1783 long ret = 0; 1784 1785 if ((ctxt == NULL) || (node == NULL)) return(-1); 1786 xmlNodeDumpOutputInternal(ctxt, node); 1787 return(ret); 1788 } 1789 1790 /** 1791 * xmlSaveFlush: 1792 * @ctxt: a document saving context 1793 * 1794 * Flush a document saving context, i.e. make sure that all bytes have 1795 * been output. 1796 * 1797 * Returns the number of byte written or -1 in case of error. 1798 */ 1799 int 1800 xmlSaveFlush(xmlSaveCtxtPtr ctxt) 1801 { 1802 if (ctxt == NULL) return(-1); 1803 if (ctxt->buf == NULL) return(-1); 1804 return(xmlOutputBufferFlush(ctxt->buf)); 1805 } 1806 1807 /** 1808 * xmlSaveClose: 1809 * @ctxt: a document saving context 1810 * 1811 * Close a document saving context, i.e. make sure that all bytes have 1812 * been output and free the associated data. 1813 * 1814 * Returns the number of byte written or -1 in case of error. 1815 */ 1816 int 1817 xmlSaveClose(xmlSaveCtxtPtr ctxt) 1818 { 1819 int ret; 1820 1821 if (ctxt == NULL) return(-1); 1822 ret = xmlSaveFlush(ctxt); 1823 xmlFreeSaveCtxt(ctxt); 1824 return(ret); 1825 } 1826 1827 /** 1828 * xmlSaveSetEscape: 1829 * @ctxt: a document saving context 1830 * @escape: the escaping function 1831 * 1832 * Set a custom escaping function to be used for text in element content 1833 * 1834 * Returns 0 if successful or -1 in case of error. 1835 */ 1836 int 1837 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape) 1838 { 1839 if (ctxt == NULL) return(-1); 1840 ctxt->escape = escape; 1841 return(0); 1842 } 1843 1844 /** 1845 * xmlSaveSetAttrEscape: 1846 * @ctxt: a document saving context 1847 * @escape: the escaping function 1848 * 1849 * Set a custom escaping function to be used for text in attribute content 1850 * 1851 * Returns 0 if successful or -1 in case of error. 1852 */ 1853 int 1854 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape) 1855 { 1856 if (ctxt == NULL) return(-1); 1857 ctxt->escapeAttr = escape; 1858 return(0); 1859 } 1860 1861 /************************************************************************ 1862 * * 1863 * Public entry points based on buffers * 1864 * * 1865 ************************************************************************/ 1866 /** 1867 * xmlAttrSerializeTxtContent: 1868 * @buf: the XML buffer output 1869 * @doc: the document 1870 * @attr: the attribute node 1871 * @string: the text content 1872 * 1873 * Serialize text attribute values to an xml simple buffer 1874 */ 1875 void 1876 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc, 1877 xmlAttrPtr attr, const xmlChar * string) 1878 { 1879 xmlChar *base, *cur; 1880 1881 if (string == NULL) 1882 return; 1883 base = cur = (xmlChar *) string; 1884 while (*cur != 0) { 1885 if (*cur == '\n') { 1886 if (base != cur) 1887 xmlBufferAdd(buf, base, cur - base); 1888 xmlBufferAdd(buf, BAD_CAST " ", 5); 1889 cur++; 1890 base = cur; 1891 } else if (*cur == '\r') { 1892 if (base != cur) 1893 xmlBufferAdd(buf, base, cur - base); 1894 xmlBufferAdd(buf, BAD_CAST " ", 5); 1895 cur++; 1896 base = cur; 1897 } else if (*cur == '\t') { 1898 if (base != cur) 1899 xmlBufferAdd(buf, base, cur - base); 1900 xmlBufferAdd(buf, BAD_CAST "	", 4); 1901 cur++; 1902 base = cur; 1903 } else if (*cur == '"') { 1904 if (base != cur) 1905 xmlBufferAdd(buf, base, cur - base); 1906 xmlBufferAdd(buf, BAD_CAST """, 6); 1907 cur++; 1908 base = cur; 1909 } else if (*cur == '<') { 1910 if (base != cur) 1911 xmlBufferAdd(buf, base, cur - base); 1912 xmlBufferAdd(buf, BAD_CAST "<", 4); 1913 cur++; 1914 base = cur; 1915 } else if (*cur == '>') { 1916 if (base != cur) 1917 xmlBufferAdd(buf, base, cur - base); 1918 xmlBufferAdd(buf, BAD_CAST ">", 4); 1919 cur++; 1920 base = cur; 1921 } else if (*cur == '&') { 1922 if (base != cur) 1923 xmlBufferAdd(buf, base, cur - base); 1924 xmlBufferAdd(buf, BAD_CAST "&", 5); 1925 cur++; 1926 base = cur; 1927 } else if ((*cur >= 0x80) && ((doc == NULL) || 1928 (doc->encoding == NULL))) { 1929 /* 1930 * We assume we have UTF-8 content. 1931 */ 1932 unsigned char tmp[10]; 1933 int val = 0, l = 1; 1934 1935 if (base != cur) 1936 xmlBufferAdd(buf, base, cur - base); 1937 if (*cur < 0xC0) { 1938 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL); 1939 if (doc != NULL) 1940 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1"); 1941 xmlSerializeHexCharRef(tmp, *cur); 1942 xmlBufferAdd(buf, (xmlChar *) tmp, -1); 1943 cur++; 1944 base = cur; 1945 continue; 1946 } else if (*cur < 0xE0) { 1947 val = (cur[0]) & 0x1F; 1948 val <<= 6; 1949 val |= (cur[1]) & 0x3F; 1950 l = 2; 1951 } else if (*cur < 0xF0) { 1952 val = (cur[0]) & 0x0F; 1953 val <<= 6; 1954 val |= (cur[1]) & 0x3F; 1955 val <<= 6; 1956 val |= (cur[2]) & 0x3F; 1957 l = 3; 1958 } else if (*cur < 0xF8) { 1959 val = (cur[0]) & 0x07; 1960 val <<= 6; 1961 val |= (cur[1]) & 0x3F; 1962 val <<= 6; 1963 val |= (cur[2]) & 0x3F; 1964 val <<= 6; 1965 val |= (cur[3]) & 0x3F; 1966 l = 4; 1967 } 1968 if ((l == 1) || (!IS_CHAR(val))) { 1969 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL); 1970 if (doc != NULL) 1971 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1"); 1972 1973 xmlSerializeHexCharRef(tmp, *cur); 1974 xmlBufferAdd(buf, (xmlChar *) tmp, -1); 1975 cur++; 1976 base = cur; 1977 continue; 1978 } 1979 /* 1980 * We could do multiple things here. Just save 1981 * as a char ref 1982 */ 1983 xmlSerializeHexCharRef(tmp, val); 1984 xmlBufferAdd(buf, (xmlChar *) tmp, -1); 1985 cur += l; 1986 base = cur; 1987 } else { 1988 cur++; 1989 } 1990 } 1991 if (base != cur) 1992 xmlBufferAdd(buf, base, cur - base); 1993 } 1994 1995 /** 1996 * xmlNodeDump: 1997 * @buf: the XML buffer output 1998 * @doc: the document 1999 * @cur: the current node 2000 * @level: the imbrication level for indenting 2001 * @format: is formatting allowed 2002 * 2003 * Dump an XML node, recursive behaviour,children are printed too. 2004 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1 2005 * or xmlKeepBlanksDefault(0) was called 2006 * 2007 * Returns the number of bytes written to the buffer or -1 in case of error 2008 */ 2009 int 2010 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level, 2011 int format) 2012 { 2013 unsigned int use; 2014 int ret; 2015 xmlOutputBufferPtr outbuf; 2016 2017 xmlInitParser(); 2018 2019 if (cur == NULL) { 2020 #ifdef DEBUG_TREE 2021 xmlGenericError(xmlGenericErrorContext, 2022 "xmlNodeDump : node == NULL\n"); 2023 #endif 2024 return (-1); 2025 } 2026 if (buf == NULL) { 2027 #ifdef DEBUG_TREE 2028 xmlGenericError(xmlGenericErrorContext, 2029 "xmlNodeDump : buf == NULL\n"); 2030 #endif 2031 return (-1); 2032 } 2033 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer)); 2034 if (outbuf == NULL) { 2035 xmlSaveErrMemory("creating buffer"); 2036 return (-1); 2037 } 2038 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer)); 2039 outbuf->buffer = buf; 2040 outbuf->encoder = NULL; 2041 outbuf->writecallback = NULL; 2042 outbuf->closecallback = NULL; 2043 outbuf->context = NULL; 2044 outbuf->written = 0; 2045 2046 use = buf->use; 2047 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL); 2048 xmlFree(outbuf); 2049 ret = buf->use - use; 2050 return (ret); 2051 } 2052 2053 /** 2054 * xmlElemDump: 2055 * @f: the FILE * for the output 2056 * @doc: the document 2057 * @cur: the current node 2058 * 2059 * Dump an XML/HTML node, recursive behaviour, children are printed too. 2060 */ 2061 void 2062 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur) 2063 { 2064 xmlOutputBufferPtr outbuf; 2065 2066 xmlInitParser(); 2067 2068 if (cur == NULL) { 2069 #ifdef DEBUG_TREE 2070 xmlGenericError(xmlGenericErrorContext, 2071 "xmlElemDump : cur == NULL\n"); 2072 #endif 2073 return; 2074 } 2075 #ifdef DEBUG_TREE 2076 if (doc == NULL) { 2077 xmlGenericError(xmlGenericErrorContext, 2078 "xmlElemDump : doc == NULL\n"); 2079 } 2080 #endif 2081 2082 outbuf = xmlOutputBufferCreateFile(f, NULL); 2083 if (outbuf == NULL) 2084 return; 2085 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) { 2086 #ifdef LIBXML_HTML_ENABLED 2087 htmlNodeDumpOutput(outbuf, doc, cur, NULL); 2088 #else 2089 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n"); 2090 #endif /* LIBXML_HTML_ENABLED */ 2091 } else 2092 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL); 2093 xmlOutputBufferClose(outbuf); 2094 } 2095 2096 /************************************************************************ 2097 * * 2098 * Saving functions front-ends * 2099 * * 2100 ************************************************************************/ 2101 2102 /** 2103 * xmlNodeDumpOutput: 2104 * @buf: the XML buffer output 2105 * @doc: the document 2106 * @cur: the current node 2107 * @level: the imbrication level for indenting 2108 * @format: is formatting allowed 2109 * @encoding: an optional encoding string 2110 * 2111 * Dump an XML node, recursive behaviour, children are printed too. 2112 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1 2113 * or xmlKeepBlanksDefault(0) was called 2114 */ 2115 void 2116 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, 2117 int level, int format, const char *encoding) 2118 { 2119 xmlSaveCtxt ctxt; 2120 #ifdef LIBXML_HTML_ENABLED 2121 xmlDtdPtr dtd; 2122 int is_xhtml = 0; 2123 #endif 2124 2125 xmlInitParser(); 2126 2127 if ((buf == NULL) || (cur == NULL)) return; 2128 2129 if (encoding == NULL) 2130 encoding = "UTF-8"; 2131 2132 memset(&ctxt, 0, sizeof(ctxt)); 2133 ctxt.doc = doc; 2134 ctxt.buf = buf; 2135 ctxt.level = level; 2136 ctxt.format = format; 2137 ctxt.encoding = (const xmlChar *) encoding; 2138 xmlSaveCtxtInit(&ctxt); 2139 ctxt.options |= XML_SAVE_AS_XML; 2140 2141 #ifdef LIBXML_HTML_ENABLED 2142 dtd = xmlGetIntSubset(doc); 2143 if (dtd != NULL) { 2144 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID); 2145 if (is_xhtml < 0) 2146 is_xhtml = 0; 2147 } 2148 2149 if (is_xhtml) 2150 xhtmlNodeDumpOutput(&ctxt, cur); 2151 else 2152 #endif 2153 xmlNodeDumpOutputInternal(&ctxt, cur); 2154 } 2155 2156 /** 2157 * xmlDocDumpFormatMemoryEnc: 2158 * @out_doc: Document to generate XML text from 2159 * @doc_txt_ptr: Memory pointer for allocated XML text 2160 * @doc_txt_len: Length of the generated XML text 2161 * @txt_encoding: Character encoding to use when generating XML text 2162 * @format: should formatting spaces been added 2163 * 2164 * Dump the current DOM tree into memory using the character encoding specified 2165 * by the caller. Note it is up to the caller of this function to free the 2166 * allocated memory with xmlFree(). 2167 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1 2168 * or xmlKeepBlanksDefault(0) was called 2169 */ 2170 2171 void 2172 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr, 2173 int * doc_txt_len, const char * txt_encoding, 2174 int format) { 2175 xmlSaveCtxt ctxt; 2176 int dummy = 0; 2177 xmlOutputBufferPtr out_buff = NULL; 2178 xmlCharEncodingHandlerPtr conv_hdlr = NULL; 2179 2180 if (doc_txt_len == NULL) { 2181 doc_txt_len = &dummy; /* Continue, caller just won't get length */ 2182 } 2183 2184 if (doc_txt_ptr == NULL) { 2185 *doc_txt_len = 0; 2186 return; 2187 } 2188 2189 *doc_txt_ptr = NULL; 2190 *doc_txt_len = 0; 2191 2192 if (out_doc == NULL) { 2193 /* No document, no output */ 2194 return; 2195 } 2196 2197 /* 2198 * Validate the encoding value, if provided. 2199 * This logic is copied from xmlSaveFileEnc. 2200 */ 2201 2202 if (txt_encoding == NULL) 2203 txt_encoding = (const char *) out_doc->encoding; 2204 if (txt_encoding != NULL) { 2205 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding); 2206 if ( conv_hdlr == NULL ) { 2207 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc, 2208 txt_encoding); 2209 return; 2210 } 2211 } 2212 2213 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) { 2214 xmlSaveErrMemory("creating buffer"); 2215 return; 2216 } 2217 2218 memset(&ctxt, 0, sizeof(ctxt)); 2219 ctxt.doc = out_doc; 2220 ctxt.buf = out_buff; 2221 ctxt.level = 0; 2222 ctxt.format = format; 2223 ctxt.encoding = (const xmlChar *) txt_encoding; 2224 xmlSaveCtxtInit(&ctxt); 2225 ctxt.options |= XML_SAVE_AS_XML; 2226 xmlDocContentDumpOutput(&ctxt, out_doc); 2227 xmlOutputBufferFlush(out_buff); 2228 if (out_buff->conv != NULL) { 2229 *doc_txt_len = out_buff->conv->use; 2230 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len); 2231 } else { 2232 *doc_txt_len = out_buff->buffer->use; 2233 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len); 2234 } 2235 (void)xmlOutputBufferClose(out_buff); 2236 2237 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) { 2238 *doc_txt_len = 0; 2239 xmlSaveErrMemory("creating output"); 2240 } 2241 2242 return; 2243 } 2244 2245 /** 2246 * xmlDocDumpMemory: 2247 * @cur: the document 2248 * @mem: OUT: the memory pointer 2249 * @size: OUT: the memory length 2250 * 2251 * Dump an XML document in memory and return the #xmlChar * and it's size 2252 * in bytes. It's up to the caller to free the memory with xmlFree(). 2253 * The resulting byte array is zero terminated, though the last 0 is not 2254 * included in the returned size. 2255 */ 2256 void 2257 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) { 2258 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0); 2259 } 2260 2261 /** 2262 * xmlDocDumpFormatMemory: 2263 * @cur: the document 2264 * @mem: OUT: the memory pointer 2265 * @size: OUT: the memory length 2266 * @format: should formatting spaces been added 2267 * 2268 * 2269 * Dump an XML document in memory and return the #xmlChar * and it's size. 2270 * It's up to the caller to free the memory with xmlFree(). 2271 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1 2272 * or xmlKeepBlanksDefault(0) was called 2273 */ 2274 void 2275 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) { 2276 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format); 2277 } 2278 2279 /** 2280 * xmlDocDumpMemoryEnc: 2281 * @out_doc: Document to generate XML text from 2282 * @doc_txt_ptr: Memory pointer for allocated XML text 2283 * @doc_txt_len: Length of the generated XML text 2284 * @txt_encoding: Character encoding to use when generating XML text 2285 * 2286 * Dump the current DOM tree into memory using the character encoding specified 2287 * by the caller. Note it is up to the caller of this function to free the 2288 * allocated memory with xmlFree(). 2289 */ 2290 2291 void 2292 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr, 2293 int * doc_txt_len, const char * txt_encoding) { 2294 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len, 2295 txt_encoding, 0); 2296 } 2297 2298 /** 2299 * xmlDocFormatDump: 2300 * @f: the FILE* 2301 * @cur: the document 2302 * @format: should formatting spaces been added 2303 * 2304 * Dump an XML document to an open FILE. 2305 * 2306 * returns: the number of bytes written or -1 in case of failure. 2307 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1 2308 * or xmlKeepBlanksDefault(0) was called 2309 */ 2310 int 2311 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) { 2312 xmlSaveCtxt ctxt; 2313 xmlOutputBufferPtr buf; 2314 const char * encoding; 2315 xmlCharEncodingHandlerPtr handler = NULL; 2316 int ret; 2317 2318 if (cur == NULL) { 2319 #ifdef DEBUG_TREE 2320 xmlGenericError(xmlGenericErrorContext, 2321 "xmlDocDump : document == NULL\n"); 2322 #endif 2323 return(-1); 2324 } 2325 encoding = (const char *) cur->encoding; 2326 2327 if (encoding != NULL) { 2328 handler = xmlFindCharEncodingHandler(encoding); 2329 if (handler == NULL) { 2330 xmlFree((char *) cur->encoding); 2331 cur->encoding = NULL; 2332 encoding = NULL; 2333 } 2334 } 2335 buf = xmlOutputBufferCreateFile(f, handler); 2336 if (buf == NULL) return(-1); 2337 memset(&ctxt, 0, sizeof(ctxt)); 2338 ctxt.doc = cur; 2339 ctxt.buf = buf; 2340 ctxt.level = 0; 2341 ctxt.format = format; 2342 ctxt.encoding = (const xmlChar *) encoding; 2343 xmlSaveCtxtInit(&ctxt); 2344 ctxt.options |= XML_SAVE_AS_XML; 2345 xmlDocContentDumpOutput(&ctxt, cur); 2346 2347 ret = xmlOutputBufferClose(buf); 2348 return(ret); 2349 } 2350 2351 /** 2352 * xmlDocDump: 2353 * @f: the FILE* 2354 * @cur: the document 2355 * 2356 * Dump an XML document to an open FILE. 2357 * 2358 * returns: the number of bytes written or -1 in case of failure. 2359 */ 2360 int 2361 xmlDocDump(FILE *f, xmlDocPtr cur) { 2362 return(xmlDocFormatDump (f, cur, 0)); 2363 } 2364 2365 /** 2366 * xmlSaveFileTo: 2367 * @buf: an output I/O buffer 2368 * @cur: the document 2369 * @encoding: the encoding if any assuming the I/O layer handles the trancoding 2370 * 2371 * Dump an XML document to an I/O buffer. 2372 * Warning ! This call xmlOutputBufferClose() on buf which is not available 2373 * after this call. 2374 * 2375 * returns: the number of bytes written or -1 in case of failure. 2376 */ 2377 int 2378 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) { 2379 xmlSaveCtxt ctxt; 2380 int ret; 2381 2382 if (buf == NULL) return(-1); 2383 if (cur == NULL) { 2384 xmlOutputBufferClose(buf); 2385 return(-1); 2386 } 2387 memset(&ctxt, 0, sizeof(ctxt)); 2388 ctxt.doc = cur; 2389 ctxt.buf = buf; 2390 ctxt.level = 0; 2391 ctxt.format = 0; 2392 ctxt.encoding = (const xmlChar *) encoding; 2393 xmlSaveCtxtInit(&ctxt); 2394 ctxt.options |= XML_SAVE_AS_XML; 2395 xmlDocContentDumpOutput(&ctxt, cur); 2396 ret = xmlOutputBufferClose(buf); 2397 return(ret); 2398 } 2399 2400 /** 2401 * xmlSaveFormatFileTo: 2402 * @buf: an output I/O buffer 2403 * @cur: the document 2404 * @encoding: the encoding if any assuming the I/O layer handles the trancoding 2405 * @format: should formatting spaces been added 2406 * 2407 * Dump an XML document to an I/O buffer. 2408 * Warning ! This call xmlOutputBufferClose() on buf which is not available 2409 * after this call. 2410 * 2411 * returns: the number of bytes written or -1 in case of failure. 2412 */ 2413 int 2414 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, 2415 const char *encoding, int format) 2416 { 2417 xmlSaveCtxt ctxt; 2418 int ret; 2419 2420 if (buf == NULL) return(-1); 2421 if ((cur == NULL) || 2422 ((cur->type != XML_DOCUMENT_NODE) && 2423 (cur->type != XML_HTML_DOCUMENT_NODE))) { 2424 xmlOutputBufferClose(buf); 2425 return(-1); 2426 } 2427 memset(&ctxt, 0, sizeof(ctxt)); 2428 ctxt.doc = cur; 2429 ctxt.buf = buf; 2430 ctxt.level = 0; 2431 ctxt.format = format; 2432 ctxt.encoding = (const xmlChar *) encoding; 2433 xmlSaveCtxtInit(&ctxt); 2434 ctxt.options |= XML_SAVE_AS_XML; 2435 xmlDocContentDumpOutput(&ctxt, cur); 2436 ret = xmlOutputBufferClose(buf); 2437 return (ret); 2438 } 2439 2440 /** 2441 * xmlSaveFormatFileEnc: 2442 * @filename: the filename or URL to output 2443 * @cur: the document being saved 2444 * @encoding: the name of the encoding to use or NULL. 2445 * @format: should formatting spaces be added. 2446 * 2447 * Dump an XML document to a file or an URL. 2448 * 2449 * Returns the number of bytes written or -1 in case of error. 2450 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1 2451 * or xmlKeepBlanksDefault(0) was called 2452 */ 2453 int 2454 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur, 2455 const char * encoding, int format ) { 2456 xmlSaveCtxt ctxt; 2457 xmlOutputBufferPtr buf; 2458 xmlCharEncodingHandlerPtr handler = NULL; 2459 int ret; 2460 2461 if (cur == NULL) 2462 return(-1); 2463 2464 if (encoding == NULL) 2465 encoding = (const char *) cur->encoding; 2466 2467 if (encoding != NULL) { 2468 2469 handler = xmlFindCharEncodingHandler(encoding); 2470 if (handler == NULL) 2471 return(-1); 2472 } 2473 2474 #ifdef HAVE_ZLIB_H 2475 if (cur->compression < 0) cur->compression = xmlGetCompressMode(); 2476 #endif 2477 /* 2478 * save the content to a temp buffer. 2479 */ 2480 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression); 2481 if (buf == NULL) return(-1); 2482 memset(&ctxt, 0, sizeof(ctxt)); 2483 ctxt.doc = cur; 2484 ctxt.buf = buf; 2485 ctxt.level = 0; 2486 ctxt.format = format; 2487 ctxt.encoding = (const xmlChar *) encoding; 2488 xmlSaveCtxtInit(&ctxt); 2489 ctxt.options |= XML_SAVE_AS_XML; 2490 2491 xmlDocContentDumpOutput(&ctxt, cur); 2492 2493 ret = xmlOutputBufferClose(buf); 2494 return(ret); 2495 } 2496 2497 2498 /** 2499 * xmlSaveFileEnc: 2500 * @filename: the filename (or URL) 2501 * @cur: the document 2502 * @encoding: the name of an encoding (or NULL) 2503 * 2504 * Dump an XML document, converting it to the given encoding 2505 * 2506 * returns: the number of bytes written or -1 in case of failure. 2507 */ 2508 int 2509 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) { 2510 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) ); 2511 } 2512 2513 /** 2514 * xmlSaveFormatFile: 2515 * @filename: the filename (or URL) 2516 * @cur: the document 2517 * @format: should formatting spaces been added 2518 * 2519 * Dump an XML document to a file. Will use compression if 2520 * compiled in and enabled. If @filename is "-" the stdout file is 2521 * used. If @format is set then the document will be indented on output. 2522 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1 2523 * or xmlKeepBlanksDefault(0) was called 2524 * 2525 * returns: the number of bytes written or -1 in case of failure. 2526 */ 2527 int 2528 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) { 2529 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) ); 2530 } 2531 2532 /** 2533 * xmlSaveFile: 2534 * @filename: the filename (or URL) 2535 * @cur: the document 2536 * 2537 * Dump an XML document to a file. Will use compression if 2538 * compiled in and enabled. If @filename is "-" the stdout file is 2539 * used. 2540 * returns: the number of bytes written or -1 in case of failure. 2541 */ 2542 int 2543 xmlSaveFile(const char *filename, xmlDocPtr cur) { 2544 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0)); 2545 } 2546 2547 #endif /* LIBXML_OUTPUT_ENABLED */ 2548 2549 #define bottom_xmlsave 2550 #include "elfgcchack.h" 2551