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