mscms: Fix double free on error path in EnumColorProfilesA (scan-build).
[wine.git] / libs / xml2 / xmlsave.c
blob5b5c8f1f2fdf0b355ec2680cfa560f10d77d18d6
1 /*
2 * xmlsave.c: Implementation of the document serializer
4 * See Copyright for the status of this software.
6 * daniel@veillard.com
7 */
9 #define IN_LIBXML
10 #include "libxml.h"
12 #include <string.h>
13 #include <libxml/xmlmemory.h>
14 #include <libxml/parserInternals.h>
15 #include <libxml/tree.h>
16 #include <libxml/xmlsave.h>
18 #define MAX_INDENT 60
20 #include <libxml/HTMLtree.h>
22 #include "private/buf.h"
23 #include "private/enc.h"
24 #include "private/error.h"
25 #include "private/save.h"
27 #ifdef LIBXML_OUTPUT_ENABLED
29 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
31 #define TODO \
32 xmlGenericError(xmlGenericErrorContext, \
33 "Unimplemented block at %s:%d\n", \
34 __FILE__, __LINE__);
36 struct _xmlSaveCtxt {
37 void *_private;
38 int type;
39 int fd;
40 const xmlChar *filename;
41 const xmlChar *encoding;
42 xmlCharEncodingHandlerPtr handler;
43 xmlOutputBufferPtr buf;
44 int options;
45 int level;
46 int format;
47 char indent[MAX_INDENT + 1]; /* array for indenting output */
48 int indent_nr;
49 int indent_size;
50 xmlCharEncodingOutputFunc escape; /* used for element content */
51 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
54 /************************************************************************
55 * *
56 * Output error handlers *
57 * *
58 ************************************************************************/
59 /**
60 * xmlSaveErrMemory:
61 * @extra: extra information
63 * Handle an out of memory condition
65 static void
66 xmlSaveErrMemory(const char *extra)
68 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
71 /**
72 * xmlSaveErr:
73 * @code: the error number
74 * @node: the location of the error.
75 * @extra: extra information
77 * Handle an out of memory condition
79 static void
80 xmlSaveErr(int code, xmlNodePtr node, const char *extra)
82 const char *msg = NULL;
84 switch(code) {
85 case XML_SAVE_NOT_UTF8:
86 msg = "string is not in UTF-8\n";
87 break;
88 case XML_SAVE_CHAR_INVALID:
89 msg = "invalid character value\n";
90 break;
91 case XML_SAVE_UNKNOWN_ENCODING:
92 msg = "unknown encoding %s\n";
93 break;
94 case XML_SAVE_NO_DOCTYPE:
95 msg = "document has no DOCTYPE\n";
96 break;
97 default:
98 msg = "unexpected error number\n";
100 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
103 /************************************************************************
105 * Special escaping routines *
107 ************************************************************************/
108 static unsigned char *
109 xmlSerializeHexCharRef(unsigned char *out, int val) {
110 unsigned char *ptr;
112 *out++ = '&';
113 *out++ = '#';
114 *out++ = 'x';
115 if (val < 0x10) ptr = out;
116 else if (val < 0x100) ptr = out + 1;
117 else if (val < 0x1000) ptr = out + 2;
118 else if (val < 0x10000) ptr = out + 3;
119 else if (val < 0x100000) ptr = out + 4;
120 else ptr = out + 5;
121 out = ptr + 1;
122 while (val > 0) {
123 switch (val & 0xF) {
124 case 0: *ptr-- = '0'; break;
125 case 1: *ptr-- = '1'; break;
126 case 2: *ptr-- = '2'; break;
127 case 3: *ptr-- = '3'; break;
128 case 4: *ptr-- = '4'; break;
129 case 5: *ptr-- = '5'; break;
130 case 6: *ptr-- = '6'; break;
131 case 7: *ptr-- = '7'; break;
132 case 8: *ptr-- = '8'; break;
133 case 9: *ptr-- = '9'; break;
134 case 0xA: *ptr-- = 'A'; break;
135 case 0xB: *ptr-- = 'B'; break;
136 case 0xC: *ptr-- = 'C'; break;
137 case 0xD: *ptr-- = 'D'; break;
138 case 0xE: *ptr-- = 'E'; break;
139 case 0xF: *ptr-- = 'F'; break;
140 default: *ptr-- = '0'; break;
142 val >>= 4;
144 *out++ = ';';
145 *out = 0;
146 return(out);
150 * xmlEscapeEntities:
151 * @out: a pointer to an array of bytes to store the result
152 * @outlen: the length of @out
153 * @in: a pointer to an array of unescaped UTF-8 bytes
154 * @inlen: the length of @in
156 * Take a block of UTF-8 chars in and escape them. Used when there is no
157 * encoding specified.
159 * Returns 0 if success, or -1 otherwise
160 * The value of @inlen after return is the number of octets consumed
161 * if the return value is positive, else unpredictable.
162 * The value of @outlen after return is the number of octets consumed.
164 static int
165 xmlEscapeEntities(unsigned char* out, int *outlen,
166 const xmlChar* in, int *inlen) {
167 unsigned char* outstart = out;
168 const unsigned char* base = in;
169 unsigned char* outend = out + *outlen;
170 const unsigned char* inend;
171 int val;
173 inend = in + (*inlen);
175 while ((in < inend) && (out < outend)) {
176 if (*in == '<') {
177 if (outend - out < 4) break;
178 *out++ = '&';
179 *out++ = 'l';
180 *out++ = 't';
181 *out++ = ';';
182 in++;
183 continue;
184 } else if (*in == '>') {
185 if (outend - out < 4) break;
186 *out++ = '&';
187 *out++ = 'g';
188 *out++ = 't';
189 *out++ = ';';
190 in++;
191 continue;
192 } else if (*in == '&') {
193 if (outend - out < 5) break;
194 *out++ = '&';
195 *out++ = 'a';
196 *out++ = 'm';
197 *out++ = 'p';
198 *out++ = ';';
199 in++;
200 continue;
201 } else if (((*in >= 0x20) && (*in < 0x80)) ||
202 (*in == '\n') || (*in == '\t')) {
204 * default case, just copy !
206 *out++ = *in++;
207 continue;
208 } else if (*in >= 0x80) {
210 * We assume we have UTF-8 input.
212 if (outend - out < 11) break;
214 if (*in < 0xC0) {
215 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
216 in++;
217 goto error;
218 } else if (*in < 0xE0) {
219 if (inend - in < 2) break;
220 val = (in[0]) & 0x1F;
221 val <<= 6;
222 val |= (in[1]) & 0x3F;
223 in += 2;
224 } else if (*in < 0xF0) {
225 if (inend - in < 3) break;
226 val = (in[0]) & 0x0F;
227 val <<= 6;
228 val |= (in[1]) & 0x3F;
229 val <<= 6;
230 val |= (in[2]) & 0x3F;
231 in += 3;
232 } else if (*in < 0xF8) {
233 if (inend - in < 4) break;
234 val = (in[0]) & 0x07;
235 val <<= 6;
236 val |= (in[1]) & 0x3F;
237 val <<= 6;
238 val |= (in[2]) & 0x3F;
239 val <<= 6;
240 val |= (in[3]) & 0x3F;
241 in += 4;
242 } else {
243 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
244 in++;
245 goto error;
247 if (!IS_CHAR(val)) {
248 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
249 in++;
250 goto error;
254 * We could do multiple things here. Just save as a char ref
256 out = xmlSerializeHexCharRef(out, val);
257 } else if (IS_BYTE_CHAR(*in)) {
258 if (outend - out < 6) break;
259 out = xmlSerializeHexCharRef(out, *in++);
260 } else {
261 xmlGenericError(xmlGenericErrorContext,
262 "xmlEscapeEntities : char out of range\n");
263 in++;
264 goto error;
267 *outlen = out - outstart;
268 *inlen = in - base;
269 return(0);
270 error:
271 *outlen = out - outstart;
272 *inlen = in - base;
273 return(-1);
276 /************************************************************************
278 * Allocation and deallocation *
280 ************************************************************************/
282 * xmlSaveCtxtInit:
283 * @ctxt: the saving context
285 * Initialize a saving context
287 static void
288 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
290 int i;
291 int len;
293 if (ctxt == NULL) return;
294 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
295 ctxt->escape = xmlEscapeEntities;
296 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
297 if ((xmlTreeIndentString == NULL) || (len == 0)) {
298 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
299 } else {
300 ctxt->indent_size = len;
301 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
302 for (i = 0;i < ctxt->indent_nr;i++)
303 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
304 ctxt->indent_size);
305 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
308 if (xmlSaveNoEmptyTags) {
309 ctxt->options |= XML_SAVE_NO_EMPTY;
314 * xmlFreeSaveCtxt:
316 * Free a saving context, destroying the output in any remaining buffer
318 static void
319 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
321 if (ctxt == NULL) return;
322 if (ctxt->encoding != NULL)
323 xmlFree((char *) ctxt->encoding);
324 if (ctxt->buf != NULL)
325 xmlOutputBufferClose(ctxt->buf);
326 xmlFree(ctxt);
330 * xmlNewSaveCtxt:
332 * Create a new saving context
334 * Returns the new structure or NULL in case of error
336 static xmlSaveCtxtPtr
337 xmlNewSaveCtxt(const char *encoding, int options)
339 xmlSaveCtxtPtr ret;
341 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
342 if (ret == NULL) {
343 xmlSaveErrMemory("creating saving context");
344 return ( NULL );
346 memset(ret, 0, sizeof(xmlSaveCtxt));
348 if (encoding != NULL) {
349 ret->handler = xmlFindCharEncodingHandler(encoding);
350 if (ret->handler == NULL) {
351 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
352 xmlFreeSaveCtxt(ret);
353 return(NULL);
355 ret->encoding = xmlStrdup((const xmlChar *)encoding);
356 ret->escape = NULL;
358 xmlSaveCtxtInit(ret);
361 * Use the options
364 /* Re-check this option as it may already have been set */
365 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
366 options |= XML_SAVE_NO_EMPTY;
369 ret->options = options;
370 if (options & XML_SAVE_FORMAT)
371 ret->format = 1;
372 else if (options & XML_SAVE_WSNONSIG)
373 ret->format = 2;
375 return(ret);
378 /************************************************************************
380 * Dumping XML tree content to a simple buffer *
382 ************************************************************************/
384 * xmlAttrSerializeContent:
385 * @buf: the XML buffer output
386 * @doc: the document
387 * @attr: the attribute pointer
389 * Serialize the attribute in the buffer
391 static void
392 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
394 xmlNodePtr children;
396 children = attr->children;
397 while (children != NULL) {
398 switch (children->type) {
399 case XML_TEXT_NODE:
400 xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
401 attr, children->content);
402 break;
403 case XML_ENTITY_REF_NODE:
404 xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
405 xmlBufAdd(buf->buffer, children->name,
406 xmlStrlen(children->name));
407 xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
408 break;
409 default:
410 /* should not happen unless we have a badly built tree */
411 break;
413 children = children->next;
418 * xmlBufDumpNotationTable:
419 * @buf: an xmlBufPtr output
420 * @table: A notation table
422 * This will dump the content of the notation table as an XML DTD definition
424 static void
425 xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
426 xmlBufferPtr buffer;
428 buffer = xmlBufferCreate();
429 if (buffer == NULL) {
431 * TODO set the error in buf
433 return;
435 xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
436 xmlDumpNotationTable(buffer, table);
437 xmlBufMergeBuffer(buf, buffer);
441 * xmlBufDumpElementDecl:
442 * @buf: an xmlBufPtr output
443 * @elem: An element table
445 * This will dump the content of the element declaration as an XML
446 * DTD definition
448 static void
449 xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
450 xmlBufferPtr buffer;
452 buffer = xmlBufferCreate();
453 if (buffer == NULL) {
455 * TODO set the error in buf
457 return;
459 xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
460 xmlDumpElementDecl(buffer, elem);
461 xmlBufMergeBuffer(buf, buffer);
465 * xmlBufDumpAttributeDecl:
466 * @buf: an xmlBufPtr output
467 * @attr: An attribute declaration
469 * This will dump the content of the attribute declaration as an XML
470 * DTD definition
472 static void
473 xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
474 xmlBufferPtr buffer;
476 buffer = xmlBufferCreate();
477 if (buffer == NULL) {
479 * TODO set the error in buf
481 return;
483 xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
484 xmlDumpAttributeDecl(buffer, attr);
485 xmlBufMergeBuffer(buf, buffer);
489 * xmlBufDumpEntityDecl:
490 * @buf: an xmlBufPtr output
491 * @ent: An entity table
493 * This will dump the content of the entity table as an XML DTD definition
495 static void
496 xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
497 xmlBufferPtr buffer;
499 buffer = xmlBufferCreate();
500 if (buffer == NULL) {
502 * TODO set the error in buf
504 return;
506 xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
507 xmlDumpEntityDecl(buffer, ent);
508 xmlBufMergeBuffer(buf, buffer);
511 /************************************************************************
513 * Dumping XML tree content to an I/O output buffer *
515 ************************************************************************/
517 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
518 xmlOutputBufferPtr buf = ctxt->buf;
520 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
521 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
522 if (buf->encoder == NULL) {
523 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
524 (const char *)encoding);
525 return(-1);
527 buf->conv = xmlBufCreate();
528 if (buf->conv == NULL) {
529 xmlCharEncCloseFunc(buf->encoder);
530 xmlSaveErrMemory("creating encoding buffer");
531 return(-1);
534 * initialize the state, e.g. if outputting a BOM
536 xmlCharEncOutput(buf, 1);
538 return(0);
541 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
542 xmlOutputBufferPtr buf = ctxt->buf;
543 xmlOutputBufferFlush(buf);
544 xmlCharEncCloseFunc(buf->encoder);
545 xmlBufFree(buf->conv);
546 buf->encoder = NULL;
547 buf->conv = NULL;
548 return(0);
551 #ifdef LIBXML_HTML_ENABLED
552 static void
553 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
554 #endif
555 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
556 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
559 * xmlOutputBufferWriteWSNonSig:
560 * @ctxt: The save context
561 * @extra: Number of extra indents to apply to ctxt->level
563 * Write out formatting for non-significant whitespace output.
565 static void
566 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
568 int i;
569 if ((ctxt == NULL) || (ctxt->buf == NULL))
570 return;
571 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
572 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
573 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
574 ((ctxt->level + extra - i) > ctxt->indent_nr ?
575 ctxt->indent_nr : (ctxt->level + extra - i)),
576 ctxt->indent);
581 * xmlNsDumpOutput:
582 * @buf: the XML buffer output
583 * @cur: a namespace
584 * @ctxt: the output save context. Optional.
586 * Dump a local Namespace definition.
587 * Should be called in the context of attributes dumps.
588 * If @ctxt is supplied, @buf should be its buffer.
590 static void
591 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
592 if ((cur == NULL) || (buf == NULL)) return;
593 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
594 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
595 return;
597 if (ctxt != NULL && ctxt->format == 2)
598 xmlOutputBufferWriteWSNonSig(ctxt, 2);
599 else
600 xmlOutputBufferWrite(buf, 1, " ");
602 /* Within the context of an element attributes */
603 if (cur->prefix != NULL) {
604 xmlOutputBufferWrite(buf, 6, "xmlns:");
605 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
606 } else
607 xmlOutputBufferWrite(buf, 5, "xmlns");
608 xmlOutputBufferWrite(buf, 1, "=");
609 xmlBufWriteQuotedString(buf->buffer, cur->href);
614 * xmlNsDumpOutputCtxt
615 * @ctxt: the save context
616 * @cur: a namespace
618 * Dump a local Namespace definition to a save context.
619 * Should be called in the context of attribute dumps.
621 static void
622 xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
623 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
627 * xmlNsListDumpOutputCtxt
628 * @ctxt: the save context
629 * @cur: the first namespace
631 * Dump a list of local namespace definitions to a save context.
632 * Should be called in the context of attribute dumps.
634 static void
635 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
636 while (cur != NULL) {
637 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
638 cur = cur->next;
643 * xmlNsListDumpOutput:
644 * @buf: the XML buffer output
645 * @cur: the first namespace
647 * Dump a list of local Namespace definitions.
648 * Should be called in the context of attributes dumps.
650 void
651 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
652 while (cur != NULL) {
653 xmlNsDumpOutput(buf, cur, NULL);
654 cur = cur->next;
659 * xmlDtdDumpOutput:
660 * @buf: the XML buffer output
661 * @dtd: the pointer to the DTD
663 * Dump the XML document DTD, if any.
665 static void
666 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
667 xmlOutputBufferPtr buf;
668 xmlNodePtr cur;
669 int format, level;
671 if (dtd == NULL) return;
672 if ((ctxt == NULL) || (ctxt->buf == NULL))
673 return;
674 buf = ctxt->buf;
675 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
676 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
677 if (dtd->ExternalID != NULL) {
678 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
679 xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
680 xmlOutputBufferWrite(buf, 1, " ");
681 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
682 } else if (dtd->SystemID != NULL) {
683 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
684 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
686 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
687 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
688 (dtd->pentities == NULL)) {
689 xmlOutputBufferWrite(buf, 1, ">");
690 return;
692 xmlOutputBufferWrite(buf, 3, " [\n");
694 * Dump the notations first they are not in the DTD children list
695 * Do this only on a standalone DTD or on the internal subset though.
697 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
698 (dtd->doc->intSubset == dtd))) {
699 xmlBufDumpNotationTable(buf->buffer,
700 (xmlNotationTablePtr) dtd->notations);
702 format = ctxt->format;
703 level = ctxt->level;
704 ctxt->format = 0;
705 ctxt->level = -1;
706 for (cur = dtd->children; cur != NULL; cur = cur->next) {
707 xmlNodeDumpOutputInternal(ctxt, cur);
709 ctxt->format = format;
710 ctxt->level = level;
711 xmlOutputBufferWrite(buf, 2, "]>");
715 * xmlAttrDumpOutput:
716 * @buf: the XML buffer output
717 * @cur: the attribute pointer
719 * Dump an XML attribute
721 static void
722 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
723 xmlOutputBufferPtr buf;
725 if (cur == NULL) return;
726 buf = ctxt->buf;
727 if (buf == NULL) return;
728 if (ctxt->format == 2)
729 xmlOutputBufferWriteWSNonSig(ctxt, 2);
730 else
731 xmlOutputBufferWrite(buf, 1, " ");
732 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
733 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
734 xmlOutputBufferWrite(buf, 1, ":");
736 xmlOutputBufferWriteString(buf, (const char *)cur->name);
737 xmlOutputBufferWrite(buf, 2, "=\"");
738 xmlAttrSerializeContent(buf, cur);
739 xmlOutputBufferWrite(buf, 1, "\"");
742 #ifdef LIBXML_HTML_ENABLED
744 * htmlNodeDumpOutputInternal:
745 * @cur: the current node
747 * Dump an HTML node, recursive behaviour, children are printed too.
749 static int
750 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
751 const xmlChar *oldenc = NULL;
752 const xmlChar *oldctxtenc = ctxt->encoding;
753 const xmlChar *encoding = ctxt->encoding;
754 xmlOutputBufferPtr buf = ctxt->buf;
755 int switched_encoding = 0;
756 xmlDocPtr doc;
758 xmlInitParser();
760 doc = cur->doc;
761 if (doc != NULL) {
762 oldenc = doc->encoding;
763 if (ctxt->encoding != NULL) {
764 doc->encoding = BAD_CAST ctxt->encoding;
765 } else if (doc->encoding != NULL) {
766 encoding = doc->encoding;
770 if ((encoding != NULL) && (doc != NULL))
771 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
772 if ((encoding == NULL) && (doc != NULL))
773 encoding = htmlGetMetaEncoding(doc);
774 if (encoding == NULL)
775 encoding = BAD_CAST "HTML";
776 if ((encoding != NULL) && (oldctxtenc == NULL) &&
777 (buf->encoder == NULL) && (buf->conv == NULL)) {
778 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
779 doc->encoding = oldenc;
780 return(-1);
782 switched_encoding = 1;
784 if (ctxt->options & XML_SAVE_FORMAT)
785 htmlNodeDumpFormatOutput(buf, doc, cur,
786 (const char *)encoding, 1);
787 else
788 htmlNodeDumpFormatOutput(buf, doc, cur,
789 (const char *)encoding, 0);
791 * Restore the state of the saving context at the end of the document
793 if ((switched_encoding) && (oldctxtenc == NULL)) {
794 xmlSaveClearEncoding(ctxt);
796 if (doc != NULL)
797 doc->encoding = oldenc;
798 return(0);
800 #endif
803 * xmlNodeDumpOutputInternal:
804 * @cur: the current node
806 * Dump an XML node, recursive behaviour, children are printed too.
808 static void
809 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
810 int format = ctxt->format;
811 xmlNodePtr tmp, root, unformattedNode = NULL, parent;
812 xmlAttrPtr attr;
813 xmlChar *start, *end;
814 xmlOutputBufferPtr buf;
816 if (cur == NULL) return;
817 buf = ctxt->buf;
819 root = cur;
820 parent = cur->parent;
821 while (1) {
822 switch (cur->type) {
823 case XML_DOCUMENT_NODE:
824 case XML_HTML_DOCUMENT_NODE:
825 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
826 break;
828 case XML_DTD_NODE:
829 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
830 break;
832 case XML_DOCUMENT_FRAG_NODE:
833 /* Always validate cur->parent when descending. */
834 if ((cur->parent == parent) && (cur->children != NULL)) {
835 parent = cur;
836 cur = cur->children;
837 continue;
839 break;
841 case XML_ELEMENT_DECL:
842 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
843 break;
845 case XML_ATTRIBUTE_DECL:
846 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
847 break;
849 case XML_ENTITY_DECL:
850 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
851 break;
853 case XML_ELEMENT_NODE:
854 if ((cur != root) && (ctxt->format == 1) &&
855 (xmlIndentTreeOutput))
856 xmlOutputBufferWrite(buf, ctxt->indent_size *
857 (ctxt->level > ctxt->indent_nr ?
858 ctxt->indent_nr : ctxt->level),
859 ctxt->indent);
862 * Some users like lxml are known to pass nodes with a corrupted
863 * tree structure. Fall back to a recursive call to handle this
864 * case.
866 if ((cur->parent != parent) && (cur->children != NULL)) {
867 xmlNodeDumpOutputInternal(ctxt, cur);
868 break;
871 xmlOutputBufferWrite(buf, 1, "<");
872 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
873 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
874 xmlOutputBufferWrite(buf, 1, ":");
876 xmlOutputBufferWriteString(buf, (const char *)cur->name);
877 if (cur->nsDef)
878 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
879 for (attr = cur->properties; attr != NULL; attr = attr->next)
880 xmlAttrDumpOutput(ctxt, attr);
882 if (cur->children == NULL) {
883 if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
884 if (ctxt->format == 2)
885 xmlOutputBufferWriteWSNonSig(ctxt, 0);
886 xmlOutputBufferWrite(buf, 2, "/>");
887 } else {
888 if (ctxt->format == 2)
889 xmlOutputBufferWriteWSNonSig(ctxt, 1);
890 xmlOutputBufferWrite(buf, 3, "></");
891 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
892 xmlOutputBufferWriteString(buf,
893 (const char *)cur->ns->prefix);
894 xmlOutputBufferWrite(buf, 1, ":");
896 xmlOutputBufferWriteString(buf, (const char *)cur->name);
897 if (ctxt->format == 2)
898 xmlOutputBufferWriteWSNonSig(ctxt, 0);
899 xmlOutputBufferWrite(buf, 1, ">");
901 } else {
902 if (ctxt->format == 1) {
903 tmp = cur->children;
904 while (tmp != NULL) {
905 if ((tmp->type == XML_TEXT_NODE) ||
906 (tmp->type == XML_CDATA_SECTION_NODE) ||
907 (tmp->type == XML_ENTITY_REF_NODE)) {
908 ctxt->format = 0;
909 unformattedNode = cur;
910 break;
912 tmp = tmp->next;
915 if (ctxt->format == 2)
916 xmlOutputBufferWriteWSNonSig(ctxt, 1);
917 xmlOutputBufferWrite(buf, 1, ">");
918 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
919 if (ctxt->level >= 0) ctxt->level++;
920 parent = cur;
921 cur = cur->children;
922 continue;
925 break;
927 case XML_TEXT_NODE:
928 if (cur->content == NULL)
929 break;
930 if (cur->name != xmlStringTextNoenc) {
931 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
932 } else {
934 * Disable escaping, needed for XSLT
936 xmlOutputBufferWriteString(buf, (const char *) cur->content);
938 break;
940 case XML_PI_NODE:
941 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
942 xmlOutputBufferWrite(buf, ctxt->indent_size *
943 (ctxt->level > ctxt->indent_nr ?
944 ctxt->indent_nr : ctxt->level),
945 ctxt->indent);
947 if (cur->content != NULL) {
948 xmlOutputBufferWrite(buf, 2, "<?");
949 xmlOutputBufferWriteString(buf, (const char *)cur->name);
950 if (cur->content != NULL) {
951 if (ctxt->format == 2)
952 xmlOutputBufferWriteWSNonSig(ctxt, 0);
953 else
954 xmlOutputBufferWrite(buf, 1, " ");
955 xmlOutputBufferWriteString(buf,
956 (const char *)cur->content);
958 xmlOutputBufferWrite(buf, 2, "?>");
959 } else {
960 xmlOutputBufferWrite(buf, 2, "<?");
961 xmlOutputBufferWriteString(buf, (const char *)cur->name);
962 if (ctxt->format == 2)
963 xmlOutputBufferWriteWSNonSig(ctxt, 0);
964 xmlOutputBufferWrite(buf, 2, "?>");
966 break;
968 case XML_COMMENT_NODE:
969 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
970 xmlOutputBufferWrite(buf, ctxt->indent_size *
971 (ctxt->level > ctxt->indent_nr ?
972 ctxt->indent_nr : ctxt->level),
973 ctxt->indent);
975 if (cur->content != NULL) {
976 xmlOutputBufferWrite(buf, 4, "<!--");
977 xmlOutputBufferWriteString(buf, (const char *)cur->content);
978 xmlOutputBufferWrite(buf, 3, "-->");
980 break;
982 case XML_ENTITY_REF_NODE:
983 xmlOutputBufferWrite(buf, 1, "&");
984 xmlOutputBufferWriteString(buf, (const char *)cur->name);
985 xmlOutputBufferWrite(buf, 1, ";");
986 break;
988 case XML_CDATA_SECTION_NODE:
989 if (cur->content == NULL || *cur->content == '\0') {
990 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
991 } else {
992 start = end = cur->content;
993 while (*end != '\0') {
994 if ((*end == ']') && (*(end + 1) == ']') &&
995 (*(end + 2) == '>')) {
996 end = end + 2;
997 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
998 xmlOutputBufferWrite(buf, end - start,
999 (const char *)start);
1000 xmlOutputBufferWrite(buf, 3, "]]>");
1001 start = end;
1003 end++;
1005 if (start != end) {
1006 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1007 xmlOutputBufferWriteString(buf, (const char *)start);
1008 xmlOutputBufferWrite(buf, 3, "]]>");
1011 break;
1013 case XML_ATTRIBUTE_NODE:
1014 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1015 break;
1017 case XML_NAMESPACE_DECL:
1018 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1019 break;
1021 default:
1022 break;
1025 while (1) {
1026 if (cur == root)
1027 return;
1028 if ((ctxt->format == 1) &&
1029 (cur->type != XML_XINCLUDE_START) &&
1030 (cur->type != XML_XINCLUDE_END))
1031 xmlOutputBufferWrite(buf, 1, "\n");
1032 if (cur->next != NULL) {
1033 cur = cur->next;
1034 break;
1037 cur = parent;
1038 /* cur->parent was validated when descending. */
1039 parent = cur->parent;
1041 if (cur->type == XML_ELEMENT_NODE) {
1042 if (ctxt->level > 0) ctxt->level--;
1043 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1044 xmlOutputBufferWrite(buf, ctxt->indent_size *
1045 (ctxt->level > ctxt->indent_nr ?
1046 ctxt->indent_nr : ctxt->level),
1047 ctxt->indent);
1049 xmlOutputBufferWrite(buf, 2, "</");
1050 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1051 xmlOutputBufferWriteString(buf,
1052 (const char *)cur->ns->prefix);
1053 xmlOutputBufferWrite(buf, 1, ":");
1056 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1057 if (ctxt->format == 2)
1058 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1059 xmlOutputBufferWrite(buf, 1, ">");
1061 if (cur == unformattedNode) {
1062 ctxt->format = format;
1063 unformattedNode = NULL;
1071 * xmlDocContentDumpOutput:
1072 * @cur: the document
1074 * Dump an XML document.
1076 static int
1077 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1078 #ifdef LIBXML_HTML_ENABLED
1079 xmlDtdPtr dtd;
1080 int is_xhtml = 0;
1081 #endif
1082 const xmlChar *oldenc = cur->encoding;
1083 const xmlChar *oldctxtenc = ctxt->encoding;
1084 const xmlChar *encoding = ctxt->encoding;
1085 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1086 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1087 xmlOutputBufferPtr buf = ctxt->buf;
1088 xmlCharEncoding enc;
1089 int switched_encoding = 0;
1091 xmlInitParser();
1093 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1094 (cur->type != XML_DOCUMENT_NODE))
1095 return(-1);
1097 if (ctxt->encoding != NULL) {
1098 cur->encoding = BAD_CAST ctxt->encoding;
1099 } else if (cur->encoding != NULL) {
1100 encoding = cur->encoding;
1103 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1104 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1105 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1106 (ctxt->options & XML_SAVE_AS_HTML)) {
1107 #ifdef LIBXML_HTML_ENABLED
1108 if (encoding != NULL)
1109 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1110 if (encoding == NULL)
1111 encoding = htmlGetMetaEncoding(cur);
1112 if (encoding == NULL)
1113 encoding = BAD_CAST "HTML";
1114 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1115 (buf->encoder == NULL) && (buf->conv == NULL)) {
1116 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1117 cur->encoding = oldenc;
1118 return(-1);
1121 if (ctxt->options & XML_SAVE_FORMAT)
1122 htmlDocContentDumpFormatOutput(buf, cur,
1123 (const char *)encoding, 1);
1124 else
1125 htmlDocContentDumpFormatOutput(buf, cur,
1126 (const char *)encoding, 0);
1127 if (ctxt->encoding != NULL)
1128 cur->encoding = oldenc;
1129 return(0);
1130 #else
1131 return(-1);
1132 #endif
1133 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1134 (ctxt->options & XML_SAVE_AS_XML) ||
1135 (ctxt->options & XML_SAVE_XHTML)) {
1136 enc = xmlParseCharEncoding((const char*) encoding);
1137 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1138 (buf->encoder == NULL) && (buf->conv == NULL) &&
1139 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1140 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1141 (enc != XML_CHAR_ENCODING_NONE) &&
1142 (enc != XML_CHAR_ENCODING_ASCII)) {
1144 * we need to switch to this encoding but just for this
1145 * document since we output the XMLDecl the conversion
1146 * must be done to not generate not well formed documents.
1148 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1149 cur->encoding = oldenc;
1150 return(-1);
1152 switched_encoding = 1;
1154 if (ctxt->escape == xmlEscapeEntities)
1155 ctxt->escape = NULL;
1156 if (ctxt->escapeAttr == xmlEscapeEntities)
1157 ctxt->escapeAttr = NULL;
1162 * Save the XML declaration
1164 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1165 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1166 if (cur->version != NULL)
1167 xmlBufWriteQuotedString(buf->buffer, cur->version);
1168 else
1169 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1170 if (encoding != NULL) {
1171 xmlOutputBufferWrite(buf, 10, " encoding=");
1172 xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1174 switch (cur->standalone) {
1175 case 0:
1176 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1177 break;
1178 case 1:
1179 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1180 break;
1182 xmlOutputBufferWrite(buf, 3, "?>\n");
1185 #ifdef LIBXML_HTML_ENABLED
1186 if (ctxt->options & XML_SAVE_XHTML)
1187 is_xhtml = 1;
1188 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1189 dtd = xmlGetIntSubset(cur);
1190 if (dtd != NULL) {
1191 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1192 if (is_xhtml < 0) is_xhtml = 0;
1195 #endif
1196 if (cur->children != NULL) {
1197 xmlNodePtr child = cur->children;
1199 while (child != NULL) {
1200 ctxt->level = 0;
1201 #ifdef LIBXML_HTML_ENABLED
1202 if (is_xhtml)
1203 xhtmlNodeDumpOutput(ctxt, child);
1204 else
1205 #endif
1206 xmlNodeDumpOutputInternal(ctxt, child);
1207 if ((child->type != XML_XINCLUDE_START) &&
1208 (child->type != XML_XINCLUDE_END))
1209 xmlOutputBufferWrite(buf, 1, "\n");
1210 child = child->next;
1216 * Restore the state of the saving context at the end of the document
1218 if ((switched_encoding) && (oldctxtenc == NULL)) {
1219 xmlSaveClearEncoding(ctxt);
1220 ctxt->escape = oldescape;
1221 ctxt->escapeAttr = oldescapeAttr;
1223 cur->encoding = oldenc;
1224 return(0);
1227 #ifdef LIBXML_HTML_ENABLED
1228 /************************************************************************
1230 * Functions specific to XHTML serialization *
1232 ************************************************************************/
1235 * xhtmlIsEmpty:
1236 * @node: the node
1238 * Check if a node is an empty xhtml node
1240 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1242 static int
1243 xhtmlIsEmpty(xmlNodePtr node) {
1244 if (node == NULL)
1245 return(-1);
1246 if (node->type != XML_ELEMENT_NODE)
1247 return(0);
1248 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1249 return(0);
1250 if (node->children != NULL)
1251 return(0);
1252 switch (node->name[0]) {
1253 case 'a':
1254 if (xmlStrEqual(node->name, BAD_CAST "area"))
1255 return(1);
1256 return(0);
1257 case 'b':
1258 if (xmlStrEqual(node->name, BAD_CAST "br"))
1259 return(1);
1260 if (xmlStrEqual(node->name, BAD_CAST "base"))
1261 return(1);
1262 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1263 return(1);
1264 return(0);
1265 case 'c':
1266 if (xmlStrEqual(node->name, BAD_CAST "col"))
1267 return(1);
1268 return(0);
1269 case 'f':
1270 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1271 return(1);
1272 return(0);
1273 case 'h':
1274 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1275 return(1);
1276 return(0);
1277 case 'i':
1278 if (xmlStrEqual(node->name, BAD_CAST "img"))
1279 return(1);
1280 if (xmlStrEqual(node->name, BAD_CAST "input"))
1281 return(1);
1282 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1283 return(1);
1284 return(0);
1285 case 'l':
1286 if (xmlStrEqual(node->name, BAD_CAST "link"))
1287 return(1);
1288 return(0);
1289 case 'm':
1290 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1291 return(1);
1292 return(0);
1293 case 'p':
1294 if (xmlStrEqual(node->name, BAD_CAST "param"))
1295 return(1);
1296 return(0);
1298 return(0);
1302 * xhtmlAttrListDumpOutput:
1303 * @cur: the first attribute pointer
1305 * Dump a list of XML attributes
1307 static void
1308 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1309 xmlAttrPtr xml_lang = NULL;
1310 xmlAttrPtr lang = NULL;
1311 xmlAttrPtr name = NULL;
1312 xmlAttrPtr id = NULL;
1313 xmlNodePtr parent;
1314 xmlOutputBufferPtr buf;
1316 if (cur == NULL) return;
1317 buf = ctxt->buf;
1318 parent = cur->parent;
1319 while (cur != NULL) {
1320 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1321 id = cur;
1322 else
1323 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1324 name = cur;
1325 else
1326 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1327 lang = cur;
1328 else
1329 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1330 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1331 xml_lang = cur;
1332 else if ((cur->ns == NULL) &&
1333 ((cur->children == NULL) ||
1334 (cur->children->content == NULL) ||
1335 (cur->children->content[0] == 0)) &&
1336 (htmlIsBooleanAttr(cur->name))) {
1337 if (cur->children != NULL)
1338 xmlFreeNode(cur->children);
1339 cur->children = xmlNewDocText(cur->doc, cur->name);
1340 if (cur->children != NULL)
1341 cur->children->parent = (xmlNodePtr) cur;
1343 xmlAttrDumpOutput(ctxt, cur);
1344 cur = cur->next;
1347 * C.8
1349 if ((name != NULL) && (id == NULL)) {
1350 if ((parent != NULL) && (parent->name != NULL) &&
1351 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1352 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1353 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1354 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1355 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1356 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1357 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1358 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1359 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1360 xmlOutputBufferWrite(buf, 5, " id=\"");
1361 xmlAttrSerializeContent(buf, name);
1362 xmlOutputBufferWrite(buf, 1, "\"");
1366 * C.7.
1368 if ((lang != NULL) && (xml_lang == NULL)) {
1369 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1370 xmlAttrSerializeContent(buf, lang);
1371 xmlOutputBufferWrite(buf, 1, "\"");
1372 } else
1373 if ((xml_lang != NULL) && (lang == NULL)) {
1374 xmlOutputBufferWrite(buf, 7, " lang=\"");
1375 xmlAttrSerializeContent(buf, xml_lang);
1376 xmlOutputBufferWrite(buf, 1, "\"");
1381 * xhtmlNodeDumpOutput:
1382 * @buf: the XML buffer output
1383 * @doc: the XHTML document
1384 * @cur: the current node
1385 * @level: the imbrication level for indenting
1386 * @format: is formatting allowed
1387 * @encoding: an optional encoding string
1389 * Dump an XHTML node, recursive behaviour, children are printed too.
1391 static void
1392 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1393 int format = ctxt->format, addmeta;
1394 xmlNodePtr tmp, root, unformattedNode = NULL;
1395 xmlChar *start, *end;
1396 xmlOutputBufferPtr buf = ctxt->buf;
1398 if (cur == NULL) return;
1400 root = cur;
1401 while (1) {
1402 switch (cur->type) {
1403 case XML_DOCUMENT_NODE:
1404 case XML_HTML_DOCUMENT_NODE:
1405 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1406 break;
1408 case XML_NAMESPACE_DECL:
1409 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1410 break;
1412 case XML_DTD_NODE:
1413 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1414 break;
1416 case XML_DOCUMENT_FRAG_NODE:
1417 if (cur->children) {
1418 cur = cur->children;
1419 continue;
1421 break;
1423 case XML_ELEMENT_DECL:
1424 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1425 break;
1427 case XML_ATTRIBUTE_DECL:
1428 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1429 break;
1431 case XML_ENTITY_DECL:
1432 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1433 break;
1435 case XML_ELEMENT_NODE:
1436 addmeta = 0;
1438 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1439 xmlOutputBufferWrite(buf, ctxt->indent_size *
1440 (ctxt->level > ctxt->indent_nr ?
1441 ctxt->indent_nr : ctxt->level),
1442 ctxt->indent);
1444 xmlOutputBufferWrite(buf, 1, "<");
1445 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1446 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1447 xmlOutputBufferWrite(buf, 1, ":");
1450 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1451 if (cur->nsDef)
1452 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1453 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1454 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1456 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1458 xmlOutputBufferWriteString(buf,
1459 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1461 if (cur->properties != NULL)
1462 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1464 if ((cur->parent != NULL) &&
1465 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1466 xmlStrEqual(cur->name, BAD_CAST"head") &&
1467 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1469 tmp = cur->children;
1470 while (tmp != NULL) {
1471 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1472 xmlChar *httpequiv;
1474 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1475 if (httpequiv != NULL) {
1476 if (xmlStrcasecmp(httpequiv,
1477 BAD_CAST"Content-Type") == 0) {
1478 xmlFree(httpequiv);
1479 break;
1481 xmlFree(httpequiv);
1484 tmp = tmp->next;
1486 if (tmp == NULL)
1487 addmeta = 1;
1490 if (cur->children == NULL) {
1491 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1492 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1494 * C.2. Empty Elements
1496 xmlOutputBufferWrite(buf, 3, " />");
1497 } else {
1498 if (addmeta == 1) {
1499 xmlOutputBufferWrite(buf, 1, ">");
1500 if (ctxt->format == 1) {
1501 xmlOutputBufferWrite(buf, 1, "\n");
1502 if (xmlIndentTreeOutput)
1503 xmlOutputBufferWrite(buf, ctxt->indent_size *
1504 (ctxt->level + 1 > ctxt->indent_nr ?
1505 ctxt->indent_nr : ctxt->level + 1),
1506 ctxt->indent);
1508 xmlOutputBufferWriteString(buf,
1509 "<meta http-equiv=\"Content-Type\" "
1510 "content=\"text/html; charset=");
1511 if (ctxt->encoding) {
1512 xmlOutputBufferWriteString(buf,
1513 (const char *)ctxt->encoding);
1514 } else {
1515 xmlOutputBufferWrite(buf, 5, "UTF-8");
1517 xmlOutputBufferWrite(buf, 4, "\" />");
1518 if (ctxt->format == 1)
1519 xmlOutputBufferWrite(buf, 1, "\n");
1520 } else {
1521 xmlOutputBufferWrite(buf, 1, ">");
1524 * C.3. Element Minimization and Empty Element Content
1526 xmlOutputBufferWrite(buf, 2, "</");
1527 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1528 xmlOutputBufferWriteString(buf,
1529 (const char *)cur->ns->prefix);
1530 xmlOutputBufferWrite(buf, 1, ":");
1532 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1533 xmlOutputBufferWrite(buf, 1, ">");
1535 } else {
1536 xmlOutputBufferWrite(buf, 1, ">");
1537 if (addmeta == 1) {
1538 if (ctxt->format == 1) {
1539 xmlOutputBufferWrite(buf, 1, "\n");
1540 if (xmlIndentTreeOutput)
1541 xmlOutputBufferWrite(buf, ctxt->indent_size *
1542 (ctxt->level + 1 > ctxt->indent_nr ?
1543 ctxt->indent_nr : ctxt->level + 1),
1544 ctxt->indent);
1546 xmlOutputBufferWriteString(buf,
1547 "<meta http-equiv=\"Content-Type\" "
1548 "content=\"text/html; charset=");
1549 if (ctxt->encoding) {
1550 xmlOutputBufferWriteString(buf,
1551 (const char *)ctxt->encoding);
1552 } else {
1553 xmlOutputBufferWrite(buf, 5, "UTF-8");
1555 xmlOutputBufferWrite(buf, 4, "\" />");
1558 if (ctxt->format == 1) {
1559 tmp = cur->children;
1560 while (tmp != NULL) {
1561 if ((tmp->type == XML_TEXT_NODE) ||
1562 (tmp->type == XML_ENTITY_REF_NODE)) {
1563 unformattedNode = cur;
1564 ctxt->format = 0;
1565 break;
1567 tmp = tmp->next;
1571 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1572 if (ctxt->level >= 0) ctxt->level++;
1573 cur = cur->children;
1574 continue;
1577 break;
1579 case XML_TEXT_NODE:
1580 if (cur->content == NULL)
1581 break;
1582 if ((cur->name == xmlStringText) ||
1583 (cur->name != xmlStringTextNoenc)) {
1584 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1585 } else {
1587 * Disable escaping, needed for XSLT
1589 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1591 break;
1593 case XML_PI_NODE:
1594 if (cur->content != NULL) {
1595 xmlOutputBufferWrite(buf, 2, "<?");
1596 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1597 if (cur->content != NULL) {
1598 xmlOutputBufferWrite(buf, 1, " ");
1599 xmlOutputBufferWriteString(buf,
1600 (const char *)cur->content);
1602 xmlOutputBufferWrite(buf, 2, "?>");
1603 } else {
1604 xmlOutputBufferWrite(buf, 2, "<?");
1605 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1606 xmlOutputBufferWrite(buf, 2, "?>");
1608 break;
1610 case XML_COMMENT_NODE:
1611 if (cur->content != NULL) {
1612 xmlOutputBufferWrite(buf, 4, "<!--");
1613 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1614 xmlOutputBufferWrite(buf, 3, "-->");
1616 break;
1618 case XML_ENTITY_REF_NODE:
1619 xmlOutputBufferWrite(buf, 1, "&");
1620 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1621 xmlOutputBufferWrite(buf, 1, ";");
1622 break;
1624 case XML_CDATA_SECTION_NODE:
1625 if (cur->content == NULL || *cur->content == '\0') {
1626 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1627 } else {
1628 start = end = cur->content;
1629 while (*end != '\0') {
1630 if (*end == ']' && *(end + 1) == ']' &&
1631 *(end + 2) == '>') {
1632 end = end + 2;
1633 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1634 xmlOutputBufferWrite(buf, end - start,
1635 (const char *)start);
1636 xmlOutputBufferWrite(buf, 3, "]]>");
1637 start = end;
1639 end++;
1641 if (start != end) {
1642 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1643 xmlOutputBufferWriteString(buf, (const char *)start);
1644 xmlOutputBufferWrite(buf, 3, "]]>");
1647 break;
1649 case XML_ATTRIBUTE_NODE:
1650 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1651 break;
1653 default:
1654 break;
1657 while (1) {
1658 if (cur == root)
1659 return;
1660 if (ctxt->format == 1)
1661 xmlOutputBufferWrite(buf, 1, "\n");
1662 if (cur->next != NULL) {
1663 cur = cur->next;
1664 break;
1668 * The parent should never be NULL here but we want to handle
1669 * corrupted documents gracefully.
1671 if (cur->parent == NULL)
1672 return;
1673 cur = cur->parent;
1675 if (cur->type == XML_ELEMENT_NODE) {
1676 if (ctxt->level > 0) ctxt->level--;
1677 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1678 xmlOutputBufferWrite(buf, ctxt->indent_size *
1679 (ctxt->level > ctxt->indent_nr ?
1680 ctxt->indent_nr : ctxt->level),
1681 ctxt->indent);
1683 xmlOutputBufferWrite(buf, 2, "</");
1684 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1685 xmlOutputBufferWriteString(buf,
1686 (const char *)cur->ns->prefix);
1687 xmlOutputBufferWrite(buf, 1, ":");
1690 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1691 xmlOutputBufferWrite(buf, 1, ">");
1693 if (cur == unformattedNode) {
1694 ctxt->format = format;
1695 unformattedNode = NULL;
1701 #endif
1703 /************************************************************************
1705 * Public entry points *
1707 ************************************************************************/
1710 * xmlSaveToFd:
1711 * @fd: a file descriptor number
1712 * @encoding: the encoding name to use or NULL
1713 * @options: a set of xmlSaveOptions
1715 * Create a document saving context serializing to a file descriptor
1716 * with the encoding and the options given.
1718 * Returns a new serialization context or NULL in case of error.
1720 xmlSaveCtxtPtr
1721 xmlSaveToFd(int fd, const char *encoding, int options)
1723 xmlSaveCtxtPtr ret;
1725 ret = xmlNewSaveCtxt(encoding, options);
1726 if (ret == NULL) return(NULL);
1727 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1728 if (ret->buf == NULL) {
1729 xmlCharEncCloseFunc(ret->handler);
1730 xmlFreeSaveCtxt(ret);
1731 return(NULL);
1733 return(ret);
1737 * xmlSaveToFilename:
1738 * @filename: a file name or an URL
1739 * @encoding: the encoding name to use or NULL
1740 * @options: a set of xmlSaveOptions
1742 * Create a document saving context serializing to a filename or possibly
1743 * to an URL (but this is less reliable) with the encoding and the options
1744 * given.
1746 * Returns a new serialization context or NULL in case of error.
1748 xmlSaveCtxtPtr
1749 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1751 xmlSaveCtxtPtr ret;
1752 int compression = 0; /* TODO handle compression option */
1754 ret = xmlNewSaveCtxt(encoding, options);
1755 if (ret == NULL) return(NULL);
1756 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1757 compression);
1758 if (ret->buf == NULL) {
1759 xmlCharEncCloseFunc(ret->handler);
1760 xmlFreeSaveCtxt(ret);
1761 return(NULL);
1763 return(ret);
1767 * xmlSaveToBuffer:
1768 * @buffer: a buffer
1769 * @encoding: the encoding name to use or NULL
1770 * @options: a set of xmlSaveOptions
1772 * Create a document saving context serializing to a buffer
1773 * with the encoding and the options given
1775 * Returns a new serialization context or NULL in case of error.
1778 xmlSaveCtxtPtr
1779 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1781 xmlSaveCtxtPtr ret;
1783 ret = xmlNewSaveCtxt(encoding, options);
1784 if (ret == NULL) return(NULL);
1785 ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
1786 if (ret->buf == NULL) {
1787 xmlCharEncCloseFunc(ret->handler);
1788 xmlFreeSaveCtxt(ret);
1789 return(NULL);
1791 return(ret);
1795 * xmlSaveToIO:
1796 * @iowrite: an I/O write function
1797 * @ioclose: an I/O close function
1798 * @ioctx: an I/O handler
1799 * @encoding: the encoding name to use or NULL
1800 * @options: a set of xmlSaveOptions
1802 * Create a document saving context serializing to a file descriptor
1803 * with the encoding and the options given
1805 * Returns a new serialization context or NULL in case of error.
1807 xmlSaveCtxtPtr
1808 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1809 xmlOutputCloseCallback ioclose,
1810 void *ioctx, const char *encoding, int options)
1812 xmlSaveCtxtPtr ret;
1814 ret = xmlNewSaveCtxt(encoding, options);
1815 if (ret == NULL) return(NULL);
1816 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1817 if (ret->buf == NULL) {
1818 xmlCharEncCloseFunc(ret->handler);
1819 xmlFreeSaveCtxt(ret);
1820 return(NULL);
1822 return(ret);
1826 * xmlSaveDoc:
1827 * @ctxt: a document saving context
1828 * @doc: a document
1830 * Save a full document to a saving context
1831 * TODO: The function is not fully implemented yet as it does not return the
1832 * byte count but 0 instead
1834 * Returns the number of byte written or -1 in case of error
1836 long
1837 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1839 long ret = 0;
1841 if ((ctxt == NULL) || (doc == NULL)) return(-1);
1842 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1843 return(-1);
1844 return(ret);
1848 * xmlSaveTree:
1849 * @ctxt: a document saving context
1850 * @node: the top node of the subtree to save
1852 * Save a subtree starting at the node parameter to a saving context
1853 * TODO: The function is not fully implemented yet as it does not return the
1854 * byte count but 0 instead
1856 * Returns the number of byte written or -1 in case of error
1858 long
1859 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
1861 long ret = 0;
1863 if ((ctxt == NULL) || (cur == NULL)) return(-1);
1864 #ifdef LIBXML_HTML_ENABLED
1865 if (ctxt->options & XML_SAVE_XHTML) {
1866 xhtmlNodeDumpOutput(ctxt, cur);
1867 return(ret);
1869 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
1870 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
1871 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
1872 (ctxt->options & XML_SAVE_AS_HTML)) {
1873 htmlNodeDumpOutputInternal(ctxt, cur);
1874 return(ret);
1876 #endif
1877 xmlNodeDumpOutputInternal(ctxt, cur);
1878 return(ret);
1882 * xmlSaveFlush:
1883 * @ctxt: a document saving context
1885 * Flush a document saving context, i.e. make sure that all bytes have
1886 * been output.
1888 * Returns the number of byte written or -1 in case of error.
1891 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1893 if (ctxt == NULL) return(-1);
1894 if (ctxt->buf == NULL) return(-1);
1895 return(xmlOutputBufferFlush(ctxt->buf));
1899 * xmlSaveClose:
1900 * @ctxt: a document saving context
1902 * Close a document saving context, i.e. make sure that all bytes have
1903 * been output and free the associated data.
1905 * Returns the number of byte written or -1 in case of error.
1908 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1910 int ret;
1912 if (ctxt == NULL) return(-1);
1913 ret = xmlSaveFlush(ctxt);
1914 xmlFreeSaveCtxt(ctxt);
1915 return(ret);
1919 * xmlSaveSetEscape:
1920 * @ctxt: a document saving context
1921 * @escape: the escaping function
1923 * Set a custom escaping function to be used for text in element content
1925 * Returns 0 if successful or -1 in case of error.
1928 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1930 if (ctxt == NULL) return(-1);
1931 ctxt->escape = escape;
1932 return(0);
1936 * xmlSaveSetAttrEscape:
1937 * @ctxt: a document saving context
1938 * @escape: the escaping function
1940 * Set a custom escaping function to be used for text in attribute content
1942 * Returns 0 if successful or -1 in case of error.
1945 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1947 if (ctxt == NULL) return(-1);
1948 ctxt->escapeAttr = escape;
1949 return(0);
1952 /************************************************************************
1954 * Public entry points based on buffers *
1956 ************************************************************************/
1959 * xmlBufAttrSerializeTxtContent:
1960 * @buf: and xmlBufPtr output
1961 * @doc: the document
1962 * @attr: the attribute node
1963 * @string: the text content
1965 * Serialize text attribute values to an xmlBufPtr
1967 void
1968 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
1969 xmlAttrPtr attr, const xmlChar * string)
1971 xmlChar *base, *cur;
1973 if (string == NULL)
1974 return;
1975 base = cur = (xmlChar *) string;
1976 while (*cur != 0) {
1977 if (*cur == '\n') {
1978 if (base != cur)
1979 xmlBufAdd(buf, base, cur - base);
1980 xmlBufAdd(buf, BAD_CAST "&#10;", 5);
1981 cur++;
1982 base = cur;
1983 } else if (*cur == '\r') {
1984 if (base != cur)
1985 xmlBufAdd(buf, base, cur - base);
1986 xmlBufAdd(buf, BAD_CAST "&#13;", 5);
1987 cur++;
1988 base = cur;
1989 } else if (*cur == '\t') {
1990 if (base != cur)
1991 xmlBufAdd(buf, base, cur - base);
1992 xmlBufAdd(buf, BAD_CAST "&#9;", 4);
1993 cur++;
1994 base = cur;
1995 } else if (*cur == '"') {
1996 if (base != cur)
1997 xmlBufAdd(buf, base, cur - base);
1998 xmlBufAdd(buf, BAD_CAST "&quot;", 6);
1999 cur++;
2000 base = cur;
2001 } else if (*cur == '<') {
2002 if (base != cur)
2003 xmlBufAdd(buf, base, cur - base);
2004 xmlBufAdd(buf, BAD_CAST "&lt;", 4);
2005 cur++;
2006 base = cur;
2007 } else if (*cur == '>') {
2008 if (base != cur)
2009 xmlBufAdd(buf, base, cur - base);
2010 xmlBufAdd(buf, BAD_CAST "&gt;", 4);
2011 cur++;
2012 base = cur;
2013 } else if (*cur == '&') {
2014 if (base != cur)
2015 xmlBufAdd(buf, base, cur - base);
2016 xmlBufAdd(buf, BAD_CAST "&amp;", 5);
2017 cur++;
2018 base = cur;
2019 } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2020 ((doc == NULL) || (doc->encoding == NULL))) {
2022 * We assume we have UTF-8 content.
2024 unsigned char tmp[12];
2025 int val = 0, l = 1;
2027 if (base != cur)
2028 xmlBufAdd(buf, base, cur - base);
2029 if (*cur < 0xC0) {
2030 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2031 xmlSerializeHexCharRef(tmp, *cur);
2032 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2033 cur++;
2034 base = cur;
2035 continue;
2036 } else if (*cur < 0xE0) {
2037 val = (cur[0]) & 0x1F;
2038 val <<= 6;
2039 val |= (cur[1]) & 0x3F;
2040 l = 2;
2041 } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2042 val = (cur[0]) & 0x0F;
2043 val <<= 6;
2044 val |= (cur[1]) & 0x3F;
2045 val <<= 6;
2046 val |= (cur[2]) & 0x3F;
2047 l = 3;
2048 } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2049 val = (cur[0]) & 0x07;
2050 val <<= 6;
2051 val |= (cur[1]) & 0x3F;
2052 val <<= 6;
2053 val |= (cur[2]) & 0x3F;
2054 val <<= 6;
2055 val |= (cur[3]) & 0x3F;
2056 l = 4;
2058 if ((l == 1) || (!IS_CHAR(val))) {
2059 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2060 xmlSerializeHexCharRef(tmp, *cur);
2061 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2062 cur++;
2063 base = cur;
2064 continue;
2067 * We could do multiple things here. Just save
2068 * as a char ref
2070 xmlSerializeHexCharRef(tmp, val);
2071 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2072 cur += l;
2073 base = cur;
2074 } else {
2075 cur++;
2078 if (base != cur)
2079 xmlBufAdd(buf, base, cur - base);
2083 * xmlAttrSerializeTxtContent:
2084 * @buf: the XML buffer output
2085 * @doc: the document
2086 * @attr: the attribute node
2087 * @string: the text content
2089 * Serialize text attribute values to an xml simple buffer
2091 void
2092 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2093 xmlAttrPtr attr, const xmlChar * string)
2095 xmlBufPtr buffer;
2097 if ((buf == NULL) || (string == NULL))
2098 return;
2099 buffer = xmlBufFromBuffer(buf);
2100 if (buffer == NULL)
2101 return;
2102 xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2103 xmlBufBackToBuffer(buffer);
2107 * xmlNodeDump:
2108 * @buf: the XML buffer output
2109 * @doc: the document
2110 * @cur: the current node
2111 * @level: the imbrication level for indenting
2112 * @format: is formatting allowed
2114 * Dump an XML node, recursive behaviour,children are printed too.
2115 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2116 * or xmlKeepBlanksDefault(0) was called.
2117 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2118 * deprecated, use xmlNodeDumpOutput() instead.
2120 * Returns the number of bytes written to the buffer or -1 in case of error
2123 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2124 int format)
2126 xmlBufPtr buffer;
2127 size_t ret;
2129 if ((buf == NULL) || (cur == NULL))
2130 return(-1);
2131 buffer = xmlBufFromBuffer(buf);
2132 if (buffer == NULL)
2133 return(-1);
2134 ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2135 xmlBufBackToBuffer(buffer);
2136 if (ret > INT_MAX)
2137 return(-1);
2138 return(ret);
2142 * xmlBufNodeDump:
2143 * @buf: the XML buffer output
2144 * @doc: the document
2145 * @cur: the current node
2146 * @level: the imbrication level for indenting
2147 * @format: is formatting allowed
2149 * Dump an XML node, recursive behaviour,children are printed too.
2150 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2151 * or xmlKeepBlanksDefault(0) was called
2153 * Returns the number of bytes written to the buffer, in case of error 0
2154 * is returned or @buf stores the error
2157 size_t
2158 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2159 int format)
2161 size_t use;
2162 int ret;
2163 xmlOutputBufferPtr outbuf;
2164 int oldalloc;
2166 xmlInitParser();
2168 if (cur == NULL) {
2169 #ifdef DEBUG_TREE
2170 xmlGenericError(xmlGenericErrorContext,
2171 "xmlNodeDump : node == NULL\n");
2172 #endif
2173 return (-1);
2175 if (buf == NULL) {
2176 #ifdef DEBUG_TREE
2177 xmlGenericError(xmlGenericErrorContext,
2178 "xmlNodeDump : buf == NULL\n");
2179 #endif
2180 return (-1);
2182 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2183 if (outbuf == NULL) {
2184 xmlSaveErrMemory("creating buffer");
2185 return (-1);
2187 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2188 outbuf->buffer = buf;
2189 outbuf->encoder = NULL;
2190 outbuf->writecallback = NULL;
2191 outbuf->closecallback = NULL;
2192 outbuf->context = NULL;
2193 outbuf->written = 0;
2195 use = xmlBufUse(buf);
2196 oldalloc = xmlBufGetAllocationScheme(buf);
2197 xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
2198 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2199 xmlBufSetAllocationScheme(buf, oldalloc);
2200 xmlFree(outbuf);
2201 ret = xmlBufUse(buf) - use;
2202 return (ret);
2206 * xmlElemDump:
2207 * @f: the FILE * for the output
2208 * @doc: the document
2209 * @cur: the current node
2211 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2213 void
2214 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2216 xmlOutputBufferPtr outbuf;
2218 xmlInitParser();
2220 if (cur == NULL) {
2221 #ifdef DEBUG_TREE
2222 xmlGenericError(xmlGenericErrorContext,
2223 "xmlElemDump : cur == NULL\n");
2224 #endif
2225 return;
2227 #ifdef DEBUG_TREE
2228 if (doc == NULL) {
2229 xmlGenericError(xmlGenericErrorContext,
2230 "xmlElemDump : doc == NULL\n");
2232 #endif
2234 outbuf = xmlOutputBufferCreateFile(f, NULL);
2235 if (outbuf == NULL)
2236 return;
2237 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2238 #ifdef LIBXML_HTML_ENABLED
2239 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2240 #else
2241 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2242 #endif /* LIBXML_HTML_ENABLED */
2243 } else
2244 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2245 xmlOutputBufferClose(outbuf);
2248 /************************************************************************
2250 * Saving functions front-ends *
2252 ************************************************************************/
2255 * xmlNodeDumpOutput:
2256 * @buf: the XML buffer output
2257 * @doc: the document
2258 * @cur: the current node
2259 * @level: the imbrication level for indenting
2260 * @format: is formatting allowed
2261 * @encoding: an optional encoding string
2263 * Dump an XML node, recursive behaviour, children are printed too.
2264 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2265 * or xmlKeepBlanksDefault(0) was called
2267 void
2268 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2269 int level, int format, const char *encoding)
2271 xmlSaveCtxt ctxt;
2272 #ifdef LIBXML_HTML_ENABLED
2273 xmlDtdPtr dtd;
2274 int is_xhtml = 0;
2275 #endif
2277 xmlInitParser();
2279 if ((buf == NULL) || (cur == NULL)) return;
2281 if (encoding == NULL)
2282 encoding = "UTF-8";
2284 memset(&ctxt, 0, sizeof(ctxt));
2285 ctxt.buf = buf;
2286 ctxt.level = level;
2287 ctxt.format = format ? 1 : 0;
2288 ctxt.encoding = (const xmlChar *) encoding;
2289 xmlSaveCtxtInit(&ctxt);
2290 ctxt.options |= XML_SAVE_AS_XML;
2292 #ifdef LIBXML_HTML_ENABLED
2293 dtd = xmlGetIntSubset(doc);
2294 if (dtd != NULL) {
2295 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2296 if (is_xhtml < 0)
2297 is_xhtml = 0;
2300 if (is_xhtml)
2301 xhtmlNodeDumpOutput(&ctxt, cur);
2302 else
2303 #endif
2304 xmlNodeDumpOutputInternal(&ctxt, cur);
2308 * xmlDocDumpFormatMemoryEnc:
2309 * @out_doc: Document to generate XML text from
2310 * @doc_txt_ptr: Memory pointer for allocated XML text
2311 * @doc_txt_len: Length of the generated XML text
2312 * @txt_encoding: Character encoding to use when generating XML text
2313 * @format: should formatting spaces been added
2315 * Dump the current DOM tree into memory using the character encoding specified
2316 * by the caller. Note it is up to the caller of this function to free the
2317 * allocated memory with xmlFree().
2318 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2319 * or xmlKeepBlanksDefault(0) was called
2322 void
2323 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2324 int * doc_txt_len, const char * txt_encoding,
2325 int format) {
2326 xmlSaveCtxt ctxt;
2327 int dummy = 0;
2328 xmlOutputBufferPtr out_buff = NULL;
2329 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2331 if (doc_txt_len == NULL) {
2332 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2335 if (doc_txt_ptr == NULL) {
2336 *doc_txt_len = 0;
2337 return;
2340 *doc_txt_ptr = NULL;
2341 *doc_txt_len = 0;
2343 if (out_doc == NULL) {
2344 /* No document, no output */
2345 return;
2349 * Validate the encoding value, if provided.
2350 * This logic is copied from xmlSaveFileEnc.
2353 if (txt_encoding == NULL)
2354 txt_encoding = (const char *) out_doc->encoding;
2355 if (txt_encoding != NULL) {
2356 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2357 if ( conv_hdlr == NULL ) {
2358 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2359 txt_encoding);
2360 return;
2364 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2365 xmlSaveErrMemory("creating buffer");
2366 xmlCharEncCloseFunc(conv_hdlr);
2367 return;
2370 memset(&ctxt, 0, sizeof(ctxt));
2371 ctxt.buf = out_buff;
2372 ctxt.level = 0;
2373 ctxt.format = format ? 1 : 0;
2374 ctxt.encoding = (const xmlChar *) txt_encoding;
2375 xmlSaveCtxtInit(&ctxt);
2376 ctxt.options |= XML_SAVE_AS_XML;
2377 xmlDocContentDumpOutput(&ctxt, out_doc);
2378 xmlOutputBufferFlush(out_buff);
2379 if (out_buff->conv != NULL) {
2380 *doc_txt_len = xmlBufUse(out_buff->conv);
2381 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2382 } else {
2383 *doc_txt_len = xmlBufUse(out_buff->buffer);
2384 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2386 (void)xmlOutputBufferClose(out_buff);
2388 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2389 *doc_txt_len = 0;
2390 xmlSaveErrMemory("creating output");
2393 return;
2397 * xmlDocDumpMemory:
2398 * @cur: the document
2399 * @mem: OUT: the memory pointer
2400 * @size: OUT: the memory length
2402 * Dump an XML document in memory and return the #xmlChar * and it's size
2403 * in bytes. It's up to the caller to free the memory with xmlFree().
2404 * The resulting byte array is zero terminated, though the last 0 is not
2405 * included in the returned size.
2407 void
2408 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2409 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2413 * xmlDocDumpFormatMemory:
2414 * @cur: the document
2415 * @mem: OUT: the memory pointer
2416 * @size: OUT: the memory length
2417 * @format: should formatting spaces been added
2420 * Dump an XML document in memory and return the #xmlChar * and it's size.
2421 * It's up to the caller to free the memory with xmlFree().
2422 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2423 * or xmlKeepBlanksDefault(0) was called
2425 void
2426 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2427 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2431 * xmlDocDumpMemoryEnc:
2432 * @out_doc: Document to generate XML text from
2433 * @doc_txt_ptr: Memory pointer for allocated XML text
2434 * @doc_txt_len: Length of the generated XML text
2435 * @txt_encoding: Character encoding to use when generating XML text
2437 * Dump the current DOM tree into memory using the character encoding specified
2438 * by the caller. Note it is up to the caller of this function to free the
2439 * allocated memory with xmlFree().
2442 void
2443 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2444 int * doc_txt_len, const char * txt_encoding) {
2445 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2446 txt_encoding, 0);
2450 * xmlDocFormatDump:
2451 * @f: the FILE*
2452 * @cur: the document
2453 * @format: should formatting spaces been added
2455 * Dump an XML document to an open FILE.
2457 * returns: the number of bytes written or -1 in case of failure.
2458 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2459 * or xmlKeepBlanksDefault(0) was called
2462 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2463 xmlSaveCtxt ctxt;
2464 xmlOutputBufferPtr buf;
2465 const char * encoding;
2466 xmlCharEncodingHandlerPtr handler = NULL;
2467 int ret;
2469 if (cur == NULL) {
2470 #ifdef DEBUG_TREE
2471 xmlGenericError(xmlGenericErrorContext,
2472 "xmlDocDump : document == NULL\n");
2473 #endif
2474 return(-1);
2476 encoding = (const char *) cur->encoding;
2478 if (encoding != NULL) {
2479 handler = xmlFindCharEncodingHandler(encoding);
2480 if (handler == NULL) {
2481 xmlFree((char *) cur->encoding);
2482 cur->encoding = NULL;
2483 encoding = NULL;
2486 buf = xmlOutputBufferCreateFile(f, handler);
2487 if (buf == NULL) return(-1);
2488 memset(&ctxt, 0, sizeof(ctxt));
2489 ctxt.buf = buf;
2490 ctxt.level = 0;
2491 ctxt.format = format ? 1 : 0;
2492 ctxt.encoding = (const xmlChar *) encoding;
2493 xmlSaveCtxtInit(&ctxt);
2494 ctxt.options |= XML_SAVE_AS_XML;
2495 xmlDocContentDumpOutput(&ctxt, cur);
2497 ret = xmlOutputBufferClose(buf);
2498 return(ret);
2502 * xmlDocDump:
2503 * @f: the FILE*
2504 * @cur: the document
2506 * Dump an XML document to an open FILE.
2508 * returns: the number of bytes written or -1 in case of failure.
2511 xmlDocDump(FILE *f, xmlDocPtr cur) {
2512 return(xmlDocFormatDump (f, cur, 0));
2516 * xmlSaveFileTo:
2517 * @buf: an output I/O buffer
2518 * @cur: the document
2519 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2521 * Dump an XML document to an I/O buffer.
2522 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2523 * after this call.
2525 * returns: the number of bytes written or -1 in case of failure.
2528 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2529 xmlSaveCtxt ctxt;
2530 int ret;
2532 if (buf == NULL) return(-1);
2533 if (cur == NULL) {
2534 xmlOutputBufferClose(buf);
2535 return(-1);
2537 memset(&ctxt, 0, sizeof(ctxt));
2538 ctxt.buf = buf;
2539 ctxt.level = 0;
2540 ctxt.format = 0;
2541 ctxt.encoding = (const xmlChar *) encoding;
2542 xmlSaveCtxtInit(&ctxt);
2543 ctxt.options |= XML_SAVE_AS_XML;
2544 xmlDocContentDumpOutput(&ctxt, cur);
2545 ret = xmlOutputBufferClose(buf);
2546 return(ret);
2550 * xmlSaveFormatFileTo:
2551 * @buf: an output I/O buffer
2552 * @cur: the document
2553 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2554 * @format: should formatting spaces been added
2556 * Dump an XML document to an I/O buffer.
2557 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2558 * after this call.
2560 * returns: the number of bytes written or -1 in case of failure.
2563 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2564 const char *encoding, int format)
2566 xmlSaveCtxt ctxt;
2567 int ret;
2569 if (buf == NULL) return(-1);
2570 if ((cur == NULL) ||
2571 ((cur->type != XML_DOCUMENT_NODE) &&
2572 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2573 xmlOutputBufferClose(buf);
2574 return(-1);
2576 memset(&ctxt, 0, sizeof(ctxt));
2577 ctxt.buf = buf;
2578 ctxt.level = 0;
2579 ctxt.format = format ? 1 : 0;
2580 ctxt.encoding = (const xmlChar *) encoding;
2581 xmlSaveCtxtInit(&ctxt);
2582 ctxt.options |= XML_SAVE_AS_XML;
2583 xmlDocContentDumpOutput(&ctxt, cur);
2584 ret = xmlOutputBufferClose(buf);
2585 return (ret);
2589 * xmlSaveFormatFileEnc:
2590 * @filename: the filename or URL to output
2591 * @cur: the document being saved
2592 * @encoding: the name of the encoding to use or NULL.
2593 * @format: should formatting spaces be added.
2595 * Dump an XML document to a file or an URL.
2597 * Returns the number of bytes written or -1 in case of error.
2598 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2599 * or xmlKeepBlanksDefault(0) was called
2602 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2603 const char * encoding, int format ) {
2604 xmlSaveCtxt ctxt;
2605 xmlOutputBufferPtr buf;
2606 xmlCharEncodingHandlerPtr handler = NULL;
2607 int ret;
2609 if (cur == NULL)
2610 return(-1);
2612 if (encoding == NULL)
2613 encoding = (const char *) cur->encoding;
2615 if (encoding != NULL) {
2617 handler = xmlFindCharEncodingHandler(encoding);
2618 if (handler == NULL)
2619 return(-1);
2622 #ifdef LIBXML_ZLIB_ENABLED
2623 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2624 #endif
2626 * save the content to a temp buffer.
2628 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2629 if (buf == NULL) return(-1);
2630 memset(&ctxt, 0, sizeof(ctxt));
2631 ctxt.buf = buf;
2632 ctxt.level = 0;
2633 ctxt.format = format ? 1 : 0;
2634 ctxt.encoding = (const xmlChar *) encoding;
2635 xmlSaveCtxtInit(&ctxt);
2636 ctxt.options |= XML_SAVE_AS_XML;
2638 xmlDocContentDumpOutput(&ctxt, cur);
2640 ret = xmlOutputBufferClose(buf);
2641 return(ret);
2646 * xmlSaveFileEnc:
2647 * @filename: the filename (or URL)
2648 * @cur: the document
2649 * @encoding: the name of an encoding (or NULL)
2651 * Dump an XML document, converting it to the given encoding
2653 * returns: the number of bytes written or -1 in case of failure.
2656 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2657 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2661 * xmlSaveFormatFile:
2662 * @filename: the filename (or URL)
2663 * @cur: the document
2664 * @format: should formatting spaces been added
2666 * Dump an XML document to a file. Will use compression if
2667 * compiled in and enabled. If @filename is "-" the stdout file is
2668 * used. If @format is set then the document will be indented on output.
2669 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2670 * or xmlKeepBlanksDefault(0) was called
2672 * returns: the number of bytes written or -1 in case of failure.
2675 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2676 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2680 * xmlSaveFile:
2681 * @filename: the filename (or URL)
2682 * @cur: the document
2684 * Dump an XML document to a file. Will use compression if
2685 * compiled in and enabled. If @filename is "-" the stdout file is
2686 * used.
2687 * returns: the number of bytes written or -1 in case of failure.
2690 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2691 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2694 #endif /* LIBXML_OUTPUT_ENABLED */