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