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