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