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