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