mshtml: Implement length prop for Storage.
[wine.git] / libs / xml2 / xmlsave.c
blob9b42c66cac9a562e1c5f50c04b535c537ff70aa0
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 "buf.h"
23 #include "enc.h"
24 #include "save.h"
26 /************************************************************************
27 * *
28 * XHTML detection *
29 * *
30 ************************************************************************/
31 #define XHTML_STRICT_PUBLIC_ID BAD_CAST \
32 "-//W3C//DTD XHTML 1.0 Strict//EN"
33 #define XHTML_STRICT_SYSTEM_ID BAD_CAST \
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
35 #define XHTML_FRAME_PUBLIC_ID BAD_CAST \
36 "-//W3C//DTD XHTML 1.0 Frameset//EN"
37 #define XHTML_FRAME_SYSTEM_ID BAD_CAST \
38 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
39 #define XHTML_TRANS_PUBLIC_ID BAD_CAST \
40 "-//W3C//DTD XHTML 1.0 Transitional//EN"
41 #define XHTML_TRANS_SYSTEM_ID BAD_CAST \
42 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
44 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
45 /**
46 * xmlIsXHTML:
47 * @systemID: the system identifier
48 * @publicID: the public identifier
50 * Try to find if the document correspond to an XHTML DTD
52 * Returns 1 if true, 0 if not and -1 in case of error
54 int
55 xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
56 if ((systemID == NULL) && (publicID == NULL))
57 return(-1);
58 if (publicID != NULL) {
59 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
60 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
61 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
63 if (systemID != NULL) {
64 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
65 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
66 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
68 return(0);
71 #ifdef LIBXML_OUTPUT_ENABLED
73 #define TODO \
74 xmlGenericError(xmlGenericErrorContext, \
75 "Unimplemented block at %s:%d\n", \
76 __FILE__, __LINE__);
78 struct _xmlSaveCtxt {
79 void *_private;
80 int type;
81 int fd;
82 const xmlChar *filename;
83 const xmlChar *encoding;
84 xmlCharEncodingHandlerPtr handler;
85 xmlOutputBufferPtr buf;
86 int options;
87 int level;
88 int format;
89 char indent[MAX_INDENT + 1]; /* array for indenting output */
90 int indent_nr;
91 int indent_size;
92 xmlCharEncodingOutputFunc escape; /* used for element content */
93 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
96 /************************************************************************
97 * *
98 * Output error handlers *
99 * *
100 ************************************************************************/
102 * xmlSaveErrMemory:
103 * @extra: extra information
105 * Handle an out of memory condition
107 static void
108 xmlSaveErrMemory(const char *extra)
110 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
114 * xmlSaveErr:
115 * @code: the error number
116 * @node: the location of the error.
117 * @extra: extra information
119 * Handle an out of memory condition
121 static void
122 xmlSaveErr(int code, xmlNodePtr node, const char *extra)
124 const char *msg = NULL;
126 switch(code) {
127 case XML_SAVE_NOT_UTF8:
128 msg = "string is not in UTF-8\n";
129 break;
130 case XML_SAVE_CHAR_INVALID:
131 msg = "invalid character value\n";
132 break;
133 case XML_SAVE_UNKNOWN_ENCODING:
134 msg = "unknown encoding %s\n";
135 break;
136 case XML_SAVE_NO_DOCTYPE:
137 msg = "document has no DOCTYPE\n";
138 break;
139 default:
140 msg = "unexpected error number\n";
142 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
145 /************************************************************************
147 * Special escaping routines *
149 ************************************************************************/
150 static unsigned char *
151 xmlSerializeHexCharRef(unsigned char *out, int val) {
152 unsigned char *ptr;
154 *out++ = '&';
155 *out++ = '#';
156 *out++ = 'x';
157 if (val < 0x10) ptr = out;
158 else if (val < 0x100) ptr = out + 1;
159 else if (val < 0x1000) ptr = out + 2;
160 else if (val < 0x10000) ptr = out + 3;
161 else if (val < 0x100000) ptr = out + 4;
162 else ptr = out + 5;
163 out = ptr + 1;
164 while (val > 0) {
165 switch (val & 0xF) {
166 case 0: *ptr-- = '0'; break;
167 case 1: *ptr-- = '1'; break;
168 case 2: *ptr-- = '2'; break;
169 case 3: *ptr-- = '3'; break;
170 case 4: *ptr-- = '4'; break;
171 case 5: *ptr-- = '5'; break;
172 case 6: *ptr-- = '6'; break;
173 case 7: *ptr-- = '7'; break;
174 case 8: *ptr-- = '8'; break;
175 case 9: *ptr-- = '9'; break;
176 case 0xA: *ptr-- = 'A'; break;
177 case 0xB: *ptr-- = 'B'; break;
178 case 0xC: *ptr-- = 'C'; break;
179 case 0xD: *ptr-- = 'D'; break;
180 case 0xE: *ptr-- = 'E'; break;
181 case 0xF: *ptr-- = 'F'; break;
182 default: *ptr-- = '0'; break;
184 val >>= 4;
186 *out++ = ';';
187 *out = 0;
188 return(out);
192 * xmlEscapeEntities:
193 * @out: a pointer to an array of bytes to store the result
194 * @outlen: the length of @out
195 * @in: a pointer to an array of unescaped UTF-8 bytes
196 * @inlen: the length of @in
198 * Take a block of UTF-8 chars in and escape them. Used when there is no
199 * encoding specified.
201 * Returns 0 if success, or -1 otherwise
202 * The value of @inlen after return is the number of octets consumed
203 * if the return value is positive, else unpredictable.
204 * The value of @outlen after return is the number of octets consumed.
206 static int
207 xmlEscapeEntities(unsigned char* out, int *outlen,
208 const xmlChar* in, int *inlen) {
209 unsigned char* outstart = out;
210 const unsigned char* base = in;
211 unsigned char* outend = out + *outlen;
212 const unsigned char* inend;
213 int val;
215 inend = in + (*inlen);
217 while ((in < inend) && (out < outend)) {
218 if (*in == '<') {
219 if (outend - out < 4) break;
220 *out++ = '&';
221 *out++ = 'l';
222 *out++ = 't';
223 *out++ = ';';
224 in++;
225 continue;
226 } else if (*in == '>') {
227 if (outend - out < 4) break;
228 *out++ = '&';
229 *out++ = 'g';
230 *out++ = 't';
231 *out++ = ';';
232 in++;
233 continue;
234 } else if (*in == '&') {
235 if (outend - out < 5) break;
236 *out++ = '&';
237 *out++ = 'a';
238 *out++ = 'm';
239 *out++ = 'p';
240 *out++ = ';';
241 in++;
242 continue;
243 } else if (((*in >= 0x20) && (*in < 0x80)) ||
244 (*in == '\n') || (*in == '\t')) {
246 * default case, just copy !
248 *out++ = *in++;
249 continue;
250 } else if (*in >= 0x80) {
252 * We assume we have UTF-8 input.
254 if (outend - out < 11) break;
256 if (*in < 0xC0) {
257 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
258 in++;
259 goto error;
260 } else if (*in < 0xE0) {
261 if (inend - in < 2) break;
262 val = (in[0]) & 0x1F;
263 val <<= 6;
264 val |= (in[1]) & 0x3F;
265 in += 2;
266 } else if (*in < 0xF0) {
267 if (inend - in < 3) break;
268 val = (in[0]) & 0x0F;
269 val <<= 6;
270 val |= (in[1]) & 0x3F;
271 val <<= 6;
272 val |= (in[2]) & 0x3F;
273 in += 3;
274 } else if (*in < 0xF8) {
275 if (inend - in < 4) break;
276 val = (in[0]) & 0x07;
277 val <<= 6;
278 val |= (in[1]) & 0x3F;
279 val <<= 6;
280 val |= (in[2]) & 0x3F;
281 val <<= 6;
282 val |= (in[3]) & 0x3F;
283 in += 4;
284 } else {
285 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
286 in++;
287 goto error;
289 if (!IS_CHAR(val)) {
290 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
291 in++;
292 goto error;
296 * We could do multiple things here. Just save as a char ref
298 out = xmlSerializeHexCharRef(out, val);
299 } else if (IS_BYTE_CHAR(*in)) {
300 if (outend - out < 6) break;
301 out = xmlSerializeHexCharRef(out, *in++);
302 } else {
303 xmlGenericError(xmlGenericErrorContext,
304 "xmlEscapeEntities : char out of range\n");
305 in++;
306 goto error;
309 *outlen = out - outstart;
310 *inlen = in - base;
311 return(0);
312 error:
313 *outlen = out - outstart;
314 *inlen = in - base;
315 return(-1);
318 /************************************************************************
320 * Allocation and deallocation *
322 ************************************************************************/
324 * xmlSaveCtxtInit:
325 * @ctxt: the saving context
327 * Initialize a saving context
329 static void
330 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
332 int i;
333 int len;
335 if (ctxt == NULL) return;
336 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
337 ctxt->escape = xmlEscapeEntities;
338 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
339 if ((xmlTreeIndentString == NULL) || (len == 0)) {
340 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
341 } else {
342 ctxt->indent_size = len;
343 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
344 for (i = 0;i < ctxt->indent_nr;i++)
345 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
346 ctxt->indent_size);
347 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
350 if (xmlSaveNoEmptyTags) {
351 ctxt->options |= XML_SAVE_NO_EMPTY;
356 * xmlFreeSaveCtxt:
358 * Free a saving context, destroying the output in any remaining buffer
360 static void
361 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
363 if (ctxt == NULL) return;
364 if (ctxt->encoding != NULL)
365 xmlFree((char *) ctxt->encoding);
366 if (ctxt->buf != NULL)
367 xmlOutputBufferClose(ctxt->buf);
368 xmlFree(ctxt);
372 * xmlNewSaveCtxt:
374 * Create a new saving context
376 * Returns the new structure or NULL in case of error
378 static xmlSaveCtxtPtr
379 xmlNewSaveCtxt(const char *encoding, int options)
381 xmlSaveCtxtPtr ret;
383 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
384 if (ret == NULL) {
385 xmlSaveErrMemory("creating saving context");
386 return ( NULL );
388 memset(ret, 0, sizeof(xmlSaveCtxt));
390 if (encoding != NULL) {
391 ret->handler = xmlFindCharEncodingHandler(encoding);
392 if (ret->handler == NULL) {
393 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
394 xmlFreeSaveCtxt(ret);
395 return(NULL);
397 ret->encoding = xmlStrdup((const xmlChar *)encoding);
398 ret->escape = NULL;
400 xmlSaveCtxtInit(ret);
403 * Use the options
406 /* Re-check this option as it may already have been set */
407 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
408 options |= XML_SAVE_NO_EMPTY;
411 ret->options = options;
412 if (options & XML_SAVE_FORMAT)
413 ret->format = 1;
414 else if (options & XML_SAVE_WSNONSIG)
415 ret->format = 2;
417 return(ret);
420 /************************************************************************
422 * Dumping XML tree content to a simple buffer *
424 ************************************************************************/
426 * xmlAttrSerializeContent:
427 * @buf: the XML buffer output
428 * @doc: the document
429 * @attr: the attribute pointer
431 * Serialize the attribute in the buffer
433 static void
434 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
436 xmlNodePtr children;
438 children = attr->children;
439 while (children != NULL) {
440 switch (children->type) {
441 case XML_TEXT_NODE:
442 xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
443 attr, children->content);
444 break;
445 case XML_ENTITY_REF_NODE:
446 xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
447 xmlBufAdd(buf->buffer, children->name,
448 xmlStrlen(children->name));
449 xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
450 break;
451 default:
452 /* should not happen unless we have a badly built tree */
453 break;
455 children = children->next;
460 * xmlBufDumpNotationTable:
461 * @buf: an xmlBufPtr output
462 * @table: A notation table
464 * This will dump the content of the notation table as an XML DTD definition
466 void
467 xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
468 xmlBufferPtr buffer;
470 buffer = xmlBufferCreate();
471 if (buffer == NULL) {
473 * TODO set the error in buf
475 return;
477 xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
478 xmlDumpNotationTable(buffer, table);
479 xmlBufMergeBuffer(buf, buffer);
483 * xmlBufDumpElementDecl:
484 * @buf: an xmlBufPtr output
485 * @elem: An element table
487 * This will dump the content of the element declaration as an XML
488 * DTD definition
490 void
491 xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
492 xmlBufferPtr buffer;
494 buffer = xmlBufferCreate();
495 if (buffer == NULL) {
497 * TODO set the error in buf
499 return;
501 xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
502 xmlDumpElementDecl(buffer, elem);
503 xmlBufMergeBuffer(buf, buffer);
507 * xmlBufDumpAttributeDecl:
508 * @buf: an xmlBufPtr output
509 * @attr: An attribute declaration
511 * This will dump the content of the attribute declaration as an XML
512 * DTD definition
514 void
515 xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
516 xmlBufferPtr buffer;
518 buffer = xmlBufferCreate();
519 if (buffer == NULL) {
521 * TODO set the error in buf
523 return;
525 xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
526 xmlDumpAttributeDecl(buffer, attr);
527 xmlBufMergeBuffer(buf, buffer);
531 * xmlBufDumpEntityDecl:
532 * @buf: an xmlBufPtr output
533 * @ent: An entity table
535 * This will dump the content of the entity table as an XML DTD definition
537 void
538 xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
539 xmlBufferPtr buffer;
541 buffer = xmlBufferCreate();
542 if (buffer == NULL) {
544 * TODO set the error in buf
546 return;
548 xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT);
549 xmlDumpEntityDecl(buffer, ent);
550 xmlBufMergeBuffer(buf, buffer);
553 /************************************************************************
555 * Dumping XML tree content to an I/O output buffer *
557 ************************************************************************/
559 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
560 xmlOutputBufferPtr buf = ctxt->buf;
562 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
563 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
564 if (buf->encoder == NULL) {
565 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
566 (const char *)encoding);
567 return(-1);
569 buf->conv = xmlBufCreate();
570 if (buf->conv == NULL) {
571 xmlCharEncCloseFunc(buf->encoder);
572 xmlSaveErrMemory("creating encoding buffer");
573 return(-1);
576 * initialize the state, e.g. if outputting a BOM
578 xmlCharEncOutput(buf, 1);
580 return(0);
583 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
584 xmlOutputBufferPtr buf = ctxt->buf;
585 xmlOutputBufferFlush(buf);
586 xmlCharEncCloseFunc(buf->encoder);
587 xmlBufFree(buf->conv);
588 buf->encoder = NULL;
589 buf->conv = NULL;
590 return(0);
593 #ifdef LIBXML_HTML_ENABLED
594 static void
595 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
596 #endif
597 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
598 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
599 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
602 * xmlOutputBufferWriteWSNonSig:
603 * @ctxt: The save context
604 * @extra: Number of extra indents to apply to ctxt->level
606 * Write out formatting for non-significant whitespace output.
608 static void
609 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
611 int i;
612 if ((ctxt == NULL) || (ctxt->buf == NULL))
613 return;
614 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
615 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
616 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
617 ((ctxt->level + extra - i) > ctxt->indent_nr ?
618 ctxt->indent_nr : (ctxt->level + extra - i)),
619 ctxt->indent);
624 * xmlNsDumpOutput:
625 * @buf: the XML buffer output
626 * @cur: a namespace
627 * @ctxt: the output save context. Optional.
629 * Dump a local Namespace definition.
630 * Should be called in the context of attributes dumps.
631 * If @ctxt is supplied, @buf should be its buffer.
633 static void
634 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
635 if ((cur == NULL) || (buf == NULL)) return;
636 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
637 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
638 return;
640 if (ctxt != NULL && ctxt->format == 2)
641 xmlOutputBufferWriteWSNonSig(ctxt, 2);
642 else
643 xmlOutputBufferWrite(buf, 1, " ");
645 /* Within the context of an element attributes */
646 if (cur->prefix != NULL) {
647 xmlOutputBufferWrite(buf, 6, "xmlns:");
648 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
649 } else
650 xmlOutputBufferWrite(buf, 5, "xmlns");
651 xmlOutputBufferWrite(buf, 1, "=");
652 xmlBufWriteQuotedString(buf->buffer, cur->href);
657 * xmlNsDumpOutputCtxt
658 * @ctxt: the save context
659 * @cur: a namespace
661 * Dump a local Namespace definition to a save context.
662 * Should be called in the context of attribute dumps.
664 static void
665 xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
666 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
670 * xmlNsListDumpOutputCtxt
671 * @ctxt: the save context
672 * @cur: the first namespace
674 * Dump a list of local namespace definitions to a save context.
675 * Should be called in the context of attribute dumps.
677 static void
678 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
679 while (cur != NULL) {
680 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
681 cur = cur->next;
686 * xmlNsListDumpOutput:
687 * @buf: the XML buffer output
688 * @cur: the first namespace
690 * Dump a list of local Namespace definitions.
691 * Should be called in the context of attributes dumps.
693 void
694 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
695 while (cur != NULL) {
696 xmlNsDumpOutput(buf, cur, NULL);
697 cur = cur->next;
702 * xmlDtdDumpOutput:
703 * @buf: the XML buffer output
704 * @dtd: the pointer to the DTD
706 * Dump the XML document DTD, if any.
708 static void
709 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
710 xmlOutputBufferPtr buf;
711 xmlNodePtr cur;
712 int format, level;
714 if (dtd == NULL) return;
715 if ((ctxt == NULL) || (ctxt->buf == NULL))
716 return;
717 buf = ctxt->buf;
718 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
719 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
720 if (dtd->ExternalID != NULL) {
721 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
722 xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
723 xmlOutputBufferWrite(buf, 1, " ");
724 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
725 } else if (dtd->SystemID != NULL) {
726 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
727 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
729 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
730 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
731 (dtd->pentities == NULL)) {
732 xmlOutputBufferWrite(buf, 1, ">");
733 return;
735 xmlOutputBufferWrite(buf, 3, " [\n");
737 * Dump the notations first they are not in the DTD children list
738 * Do this only on a standalone DTD or on the internal subset though.
740 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
741 (dtd->doc->intSubset == dtd))) {
742 xmlBufDumpNotationTable(buf->buffer,
743 (xmlNotationTablePtr) dtd->notations);
745 format = ctxt->format;
746 level = ctxt->level;
747 ctxt->format = 0;
748 ctxt->level = -1;
749 for (cur = dtd->children; cur != NULL; cur = cur->next) {
750 xmlNodeDumpOutputInternal(ctxt, cur);
752 ctxt->format = format;
753 ctxt->level = level;
754 xmlOutputBufferWrite(buf, 2, "]>");
758 * xmlAttrDumpOutput:
759 * @buf: the XML buffer output
760 * @cur: the attribute pointer
762 * Dump an XML attribute
764 static void
765 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
766 xmlOutputBufferPtr buf;
768 if (cur == NULL) return;
769 buf = ctxt->buf;
770 if (buf == NULL) return;
771 if (ctxt->format == 2)
772 xmlOutputBufferWriteWSNonSig(ctxt, 2);
773 else
774 xmlOutputBufferWrite(buf, 1, " ");
775 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
776 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
777 xmlOutputBufferWrite(buf, 1, ":");
779 xmlOutputBufferWriteString(buf, (const char *)cur->name);
780 xmlOutputBufferWrite(buf, 2, "=\"");
781 xmlAttrSerializeContent(buf, cur);
782 xmlOutputBufferWrite(buf, 1, "\"");
785 #ifdef LIBXML_HTML_ENABLED
787 * htmlNodeDumpOutputInternal:
788 * @cur: the current node
790 * Dump an HTML node, recursive behaviour, children are printed too.
792 static int
793 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
794 const xmlChar *oldenc = NULL;
795 const xmlChar *oldctxtenc = ctxt->encoding;
796 const xmlChar *encoding = ctxt->encoding;
797 xmlOutputBufferPtr buf = ctxt->buf;
798 int switched_encoding = 0;
799 xmlDocPtr doc;
801 xmlInitParser();
803 doc = cur->doc;
804 if (doc != NULL) {
805 oldenc = doc->encoding;
806 if (ctxt->encoding != NULL) {
807 doc->encoding = BAD_CAST ctxt->encoding;
808 } else if (doc->encoding != NULL) {
809 encoding = doc->encoding;
813 if ((encoding != NULL) && (doc != NULL))
814 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
815 if ((encoding == NULL) && (doc != NULL))
816 encoding = htmlGetMetaEncoding(doc);
817 if (encoding == NULL)
818 encoding = BAD_CAST "HTML";
819 if ((encoding != NULL) && (oldctxtenc == NULL) &&
820 (buf->encoder == NULL) && (buf->conv == NULL)) {
821 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
822 doc->encoding = oldenc;
823 return(-1);
825 switched_encoding = 1;
827 if (ctxt->options & XML_SAVE_FORMAT)
828 htmlNodeDumpFormatOutput(buf, doc, cur,
829 (const char *)encoding, 1);
830 else
831 htmlNodeDumpFormatOutput(buf, doc, cur,
832 (const char *)encoding, 0);
834 * Restore the state of the saving context at the end of the document
836 if ((switched_encoding) && (oldctxtenc == NULL)) {
837 xmlSaveClearEncoding(ctxt);
839 if (doc != NULL)
840 doc->encoding = oldenc;
841 return(0);
843 #endif
846 * xmlNodeDumpOutputInternal:
847 * @cur: the current node
849 * Dump an XML node, recursive behaviour, children are printed too.
851 static void
852 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
853 int format = ctxt->format;
854 xmlNodePtr tmp, root, unformattedNode = NULL, parent;
855 xmlAttrPtr attr;
856 xmlChar *start, *end;
857 xmlOutputBufferPtr buf;
859 if (cur == NULL) return;
860 buf = ctxt->buf;
862 root = cur;
863 parent = cur->parent;
864 while (1) {
865 switch (cur->type) {
866 case XML_DOCUMENT_NODE:
867 case XML_HTML_DOCUMENT_NODE:
868 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
869 break;
871 case XML_DTD_NODE:
872 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
873 break;
875 case XML_DOCUMENT_FRAG_NODE:
876 /* Always validate cur->parent when descending. */
877 if ((cur->parent == parent) && (cur->children != NULL)) {
878 parent = cur;
879 cur = cur->children;
880 continue;
882 break;
884 case XML_ELEMENT_DECL:
885 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
886 break;
888 case XML_ATTRIBUTE_DECL:
889 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
890 break;
892 case XML_ENTITY_DECL:
893 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
894 break;
896 case XML_ELEMENT_NODE:
897 if ((cur != root) && (ctxt->format == 1) &&
898 (xmlIndentTreeOutput))
899 xmlOutputBufferWrite(buf, ctxt->indent_size *
900 (ctxt->level > ctxt->indent_nr ?
901 ctxt->indent_nr : ctxt->level),
902 ctxt->indent);
905 * Some users like lxml are known to pass nodes with a corrupted
906 * tree structure. Fall back to a recursive call to handle this
907 * case.
909 if ((cur->parent != parent) && (cur->children != NULL)) {
910 xmlNodeDumpOutputInternal(ctxt, cur);
911 break;
914 xmlOutputBufferWrite(buf, 1, "<");
915 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
916 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
917 xmlOutputBufferWrite(buf, 1, ":");
919 xmlOutputBufferWriteString(buf, (const char *)cur->name);
920 if (cur->nsDef)
921 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
922 for (attr = cur->properties; attr != NULL; attr = attr->next)
923 xmlAttrDumpOutput(ctxt, attr);
925 if (cur->children == NULL) {
926 if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
927 if (ctxt->format == 2)
928 xmlOutputBufferWriteWSNonSig(ctxt, 0);
929 xmlOutputBufferWrite(buf, 2, "/>");
930 } else {
931 if (ctxt->format == 2)
932 xmlOutputBufferWriteWSNonSig(ctxt, 1);
933 xmlOutputBufferWrite(buf, 3, "></");
934 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
935 xmlOutputBufferWriteString(buf,
936 (const char *)cur->ns->prefix);
937 xmlOutputBufferWrite(buf, 1, ":");
939 xmlOutputBufferWriteString(buf, (const char *)cur->name);
940 if (ctxt->format == 2)
941 xmlOutputBufferWriteWSNonSig(ctxt, 0);
942 xmlOutputBufferWrite(buf, 1, ">");
944 } else {
945 if (ctxt->format == 1) {
946 tmp = cur->children;
947 while (tmp != NULL) {
948 if ((tmp->type == XML_TEXT_NODE) ||
949 (tmp->type == XML_CDATA_SECTION_NODE) ||
950 (tmp->type == XML_ENTITY_REF_NODE)) {
951 ctxt->format = 0;
952 unformattedNode = cur;
953 break;
955 tmp = tmp->next;
958 if (ctxt->format == 2)
959 xmlOutputBufferWriteWSNonSig(ctxt, 1);
960 xmlOutputBufferWrite(buf, 1, ">");
961 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
962 if (ctxt->level >= 0) ctxt->level++;
963 parent = cur;
964 cur = cur->children;
965 continue;
968 break;
970 case XML_TEXT_NODE:
971 if (cur->content == NULL)
972 break;
973 if (cur->name != xmlStringTextNoenc) {
974 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
975 } else {
977 * Disable escaping, needed for XSLT
979 xmlOutputBufferWriteString(buf, (const char *) cur->content);
981 break;
983 case XML_PI_NODE:
984 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
985 xmlOutputBufferWrite(buf, ctxt->indent_size *
986 (ctxt->level > ctxt->indent_nr ?
987 ctxt->indent_nr : ctxt->level),
988 ctxt->indent);
990 if (cur->content != NULL) {
991 xmlOutputBufferWrite(buf, 2, "<?");
992 xmlOutputBufferWriteString(buf, (const char *)cur->name);
993 if (cur->content != NULL) {
994 if (ctxt->format == 2)
995 xmlOutputBufferWriteWSNonSig(ctxt, 0);
996 else
997 xmlOutputBufferWrite(buf, 1, " ");
998 xmlOutputBufferWriteString(buf,
999 (const char *)cur->content);
1001 xmlOutputBufferWrite(buf, 2, "?>");
1002 } else {
1003 xmlOutputBufferWrite(buf, 2, "<?");
1004 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1005 if (ctxt->format == 2)
1006 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1007 xmlOutputBufferWrite(buf, 2, "?>");
1009 break;
1011 case XML_COMMENT_NODE:
1012 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1013 xmlOutputBufferWrite(buf, ctxt->indent_size *
1014 (ctxt->level > ctxt->indent_nr ?
1015 ctxt->indent_nr : ctxt->level),
1016 ctxt->indent);
1018 if (cur->content != NULL) {
1019 xmlOutputBufferWrite(buf, 4, "<!--");
1020 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1021 xmlOutputBufferWrite(buf, 3, "-->");
1023 break;
1025 case XML_ENTITY_REF_NODE:
1026 xmlOutputBufferWrite(buf, 1, "&");
1027 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1028 xmlOutputBufferWrite(buf, 1, ";");
1029 break;
1031 case XML_CDATA_SECTION_NODE:
1032 if (cur->content == NULL || *cur->content == '\0') {
1033 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1034 } else {
1035 start = end = cur->content;
1036 while (*end != '\0') {
1037 if ((*end == ']') && (*(end + 1) == ']') &&
1038 (*(end + 2) == '>')) {
1039 end = end + 2;
1040 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1041 xmlOutputBufferWrite(buf, end - start,
1042 (const char *)start);
1043 xmlOutputBufferWrite(buf, 3, "]]>");
1044 start = end;
1046 end++;
1048 if (start != end) {
1049 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1050 xmlOutputBufferWriteString(buf, (const char *)start);
1051 xmlOutputBufferWrite(buf, 3, "]]>");
1054 break;
1056 case XML_ATTRIBUTE_NODE:
1057 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1058 break;
1060 case XML_NAMESPACE_DECL:
1061 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1062 break;
1064 default:
1065 break;
1068 while (1) {
1069 if (cur == root)
1070 return;
1071 if ((ctxt->format == 1) &&
1072 (cur->type != XML_XINCLUDE_START) &&
1073 (cur->type != XML_XINCLUDE_END))
1074 xmlOutputBufferWrite(buf, 1, "\n");
1075 if (cur->next != NULL) {
1076 cur = cur->next;
1077 break;
1080 cur = parent;
1081 /* cur->parent was validated when descending. */
1082 parent = cur->parent;
1084 if (cur->type == XML_ELEMENT_NODE) {
1085 if (ctxt->level > 0) ctxt->level--;
1086 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1087 xmlOutputBufferWrite(buf, ctxt->indent_size *
1088 (ctxt->level > ctxt->indent_nr ?
1089 ctxt->indent_nr : ctxt->level),
1090 ctxt->indent);
1092 xmlOutputBufferWrite(buf, 2, "</");
1093 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1094 xmlOutputBufferWriteString(buf,
1095 (const char *)cur->ns->prefix);
1096 xmlOutputBufferWrite(buf, 1, ":");
1099 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1100 if (ctxt->format == 2)
1101 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1102 xmlOutputBufferWrite(buf, 1, ">");
1104 if (cur == unformattedNode) {
1105 ctxt->format = format;
1106 unformattedNode = NULL;
1114 * xmlDocContentDumpOutput:
1115 * @cur: the document
1117 * Dump an XML document.
1119 static int
1120 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1121 #ifdef LIBXML_HTML_ENABLED
1122 xmlDtdPtr dtd;
1123 int is_xhtml = 0;
1124 #endif
1125 const xmlChar *oldenc = cur->encoding;
1126 const xmlChar *oldctxtenc = ctxt->encoding;
1127 const xmlChar *encoding = ctxt->encoding;
1128 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1129 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1130 xmlOutputBufferPtr buf = ctxt->buf;
1131 xmlCharEncoding enc;
1132 int switched_encoding = 0;
1134 xmlInitParser();
1136 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1137 (cur->type != XML_DOCUMENT_NODE))
1138 return(-1);
1140 if (ctxt->encoding != NULL) {
1141 cur->encoding = BAD_CAST ctxt->encoding;
1142 } else if (cur->encoding != NULL) {
1143 encoding = cur->encoding;
1146 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1147 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1148 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1149 (ctxt->options & XML_SAVE_AS_HTML)) {
1150 #ifdef LIBXML_HTML_ENABLED
1151 if (encoding != NULL)
1152 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1153 if (encoding == NULL)
1154 encoding = htmlGetMetaEncoding(cur);
1155 if (encoding == NULL)
1156 encoding = BAD_CAST "HTML";
1157 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1158 (buf->encoder == NULL) && (buf->conv == NULL)) {
1159 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1160 cur->encoding = oldenc;
1161 return(-1);
1164 if (ctxt->options & XML_SAVE_FORMAT)
1165 htmlDocContentDumpFormatOutput(buf, cur,
1166 (const char *)encoding, 1);
1167 else
1168 htmlDocContentDumpFormatOutput(buf, cur,
1169 (const char *)encoding, 0);
1170 if (ctxt->encoding != NULL)
1171 cur->encoding = oldenc;
1172 return(0);
1173 #else
1174 return(-1);
1175 #endif
1176 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1177 (ctxt->options & XML_SAVE_AS_XML) ||
1178 (ctxt->options & XML_SAVE_XHTML)) {
1179 enc = xmlParseCharEncoding((const char*) encoding);
1180 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1181 (buf->encoder == NULL) && (buf->conv == NULL) &&
1182 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1183 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1184 (enc != XML_CHAR_ENCODING_NONE) &&
1185 (enc != XML_CHAR_ENCODING_ASCII)) {
1187 * we need to switch to this encoding but just for this
1188 * document since we output the XMLDecl the conversion
1189 * must be done to not generate not well formed documents.
1191 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1192 cur->encoding = oldenc;
1193 return(-1);
1195 switched_encoding = 1;
1197 if (ctxt->escape == xmlEscapeEntities)
1198 ctxt->escape = NULL;
1199 if (ctxt->escapeAttr == xmlEscapeEntities)
1200 ctxt->escapeAttr = NULL;
1205 * Save the XML declaration
1207 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1208 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1209 if (cur->version != NULL)
1210 xmlBufWriteQuotedString(buf->buffer, cur->version);
1211 else
1212 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1213 if (encoding != NULL) {
1214 xmlOutputBufferWrite(buf, 10, " encoding=");
1215 xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1217 switch (cur->standalone) {
1218 case 0:
1219 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1220 break;
1221 case 1:
1222 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1223 break;
1225 xmlOutputBufferWrite(buf, 3, "?>\n");
1228 #ifdef LIBXML_HTML_ENABLED
1229 if (ctxt->options & XML_SAVE_XHTML)
1230 is_xhtml = 1;
1231 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1232 dtd = xmlGetIntSubset(cur);
1233 if (dtd != NULL) {
1234 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1235 if (is_xhtml < 0) is_xhtml = 0;
1238 #endif
1239 if (cur->children != NULL) {
1240 xmlNodePtr child = cur->children;
1242 while (child != NULL) {
1243 ctxt->level = 0;
1244 #ifdef LIBXML_HTML_ENABLED
1245 if (is_xhtml)
1246 xhtmlNodeDumpOutput(ctxt, child);
1247 else
1248 #endif
1249 xmlNodeDumpOutputInternal(ctxt, child);
1250 if ((child->type != XML_XINCLUDE_START) &&
1251 (child->type != XML_XINCLUDE_END))
1252 xmlOutputBufferWrite(buf, 1, "\n");
1253 child = child->next;
1259 * Restore the state of the saving context at the end of the document
1261 if ((switched_encoding) && (oldctxtenc == NULL)) {
1262 xmlSaveClearEncoding(ctxt);
1263 ctxt->escape = oldescape;
1264 ctxt->escapeAttr = oldescapeAttr;
1266 cur->encoding = oldenc;
1267 return(0);
1270 #ifdef LIBXML_HTML_ENABLED
1271 /************************************************************************
1273 * Functions specific to XHTML serialization *
1275 ************************************************************************/
1278 * xhtmlIsEmpty:
1279 * @node: the node
1281 * Check if a node is an empty xhtml node
1283 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1285 static int
1286 xhtmlIsEmpty(xmlNodePtr node) {
1287 if (node == NULL)
1288 return(-1);
1289 if (node->type != XML_ELEMENT_NODE)
1290 return(0);
1291 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1292 return(0);
1293 if (node->children != NULL)
1294 return(0);
1295 switch (node->name[0]) {
1296 case 'a':
1297 if (xmlStrEqual(node->name, BAD_CAST "area"))
1298 return(1);
1299 return(0);
1300 case 'b':
1301 if (xmlStrEqual(node->name, BAD_CAST "br"))
1302 return(1);
1303 if (xmlStrEqual(node->name, BAD_CAST "base"))
1304 return(1);
1305 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1306 return(1);
1307 return(0);
1308 case 'c':
1309 if (xmlStrEqual(node->name, BAD_CAST "col"))
1310 return(1);
1311 return(0);
1312 case 'f':
1313 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1314 return(1);
1315 return(0);
1316 case 'h':
1317 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1318 return(1);
1319 return(0);
1320 case 'i':
1321 if (xmlStrEqual(node->name, BAD_CAST "img"))
1322 return(1);
1323 if (xmlStrEqual(node->name, BAD_CAST "input"))
1324 return(1);
1325 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1326 return(1);
1327 return(0);
1328 case 'l':
1329 if (xmlStrEqual(node->name, BAD_CAST "link"))
1330 return(1);
1331 return(0);
1332 case 'm':
1333 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1334 return(1);
1335 return(0);
1336 case 'p':
1337 if (xmlStrEqual(node->name, BAD_CAST "param"))
1338 return(1);
1339 return(0);
1341 return(0);
1345 * xhtmlAttrListDumpOutput:
1346 * @cur: the first attribute pointer
1348 * Dump a list of XML attributes
1350 static void
1351 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1352 xmlAttrPtr xml_lang = NULL;
1353 xmlAttrPtr lang = NULL;
1354 xmlAttrPtr name = NULL;
1355 xmlAttrPtr id = NULL;
1356 xmlNodePtr parent;
1357 xmlOutputBufferPtr buf;
1359 if (cur == NULL) return;
1360 buf = ctxt->buf;
1361 parent = cur->parent;
1362 while (cur != NULL) {
1363 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1364 id = cur;
1365 else
1366 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1367 name = cur;
1368 else
1369 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1370 lang = cur;
1371 else
1372 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1373 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1374 xml_lang = cur;
1375 else if ((cur->ns == NULL) &&
1376 ((cur->children == NULL) ||
1377 (cur->children->content == NULL) ||
1378 (cur->children->content[0] == 0)) &&
1379 (htmlIsBooleanAttr(cur->name))) {
1380 if (cur->children != NULL)
1381 xmlFreeNode(cur->children);
1382 cur->children = xmlNewDocText(cur->doc, cur->name);
1383 if (cur->children != NULL)
1384 cur->children->parent = (xmlNodePtr) cur;
1386 xmlAttrDumpOutput(ctxt, cur);
1387 cur = cur->next;
1390 * C.8
1392 if ((name != NULL) && (id == NULL)) {
1393 if ((parent != NULL) && (parent->name != NULL) &&
1394 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1395 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1396 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1397 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1398 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1399 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1400 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1401 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1402 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1403 xmlOutputBufferWrite(buf, 5, " id=\"");
1404 xmlAttrSerializeContent(buf, name);
1405 xmlOutputBufferWrite(buf, 1, "\"");
1409 * C.7.
1411 if ((lang != NULL) && (xml_lang == NULL)) {
1412 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1413 xmlAttrSerializeContent(buf, lang);
1414 xmlOutputBufferWrite(buf, 1, "\"");
1415 } else
1416 if ((xml_lang != NULL) && (lang == NULL)) {
1417 xmlOutputBufferWrite(buf, 7, " lang=\"");
1418 xmlAttrSerializeContent(buf, xml_lang);
1419 xmlOutputBufferWrite(buf, 1, "\"");
1424 * xhtmlNodeDumpOutput:
1425 * @buf: the XML buffer output
1426 * @doc: the XHTML document
1427 * @cur: the current node
1428 * @level: the imbrication level for indenting
1429 * @format: is formatting allowed
1430 * @encoding: an optional encoding string
1432 * Dump an XHTML node, recursive behaviour, children are printed too.
1434 static void
1435 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1436 int format = ctxt->format, addmeta;
1437 xmlNodePtr tmp, root, unformattedNode = NULL;
1438 xmlChar *start, *end;
1439 xmlOutputBufferPtr buf = ctxt->buf;
1441 if (cur == NULL) return;
1443 root = cur;
1444 while (1) {
1445 switch (cur->type) {
1446 case XML_DOCUMENT_NODE:
1447 case XML_HTML_DOCUMENT_NODE:
1448 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1449 break;
1451 case XML_NAMESPACE_DECL:
1452 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1453 break;
1455 case XML_DTD_NODE:
1456 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1457 break;
1459 case XML_DOCUMENT_FRAG_NODE:
1460 if (cur->children) {
1461 cur = cur->children;
1462 continue;
1464 break;
1466 case XML_ELEMENT_DECL:
1467 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1468 break;
1470 case XML_ATTRIBUTE_DECL:
1471 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1472 break;
1474 case XML_ENTITY_DECL:
1475 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1476 break;
1478 case XML_ELEMENT_NODE:
1479 addmeta = 0;
1481 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1482 xmlOutputBufferWrite(buf, ctxt->indent_size *
1483 (ctxt->level > ctxt->indent_nr ?
1484 ctxt->indent_nr : ctxt->level),
1485 ctxt->indent);
1487 xmlOutputBufferWrite(buf, 1, "<");
1488 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1489 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1490 xmlOutputBufferWrite(buf, 1, ":");
1493 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1494 if (cur->nsDef)
1495 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1496 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1497 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1499 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1501 xmlOutputBufferWriteString(buf,
1502 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1504 if (cur->properties != NULL)
1505 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1507 if ((cur->parent != NULL) &&
1508 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1509 xmlStrEqual(cur->name, BAD_CAST"head") &&
1510 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1512 tmp = cur->children;
1513 while (tmp != NULL) {
1514 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1515 xmlChar *httpequiv;
1517 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1518 if (httpequiv != NULL) {
1519 if (xmlStrcasecmp(httpequiv,
1520 BAD_CAST"Content-Type") == 0) {
1521 xmlFree(httpequiv);
1522 break;
1524 xmlFree(httpequiv);
1527 tmp = tmp->next;
1529 if (tmp == NULL)
1530 addmeta = 1;
1533 if (cur->children == NULL) {
1534 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1535 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1537 * C.2. Empty Elements
1539 xmlOutputBufferWrite(buf, 3, " />");
1540 } else {
1541 if (addmeta == 1) {
1542 xmlOutputBufferWrite(buf, 1, ">");
1543 if (ctxt->format == 1) {
1544 xmlOutputBufferWrite(buf, 1, "\n");
1545 if (xmlIndentTreeOutput)
1546 xmlOutputBufferWrite(buf, ctxt->indent_size *
1547 (ctxt->level + 1 > ctxt->indent_nr ?
1548 ctxt->indent_nr : ctxt->level + 1),
1549 ctxt->indent);
1551 xmlOutputBufferWriteString(buf,
1552 "<meta http-equiv=\"Content-Type\" "
1553 "content=\"text/html; charset=");
1554 if (ctxt->encoding) {
1555 xmlOutputBufferWriteString(buf,
1556 (const char *)ctxt->encoding);
1557 } else {
1558 xmlOutputBufferWrite(buf, 5, "UTF-8");
1560 xmlOutputBufferWrite(buf, 4, "\" />");
1561 if (ctxt->format == 1)
1562 xmlOutputBufferWrite(buf, 1, "\n");
1563 } else {
1564 xmlOutputBufferWrite(buf, 1, ">");
1567 * C.3. Element Minimization and Empty Element Content
1569 xmlOutputBufferWrite(buf, 2, "</");
1570 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1571 xmlOutputBufferWriteString(buf,
1572 (const char *)cur->ns->prefix);
1573 xmlOutputBufferWrite(buf, 1, ":");
1575 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1576 xmlOutputBufferWrite(buf, 1, ">");
1578 } else {
1579 xmlOutputBufferWrite(buf, 1, ">");
1580 if (addmeta == 1) {
1581 if (ctxt->format == 1) {
1582 xmlOutputBufferWrite(buf, 1, "\n");
1583 if (xmlIndentTreeOutput)
1584 xmlOutputBufferWrite(buf, ctxt->indent_size *
1585 (ctxt->level + 1 > ctxt->indent_nr ?
1586 ctxt->indent_nr : ctxt->level + 1),
1587 ctxt->indent);
1589 xmlOutputBufferWriteString(buf,
1590 "<meta http-equiv=\"Content-Type\" "
1591 "content=\"text/html; charset=");
1592 if (ctxt->encoding) {
1593 xmlOutputBufferWriteString(buf,
1594 (const char *)ctxt->encoding);
1595 } else {
1596 xmlOutputBufferWrite(buf, 5, "UTF-8");
1598 xmlOutputBufferWrite(buf, 4, "\" />");
1601 if (ctxt->format == 1) {
1602 tmp = cur->children;
1603 while (tmp != NULL) {
1604 if ((tmp->type == XML_TEXT_NODE) ||
1605 (tmp->type == XML_ENTITY_REF_NODE)) {
1606 unformattedNode = cur;
1607 ctxt->format = 0;
1608 break;
1610 tmp = tmp->next;
1614 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1615 if (ctxt->level >= 0) ctxt->level++;
1616 cur = cur->children;
1617 continue;
1620 break;
1622 case XML_TEXT_NODE:
1623 if (cur->content == NULL)
1624 break;
1625 if ((cur->name == xmlStringText) ||
1626 (cur->name != xmlStringTextNoenc)) {
1627 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1628 } else {
1630 * Disable escaping, needed for XSLT
1632 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1634 break;
1636 case XML_PI_NODE:
1637 if (cur->content != NULL) {
1638 xmlOutputBufferWrite(buf, 2, "<?");
1639 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1640 if (cur->content != NULL) {
1641 xmlOutputBufferWrite(buf, 1, " ");
1642 xmlOutputBufferWriteString(buf,
1643 (const char *)cur->content);
1645 xmlOutputBufferWrite(buf, 2, "?>");
1646 } else {
1647 xmlOutputBufferWrite(buf, 2, "<?");
1648 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1649 xmlOutputBufferWrite(buf, 2, "?>");
1651 break;
1653 case XML_COMMENT_NODE:
1654 if (cur->content != NULL) {
1655 xmlOutputBufferWrite(buf, 4, "<!--");
1656 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1657 xmlOutputBufferWrite(buf, 3, "-->");
1659 break;
1661 case XML_ENTITY_REF_NODE:
1662 xmlOutputBufferWrite(buf, 1, "&");
1663 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1664 xmlOutputBufferWrite(buf, 1, ";");
1665 break;
1667 case XML_CDATA_SECTION_NODE:
1668 if (cur->content == NULL || *cur->content == '\0') {
1669 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1670 } else {
1671 start = end = cur->content;
1672 while (*end != '\0') {
1673 if (*end == ']' && *(end + 1) == ']' &&
1674 *(end + 2) == '>') {
1675 end = end + 2;
1676 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1677 xmlOutputBufferWrite(buf, end - start,
1678 (const char *)start);
1679 xmlOutputBufferWrite(buf, 3, "]]>");
1680 start = end;
1682 end++;
1684 if (start != end) {
1685 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1686 xmlOutputBufferWriteString(buf, (const char *)start);
1687 xmlOutputBufferWrite(buf, 3, "]]>");
1690 break;
1692 case XML_ATTRIBUTE_NODE:
1693 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1694 break;
1696 default:
1697 break;
1700 while (1) {
1701 if (cur == root)
1702 return;
1703 if (ctxt->format == 1)
1704 xmlOutputBufferWrite(buf, 1, "\n");
1705 if (cur->next != NULL) {
1706 cur = cur->next;
1707 break;
1711 * The parent should never be NULL here but we want to handle
1712 * corrupted documents gracefully.
1714 if (cur->parent == NULL)
1715 return;
1716 cur = cur->parent;
1718 if (cur->type == XML_ELEMENT_NODE) {
1719 if (ctxt->level > 0) ctxt->level--;
1720 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1721 xmlOutputBufferWrite(buf, ctxt->indent_size *
1722 (ctxt->level > ctxt->indent_nr ?
1723 ctxt->indent_nr : ctxt->level),
1724 ctxt->indent);
1726 xmlOutputBufferWrite(buf, 2, "</");
1727 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1728 xmlOutputBufferWriteString(buf,
1729 (const char *)cur->ns->prefix);
1730 xmlOutputBufferWrite(buf, 1, ":");
1733 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1734 xmlOutputBufferWrite(buf, 1, ">");
1736 if (cur == unformattedNode) {
1737 ctxt->format = format;
1738 unformattedNode = NULL;
1744 #endif
1746 /************************************************************************
1748 * Public entry points *
1750 ************************************************************************/
1753 * xmlSaveToFd:
1754 * @fd: a file descriptor number
1755 * @encoding: the encoding name to use or NULL
1756 * @options: a set of xmlSaveOptions
1758 * Create a document saving context serializing to a file descriptor
1759 * with the encoding and the options given.
1761 * Returns a new serialization context or NULL in case of error.
1763 xmlSaveCtxtPtr
1764 xmlSaveToFd(int fd, const char *encoding, int options)
1766 xmlSaveCtxtPtr ret;
1768 ret = xmlNewSaveCtxt(encoding, options);
1769 if (ret == NULL) return(NULL);
1770 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1771 if (ret->buf == NULL) {
1772 xmlCharEncCloseFunc(ret->handler);
1773 xmlFreeSaveCtxt(ret);
1774 return(NULL);
1776 return(ret);
1780 * xmlSaveToFilename:
1781 * @filename: a file name or an URL
1782 * @encoding: the encoding name to use or NULL
1783 * @options: a set of xmlSaveOptions
1785 * Create a document saving context serializing to a filename or possibly
1786 * to an URL (but this is less reliable) with the encoding and the options
1787 * given.
1789 * Returns a new serialization context or NULL in case of error.
1791 xmlSaveCtxtPtr
1792 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1794 xmlSaveCtxtPtr ret;
1795 int compression = 0; /* TODO handle compression option */
1797 ret = xmlNewSaveCtxt(encoding, options);
1798 if (ret == NULL) return(NULL);
1799 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1800 compression);
1801 if (ret->buf == NULL) {
1802 xmlCharEncCloseFunc(ret->handler);
1803 xmlFreeSaveCtxt(ret);
1804 return(NULL);
1806 return(ret);
1810 * xmlSaveToBuffer:
1811 * @buffer: a buffer
1812 * @encoding: the encoding name to use or NULL
1813 * @options: a set of xmlSaveOptions
1815 * Create a document saving context serializing to a buffer
1816 * with the encoding and the options given
1818 * Returns a new serialization context or NULL in case of error.
1821 xmlSaveCtxtPtr
1822 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1824 xmlSaveCtxtPtr ret;
1826 ret = xmlNewSaveCtxt(encoding, options);
1827 if (ret == NULL) return(NULL);
1828 ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
1829 if (ret->buf == NULL) {
1830 xmlCharEncCloseFunc(ret->handler);
1831 xmlFreeSaveCtxt(ret);
1832 return(NULL);
1834 return(ret);
1838 * xmlSaveToIO:
1839 * @iowrite: an I/O write function
1840 * @ioclose: an I/O close function
1841 * @ioctx: an I/O handler
1842 * @encoding: the encoding name to use or NULL
1843 * @options: a set of xmlSaveOptions
1845 * Create a document saving context serializing to a file descriptor
1846 * with the encoding and the options given
1848 * Returns a new serialization context or NULL in case of error.
1850 xmlSaveCtxtPtr
1851 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1852 xmlOutputCloseCallback ioclose,
1853 void *ioctx, const char *encoding, int options)
1855 xmlSaveCtxtPtr ret;
1857 ret = xmlNewSaveCtxt(encoding, options);
1858 if (ret == NULL) return(NULL);
1859 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1860 if (ret->buf == NULL) {
1861 xmlCharEncCloseFunc(ret->handler);
1862 xmlFreeSaveCtxt(ret);
1863 return(NULL);
1865 return(ret);
1869 * xmlSaveDoc:
1870 * @ctxt: a document saving context
1871 * @doc: a document
1873 * Save a full document to a saving context
1874 * TODO: The function is not fully implemented yet as it does not return the
1875 * byte count but 0 instead
1877 * Returns the number of byte written or -1 in case of error
1879 long
1880 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1882 long ret = 0;
1884 if ((ctxt == NULL) || (doc == NULL)) return(-1);
1885 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1886 return(-1);
1887 return(ret);
1891 * xmlSaveTree:
1892 * @ctxt: a document saving context
1893 * @node: the top node of the subtree to save
1895 * Save a subtree starting at the node parameter to a saving context
1896 * TODO: The function is not fully implemented yet as it does not return the
1897 * byte count but 0 instead
1899 * Returns the number of byte written or -1 in case of error
1901 long
1902 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
1904 long ret = 0;
1906 if ((ctxt == NULL) || (cur == NULL)) return(-1);
1907 #ifdef LIBXML_HTML_ENABLED
1908 if (ctxt->options & XML_SAVE_XHTML) {
1909 xhtmlNodeDumpOutput(ctxt, cur);
1910 return(ret);
1912 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
1913 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
1914 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
1915 (ctxt->options & XML_SAVE_AS_HTML)) {
1916 htmlNodeDumpOutputInternal(ctxt, cur);
1917 return(ret);
1919 #endif
1920 xmlNodeDumpOutputInternal(ctxt, cur);
1921 return(ret);
1925 * xmlSaveFlush:
1926 * @ctxt: a document saving context
1928 * Flush a document saving context, i.e. make sure that all bytes have
1929 * been output.
1931 * Returns the number of byte written or -1 in case of error.
1934 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1936 if (ctxt == NULL) return(-1);
1937 if (ctxt->buf == NULL) return(-1);
1938 return(xmlOutputBufferFlush(ctxt->buf));
1942 * xmlSaveClose:
1943 * @ctxt: a document saving context
1945 * Close a document saving context, i.e. make sure that all bytes have
1946 * been output and free the associated data.
1948 * Returns the number of byte written or -1 in case of error.
1951 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1953 int ret;
1955 if (ctxt == NULL) return(-1);
1956 ret = xmlSaveFlush(ctxt);
1957 xmlFreeSaveCtxt(ctxt);
1958 return(ret);
1962 * xmlSaveSetEscape:
1963 * @ctxt: a document saving context
1964 * @escape: the escaping function
1966 * Set a custom escaping function to be used for text in element content
1968 * Returns 0 if successful or -1 in case of error.
1971 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1973 if (ctxt == NULL) return(-1);
1974 ctxt->escape = escape;
1975 return(0);
1979 * xmlSaveSetAttrEscape:
1980 * @ctxt: a document saving context
1981 * @escape: the escaping function
1983 * Set a custom escaping function to be used for text in attribute content
1985 * Returns 0 if successful or -1 in case of error.
1988 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1990 if (ctxt == NULL) return(-1);
1991 ctxt->escapeAttr = escape;
1992 return(0);
1995 /************************************************************************
1997 * Public entry points based on buffers *
1999 ************************************************************************/
2002 * xmlBufAttrSerializeTxtContent:
2003 * @buf: and xmlBufPtr output
2004 * @doc: the document
2005 * @attr: the attribute node
2006 * @string: the text content
2008 * Serialize text attribute values to an xmlBufPtr
2010 void
2011 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
2012 xmlAttrPtr attr, const xmlChar * string)
2014 xmlChar *base, *cur;
2016 if (string == NULL)
2017 return;
2018 base = cur = (xmlChar *) string;
2019 while (*cur != 0) {
2020 if (*cur == '\n') {
2021 if (base != cur)
2022 xmlBufAdd(buf, base, cur - base);
2023 xmlBufAdd(buf, BAD_CAST "&#10;", 5);
2024 cur++;
2025 base = cur;
2026 } else if (*cur == '\r') {
2027 if (base != cur)
2028 xmlBufAdd(buf, base, cur - base);
2029 xmlBufAdd(buf, BAD_CAST "&#13;", 5);
2030 cur++;
2031 base = cur;
2032 } else if (*cur == '\t') {
2033 if (base != cur)
2034 xmlBufAdd(buf, base, cur - base);
2035 xmlBufAdd(buf, BAD_CAST "&#9;", 4);
2036 cur++;
2037 base = cur;
2038 } else if (*cur == '"') {
2039 if (base != cur)
2040 xmlBufAdd(buf, base, cur - base);
2041 xmlBufAdd(buf, BAD_CAST "&quot;", 6);
2042 cur++;
2043 base = cur;
2044 } else if (*cur == '<') {
2045 if (base != cur)
2046 xmlBufAdd(buf, base, cur - base);
2047 xmlBufAdd(buf, BAD_CAST "&lt;", 4);
2048 cur++;
2049 base = cur;
2050 } else if (*cur == '>') {
2051 if (base != cur)
2052 xmlBufAdd(buf, base, cur - base);
2053 xmlBufAdd(buf, BAD_CAST "&gt;", 4);
2054 cur++;
2055 base = cur;
2056 } else if (*cur == '&') {
2057 if (base != cur)
2058 xmlBufAdd(buf, base, cur - base);
2059 xmlBufAdd(buf, BAD_CAST "&amp;", 5);
2060 cur++;
2061 base = cur;
2062 } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2063 ((doc == NULL) || (doc->encoding == NULL))) {
2065 * We assume we have UTF-8 content.
2067 unsigned char tmp[12];
2068 int val = 0, l = 1;
2070 if (base != cur)
2071 xmlBufAdd(buf, base, cur - base);
2072 if (*cur < 0xC0) {
2073 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2074 xmlSerializeHexCharRef(tmp, *cur);
2075 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2076 cur++;
2077 base = cur;
2078 continue;
2079 } else if (*cur < 0xE0) {
2080 val = (cur[0]) & 0x1F;
2081 val <<= 6;
2082 val |= (cur[1]) & 0x3F;
2083 l = 2;
2084 } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2085 val = (cur[0]) & 0x0F;
2086 val <<= 6;
2087 val |= (cur[1]) & 0x3F;
2088 val <<= 6;
2089 val |= (cur[2]) & 0x3F;
2090 l = 3;
2091 } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2092 val = (cur[0]) & 0x07;
2093 val <<= 6;
2094 val |= (cur[1]) & 0x3F;
2095 val <<= 6;
2096 val |= (cur[2]) & 0x3F;
2097 val <<= 6;
2098 val |= (cur[3]) & 0x3F;
2099 l = 4;
2101 if ((l == 1) || (!IS_CHAR(val))) {
2102 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2103 xmlSerializeHexCharRef(tmp, *cur);
2104 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2105 cur++;
2106 base = cur;
2107 continue;
2110 * We could do multiple things here. Just save
2111 * as a char ref
2113 xmlSerializeHexCharRef(tmp, val);
2114 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2115 cur += l;
2116 base = cur;
2117 } else {
2118 cur++;
2121 if (base != cur)
2122 xmlBufAdd(buf, base, cur - base);
2126 * xmlAttrSerializeTxtContent:
2127 * @buf: the XML buffer output
2128 * @doc: the document
2129 * @attr: the attribute node
2130 * @string: the text content
2132 * Serialize text attribute values to an xml simple buffer
2134 void
2135 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2136 xmlAttrPtr attr, const xmlChar * string)
2138 xmlBufPtr buffer;
2140 if ((buf == NULL) || (string == NULL))
2141 return;
2142 buffer = xmlBufFromBuffer(buf);
2143 if (buffer == NULL)
2144 return;
2145 xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2146 xmlBufBackToBuffer(buffer);
2150 * xmlNodeDump:
2151 * @buf: the XML buffer output
2152 * @doc: the document
2153 * @cur: the current node
2154 * @level: the imbrication level for indenting
2155 * @format: is formatting allowed
2157 * Dump an XML node, recursive behaviour,children are printed too.
2158 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2159 * or xmlKeepBlanksDefault(0) was called.
2160 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2161 * deprecated, use xmlNodeDumpOutput() instead.
2163 * Returns the number of bytes written to the buffer or -1 in case of error
2166 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2167 int format)
2169 xmlBufPtr buffer;
2170 size_t ret;
2172 if ((buf == NULL) || (cur == NULL))
2173 return(-1);
2174 buffer = xmlBufFromBuffer(buf);
2175 if (buffer == NULL)
2176 return(-1);
2177 ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2178 xmlBufBackToBuffer(buffer);
2179 if (ret > INT_MAX)
2180 return(-1);
2181 return((int) ret);
2185 * xmlBufNodeDump:
2186 * @buf: the XML buffer output
2187 * @doc: the document
2188 * @cur: the current node
2189 * @level: the imbrication level for indenting
2190 * @format: is formatting allowed
2192 * Dump an XML node, recursive behaviour,children are printed too.
2193 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2194 * or xmlKeepBlanksDefault(0) was called
2196 * Returns the number of bytes written to the buffer, in case of error 0
2197 * is returned or @buf stores the error
2200 size_t
2201 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2202 int format)
2204 size_t use;
2205 int ret;
2206 xmlOutputBufferPtr outbuf;
2207 int oldalloc;
2209 xmlInitParser();
2211 if (cur == NULL) {
2212 #ifdef DEBUG_TREE
2213 xmlGenericError(xmlGenericErrorContext,
2214 "xmlNodeDump : node == NULL\n");
2215 #endif
2216 return (-1);
2218 if (buf == NULL) {
2219 #ifdef DEBUG_TREE
2220 xmlGenericError(xmlGenericErrorContext,
2221 "xmlNodeDump : buf == NULL\n");
2222 #endif
2223 return (-1);
2225 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2226 if (outbuf == NULL) {
2227 xmlSaveErrMemory("creating buffer");
2228 return (-1);
2230 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2231 outbuf->buffer = buf;
2232 outbuf->encoder = NULL;
2233 outbuf->writecallback = NULL;
2234 outbuf->closecallback = NULL;
2235 outbuf->context = NULL;
2236 outbuf->written = 0;
2238 use = xmlBufUse(buf);
2239 oldalloc = xmlBufGetAllocationScheme(buf);
2240 xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
2241 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2242 xmlBufSetAllocationScheme(buf, oldalloc);
2243 xmlFree(outbuf);
2244 ret = xmlBufUse(buf) - use;
2245 return (ret);
2249 * xmlElemDump:
2250 * @f: the FILE * for the output
2251 * @doc: the document
2252 * @cur: the current node
2254 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2256 void
2257 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2259 xmlOutputBufferPtr outbuf;
2261 xmlInitParser();
2263 if (cur == NULL) {
2264 #ifdef DEBUG_TREE
2265 xmlGenericError(xmlGenericErrorContext,
2266 "xmlElemDump : cur == NULL\n");
2267 #endif
2268 return;
2270 #ifdef DEBUG_TREE
2271 if (doc == NULL) {
2272 xmlGenericError(xmlGenericErrorContext,
2273 "xmlElemDump : doc == NULL\n");
2275 #endif
2277 outbuf = xmlOutputBufferCreateFile(f, NULL);
2278 if (outbuf == NULL)
2279 return;
2280 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2281 #ifdef LIBXML_HTML_ENABLED
2282 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2283 #else
2284 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2285 #endif /* LIBXML_HTML_ENABLED */
2286 } else
2287 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2288 xmlOutputBufferClose(outbuf);
2291 /************************************************************************
2293 * Saving functions front-ends *
2295 ************************************************************************/
2298 * xmlNodeDumpOutput:
2299 * @buf: the XML buffer output
2300 * @doc: the document
2301 * @cur: the current node
2302 * @level: the imbrication level for indenting
2303 * @format: is formatting allowed
2304 * @encoding: an optional encoding string
2306 * Dump an XML node, recursive behaviour, children are printed too.
2307 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2308 * or xmlKeepBlanksDefault(0) was called
2310 void
2311 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2312 int level, int format, const char *encoding)
2314 xmlSaveCtxt ctxt;
2315 #ifdef LIBXML_HTML_ENABLED
2316 xmlDtdPtr dtd;
2317 int is_xhtml = 0;
2318 #endif
2320 xmlInitParser();
2322 if ((buf == NULL) || (cur == NULL)) return;
2324 if (encoding == NULL)
2325 encoding = "UTF-8";
2327 memset(&ctxt, 0, sizeof(ctxt));
2328 ctxt.buf = buf;
2329 ctxt.level = level;
2330 ctxt.format = format ? 1 : 0;
2331 ctxt.encoding = (const xmlChar *) encoding;
2332 xmlSaveCtxtInit(&ctxt);
2333 ctxt.options |= XML_SAVE_AS_XML;
2335 #ifdef LIBXML_HTML_ENABLED
2336 dtd = xmlGetIntSubset(doc);
2337 if (dtd != NULL) {
2338 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2339 if (is_xhtml < 0)
2340 is_xhtml = 0;
2343 if (is_xhtml)
2344 xhtmlNodeDumpOutput(&ctxt, cur);
2345 else
2346 #endif
2347 xmlNodeDumpOutputInternal(&ctxt, cur);
2351 * xmlDocDumpFormatMemoryEnc:
2352 * @out_doc: Document to generate XML text from
2353 * @doc_txt_ptr: Memory pointer for allocated XML text
2354 * @doc_txt_len: Length of the generated XML text
2355 * @txt_encoding: Character encoding to use when generating XML text
2356 * @format: should formatting spaces been added
2358 * Dump the current DOM tree into memory using the character encoding specified
2359 * by the caller. Note it is up to the caller of this function to free the
2360 * allocated memory with xmlFree().
2361 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2362 * or xmlKeepBlanksDefault(0) was called
2365 void
2366 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2367 int * doc_txt_len, const char * txt_encoding,
2368 int format) {
2369 xmlSaveCtxt ctxt;
2370 int dummy = 0;
2371 xmlOutputBufferPtr out_buff = NULL;
2372 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2374 if (doc_txt_len == NULL) {
2375 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2378 if (doc_txt_ptr == NULL) {
2379 *doc_txt_len = 0;
2380 return;
2383 *doc_txt_ptr = NULL;
2384 *doc_txt_len = 0;
2386 if (out_doc == NULL) {
2387 /* No document, no output */
2388 return;
2392 * Validate the encoding value, if provided.
2393 * This logic is copied from xmlSaveFileEnc.
2396 if (txt_encoding == NULL)
2397 txt_encoding = (const char *) out_doc->encoding;
2398 if (txt_encoding != NULL) {
2399 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2400 if ( conv_hdlr == NULL ) {
2401 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2402 txt_encoding);
2403 return;
2407 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2408 xmlSaveErrMemory("creating buffer");
2409 return;
2412 memset(&ctxt, 0, sizeof(ctxt));
2413 ctxt.buf = out_buff;
2414 ctxt.level = 0;
2415 ctxt.format = format ? 1 : 0;
2416 ctxt.encoding = (const xmlChar *) txt_encoding;
2417 xmlSaveCtxtInit(&ctxt);
2418 ctxt.options |= XML_SAVE_AS_XML;
2419 xmlDocContentDumpOutput(&ctxt, out_doc);
2420 xmlOutputBufferFlush(out_buff);
2421 if (out_buff->conv != NULL) {
2422 *doc_txt_len = xmlBufUse(out_buff->conv);
2423 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2424 } else {
2425 *doc_txt_len = xmlBufUse(out_buff->buffer);
2426 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2428 (void)xmlOutputBufferClose(out_buff);
2430 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2431 *doc_txt_len = 0;
2432 xmlSaveErrMemory("creating output");
2435 return;
2439 * xmlDocDumpMemory:
2440 * @cur: the document
2441 * @mem: OUT: the memory pointer
2442 * @size: OUT: the memory length
2444 * Dump an XML document in memory and return the #xmlChar * and it's size
2445 * in bytes. It's up to the caller to free the memory with xmlFree().
2446 * The resulting byte array is zero terminated, though the last 0 is not
2447 * included in the returned size.
2449 void
2450 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2451 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2455 * xmlDocDumpFormatMemory:
2456 * @cur: the document
2457 * @mem: OUT: the memory pointer
2458 * @size: OUT: the memory length
2459 * @format: should formatting spaces been added
2462 * Dump an XML document in memory and return the #xmlChar * and it's size.
2463 * It's up to the caller to free the memory with xmlFree().
2464 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2465 * or xmlKeepBlanksDefault(0) was called
2467 void
2468 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2469 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2473 * xmlDocDumpMemoryEnc:
2474 * @out_doc: Document to generate XML text from
2475 * @doc_txt_ptr: Memory pointer for allocated XML text
2476 * @doc_txt_len: Length of the generated XML text
2477 * @txt_encoding: Character encoding to use when generating XML text
2479 * Dump the current DOM tree into memory using the character encoding specified
2480 * by the caller. Note it is up to the caller of this function to free the
2481 * allocated memory with xmlFree().
2484 void
2485 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2486 int * doc_txt_len, const char * txt_encoding) {
2487 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2488 txt_encoding, 0);
2492 * xmlDocFormatDump:
2493 * @f: the FILE*
2494 * @cur: the document
2495 * @format: should formatting spaces been added
2497 * Dump an XML document to an open FILE.
2499 * returns: the number of bytes written or -1 in case of failure.
2500 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2501 * or xmlKeepBlanksDefault(0) was called
2504 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2505 xmlSaveCtxt ctxt;
2506 xmlOutputBufferPtr buf;
2507 const char * encoding;
2508 xmlCharEncodingHandlerPtr handler = NULL;
2509 int ret;
2511 if (cur == NULL) {
2512 #ifdef DEBUG_TREE
2513 xmlGenericError(xmlGenericErrorContext,
2514 "xmlDocDump : document == NULL\n");
2515 #endif
2516 return(-1);
2518 encoding = (const char *) cur->encoding;
2520 if (encoding != NULL) {
2521 handler = xmlFindCharEncodingHandler(encoding);
2522 if (handler == NULL) {
2523 xmlFree((char *) cur->encoding);
2524 cur->encoding = NULL;
2525 encoding = NULL;
2528 buf = xmlOutputBufferCreateFile(f, handler);
2529 if (buf == NULL) return(-1);
2530 memset(&ctxt, 0, sizeof(ctxt));
2531 ctxt.buf = buf;
2532 ctxt.level = 0;
2533 ctxt.format = format ? 1 : 0;
2534 ctxt.encoding = (const xmlChar *) encoding;
2535 xmlSaveCtxtInit(&ctxt);
2536 ctxt.options |= XML_SAVE_AS_XML;
2537 xmlDocContentDumpOutput(&ctxt, cur);
2539 ret = xmlOutputBufferClose(buf);
2540 return(ret);
2544 * xmlDocDump:
2545 * @f: the FILE*
2546 * @cur: the document
2548 * Dump an XML document to an open FILE.
2550 * returns: the number of bytes written or -1 in case of failure.
2553 xmlDocDump(FILE *f, xmlDocPtr cur) {
2554 return(xmlDocFormatDump (f, cur, 0));
2558 * xmlSaveFileTo:
2559 * @buf: an output I/O buffer
2560 * @cur: the document
2561 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2563 * Dump an XML document to an I/O buffer.
2564 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2565 * after this call.
2567 * returns: the number of bytes written or -1 in case of failure.
2570 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2571 xmlSaveCtxt ctxt;
2572 int ret;
2574 if (buf == NULL) return(-1);
2575 if (cur == NULL) {
2576 xmlOutputBufferClose(buf);
2577 return(-1);
2579 memset(&ctxt, 0, sizeof(ctxt));
2580 ctxt.buf = buf;
2581 ctxt.level = 0;
2582 ctxt.format = 0;
2583 ctxt.encoding = (const xmlChar *) encoding;
2584 xmlSaveCtxtInit(&ctxt);
2585 ctxt.options |= XML_SAVE_AS_XML;
2586 xmlDocContentDumpOutput(&ctxt, cur);
2587 ret = xmlOutputBufferClose(buf);
2588 return(ret);
2592 * xmlSaveFormatFileTo:
2593 * @buf: an output I/O buffer
2594 * @cur: the document
2595 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2596 * @format: should formatting spaces been added
2598 * Dump an XML document to an I/O buffer.
2599 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2600 * after this call.
2602 * returns: the number of bytes written or -1 in case of failure.
2605 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2606 const char *encoding, int format)
2608 xmlSaveCtxt ctxt;
2609 int ret;
2611 if (buf == NULL) return(-1);
2612 if ((cur == NULL) ||
2613 ((cur->type != XML_DOCUMENT_NODE) &&
2614 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2615 xmlOutputBufferClose(buf);
2616 return(-1);
2618 memset(&ctxt, 0, sizeof(ctxt));
2619 ctxt.buf = buf;
2620 ctxt.level = 0;
2621 ctxt.format = format ? 1 : 0;
2622 ctxt.encoding = (const xmlChar *) encoding;
2623 xmlSaveCtxtInit(&ctxt);
2624 ctxt.options |= XML_SAVE_AS_XML;
2625 xmlDocContentDumpOutput(&ctxt, cur);
2626 ret = xmlOutputBufferClose(buf);
2627 return (ret);
2631 * xmlSaveFormatFileEnc:
2632 * @filename: the filename or URL to output
2633 * @cur: the document being saved
2634 * @encoding: the name of the encoding to use or NULL.
2635 * @format: should formatting spaces be added.
2637 * Dump an XML document to a file or an URL.
2639 * Returns the number of bytes written or -1 in case of error.
2640 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2641 * or xmlKeepBlanksDefault(0) was called
2644 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2645 const char * encoding, int format ) {
2646 xmlSaveCtxt ctxt;
2647 xmlOutputBufferPtr buf;
2648 xmlCharEncodingHandlerPtr handler = NULL;
2649 int ret;
2651 if (cur == NULL)
2652 return(-1);
2654 if (encoding == NULL)
2655 encoding = (const char *) cur->encoding;
2657 if (encoding != NULL) {
2659 handler = xmlFindCharEncodingHandler(encoding);
2660 if (handler == NULL)
2661 return(-1);
2664 #ifdef LIBXML_ZLIB_ENABLED
2665 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2666 #endif
2668 * save the content to a temp buffer.
2670 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2671 if (buf == NULL) return(-1);
2672 memset(&ctxt, 0, sizeof(ctxt));
2673 ctxt.buf = buf;
2674 ctxt.level = 0;
2675 ctxt.format = format ? 1 : 0;
2676 ctxt.encoding = (const xmlChar *) encoding;
2677 xmlSaveCtxtInit(&ctxt);
2678 ctxt.options |= XML_SAVE_AS_XML;
2680 xmlDocContentDumpOutput(&ctxt, cur);
2682 ret = xmlOutputBufferClose(buf);
2683 return(ret);
2688 * xmlSaveFileEnc:
2689 * @filename: the filename (or URL)
2690 * @cur: the document
2691 * @encoding: the name of an encoding (or NULL)
2693 * Dump an XML document, converting it to the given encoding
2695 * returns: the number of bytes written or -1 in case of failure.
2698 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2699 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2703 * xmlSaveFormatFile:
2704 * @filename: the filename (or URL)
2705 * @cur: the document
2706 * @format: should formatting spaces been added
2708 * Dump an XML document to a file. Will use compression if
2709 * compiled in and enabled. If @filename is "-" the stdout file is
2710 * used. If @format is set then the document will be indented on output.
2711 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2712 * or xmlKeepBlanksDefault(0) was called
2714 * returns: the number of bytes written or -1 in case of failure.
2717 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2718 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2722 * xmlSaveFile:
2723 * @filename: the filename (or URL)
2724 * @cur: the document
2726 * Dump an XML document to a file. Will use compression if
2727 * compiled in and enabled. If @filename is "-" the stdout file is
2728 * used.
2729 * returns: the number of bytes written or -1 in case of failure.
2732 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2733 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2736 #endif /* LIBXML_OUTPUT_ENABLED */