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