win32u: Don't report cloned monitors in EnumDisplayMonitors().
[wine.git] / libs / xml2 / xmlsave.c
blob489505f4865a87aaea0a1c8dbdf0488468651a54
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 xmlDumpNotationTable(buffer, table);
478 xmlBufMergeBuffer(buf, buffer);
482 * xmlBufDumpElementDecl:
483 * @buf: an xmlBufPtr output
484 * @elem: An element table
486 * This will dump the content of the element declaration as an XML
487 * DTD definition
489 void
490 xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
491 xmlBufferPtr buffer;
493 buffer = xmlBufferCreate();
494 if (buffer == NULL) {
496 * TODO set the error in buf
498 return;
500 xmlDumpElementDecl(buffer, elem);
501 xmlBufMergeBuffer(buf, buffer);
505 * xmlBufDumpAttributeDecl:
506 * @buf: an xmlBufPtr output
507 * @attr: An attribute declaration
509 * This will dump the content of the attribute declaration as an XML
510 * DTD definition
512 void
513 xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
514 xmlBufferPtr buffer;
516 buffer = xmlBufferCreate();
517 if (buffer == NULL) {
519 * TODO set the error in buf
521 return;
523 xmlDumpAttributeDecl(buffer, attr);
524 xmlBufMergeBuffer(buf, buffer);
528 * xmlBufDumpEntityDecl:
529 * @buf: an xmlBufPtr output
530 * @ent: An entity table
532 * This will dump the content of the entity table as an XML DTD definition
534 void
535 xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
536 xmlBufferPtr buffer;
538 buffer = xmlBufferCreate();
539 if (buffer == NULL) {
541 * TODO set the error in buf
543 return;
545 xmlDumpEntityDecl(buffer, ent);
546 xmlBufMergeBuffer(buf, buffer);
549 /************************************************************************
551 * Dumping XML tree content to an I/O output buffer *
553 ************************************************************************/
555 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
556 xmlOutputBufferPtr buf = ctxt->buf;
558 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
559 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
560 if (buf->encoder == NULL) {
561 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
562 (const char *)encoding);
563 return(-1);
565 buf->conv = xmlBufCreate();
566 if (buf->conv == NULL) {
567 xmlCharEncCloseFunc(buf->encoder);
568 xmlSaveErrMemory("creating encoding buffer");
569 return(-1);
572 * initialize the state, e.g. if outputting a BOM
574 xmlCharEncOutput(buf, 1);
576 return(0);
579 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
580 xmlOutputBufferPtr buf = ctxt->buf;
581 xmlOutputBufferFlush(buf);
582 xmlCharEncCloseFunc(buf->encoder);
583 xmlBufFree(buf->conv);
584 buf->encoder = NULL;
585 buf->conv = NULL;
586 return(0);
589 #ifdef LIBXML_HTML_ENABLED
590 static void
591 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
592 #endif
593 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
594 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
595 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
598 * xmlOutputBufferWriteWSNonSig:
599 * @ctxt: The save context
600 * @extra: Number of extra indents to apply to ctxt->level
602 * Write out formatting for non-significant whitespace output.
604 static void
605 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
607 int i;
608 if ((ctxt == NULL) || (ctxt->buf == NULL))
609 return;
610 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
611 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
612 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
613 ((ctxt->level + extra - i) > ctxt->indent_nr ?
614 ctxt->indent_nr : (ctxt->level + extra - i)),
615 ctxt->indent);
620 * xmlNsDumpOutput:
621 * @buf: the XML buffer output
622 * @cur: a namespace
623 * @ctxt: the output save context. Optional.
625 * Dump a local Namespace definition.
626 * Should be called in the context of attributes dumps.
627 * If @ctxt is supplied, @buf should be its buffer.
629 static void
630 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
631 if ((cur == NULL) || (buf == NULL)) return;
632 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
633 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
634 return;
636 if (ctxt != NULL && ctxt->format == 2)
637 xmlOutputBufferWriteWSNonSig(ctxt, 2);
638 else
639 xmlOutputBufferWrite(buf, 1, " ");
641 /* Within the context of an element attributes */
642 if (cur->prefix != NULL) {
643 xmlOutputBufferWrite(buf, 6, "xmlns:");
644 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
645 } else
646 xmlOutputBufferWrite(buf, 5, "xmlns");
647 xmlOutputBufferWrite(buf, 1, "=");
648 xmlBufWriteQuotedString(buf->buffer, cur->href);
653 * xmlNsDumpOutputCtxt
654 * @ctxt: the save context
655 * @cur: a namespace
657 * Dump a local Namespace definition to a save context.
658 * Should be called in the context of attribute dumps.
660 static void
661 xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
662 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
666 * xmlNsListDumpOutputCtxt
667 * @ctxt: the save context
668 * @cur: the first namespace
670 * Dump a list of local namespace definitions to a save context.
671 * Should be called in the context of attribute dumps.
673 static void
674 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
675 while (cur != NULL) {
676 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
677 cur = cur->next;
682 * xmlNsListDumpOutput:
683 * @buf: the XML buffer output
684 * @cur: the first namespace
686 * Dump a list of local Namespace definitions.
687 * Should be called in the context of attributes dumps.
689 void
690 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
691 while (cur != NULL) {
692 xmlNsDumpOutput(buf, cur, NULL);
693 cur = cur->next;
698 * xmlDtdDumpOutput:
699 * @buf: the XML buffer output
700 * @dtd: the pointer to the DTD
702 * Dump the XML document DTD, if any.
704 static void
705 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
706 xmlOutputBufferPtr buf;
707 xmlNodePtr cur;
708 int format, level;
710 if (dtd == NULL) return;
711 if ((ctxt == NULL) || (ctxt->buf == NULL))
712 return;
713 buf = ctxt->buf;
714 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
715 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
716 if (dtd->ExternalID != NULL) {
717 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
718 xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
719 xmlOutputBufferWrite(buf, 1, " ");
720 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
721 } else if (dtd->SystemID != NULL) {
722 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
723 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
725 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
726 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
727 (dtd->pentities == NULL)) {
728 xmlOutputBufferWrite(buf, 1, ">");
729 return;
731 xmlOutputBufferWrite(buf, 3, " [\n");
733 * Dump the notations first they are not in the DTD children list
734 * Do this only on a standalone DTD or on the internal subset though.
736 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
737 (dtd->doc->intSubset == dtd))) {
738 xmlBufDumpNotationTable(buf->buffer,
739 (xmlNotationTablePtr) dtd->notations);
741 format = ctxt->format;
742 level = ctxt->level;
743 ctxt->format = 0;
744 ctxt->level = -1;
745 for (cur = dtd->children; cur != NULL; cur = cur->next) {
746 xmlNodeDumpOutputInternal(ctxt, cur);
748 ctxt->format = format;
749 ctxt->level = level;
750 xmlOutputBufferWrite(buf, 2, "]>");
754 * xmlAttrDumpOutput:
755 * @buf: the XML buffer output
756 * @cur: the attribute pointer
758 * Dump an XML attribute
760 static void
761 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
762 xmlOutputBufferPtr buf;
764 if (cur == NULL) return;
765 buf = ctxt->buf;
766 if (buf == NULL) return;
767 if (ctxt->format == 2)
768 xmlOutputBufferWriteWSNonSig(ctxt, 2);
769 else
770 xmlOutputBufferWrite(buf, 1, " ");
771 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
772 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
773 xmlOutputBufferWrite(buf, 1, ":");
775 xmlOutputBufferWriteString(buf, (const char *)cur->name);
776 xmlOutputBufferWrite(buf, 2, "=\"");
777 xmlAttrSerializeContent(buf, cur);
778 xmlOutputBufferWrite(buf, 1, "\"");
781 #ifdef LIBXML_HTML_ENABLED
783 * htmlNodeDumpOutputInternal:
784 * @cur: the current node
786 * Dump an HTML node, recursive behaviour, children are printed too.
788 static int
789 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
790 const xmlChar *oldenc = NULL;
791 const xmlChar *oldctxtenc = ctxt->encoding;
792 const xmlChar *encoding = ctxt->encoding;
793 xmlOutputBufferPtr buf = ctxt->buf;
794 int switched_encoding = 0;
795 xmlDocPtr doc;
797 xmlInitParser();
799 doc = cur->doc;
800 if (doc != NULL) {
801 oldenc = doc->encoding;
802 if (ctxt->encoding != NULL) {
803 doc->encoding = BAD_CAST ctxt->encoding;
804 } else if (doc->encoding != NULL) {
805 encoding = doc->encoding;
809 if ((encoding != NULL) && (doc != NULL))
810 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
811 if ((encoding == NULL) && (doc != NULL))
812 encoding = htmlGetMetaEncoding(doc);
813 if (encoding == NULL)
814 encoding = BAD_CAST "HTML";
815 if ((encoding != NULL) && (oldctxtenc == NULL) &&
816 (buf->encoder == NULL) && (buf->conv == NULL)) {
817 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
818 doc->encoding = oldenc;
819 return(-1);
821 switched_encoding = 1;
823 if (ctxt->options & XML_SAVE_FORMAT)
824 htmlNodeDumpFormatOutput(buf, doc, cur,
825 (const char *)encoding, 1);
826 else
827 htmlNodeDumpFormatOutput(buf, doc, cur,
828 (const char *)encoding, 0);
830 * Restore the state of the saving context at the end of the document
832 if ((switched_encoding) && (oldctxtenc == NULL)) {
833 xmlSaveClearEncoding(ctxt);
835 if (doc != NULL)
836 doc->encoding = oldenc;
837 return(0);
839 #endif
842 * xmlNodeDumpOutputInternal:
843 * @cur: the current node
845 * Dump an XML node, recursive behaviour, children are printed too.
847 static void
848 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
849 int format = ctxt->format;
850 xmlNodePtr tmp, root, unformattedNode = NULL, parent;
851 xmlAttrPtr attr;
852 xmlChar *start, *end;
853 xmlOutputBufferPtr buf;
855 if (cur == NULL) return;
856 buf = ctxt->buf;
858 root = cur;
859 parent = cur->parent;
860 while (1) {
861 switch (cur->type) {
862 case XML_DOCUMENT_NODE:
863 case XML_HTML_DOCUMENT_NODE:
864 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
865 break;
867 case XML_DTD_NODE:
868 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
869 break;
871 case XML_DOCUMENT_FRAG_NODE:
872 /* Always validate cur->parent when descending. */
873 if ((cur->parent == parent) && (cur->children != NULL)) {
874 parent = cur;
875 cur = cur->children;
876 continue;
878 break;
880 case XML_ELEMENT_DECL:
881 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
882 break;
884 case XML_ATTRIBUTE_DECL:
885 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
886 break;
888 case XML_ENTITY_DECL:
889 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
890 break;
892 case XML_ELEMENT_NODE:
893 if ((cur != root) && (ctxt->format == 1) &&
894 (xmlIndentTreeOutput))
895 xmlOutputBufferWrite(buf, ctxt->indent_size *
896 (ctxt->level > ctxt->indent_nr ?
897 ctxt->indent_nr : ctxt->level),
898 ctxt->indent);
901 * Some users like lxml are known to pass nodes with a corrupted
902 * tree structure. Fall back to a recursive call to handle this
903 * case.
905 if ((cur->parent != parent) && (cur->children != NULL)) {
906 xmlNodeDumpOutputInternal(ctxt, cur);
907 break;
910 xmlOutputBufferWrite(buf, 1, "<");
911 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
912 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
913 xmlOutputBufferWrite(buf, 1, ":");
915 xmlOutputBufferWriteString(buf, (const char *)cur->name);
916 if (cur->nsDef)
917 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
918 for (attr = cur->properties; attr != NULL; attr = attr->next)
919 xmlAttrDumpOutput(ctxt, attr);
921 if (cur->children == NULL) {
922 if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
923 if (ctxt->format == 2)
924 xmlOutputBufferWriteWSNonSig(ctxt, 0);
925 xmlOutputBufferWrite(buf, 2, "/>");
926 } else {
927 if (ctxt->format == 2)
928 xmlOutputBufferWriteWSNonSig(ctxt, 1);
929 xmlOutputBufferWrite(buf, 3, "></");
930 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
931 xmlOutputBufferWriteString(buf,
932 (const char *)cur->ns->prefix);
933 xmlOutputBufferWrite(buf, 1, ":");
935 xmlOutputBufferWriteString(buf, (const char *)cur->name);
936 if (ctxt->format == 2)
937 xmlOutputBufferWriteWSNonSig(ctxt, 0);
938 xmlOutputBufferWrite(buf, 1, ">");
940 } else {
941 if (ctxt->format == 1) {
942 tmp = cur->children;
943 while (tmp != NULL) {
944 if ((tmp->type == XML_TEXT_NODE) ||
945 (tmp->type == XML_CDATA_SECTION_NODE) ||
946 (tmp->type == XML_ENTITY_REF_NODE)) {
947 ctxt->format = 0;
948 unformattedNode = cur;
949 break;
951 tmp = tmp->next;
954 if (ctxt->format == 2)
955 xmlOutputBufferWriteWSNonSig(ctxt, 1);
956 xmlOutputBufferWrite(buf, 1, ">");
957 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
958 if (ctxt->level >= 0) ctxt->level++;
959 parent = cur;
960 cur = cur->children;
961 continue;
964 break;
966 case XML_TEXT_NODE:
967 if (cur->content == NULL)
968 break;
969 if (cur->name != xmlStringTextNoenc) {
970 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
971 } else {
973 * Disable escaping, needed for XSLT
975 xmlOutputBufferWriteString(buf, (const char *) cur->content);
977 break;
979 case XML_PI_NODE:
980 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
981 xmlOutputBufferWrite(buf, ctxt->indent_size *
982 (ctxt->level > ctxt->indent_nr ?
983 ctxt->indent_nr : ctxt->level),
984 ctxt->indent);
986 if (cur->content != NULL) {
987 xmlOutputBufferWrite(buf, 2, "<?");
988 xmlOutputBufferWriteString(buf, (const char *)cur->name);
989 if (cur->content != NULL) {
990 if (ctxt->format == 2)
991 xmlOutputBufferWriteWSNonSig(ctxt, 0);
992 else
993 xmlOutputBufferWrite(buf, 1, " ");
994 xmlOutputBufferWriteString(buf,
995 (const char *)cur->content);
997 xmlOutputBufferWrite(buf, 2, "?>");
998 } else {
999 xmlOutputBufferWrite(buf, 2, "<?");
1000 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1001 if (ctxt->format == 2)
1002 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1003 xmlOutputBufferWrite(buf, 2, "?>");
1005 break;
1007 case XML_COMMENT_NODE:
1008 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1009 xmlOutputBufferWrite(buf, ctxt->indent_size *
1010 (ctxt->level > ctxt->indent_nr ?
1011 ctxt->indent_nr : ctxt->level),
1012 ctxt->indent);
1014 if (cur->content != NULL) {
1015 xmlOutputBufferWrite(buf, 4, "<!--");
1016 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1017 xmlOutputBufferWrite(buf, 3, "-->");
1019 break;
1021 case XML_ENTITY_REF_NODE:
1022 xmlOutputBufferWrite(buf, 1, "&");
1023 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1024 xmlOutputBufferWrite(buf, 1, ";");
1025 break;
1027 case XML_CDATA_SECTION_NODE:
1028 if (cur->content == NULL || *cur->content == '\0') {
1029 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1030 } else {
1031 start = end = cur->content;
1032 while (*end != '\0') {
1033 if ((*end == ']') && (*(end + 1) == ']') &&
1034 (*(end + 2) == '>')) {
1035 end = end + 2;
1036 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1037 xmlOutputBufferWrite(buf, end - start,
1038 (const char *)start);
1039 xmlOutputBufferWrite(buf, 3, "]]>");
1040 start = end;
1042 end++;
1044 if (start != end) {
1045 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1046 xmlOutputBufferWriteString(buf, (const char *)start);
1047 xmlOutputBufferWrite(buf, 3, "]]>");
1050 break;
1052 case XML_ATTRIBUTE_NODE:
1053 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1054 break;
1056 case XML_NAMESPACE_DECL:
1057 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1058 break;
1060 default:
1061 break;
1064 while (1) {
1065 if (cur == root)
1066 return;
1067 if ((ctxt->format == 1) &&
1068 (cur->type != XML_XINCLUDE_START) &&
1069 (cur->type != XML_XINCLUDE_END))
1070 xmlOutputBufferWrite(buf, 1, "\n");
1071 if (cur->next != NULL) {
1072 cur = cur->next;
1073 break;
1076 cur = parent;
1077 /* cur->parent was validated when descending. */
1078 parent = cur->parent;
1080 if (cur->type == XML_ELEMENT_NODE) {
1081 if (ctxt->level > 0) ctxt->level--;
1082 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1083 xmlOutputBufferWrite(buf, ctxt->indent_size *
1084 (ctxt->level > ctxt->indent_nr ?
1085 ctxt->indent_nr : ctxt->level),
1086 ctxt->indent);
1088 xmlOutputBufferWrite(buf, 2, "</");
1089 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1090 xmlOutputBufferWriteString(buf,
1091 (const char *)cur->ns->prefix);
1092 xmlOutputBufferWrite(buf, 1, ":");
1095 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1096 if (ctxt->format == 2)
1097 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1098 xmlOutputBufferWrite(buf, 1, ">");
1100 if (cur == unformattedNode) {
1101 ctxt->format = format;
1102 unformattedNode = NULL;
1110 * xmlDocContentDumpOutput:
1111 * @cur: the document
1113 * Dump an XML document.
1115 static int
1116 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1117 #ifdef LIBXML_HTML_ENABLED
1118 xmlDtdPtr dtd;
1119 int is_xhtml = 0;
1120 #endif
1121 const xmlChar *oldenc = cur->encoding;
1122 const xmlChar *oldctxtenc = ctxt->encoding;
1123 const xmlChar *encoding = ctxt->encoding;
1124 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1125 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1126 xmlOutputBufferPtr buf = ctxt->buf;
1127 xmlCharEncoding enc;
1128 int switched_encoding = 0;
1130 xmlInitParser();
1132 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1133 (cur->type != XML_DOCUMENT_NODE))
1134 return(-1);
1136 if (ctxt->encoding != NULL) {
1137 cur->encoding = BAD_CAST ctxt->encoding;
1138 } else if (cur->encoding != NULL) {
1139 encoding = cur->encoding;
1142 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1143 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1144 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1145 (ctxt->options & XML_SAVE_AS_HTML)) {
1146 #ifdef LIBXML_HTML_ENABLED
1147 if (encoding != NULL)
1148 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1149 if (encoding == NULL)
1150 encoding = htmlGetMetaEncoding(cur);
1151 if (encoding == NULL)
1152 encoding = BAD_CAST "HTML";
1153 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1154 (buf->encoder == NULL) && (buf->conv == NULL)) {
1155 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1156 cur->encoding = oldenc;
1157 return(-1);
1160 if (ctxt->options & XML_SAVE_FORMAT)
1161 htmlDocContentDumpFormatOutput(buf, cur,
1162 (const char *)encoding, 1);
1163 else
1164 htmlDocContentDumpFormatOutput(buf, cur,
1165 (const char *)encoding, 0);
1166 if (ctxt->encoding != NULL)
1167 cur->encoding = oldenc;
1168 return(0);
1169 #else
1170 return(-1);
1171 #endif
1172 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1173 (ctxt->options & XML_SAVE_AS_XML) ||
1174 (ctxt->options & XML_SAVE_XHTML)) {
1175 enc = xmlParseCharEncoding((const char*) encoding);
1176 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1177 (buf->encoder == NULL) && (buf->conv == NULL) &&
1178 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1179 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1180 (enc != XML_CHAR_ENCODING_NONE) &&
1181 (enc != XML_CHAR_ENCODING_ASCII)) {
1183 * we need to switch to this encoding but just for this
1184 * document since we output the XMLDecl the conversion
1185 * must be done to not generate not well formed documents.
1187 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1188 cur->encoding = oldenc;
1189 return(-1);
1191 switched_encoding = 1;
1193 if (ctxt->escape == xmlEscapeEntities)
1194 ctxt->escape = NULL;
1195 if (ctxt->escapeAttr == xmlEscapeEntities)
1196 ctxt->escapeAttr = NULL;
1201 * Save the XML declaration
1203 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1204 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1205 if (cur->version != NULL)
1206 xmlBufWriteQuotedString(buf->buffer, cur->version);
1207 else
1208 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1209 if (encoding != NULL) {
1210 xmlOutputBufferWrite(buf, 10, " encoding=");
1211 xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1213 switch (cur->standalone) {
1214 case 0:
1215 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1216 break;
1217 case 1:
1218 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1219 break;
1221 xmlOutputBufferWrite(buf, 3, "?>\n");
1224 #ifdef LIBXML_HTML_ENABLED
1225 if (ctxt->options & XML_SAVE_XHTML)
1226 is_xhtml = 1;
1227 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1228 dtd = xmlGetIntSubset(cur);
1229 if (dtd != NULL) {
1230 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1231 if (is_xhtml < 0) is_xhtml = 0;
1234 #endif
1235 if (cur->children != NULL) {
1236 xmlNodePtr child = cur->children;
1238 while (child != NULL) {
1239 ctxt->level = 0;
1240 #ifdef LIBXML_HTML_ENABLED
1241 if (is_xhtml)
1242 xhtmlNodeDumpOutput(ctxt, child);
1243 else
1244 #endif
1245 xmlNodeDumpOutputInternal(ctxt, child);
1246 if ((child->type != XML_XINCLUDE_START) &&
1247 (child->type != XML_XINCLUDE_END))
1248 xmlOutputBufferWrite(buf, 1, "\n");
1249 child = child->next;
1255 * Restore the state of the saving context at the end of the document
1257 if ((switched_encoding) && (oldctxtenc == NULL)) {
1258 xmlSaveClearEncoding(ctxt);
1259 ctxt->escape = oldescape;
1260 ctxt->escapeAttr = oldescapeAttr;
1262 cur->encoding = oldenc;
1263 return(0);
1266 #ifdef LIBXML_HTML_ENABLED
1267 /************************************************************************
1269 * Functions specific to XHTML serialization *
1271 ************************************************************************/
1274 * xhtmlIsEmpty:
1275 * @node: the node
1277 * Check if a node is an empty xhtml node
1279 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1281 static int
1282 xhtmlIsEmpty(xmlNodePtr node) {
1283 if (node == NULL)
1284 return(-1);
1285 if (node->type != XML_ELEMENT_NODE)
1286 return(0);
1287 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1288 return(0);
1289 if (node->children != NULL)
1290 return(0);
1291 switch (node->name[0]) {
1292 case 'a':
1293 if (xmlStrEqual(node->name, BAD_CAST "area"))
1294 return(1);
1295 return(0);
1296 case 'b':
1297 if (xmlStrEqual(node->name, BAD_CAST "br"))
1298 return(1);
1299 if (xmlStrEqual(node->name, BAD_CAST "base"))
1300 return(1);
1301 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1302 return(1);
1303 return(0);
1304 case 'c':
1305 if (xmlStrEqual(node->name, BAD_CAST "col"))
1306 return(1);
1307 return(0);
1308 case 'f':
1309 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1310 return(1);
1311 return(0);
1312 case 'h':
1313 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1314 return(1);
1315 return(0);
1316 case 'i':
1317 if (xmlStrEqual(node->name, BAD_CAST "img"))
1318 return(1);
1319 if (xmlStrEqual(node->name, BAD_CAST "input"))
1320 return(1);
1321 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1322 return(1);
1323 return(0);
1324 case 'l':
1325 if (xmlStrEqual(node->name, BAD_CAST "link"))
1326 return(1);
1327 return(0);
1328 case 'm':
1329 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1330 return(1);
1331 return(0);
1332 case 'p':
1333 if (xmlStrEqual(node->name, BAD_CAST "param"))
1334 return(1);
1335 return(0);
1337 return(0);
1341 * xhtmlAttrListDumpOutput:
1342 * @cur: the first attribute pointer
1344 * Dump a list of XML attributes
1346 static void
1347 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1348 xmlAttrPtr xml_lang = NULL;
1349 xmlAttrPtr lang = NULL;
1350 xmlAttrPtr name = NULL;
1351 xmlAttrPtr id = NULL;
1352 xmlNodePtr parent;
1353 xmlOutputBufferPtr buf;
1355 if (cur == NULL) return;
1356 buf = ctxt->buf;
1357 parent = cur->parent;
1358 while (cur != NULL) {
1359 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1360 id = cur;
1361 else
1362 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1363 name = cur;
1364 else
1365 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1366 lang = cur;
1367 else
1368 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1369 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1370 xml_lang = cur;
1371 else if ((cur->ns == NULL) &&
1372 ((cur->children == NULL) ||
1373 (cur->children->content == NULL) ||
1374 (cur->children->content[0] == 0)) &&
1375 (htmlIsBooleanAttr(cur->name))) {
1376 if (cur->children != NULL)
1377 xmlFreeNode(cur->children);
1378 cur->children = xmlNewText(cur->name);
1379 if (cur->children != NULL)
1380 cur->children->parent = (xmlNodePtr) cur;
1382 xmlAttrDumpOutput(ctxt, cur);
1383 cur = cur->next;
1386 * C.8
1388 if ((name != NULL) && (id == NULL)) {
1389 if ((parent != NULL) && (parent->name != NULL) &&
1390 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1391 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1392 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1393 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1394 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1395 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1396 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1397 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1398 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1399 xmlOutputBufferWrite(buf, 5, " id=\"");
1400 xmlAttrSerializeContent(buf, name);
1401 xmlOutputBufferWrite(buf, 1, "\"");
1405 * C.7.
1407 if ((lang != NULL) && (xml_lang == NULL)) {
1408 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1409 xmlAttrSerializeContent(buf, lang);
1410 xmlOutputBufferWrite(buf, 1, "\"");
1411 } else
1412 if ((xml_lang != NULL) && (lang == NULL)) {
1413 xmlOutputBufferWrite(buf, 7, " lang=\"");
1414 xmlAttrSerializeContent(buf, xml_lang);
1415 xmlOutputBufferWrite(buf, 1, "\"");
1420 * xhtmlNodeDumpOutput:
1421 * @buf: the XML buffer output
1422 * @doc: the XHTML document
1423 * @cur: the current node
1424 * @level: the imbrication level for indenting
1425 * @format: is formatting allowed
1426 * @encoding: an optional encoding string
1428 * Dump an XHTML node, recursive behaviour, children are printed too.
1430 static void
1431 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1432 int format = ctxt->format, addmeta;
1433 xmlNodePtr tmp, root, unformattedNode = NULL;
1434 xmlChar *start, *end;
1435 xmlOutputBufferPtr buf = ctxt->buf;
1437 if (cur == NULL) return;
1439 root = cur;
1440 while (1) {
1441 switch (cur->type) {
1442 case XML_DOCUMENT_NODE:
1443 case XML_HTML_DOCUMENT_NODE:
1444 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1445 break;
1447 case XML_NAMESPACE_DECL:
1448 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1449 break;
1451 case XML_DTD_NODE:
1452 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1453 break;
1455 case XML_DOCUMENT_FRAG_NODE:
1456 if (cur->children) {
1457 cur = cur->children;
1458 continue;
1460 break;
1462 case XML_ELEMENT_DECL:
1463 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1464 break;
1466 case XML_ATTRIBUTE_DECL:
1467 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1468 break;
1470 case XML_ENTITY_DECL:
1471 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1472 break;
1474 case XML_ELEMENT_NODE:
1475 addmeta = 0;
1477 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1478 xmlOutputBufferWrite(buf, ctxt->indent_size *
1479 (ctxt->level > ctxt->indent_nr ?
1480 ctxt->indent_nr : ctxt->level),
1481 ctxt->indent);
1483 xmlOutputBufferWrite(buf, 1, "<");
1484 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1485 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1486 xmlOutputBufferWrite(buf, 1, ":");
1489 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1490 if (cur->nsDef)
1491 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1492 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1493 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1495 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1497 xmlOutputBufferWriteString(buf,
1498 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1500 if (cur->properties != NULL)
1501 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1503 if ((cur->parent != NULL) &&
1504 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1505 xmlStrEqual(cur->name, BAD_CAST"head") &&
1506 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1508 tmp = cur->children;
1509 while (tmp != NULL) {
1510 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1511 xmlChar *httpequiv;
1513 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1514 if (httpequiv != NULL) {
1515 if (xmlStrcasecmp(httpequiv,
1516 BAD_CAST"Content-Type") == 0) {
1517 xmlFree(httpequiv);
1518 break;
1520 xmlFree(httpequiv);
1523 tmp = tmp->next;
1525 if (tmp == NULL)
1526 addmeta = 1;
1529 if (cur->children == NULL) {
1530 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1531 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1533 * C.2. Empty Elements
1535 xmlOutputBufferWrite(buf, 3, " />");
1536 } else {
1537 if (addmeta == 1) {
1538 xmlOutputBufferWrite(buf, 1, ">");
1539 if (ctxt->format == 1) {
1540 xmlOutputBufferWrite(buf, 1, "\n");
1541 if (xmlIndentTreeOutput)
1542 xmlOutputBufferWrite(buf, ctxt->indent_size *
1543 (ctxt->level + 1 > ctxt->indent_nr ?
1544 ctxt->indent_nr : ctxt->level + 1),
1545 ctxt->indent);
1547 xmlOutputBufferWriteString(buf,
1548 "<meta http-equiv=\"Content-Type\" "
1549 "content=\"text/html; charset=");
1550 if (ctxt->encoding) {
1551 xmlOutputBufferWriteString(buf,
1552 (const char *)ctxt->encoding);
1553 } else {
1554 xmlOutputBufferWrite(buf, 5, "UTF-8");
1556 xmlOutputBufferWrite(buf, 4, "\" />");
1557 if (ctxt->format == 1)
1558 xmlOutputBufferWrite(buf, 1, "\n");
1559 } else {
1560 xmlOutputBufferWrite(buf, 1, ">");
1563 * C.3. Element Minimization and Empty Element Content
1565 xmlOutputBufferWrite(buf, 2, "</");
1566 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1567 xmlOutputBufferWriteString(buf,
1568 (const char *)cur->ns->prefix);
1569 xmlOutputBufferWrite(buf, 1, ":");
1571 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1572 xmlOutputBufferWrite(buf, 1, ">");
1574 } else {
1575 xmlOutputBufferWrite(buf, 1, ">");
1576 if (addmeta == 1) {
1577 if (ctxt->format == 1) {
1578 xmlOutputBufferWrite(buf, 1, "\n");
1579 if (xmlIndentTreeOutput)
1580 xmlOutputBufferWrite(buf, ctxt->indent_size *
1581 (ctxt->level + 1 > ctxt->indent_nr ?
1582 ctxt->indent_nr : ctxt->level + 1),
1583 ctxt->indent);
1585 xmlOutputBufferWriteString(buf,
1586 "<meta http-equiv=\"Content-Type\" "
1587 "content=\"text/html; charset=");
1588 if (ctxt->encoding) {
1589 xmlOutputBufferWriteString(buf,
1590 (const char *)ctxt->encoding);
1591 } else {
1592 xmlOutputBufferWrite(buf, 5, "UTF-8");
1594 xmlOutputBufferWrite(buf, 4, "\" />");
1597 if (ctxt->format == 1) {
1598 tmp = cur->children;
1599 while (tmp != NULL) {
1600 if ((tmp->type == XML_TEXT_NODE) ||
1601 (tmp->type == XML_ENTITY_REF_NODE)) {
1602 unformattedNode = cur;
1603 ctxt->format = 0;
1604 break;
1606 tmp = tmp->next;
1610 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1611 if (ctxt->level >= 0) ctxt->level++;
1612 cur = cur->children;
1613 continue;
1616 break;
1618 case XML_TEXT_NODE:
1619 if (cur->content == NULL)
1620 break;
1621 if ((cur->name == xmlStringText) ||
1622 (cur->name != xmlStringTextNoenc)) {
1623 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1624 } else {
1626 * Disable escaping, needed for XSLT
1628 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1630 break;
1632 case XML_PI_NODE:
1633 if (cur->content != NULL) {
1634 xmlOutputBufferWrite(buf, 2, "<?");
1635 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1636 if (cur->content != NULL) {
1637 xmlOutputBufferWrite(buf, 1, " ");
1638 xmlOutputBufferWriteString(buf,
1639 (const char *)cur->content);
1641 xmlOutputBufferWrite(buf, 2, "?>");
1642 } else {
1643 xmlOutputBufferWrite(buf, 2, "<?");
1644 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1645 xmlOutputBufferWrite(buf, 2, "?>");
1647 break;
1649 case XML_COMMENT_NODE:
1650 if (cur->content != NULL) {
1651 xmlOutputBufferWrite(buf, 4, "<!--");
1652 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1653 xmlOutputBufferWrite(buf, 3, "-->");
1655 break;
1657 case XML_ENTITY_REF_NODE:
1658 xmlOutputBufferWrite(buf, 1, "&");
1659 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1660 xmlOutputBufferWrite(buf, 1, ";");
1661 break;
1663 case XML_CDATA_SECTION_NODE:
1664 if (cur->content == NULL || *cur->content == '\0') {
1665 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1666 } else {
1667 start = end = cur->content;
1668 while (*end != '\0') {
1669 if (*end == ']' && *(end + 1) == ']' &&
1670 *(end + 2) == '>') {
1671 end = end + 2;
1672 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1673 xmlOutputBufferWrite(buf, end - start,
1674 (const char *)start);
1675 xmlOutputBufferWrite(buf, 3, "]]>");
1676 start = end;
1678 end++;
1680 if (start != end) {
1681 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1682 xmlOutputBufferWriteString(buf, (const char *)start);
1683 xmlOutputBufferWrite(buf, 3, "]]>");
1686 break;
1688 case XML_ATTRIBUTE_NODE:
1689 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1690 break;
1692 default:
1693 break;
1696 while (1) {
1697 if (cur == root)
1698 return;
1699 if (ctxt->format == 1)
1700 xmlOutputBufferWrite(buf, 1, "\n");
1701 if (cur->next != NULL) {
1702 cur = cur->next;
1703 break;
1707 * The parent should never be NULL here but we want to handle
1708 * corrupted documents gracefully.
1710 if (cur->parent == NULL)
1711 return;
1712 cur = cur->parent;
1714 if (cur->type == XML_ELEMENT_NODE) {
1715 if (ctxt->level > 0) ctxt->level--;
1716 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1717 xmlOutputBufferWrite(buf, ctxt->indent_size *
1718 (ctxt->level > ctxt->indent_nr ?
1719 ctxt->indent_nr : ctxt->level),
1720 ctxt->indent);
1722 xmlOutputBufferWrite(buf, 2, "</");
1723 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1724 xmlOutputBufferWriteString(buf,
1725 (const char *)cur->ns->prefix);
1726 xmlOutputBufferWrite(buf, 1, ":");
1729 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1730 xmlOutputBufferWrite(buf, 1, ">");
1732 if (cur == unformattedNode) {
1733 ctxt->format = format;
1734 unformattedNode = NULL;
1740 #endif
1742 /************************************************************************
1744 * Public entry points *
1746 ************************************************************************/
1749 * xmlSaveToFd:
1750 * @fd: a file descriptor number
1751 * @encoding: the encoding name to use or NULL
1752 * @options: a set of xmlSaveOptions
1754 * Create a document saving context serializing to a file descriptor
1755 * with the encoding and the options given.
1757 * Returns a new serialization context or NULL in case of error.
1759 xmlSaveCtxtPtr
1760 xmlSaveToFd(int fd, const char *encoding, int options)
1762 xmlSaveCtxtPtr ret;
1764 ret = xmlNewSaveCtxt(encoding, options);
1765 if (ret == NULL) return(NULL);
1766 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1767 if (ret->buf == NULL) {
1768 xmlCharEncCloseFunc(ret->handler);
1769 xmlFreeSaveCtxt(ret);
1770 return(NULL);
1772 return(ret);
1776 * xmlSaveToFilename:
1777 * @filename: a file name or an URL
1778 * @encoding: the encoding name to use or NULL
1779 * @options: a set of xmlSaveOptions
1781 * Create a document saving context serializing to a filename or possibly
1782 * to an URL (but this is less reliable) with the encoding and the options
1783 * given.
1785 * Returns a new serialization context or NULL in case of error.
1787 xmlSaveCtxtPtr
1788 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1790 xmlSaveCtxtPtr ret;
1791 int compression = 0; /* TODO handle compression option */
1793 ret = xmlNewSaveCtxt(encoding, options);
1794 if (ret == NULL) return(NULL);
1795 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1796 compression);
1797 if (ret->buf == NULL) {
1798 xmlCharEncCloseFunc(ret->handler);
1799 xmlFreeSaveCtxt(ret);
1800 return(NULL);
1802 return(ret);
1806 * xmlSaveToBuffer:
1807 * @buffer: a buffer
1808 * @encoding: the encoding name to use or NULL
1809 * @options: a set of xmlSaveOptions
1811 * Create a document saving context serializing to a buffer
1812 * with the encoding and the options given
1814 * Returns a new serialization context or NULL in case of error.
1817 xmlSaveCtxtPtr
1818 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1820 xmlSaveCtxtPtr ret;
1822 ret = xmlNewSaveCtxt(encoding, options);
1823 if (ret == NULL) return(NULL);
1824 ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
1825 if (ret->buf == NULL) {
1826 xmlCharEncCloseFunc(ret->handler);
1827 xmlFreeSaveCtxt(ret);
1828 return(NULL);
1830 return(ret);
1834 * xmlSaveToIO:
1835 * @iowrite: an I/O write function
1836 * @ioclose: an I/O close function
1837 * @ioctx: an I/O handler
1838 * @encoding: the encoding name to use or NULL
1839 * @options: a set of xmlSaveOptions
1841 * Create a document saving context serializing to a file descriptor
1842 * with the encoding and the options given
1844 * Returns a new serialization context or NULL in case of error.
1846 xmlSaveCtxtPtr
1847 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1848 xmlOutputCloseCallback ioclose,
1849 void *ioctx, const char *encoding, int options)
1851 xmlSaveCtxtPtr ret;
1853 ret = xmlNewSaveCtxt(encoding, options);
1854 if (ret == NULL) return(NULL);
1855 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1856 if (ret->buf == NULL) {
1857 xmlCharEncCloseFunc(ret->handler);
1858 xmlFreeSaveCtxt(ret);
1859 return(NULL);
1861 return(ret);
1865 * xmlSaveDoc:
1866 * @ctxt: a document saving context
1867 * @doc: a document
1869 * Save a full document to a saving context
1870 * TODO: The function is not fully implemented yet as it does not return the
1871 * byte count but 0 instead
1873 * Returns the number of byte written or -1 in case of error
1875 long
1876 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1878 long ret = 0;
1880 if ((ctxt == NULL) || (doc == NULL)) return(-1);
1881 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1882 return(-1);
1883 return(ret);
1887 * xmlSaveTree:
1888 * @ctxt: a document saving context
1889 * @node: the top node of the subtree to save
1891 * Save a subtree starting at the node parameter to a saving context
1892 * TODO: The function is not fully implemented yet as it does not return the
1893 * byte count but 0 instead
1895 * Returns the number of byte written or -1 in case of error
1897 long
1898 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
1900 long ret = 0;
1902 if ((ctxt == NULL) || (cur == NULL)) return(-1);
1903 #ifdef LIBXML_HTML_ENABLED
1904 if (ctxt->options & XML_SAVE_XHTML) {
1905 xhtmlNodeDumpOutput(ctxt, cur);
1906 return(ret);
1908 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
1909 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
1910 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
1911 (ctxt->options & XML_SAVE_AS_HTML)) {
1912 htmlNodeDumpOutputInternal(ctxt, cur);
1913 return(ret);
1915 #endif
1916 xmlNodeDumpOutputInternal(ctxt, cur);
1917 return(ret);
1921 * xmlSaveFlush:
1922 * @ctxt: a document saving context
1924 * Flush a document saving context, i.e. make sure that all bytes have
1925 * been output.
1927 * Returns the number of byte written or -1 in case of error.
1930 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1932 if (ctxt == NULL) return(-1);
1933 if (ctxt->buf == NULL) return(-1);
1934 return(xmlOutputBufferFlush(ctxt->buf));
1938 * xmlSaveClose:
1939 * @ctxt: a document saving context
1941 * Close a document saving context, i.e. make sure that all bytes have
1942 * been output and free the associated data.
1944 * Returns the number of byte written or -1 in case of error.
1947 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1949 int ret;
1951 if (ctxt == NULL) return(-1);
1952 ret = xmlSaveFlush(ctxt);
1953 xmlFreeSaveCtxt(ctxt);
1954 return(ret);
1958 * xmlSaveSetEscape:
1959 * @ctxt: a document saving context
1960 * @escape: the escaping function
1962 * Set a custom escaping function to be used for text in element content
1964 * Returns 0 if successful or -1 in case of error.
1967 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1969 if (ctxt == NULL) return(-1);
1970 ctxt->escape = escape;
1971 return(0);
1975 * xmlSaveSetAttrEscape:
1976 * @ctxt: a document saving context
1977 * @escape: the escaping function
1979 * Set a custom escaping function to be used for text in attribute content
1981 * Returns 0 if successful or -1 in case of error.
1984 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1986 if (ctxt == NULL) return(-1);
1987 ctxt->escapeAttr = escape;
1988 return(0);
1991 /************************************************************************
1993 * Public entry points based on buffers *
1995 ************************************************************************/
1998 * xmlBufAttrSerializeTxtContent:
1999 * @buf: and xmlBufPtr output
2000 * @doc: the document
2001 * @attr: the attribute node
2002 * @string: the text content
2004 * Serialize text attribute values to an xmlBufPtr
2006 void
2007 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
2008 xmlAttrPtr attr, const xmlChar * string)
2010 xmlChar *base, *cur;
2012 if (string == NULL)
2013 return;
2014 base = cur = (xmlChar *) string;
2015 while (*cur != 0) {
2016 if (*cur == '\n') {
2017 if (base != cur)
2018 xmlBufAdd(buf, base, cur - base);
2019 xmlBufAdd(buf, BAD_CAST "&#10;", 5);
2020 cur++;
2021 base = cur;
2022 } else if (*cur == '\r') {
2023 if (base != cur)
2024 xmlBufAdd(buf, base, cur - base);
2025 xmlBufAdd(buf, BAD_CAST "&#13;", 5);
2026 cur++;
2027 base = cur;
2028 } else if (*cur == '\t') {
2029 if (base != cur)
2030 xmlBufAdd(buf, base, cur - base);
2031 xmlBufAdd(buf, BAD_CAST "&#9;", 4);
2032 cur++;
2033 base = cur;
2034 } else if (*cur == '"') {
2035 if (base != cur)
2036 xmlBufAdd(buf, base, cur - base);
2037 xmlBufAdd(buf, BAD_CAST "&quot;", 6);
2038 cur++;
2039 base = cur;
2040 } else if (*cur == '<') {
2041 if (base != cur)
2042 xmlBufAdd(buf, base, cur - base);
2043 xmlBufAdd(buf, BAD_CAST "&lt;", 4);
2044 cur++;
2045 base = cur;
2046 } else if (*cur == '>') {
2047 if (base != cur)
2048 xmlBufAdd(buf, base, cur - base);
2049 xmlBufAdd(buf, BAD_CAST "&gt;", 4);
2050 cur++;
2051 base = cur;
2052 } else if (*cur == '&') {
2053 if (base != cur)
2054 xmlBufAdd(buf, base, cur - base);
2055 xmlBufAdd(buf, BAD_CAST "&amp;", 5);
2056 cur++;
2057 base = cur;
2058 } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2059 ((doc == NULL) || (doc->encoding == NULL))) {
2061 * We assume we have UTF-8 content.
2063 unsigned char tmp[12];
2064 int val = 0, l = 1;
2066 if (base != cur)
2067 xmlBufAdd(buf, base, cur - base);
2068 if (*cur < 0xC0) {
2069 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2070 xmlSerializeHexCharRef(tmp, *cur);
2071 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2072 cur++;
2073 base = cur;
2074 continue;
2075 } else if (*cur < 0xE0) {
2076 val = (cur[0]) & 0x1F;
2077 val <<= 6;
2078 val |= (cur[1]) & 0x3F;
2079 l = 2;
2080 } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2081 val = (cur[0]) & 0x0F;
2082 val <<= 6;
2083 val |= (cur[1]) & 0x3F;
2084 val <<= 6;
2085 val |= (cur[2]) & 0x3F;
2086 l = 3;
2087 } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2088 val = (cur[0]) & 0x07;
2089 val <<= 6;
2090 val |= (cur[1]) & 0x3F;
2091 val <<= 6;
2092 val |= (cur[2]) & 0x3F;
2093 val <<= 6;
2094 val |= (cur[3]) & 0x3F;
2095 l = 4;
2097 if ((l == 1) || (!IS_CHAR(val))) {
2098 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2099 xmlSerializeHexCharRef(tmp, *cur);
2100 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2101 cur++;
2102 base = cur;
2103 continue;
2106 * We could do multiple things here. Just save
2107 * as a char ref
2109 xmlSerializeHexCharRef(tmp, val);
2110 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2111 cur += l;
2112 base = cur;
2113 } else {
2114 cur++;
2117 if (base != cur)
2118 xmlBufAdd(buf, base, cur - base);
2122 * xmlAttrSerializeTxtContent:
2123 * @buf: the XML buffer output
2124 * @doc: the document
2125 * @attr: the attribute node
2126 * @string: the text content
2128 * Serialize text attribute values to an xml simple buffer
2130 void
2131 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2132 xmlAttrPtr attr, const xmlChar * string)
2134 xmlBufPtr buffer;
2136 if ((buf == NULL) || (string == NULL))
2137 return;
2138 buffer = xmlBufFromBuffer(buf);
2139 if (buffer == NULL)
2140 return;
2141 xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2142 xmlBufBackToBuffer(buffer);
2146 * xmlNodeDump:
2147 * @buf: the XML buffer output
2148 * @doc: the document
2149 * @cur: the current node
2150 * @level: the imbrication level for indenting
2151 * @format: is formatting allowed
2153 * Dump an XML node, recursive behaviour,children are printed too.
2154 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2155 * or xmlKeepBlanksDefault(0) was called.
2156 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2157 * deprecated, use xmlNodeDumpOutput() instead.
2159 * Returns the number of bytes written to the buffer or -1 in case of error
2162 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2163 int format)
2165 xmlBufPtr buffer;
2166 size_t ret;
2168 if ((buf == NULL) || (cur == NULL))
2169 return(-1);
2170 buffer = xmlBufFromBuffer(buf);
2171 if (buffer == NULL)
2172 return(-1);
2173 ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2174 xmlBufBackToBuffer(buffer);
2175 if (ret > INT_MAX)
2176 return(-1);
2177 return((int) ret);
2181 * xmlBufNodeDump:
2182 * @buf: the XML buffer output
2183 * @doc: the document
2184 * @cur: the current node
2185 * @level: the imbrication level for indenting
2186 * @format: is formatting allowed
2188 * Dump an XML node, recursive behaviour,children are printed too.
2189 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2190 * or xmlKeepBlanksDefault(0) was called
2192 * Returns the number of bytes written to the buffer, in case of error 0
2193 * is returned or @buf stores the error
2196 size_t
2197 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2198 int format)
2200 size_t use;
2201 int ret;
2202 xmlOutputBufferPtr outbuf;
2203 int oldalloc;
2205 xmlInitParser();
2207 if (cur == NULL) {
2208 #ifdef DEBUG_TREE
2209 xmlGenericError(xmlGenericErrorContext,
2210 "xmlNodeDump : node == NULL\n");
2211 #endif
2212 return (-1);
2214 if (buf == NULL) {
2215 #ifdef DEBUG_TREE
2216 xmlGenericError(xmlGenericErrorContext,
2217 "xmlNodeDump : buf == NULL\n");
2218 #endif
2219 return (-1);
2221 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2222 if (outbuf == NULL) {
2223 xmlSaveErrMemory("creating buffer");
2224 return (-1);
2226 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2227 outbuf->buffer = buf;
2228 outbuf->encoder = NULL;
2229 outbuf->writecallback = NULL;
2230 outbuf->closecallback = NULL;
2231 outbuf->context = NULL;
2232 outbuf->written = 0;
2234 use = xmlBufUse(buf);
2235 oldalloc = xmlBufGetAllocationScheme(buf);
2236 xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
2237 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2238 xmlBufSetAllocationScheme(buf, oldalloc);
2239 xmlFree(outbuf);
2240 ret = xmlBufUse(buf) - use;
2241 return (ret);
2245 * xmlElemDump:
2246 * @f: the FILE * for the output
2247 * @doc: the document
2248 * @cur: the current node
2250 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2252 void
2253 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2255 xmlOutputBufferPtr outbuf;
2257 xmlInitParser();
2259 if (cur == NULL) {
2260 #ifdef DEBUG_TREE
2261 xmlGenericError(xmlGenericErrorContext,
2262 "xmlElemDump : cur == NULL\n");
2263 #endif
2264 return;
2266 #ifdef DEBUG_TREE
2267 if (doc == NULL) {
2268 xmlGenericError(xmlGenericErrorContext,
2269 "xmlElemDump : doc == NULL\n");
2271 #endif
2273 outbuf = xmlOutputBufferCreateFile(f, NULL);
2274 if (outbuf == NULL)
2275 return;
2276 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2277 #ifdef LIBXML_HTML_ENABLED
2278 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2279 #else
2280 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2281 #endif /* LIBXML_HTML_ENABLED */
2282 } else
2283 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2284 xmlOutputBufferClose(outbuf);
2287 /************************************************************************
2289 * Saving functions front-ends *
2291 ************************************************************************/
2294 * xmlNodeDumpOutput:
2295 * @buf: the XML buffer output
2296 * @doc: the document
2297 * @cur: the current node
2298 * @level: the imbrication level for indenting
2299 * @format: is formatting allowed
2300 * @encoding: an optional encoding string
2302 * Dump an XML node, recursive behaviour, children are printed too.
2303 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2304 * or xmlKeepBlanksDefault(0) was called
2306 void
2307 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2308 int level, int format, const char *encoding)
2310 xmlSaveCtxt ctxt;
2311 #ifdef LIBXML_HTML_ENABLED
2312 xmlDtdPtr dtd;
2313 int is_xhtml = 0;
2314 #endif
2316 xmlInitParser();
2318 if ((buf == NULL) || (cur == NULL)) return;
2320 if (encoding == NULL)
2321 encoding = "UTF-8";
2323 memset(&ctxt, 0, sizeof(ctxt));
2324 ctxt.buf = buf;
2325 ctxt.level = level;
2326 ctxt.format = format ? 1 : 0;
2327 ctxt.encoding = (const xmlChar *) encoding;
2328 xmlSaveCtxtInit(&ctxt);
2329 ctxt.options |= XML_SAVE_AS_XML;
2331 #ifdef LIBXML_HTML_ENABLED
2332 dtd = xmlGetIntSubset(doc);
2333 if (dtd != NULL) {
2334 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2335 if (is_xhtml < 0)
2336 is_xhtml = 0;
2339 if (is_xhtml)
2340 xhtmlNodeDumpOutput(&ctxt, cur);
2341 else
2342 #endif
2343 xmlNodeDumpOutputInternal(&ctxt, cur);
2347 * xmlDocDumpFormatMemoryEnc:
2348 * @out_doc: Document to generate XML text from
2349 * @doc_txt_ptr: Memory pointer for allocated XML text
2350 * @doc_txt_len: Length of the generated XML text
2351 * @txt_encoding: Character encoding to use when generating XML text
2352 * @format: should formatting spaces been added
2354 * Dump the current DOM tree into memory using the character encoding specified
2355 * by the caller. Note it is up to the caller of this function to free the
2356 * allocated memory with xmlFree().
2357 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2358 * or xmlKeepBlanksDefault(0) was called
2361 void
2362 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2363 int * doc_txt_len, const char * txt_encoding,
2364 int format) {
2365 xmlSaveCtxt ctxt;
2366 int dummy = 0;
2367 xmlOutputBufferPtr out_buff = NULL;
2368 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2370 if (doc_txt_len == NULL) {
2371 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2374 if (doc_txt_ptr == NULL) {
2375 *doc_txt_len = 0;
2376 return;
2379 *doc_txt_ptr = NULL;
2380 *doc_txt_len = 0;
2382 if (out_doc == NULL) {
2383 /* No document, no output */
2384 return;
2388 * Validate the encoding value, if provided.
2389 * This logic is copied from xmlSaveFileEnc.
2392 if (txt_encoding == NULL)
2393 txt_encoding = (const char *) out_doc->encoding;
2394 if (txt_encoding != NULL) {
2395 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2396 if ( conv_hdlr == NULL ) {
2397 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2398 txt_encoding);
2399 return;
2403 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2404 xmlSaveErrMemory("creating buffer");
2405 return;
2408 memset(&ctxt, 0, sizeof(ctxt));
2409 ctxt.buf = out_buff;
2410 ctxt.level = 0;
2411 ctxt.format = format ? 1 : 0;
2412 ctxt.encoding = (const xmlChar *) txt_encoding;
2413 xmlSaveCtxtInit(&ctxt);
2414 ctxt.options |= XML_SAVE_AS_XML;
2415 xmlDocContentDumpOutput(&ctxt, out_doc);
2416 xmlOutputBufferFlush(out_buff);
2417 if (out_buff->conv != NULL) {
2418 *doc_txt_len = xmlBufUse(out_buff->conv);
2419 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2420 } else {
2421 *doc_txt_len = xmlBufUse(out_buff->buffer);
2422 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2424 (void)xmlOutputBufferClose(out_buff);
2426 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2427 *doc_txt_len = 0;
2428 xmlSaveErrMemory("creating output");
2431 return;
2435 * xmlDocDumpMemory:
2436 * @cur: the document
2437 * @mem: OUT: the memory pointer
2438 * @size: OUT: the memory length
2440 * Dump an XML document in memory and return the #xmlChar * and it's size
2441 * in bytes. It's up to the caller to free the memory with xmlFree().
2442 * The resulting byte array is zero terminated, though the last 0 is not
2443 * included in the returned size.
2445 void
2446 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2447 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2451 * xmlDocDumpFormatMemory:
2452 * @cur: the document
2453 * @mem: OUT: the memory pointer
2454 * @size: OUT: the memory length
2455 * @format: should formatting spaces been added
2458 * Dump an XML document in memory and return the #xmlChar * and it's size.
2459 * It's up to the caller to free the memory with xmlFree().
2460 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2461 * or xmlKeepBlanksDefault(0) was called
2463 void
2464 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2465 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2469 * xmlDocDumpMemoryEnc:
2470 * @out_doc: Document to generate XML text from
2471 * @doc_txt_ptr: Memory pointer for allocated XML text
2472 * @doc_txt_len: Length of the generated XML text
2473 * @txt_encoding: Character encoding to use when generating XML text
2475 * Dump the current DOM tree into memory using the character encoding specified
2476 * by the caller. Note it is up to the caller of this function to free the
2477 * allocated memory with xmlFree().
2480 void
2481 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2482 int * doc_txt_len, const char * txt_encoding) {
2483 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2484 txt_encoding, 0);
2488 * xmlDocFormatDump:
2489 * @f: the FILE*
2490 * @cur: the document
2491 * @format: should formatting spaces been added
2493 * Dump an XML document to an open FILE.
2495 * returns: the number of bytes written or -1 in case of failure.
2496 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2497 * or xmlKeepBlanksDefault(0) was called
2500 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2501 xmlSaveCtxt ctxt;
2502 xmlOutputBufferPtr buf;
2503 const char * encoding;
2504 xmlCharEncodingHandlerPtr handler = NULL;
2505 int ret;
2507 if (cur == NULL) {
2508 #ifdef DEBUG_TREE
2509 xmlGenericError(xmlGenericErrorContext,
2510 "xmlDocDump : document == NULL\n");
2511 #endif
2512 return(-1);
2514 encoding = (const char *) cur->encoding;
2516 if (encoding != NULL) {
2517 handler = xmlFindCharEncodingHandler(encoding);
2518 if (handler == NULL) {
2519 xmlFree((char *) cur->encoding);
2520 cur->encoding = NULL;
2521 encoding = NULL;
2524 buf = xmlOutputBufferCreateFile(f, handler);
2525 if (buf == NULL) return(-1);
2526 memset(&ctxt, 0, sizeof(ctxt));
2527 ctxt.buf = buf;
2528 ctxt.level = 0;
2529 ctxt.format = format ? 1 : 0;
2530 ctxt.encoding = (const xmlChar *) encoding;
2531 xmlSaveCtxtInit(&ctxt);
2532 ctxt.options |= XML_SAVE_AS_XML;
2533 xmlDocContentDumpOutput(&ctxt, cur);
2535 ret = xmlOutputBufferClose(buf);
2536 return(ret);
2540 * xmlDocDump:
2541 * @f: the FILE*
2542 * @cur: the document
2544 * Dump an XML document to an open FILE.
2546 * returns: the number of bytes written or -1 in case of failure.
2549 xmlDocDump(FILE *f, xmlDocPtr cur) {
2550 return(xmlDocFormatDump (f, cur, 0));
2554 * xmlSaveFileTo:
2555 * @buf: an output I/O buffer
2556 * @cur: the document
2557 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2559 * Dump an XML document to an I/O buffer.
2560 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2561 * after this call.
2563 * returns: the number of bytes written or -1 in case of failure.
2566 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2567 xmlSaveCtxt ctxt;
2568 int ret;
2570 if (buf == NULL) return(-1);
2571 if (cur == NULL) {
2572 xmlOutputBufferClose(buf);
2573 return(-1);
2575 memset(&ctxt, 0, sizeof(ctxt));
2576 ctxt.buf = buf;
2577 ctxt.level = 0;
2578 ctxt.format = 0;
2579 ctxt.encoding = (const xmlChar *) encoding;
2580 xmlSaveCtxtInit(&ctxt);
2581 ctxt.options |= XML_SAVE_AS_XML;
2582 xmlDocContentDumpOutput(&ctxt, cur);
2583 ret = xmlOutputBufferClose(buf);
2584 return(ret);
2588 * xmlSaveFormatFileTo:
2589 * @buf: an output I/O buffer
2590 * @cur: the document
2591 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2592 * @format: should formatting spaces been added
2594 * Dump an XML document to an I/O buffer.
2595 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2596 * after this call.
2598 * returns: the number of bytes written or -1 in case of failure.
2601 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2602 const char *encoding, int format)
2604 xmlSaveCtxt ctxt;
2605 int ret;
2607 if (buf == NULL) return(-1);
2608 if ((cur == NULL) ||
2609 ((cur->type != XML_DOCUMENT_NODE) &&
2610 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2611 xmlOutputBufferClose(buf);
2612 return(-1);
2614 memset(&ctxt, 0, sizeof(ctxt));
2615 ctxt.buf = buf;
2616 ctxt.level = 0;
2617 ctxt.format = format ? 1 : 0;
2618 ctxt.encoding = (const xmlChar *) encoding;
2619 xmlSaveCtxtInit(&ctxt);
2620 ctxt.options |= XML_SAVE_AS_XML;
2621 xmlDocContentDumpOutput(&ctxt, cur);
2622 ret = xmlOutputBufferClose(buf);
2623 return (ret);
2627 * xmlSaveFormatFileEnc:
2628 * @filename: the filename or URL to output
2629 * @cur: the document being saved
2630 * @encoding: the name of the encoding to use or NULL.
2631 * @format: should formatting spaces be added.
2633 * Dump an XML document to a file or an URL.
2635 * Returns the number of bytes written or -1 in case of error.
2636 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2637 * or xmlKeepBlanksDefault(0) was called
2640 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2641 const char * encoding, int format ) {
2642 xmlSaveCtxt ctxt;
2643 xmlOutputBufferPtr buf;
2644 xmlCharEncodingHandlerPtr handler = NULL;
2645 int ret;
2647 if (cur == NULL)
2648 return(-1);
2650 if (encoding == NULL)
2651 encoding = (const char *) cur->encoding;
2653 if (encoding != NULL) {
2655 handler = xmlFindCharEncodingHandler(encoding);
2656 if (handler == NULL)
2657 return(-1);
2660 #ifdef LIBXML_ZLIB_ENABLED
2661 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2662 #endif
2664 * save the content to a temp buffer.
2666 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2667 if (buf == NULL) return(-1);
2668 memset(&ctxt, 0, sizeof(ctxt));
2669 ctxt.buf = buf;
2670 ctxt.level = 0;
2671 ctxt.format = format ? 1 : 0;
2672 ctxt.encoding = (const xmlChar *) encoding;
2673 xmlSaveCtxtInit(&ctxt);
2674 ctxt.options |= XML_SAVE_AS_XML;
2676 xmlDocContentDumpOutput(&ctxt, cur);
2678 ret = xmlOutputBufferClose(buf);
2679 return(ret);
2684 * xmlSaveFileEnc:
2685 * @filename: the filename (or URL)
2686 * @cur: the document
2687 * @encoding: the name of an encoding (or NULL)
2689 * Dump an XML document, converting it to the given encoding
2691 * returns: the number of bytes written or -1 in case of failure.
2694 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2695 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2699 * xmlSaveFormatFile:
2700 * @filename: the filename (or URL)
2701 * @cur: the document
2702 * @format: should formatting spaces been added
2704 * Dump an XML document to a file. Will use compression if
2705 * compiled in and enabled. If @filename is "-" the stdout file is
2706 * used. If @format is set then the document will be indented on output.
2707 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2708 * or xmlKeepBlanksDefault(0) was called
2710 * returns: the number of bytes written or -1 in case of failure.
2713 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2714 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2718 * xmlSaveFile:
2719 * @filename: the filename (or URL)
2720 * @cur: the document
2722 * Dump an XML document to a file. Will use compression if
2723 * compiled in and enabled. If @filename is "-" the stdout file is
2724 * used.
2725 * returns: the number of bytes written or -1 in case of failure.
2728 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2729 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2732 #endif /* LIBXML_OUTPUT_ENABLED */
2734 #define bottom_xmlsave
2735 #include "elfgcchack.h"