2 * xmlsave.c: Implemetation of the document serializer
4 * See Copyright for the status of this software.
13 #include <libxml/xmlmemory.h>
14 #include <libxml/parserInternals.h>
15 #include <libxml/tree.h>
16 #include <libxml/xmlsave.h>
20 #include <libxml/HTMLtree.h>
22 /************************************************************************
26 ************************************************************************/
27 #define XHTML_STRICT_PUBLIC_ID BAD_CAST \
28 "-//W3C//DTD XHTML 1.0 Strict//EN"
29 #define XHTML_STRICT_SYSTEM_ID BAD_CAST \
30 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
31 #define XHTML_FRAME_PUBLIC_ID BAD_CAST \
32 "-//W3C//DTD XHTML 1.0 Frameset//EN"
33 #define XHTML_FRAME_SYSTEM_ID BAD_CAST \
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
35 #define XHTML_TRANS_PUBLIC_ID BAD_CAST \
36 "-//W3C//DTD XHTML 1.0 Transitional//EN"
37 #define XHTML_TRANS_SYSTEM_ID BAD_CAST \
38 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
40 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
43 * @systemID: the system identifier
44 * @publicID: the public identifier
46 * Try to find if the document correspond to an XHTML DTD
48 * Returns 1 if true, 0 if not and -1 in case of error
51 xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
52 if ((systemID == NULL) && (publicID == NULL))
54 if (publicID != NULL) {
55 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
56 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
57 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
59 if (systemID != NULL) {
60 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
61 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
62 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
67 #ifdef LIBXML_OUTPUT_ENABLED
70 xmlGenericError(xmlGenericErrorContext, \
71 "Unimplemented block at %s:%d\n", \
78 const xmlChar *filename;
79 const xmlChar *encoding;
80 xmlCharEncodingHandlerPtr handler;
81 xmlOutputBufferPtr buf;
86 char indent[MAX_INDENT + 1]; /* array for indenting output */
89 xmlCharEncodingOutputFunc escape; /* used for element content */
90 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
93 /************************************************************************
95 * Output error handlers *
97 ************************************************************************/
100 * @extra: extra informations
102 * Handle an out of memory condition
105 xmlSaveErrMemory(const char *extra)
107 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
112 * @code: the error number
113 * @node: the location of the error.
114 * @extra: extra informations
116 * Handle an out of memory condition
119 xmlSaveErr(int code, xmlNodePtr node, const char *extra)
121 const char *msg = NULL;
124 case XML_SAVE_NOT_UTF8:
125 msg = "string is not in UTF-8\n";
127 case XML_SAVE_CHAR_INVALID:
128 msg = "invalid character value\n";
130 case XML_SAVE_UNKNOWN_ENCODING:
131 msg = "unknown encoding %s\n";
133 case XML_SAVE_NO_DOCTYPE:
134 msg = "document has no DOCTYPE\n";
137 msg = "unexpected error number\n";
139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
142 /************************************************************************
144 * Special escaping routines *
146 ************************************************************************/
147 static unsigned char *
148 xmlSerializeHexCharRef(unsigned char *out, int val) {
154 if (val < 0x10) ptr = out;
155 else if (val < 0x100) ptr = out + 1;
156 else if (val < 0x1000) ptr = out + 2;
157 else if (val < 0x10000) ptr = out + 3;
158 else if (val < 0x100000) ptr = out + 4;
163 case 0: *ptr-- = '0'; break;
164 case 1: *ptr-- = '1'; break;
165 case 2: *ptr-- = '2'; break;
166 case 3: *ptr-- = '3'; break;
167 case 4: *ptr-- = '4'; break;
168 case 5: *ptr-- = '5'; break;
169 case 6: *ptr-- = '6'; break;
170 case 7: *ptr-- = '7'; break;
171 case 8: *ptr-- = '8'; break;
172 case 9: *ptr-- = '9'; break;
173 case 0xA: *ptr-- = 'A'; break;
174 case 0xB: *ptr-- = 'B'; break;
175 case 0xC: *ptr-- = 'C'; break;
176 case 0xD: *ptr-- = 'D'; break;
177 case 0xE: *ptr-- = 'E'; break;
178 case 0xF: *ptr-- = 'F'; break;
179 default: *ptr-- = '0'; break;
190 * @out: a pointer to an array of bytes to store the result
191 * @outlen: the length of @out
192 * @in: a pointer to an array of unescaped UTF-8 bytes
193 * @inlen: the length of @in
195 * Take a block of UTF-8 chars in and escape them. Used when there is no
196 * encoding specified.
198 * Returns 0 if success, or -1 otherwise
199 * The value of @inlen after return is the number of octets consumed
200 * if the return value is positive, else unpredictable.
201 * The value of @outlen after return is the number of octets consumed.
204 xmlEscapeEntities(unsigned char* out, int *outlen,
205 const xmlChar* in, int *inlen) {
206 unsigned char* outstart = out;
207 const unsigned char* base = in;
208 unsigned char* outend = out + *outlen;
209 const unsigned char* inend;
212 inend = in + (*inlen);
214 while ((in < inend) && (out < outend)) {
216 if (outend - out < 4) break;
223 } else if (*in == '>') {
224 if (outend - out < 4) break;
231 } else if (*in == '&') {
232 if (outend - out < 5) break;
240 } else if (((*in >= 0x20) && (*in < 0x80)) ||
241 (*in == '\n') || (*in == '\t')) {
243 * default case, just copy !
247 } else if (*in >= 0x80) {
249 * We assume we have UTF-8 input.
251 if (outend - out < 10) break;
254 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
257 } else if (*in < 0xE0) {
258 if (inend - in < 2) break;
259 val = (in[0]) & 0x1F;
261 val |= (in[1]) & 0x3F;
263 } else if (*in < 0xF0) {
264 if (inend - in < 3) break;
265 val = (in[0]) & 0x0F;
267 val |= (in[1]) & 0x3F;
269 val |= (in[2]) & 0x3F;
271 } else if (*in < 0xF8) {
272 if (inend - in < 4) break;
273 val = (in[0]) & 0x07;
275 val |= (in[1]) & 0x3F;
277 val |= (in[2]) & 0x3F;
279 val |= (in[3]) & 0x3F;
282 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
287 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
293 * We could do multiple things here. Just save as a char ref
295 out = xmlSerializeHexCharRef(out, val);
296 } else if (IS_BYTE_CHAR(*in)) {
297 if (outend - out < 6) break;
298 out = xmlSerializeHexCharRef(out, *in++);
300 xmlGenericError(xmlGenericErrorContext,
301 "xmlEscapeEntities : char out of range\n");
306 *outlen = out - outstart;
310 *outlen = out - outstart;
315 /************************************************************************
317 * Allocation and deallocation *
319 ************************************************************************/
322 * @ctxt: the saving context
324 * Initialize a saving context
327 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
332 if (ctxt == NULL) return;
333 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
334 ctxt->escape = xmlEscapeEntities;
335 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
336 if ((xmlTreeIndentString == NULL) || (len == 0)) {
337 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
339 ctxt->indent_size = len;
340 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
341 for (i = 0;i < ctxt->indent_nr;i++)
342 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
344 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
347 if (xmlSaveNoEmptyTags) {
348 ctxt->options |= XML_SAVE_NO_EMPTY;
355 * Free a saving context, destroying the ouptut in any remaining buffer
358 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
360 if (ctxt == NULL) return;
361 if (ctxt->encoding != NULL)
362 xmlFree((char *) ctxt->encoding);
363 if (ctxt->buf != NULL)
364 xmlOutputBufferClose(ctxt->buf);
371 * Create a new saving context
373 * Returns the new structure or NULL in case of error
375 static xmlSaveCtxtPtr
376 xmlNewSaveCtxt(const char *encoding, int options)
380 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
382 xmlSaveErrMemory("creating saving context");
385 memset(ret, 0, sizeof(xmlSaveCtxt));
387 if (encoding != NULL) {
388 ret->handler = xmlFindCharEncodingHandler(encoding);
389 if (ret->handler == NULL) {
390 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
391 xmlFreeSaveCtxt(ret);
394 ret->encoding = xmlStrdup((const xmlChar *)encoding);
397 xmlSaveCtxtInit(ret);
403 /* Re-check this option as it may already have been set */
404 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
405 options |= XML_SAVE_NO_EMPTY;
408 ret->options = options;
409 if (options & XML_SAVE_FORMAT)
415 /************************************************************************
417 * Dumping XML tree content to a simple buffer *
419 ************************************************************************/
421 * xmlAttrSerializeContent:
422 * @buf: the XML buffer output
424 * @attr: the attribute pointer
426 * Serialize the attribute in the buffer
429 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
433 children = attr->children;
434 while (children != NULL) {
435 switch (children->type) {
437 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
438 attr, children->content);
440 case XML_ENTITY_REF_NODE:
441 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
442 xmlBufferAdd(buf->buffer, children->name,
443 xmlStrlen(children->name));
444 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
447 /* should not happen unless we have a badly built tree */
450 children = children->next;
454 /************************************************************************
456 * Dumping XML tree content to an I/O output buffer *
458 ************************************************************************/
460 #ifdef LIBXML_HTML_ENABLED
462 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
464 static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
465 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
466 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
467 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
471 * @buf: the XML buffer output
474 * Dump a local Namespace definition.
475 * Should be called in the context of attributes dumps.
478 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
479 if ((cur == NULL) || (buf == NULL)) return;
480 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
481 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
484 /* Within the context of an element attributes */
485 if (cur->prefix != NULL) {
486 xmlOutputBufferWrite(buf, 7, " xmlns:");
487 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
489 xmlOutputBufferWrite(buf, 6, " xmlns");
490 xmlOutputBufferWrite(buf, 1, "=");
491 xmlBufferWriteQuotedString(buf->buffer, cur->href);
496 * xmlNsListDumpOutput:
497 * @buf: the XML buffer output
498 * @cur: the first namespace
500 * Dump a list of local Namespace definitions.
501 * Should be called in the context of attributes dumps.
504 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
505 while (cur != NULL) {
506 xmlNsDumpOutput(buf, cur);
513 * @buf: the XML buffer output
514 * @dtd: the pointer to the DTD
516 * Dump the XML document DTD, if any.
519 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
520 xmlOutputBufferPtr buf;
524 if (dtd == NULL) return;
525 if ((ctxt == NULL) || (ctxt->buf == NULL))
528 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
529 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
530 if (dtd->ExternalID != NULL) {
531 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
532 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
533 xmlOutputBufferWrite(buf, 1, " ");
534 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
535 } else if (dtd->SystemID != NULL) {
536 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
537 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
539 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
540 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
541 (dtd->pentities == NULL)) {
542 xmlOutputBufferWrite(buf, 1, ">");
545 xmlOutputBufferWrite(buf, 3, " [\n");
547 * Dump the notations first they are not in the DTD children list
548 * Do this only on a standalone DTD or on the internal subset though.
550 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
551 (dtd->doc->intSubset == dtd))) {
552 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
554 format = ctxt->format;
559 ctxt->doc = dtd->doc;
560 xmlNodeListDumpOutput(ctxt, dtd->children);
561 ctxt->format = format;
564 xmlOutputBufferWrite(buf, 2, "]>");
569 * @buf: the XML buffer output
570 * @cur: the attribute pointer
572 * Dump an XML attribute
575 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
576 xmlOutputBufferPtr buf;
578 if (cur == NULL) return;
580 if (buf == NULL) return;
581 xmlOutputBufferWrite(buf, 1, " ");
582 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
583 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
584 xmlOutputBufferWrite(buf, 1, ":");
586 xmlOutputBufferWriteString(buf, (const char *)cur->name);
587 xmlOutputBufferWrite(buf, 2, "=\"");
588 xmlAttrSerializeContent(buf, cur);
589 xmlOutputBufferWrite(buf, 1, "\"");
593 * xmlAttrListDumpOutput:
594 * @buf: the XML buffer output
596 * @cur: the first attribute pointer
597 * @encoding: an optional encoding string
599 * Dump a list of XML attributes
602 xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
603 if (cur == NULL) return;
604 while (cur != NULL) {
605 xmlAttrDumpOutput(ctxt, cur);
613 * xmlNodeListDumpOutput:
614 * @cur: the first node
616 * Dump an XML node list, recursive behaviour, children are printed too.
619 xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
620 xmlOutputBufferPtr buf;
622 if (cur == NULL) return;
624 while (cur != NULL) {
625 if ((ctxt->format) && (xmlIndentTreeOutput) &&
626 (cur->type == XML_ELEMENT_NODE))
627 xmlOutputBufferWrite(buf, ctxt->indent_size *
628 (ctxt->level > ctxt->indent_nr ?
629 ctxt->indent_nr : ctxt->level),
631 xmlNodeDumpOutputInternal(ctxt, cur);
633 xmlOutputBufferWrite(buf, 1, "\n");
640 * xmlNodeDumpOutputInternal:
641 * @cur: the current node
643 * Dump an XML node, recursive behaviour, children are printed too.
646 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
649 xmlChar *start, *end;
650 xmlOutputBufferPtr buf;
652 if (cur == NULL) return;
654 if (cur->type == XML_XINCLUDE_START)
656 if (cur->type == XML_XINCLUDE_END)
658 if ((cur->type == XML_DOCUMENT_NODE) ||
659 (cur->type == XML_HTML_DOCUMENT_NODE)) {
660 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
663 if (cur->type == XML_DTD_NODE) {
664 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
667 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
668 xmlNodeListDumpOutput(ctxt, cur->children);
671 if (cur->type == XML_ELEMENT_DECL) {
672 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
675 if (cur->type == XML_ATTRIBUTE_DECL) {
676 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
679 if (cur->type == XML_ENTITY_DECL) {
680 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
683 if (cur->type == XML_TEXT_NODE) {
684 if (cur->content != NULL) {
685 if (cur->name != xmlStringTextNoenc) {
686 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
689 * Disable escaping, needed for XSLT
691 xmlOutputBufferWriteString(buf, (const char *) cur->content);
697 if (cur->type == XML_PI_NODE) {
698 if (cur->content != NULL) {
699 xmlOutputBufferWrite(buf, 2, "<?");
700 xmlOutputBufferWriteString(buf, (const char *)cur->name);
701 if (cur->content != NULL) {
702 xmlOutputBufferWrite(buf, 1, " ");
703 xmlOutputBufferWriteString(buf, (const char *)cur->content);
705 xmlOutputBufferWrite(buf, 2, "?>");
707 xmlOutputBufferWrite(buf, 2, "<?");
708 xmlOutputBufferWriteString(buf, (const char *)cur->name);
709 xmlOutputBufferWrite(buf, 2, "?>");
713 if (cur->type == XML_COMMENT_NODE) {
714 if (cur->content != NULL) {
715 xmlOutputBufferWrite(buf, 4, "<!--");
716 xmlOutputBufferWriteString(buf, (const char *)cur->content);
717 xmlOutputBufferWrite(buf, 3, "-->");
721 if (cur->type == XML_ENTITY_REF_NODE) {
722 xmlOutputBufferWrite(buf, 1, "&");
723 xmlOutputBufferWriteString(buf, (const char *)cur->name);
724 xmlOutputBufferWrite(buf, 1, ";");
727 if (cur->type == XML_CDATA_SECTION_NODE) {
728 if (cur->content == NULL) {
729 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
731 start = end = cur->content;
732 while (*end != '\0') {
733 if ((*end == ']') && (*(end + 1) == ']') &&
734 (*(end + 2) == '>')) {
736 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
737 xmlOutputBufferWrite(buf, end - start, (const char *)start);
738 xmlOutputBufferWrite(buf, 3, "]]>");
744 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
745 xmlOutputBufferWriteString(buf, (const char *)start);
746 xmlOutputBufferWrite(buf, 3, "]]>");
751 if (cur->type == XML_ATTRIBUTE_NODE) {
752 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
755 if (cur->type == XML_NAMESPACE_DECL) {
756 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
760 format = ctxt->format;
763 while (tmp != NULL) {
764 if ((tmp->type == XML_TEXT_NODE) ||
765 (tmp->type == XML_CDATA_SECTION_NODE) ||
766 (tmp->type == XML_ENTITY_REF_NODE)) {
773 xmlOutputBufferWrite(buf, 1, "<");
774 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
775 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
776 xmlOutputBufferWrite(buf, 1, ":");
779 xmlOutputBufferWriteString(buf, (const char *)cur->name);
781 xmlNsListDumpOutput(buf, cur->nsDef);
782 if (cur->properties != NULL)
783 xmlAttrListDumpOutput(ctxt, cur->properties);
785 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
786 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
787 xmlOutputBufferWrite(buf, 2, "/>");
788 ctxt->format = format;
791 xmlOutputBufferWrite(buf, 1, ">");
792 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
793 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
795 if (cur->children != NULL) {
796 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
797 if (ctxt->level >= 0) ctxt->level++;
798 xmlNodeListDumpOutput(ctxt, cur->children);
799 if (ctxt->level > 0) ctxt->level--;
800 if ((xmlIndentTreeOutput) && (ctxt->format))
801 xmlOutputBufferWrite(buf, ctxt->indent_size *
802 (ctxt->level > ctxt->indent_nr ?
803 ctxt->indent_nr : ctxt->level),
806 xmlOutputBufferWrite(buf, 2, "</");
807 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
808 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
809 xmlOutputBufferWrite(buf, 1, ":");
812 xmlOutputBufferWriteString(buf, (const char *)cur->name);
813 xmlOutputBufferWrite(buf, 1, ">");
814 ctxt->format = format;
818 * xmlDocContentDumpOutput:
821 * Dump an XML document.
824 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
825 #ifdef LIBXML_HTML_ENABLED
829 const xmlChar *oldenc = cur->encoding;
830 const xmlChar *oldctxtenc = ctxt->encoding;
831 const xmlChar *encoding = ctxt->encoding;
832 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
833 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
834 xmlOutputBufferPtr buf = ctxt->buf;
835 xmlCharEncodingHandlerPtr handler = NULL;
840 if (ctxt->encoding != NULL) {
841 cur->encoding = BAD_CAST ctxt->encoding;
842 } else if (cur->encoding != NULL) {
843 encoding = cur->encoding;
844 } else if (cur->charset != XML_CHAR_ENCODING_UTF8) {
845 encoding = (const xmlChar *)
846 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
849 enc = xmlParseCharEncoding((const char*) encoding);
850 if ((encoding != NULL) && (oldctxtenc == NULL) &&
851 (buf->encoder == NULL) && (buf->conv == NULL) &&
852 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
853 if ((enc != XML_CHAR_ENCODING_UTF8) &&
854 (enc != XML_CHAR_ENCODING_NONE) &&
855 (enc != XML_CHAR_ENCODING_ASCII)) {
857 * we need to switch to this encoding but just for this document
858 * since we output the XMLDecl the conversion must be done to not
859 * generate not well formed documents.
861 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
862 if (buf->encoder == NULL) {
863 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
864 (const char *)encoding);
867 buf->conv = xmlBufferCreate();
868 if (buf->conv == NULL) {
869 xmlCharEncCloseFunc(buf->encoder);
870 xmlSaveErrMemory("creating encoding buffer");
874 * initialize the state, e.g. if outputting a BOM
876 xmlCharEncOutFunc(buf->encoder, buf->conv, NULL);
878 if (ctxt->escape == xmlEscapeEntities)
880 if (ctxt->escapeAttr == xmlEscapeEntities)
881 ctxt->escapeAttr = NULL;
886 * Save the XML declaration
888 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
889 xmlOutputBufferWrite(buf, 14, "<?xml version=");
890 if (cur->version != NULL)
891 xmlBufferWriteQuotedString(buf->buffer, cur->version);
893 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
894 if (encoding != NULL) {
895 xmlOutputBufferWrite(buf, 10, " encoding=");
896 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
898 switch (cur->standalone) {
900 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
903 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
906 xmlOutputBufferWrite(buf, 3, "?>\n");
909 #ifdef LIBXML_HTML_ENABLED
910 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
911 dtd = xmlGetIntSubset(cur);
913 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
914 if (is_xhtml < 0) is_xhtml = 0;
918 if (cur->children != NULL) {
919 xmlNodePtr child = cur->children;
921 while (child != NULL) {
923 #ifdef LIBXML_HTML_ENABLED
925 xhtmlNodeDumpOutput(ctxt, child);
928 xmlNodeDumpOutputInternal(ctxt, child);
929 xmlOutputBufferWrite(buf, 1, "\n");
933 if (ctxt->encoding != NULL)
934 cur->encoding = oldenc;
937 * Restore the state of the saving context at the end of the document
939 if ((encoding != NULL) && (oldctxtenc == NULL) &&
940 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
941 if ((enc != XML_CHAR_ENCODING_UTF8) &&
942 (enc != XML_CHAR_ENCODING_NONE) &&
943 (enc != XML_CHAR_ENCODING_ASCII)) {
944 xmlOutputBufferFlush(buf);
945 xmlCharEncCloseFunc(buf->encoder);
946 xmlBufferFree(buf->conv);
950 ctxt->escape = oldescape;
951 ctxt->escapeAttr = oldescapeAttr;
956 #ifdef LIBXML_HTML_ENABLED
957 /************************************************************************
959 * Functions specific to XHTML serialization *
961 ************************************************************************/
967 * Check if a node is an empty xhtml node
969 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
972 xhtmlIsEmpty(xmlNodePtr node) {
975 if (node->type != XML_ELEMENT_NODE)
977 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
979 if (node->children != NULL)
981 switch (node->name[0]) {
983 if (xmlStrEqual(node->name, BAD_CAST "area"))
987 if (xmlStrEqual(node->name, BAD_CAST "br"))
989 if (xmlStrEqual(node->name, BAD_CAST "base"))
991 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
995 if (xmlStrEqual(node->name, BAD_CAST "col"))
999 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1003 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1007 if (xmlStrEqual(node->name, BAD_CAST "img"))
1009 if (xmlStrEqual(node->name, BAD_CAST "input"))
1011 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1015 if (xmlStrEqual(node->name, BAD_CAST "link"))
1019 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1023 if (xmlStrEqual(node->name, BAD_CAST "param"))
1031 * xhtmlAttrListDumpOutput:
1032 * @cur: the first attribute pointer
1034 * Dump a list of XML attributes
1037 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1038 xmlAttrPtr xml_lang = NULL;
1039 xmlAttrPtr lang = NULL;
1040 xmlAttrPtr name = NULL;
1041 xmlAttrPtr id = NULL;
1043 xmlOutputBufferPtr buf;
1045 if (cur == NULL) return;
1047 parent = cur->parent;
1048 while (cur != NULL) {
1049 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1052 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1055 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1058 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1059 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1061 else if ((cur->ns == NULL) &&
1062 ((cur->children == NULL) ||
1063 (cur->children->content == NULL) ||
1064 (cur->children->content[0] == 0)) &&
1065 (htmlIsBooleanAttr(cur->name))) {
1066 if (cur->children != NULL)
1067 xmlFreeNode(cur->children);
1068 cur->children = xmlNewText(cur->name);
1069 if (cur->children != NULL)
1070 cur->children->parent = (xmlNodePtr) cur;
1072 xmlAttrDumpOutput(ctxt, cur);
1078 if ((name != NULL) && (id == NULL)) {
1079 if ((parent != NULL) && (parent->name != NULL) &&
1080 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1081 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1082 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1083 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1084 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1085 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1086 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1087 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1088 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1089 xmlOutputBufferWrite(buf, 5, " id=\"");
1090 xmlAttrSerializeContent(buf, name);
1091 xmlOutputBufferWrite(buf, 1, "\"");
1097 if ((lang != NULL) && (xml_lang == NULL)) {
1098 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1099 xmlAttrSerializeContent(buf, lang);
1100 xmlOutputBufferWrite(buf, 1, "\"");
1102 if ((xml_lang != NULL) && (lang == NULL)) {
1103 xmlOutputBufferWrite(buf, 7, " lang=\"");
1104 xmlAttrSerializeContent(buf, xml_lang);
1105 xmlOutputBufferWrite(buf, 1, "\"");
1110 * xhtmlNodeListDumpOutput:
1111 * @buf: the XML buffer output
1112 * @doc: the XHTML document
1113 * @cur: the first node
1114 * @level: the imbrication level for indenting
1115 * @format: is formatting allowed
1116 * @encoding: an optional encoding string
1118 * Dump an XML node list, recursive behaviour, children are printed too.
1119 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1120 * or xmlKeepBlanksDefault(0) was called
1123 xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1124 xmlOutputBufferPtr buf;
1126 if (cur == NULL) return;
1128 while (cur != NULL) {
1129 if ((ctxt->format) && (xmlIndentTreeOutput) &&
1130 (cur->type == XML_ELEMENT_NODE))
1131 xmlOutputBufferWrite(buf, ctxt->indent_size *
1132 (ctxt->level > ctxt->indent_nr ?
1133 ctxt->indent_nr : ctxt->level),
1135 xhtmlNodeDumpOutput(ctxt, cur);
1137 xmlOutputBufferWrite(buf, 1, "\n");
1144 * xhtmlNodeDumpOutput:
1145 * @buf: the XML buffer output
1146 * @doc: the XHTML document
1147 * @cur: the current node
1148 * @level: the imbrication level for indenting
1149 * @format: is formatting allowed
1150 * @encoding: an optional encoding string
1152 * Dump an XHTML node, recursive behaviour, children are printed too.
1155 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1156 int format, addmeta = 0;
1158 xmlChar *start, *end;
1159 xmlOutputBufferPtr buf;
1161 if (cur == NULL) return;
1162 if ((cur->type == XML_DOCUMENT_NODE) ||
1163 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1164 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1167 if (cur->type == XML_XINCLUDE_START)
1169 if (cur->type == XML_XINCLUDE_END)
1171 if (cur->type == XML_DTD_NODE) {
1172 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1175 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
1176 xhtmlNodeListDumpOutput(ctxt, cur->children);
1180 if (cur->type == XML_ELEMENT_DECL) {
1181 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1184 if (cur->type == XML_ATTRIBUTE_DECL) {
1185 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1188 if (cur->type == XML_ENTITY_DECL) {
1189 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1192 if (cur->type == XML_TEXT_NODE) {
1193 if (cur->content != NULL) {
1194 if ((cur->name == xmlStringText) ||
1195 (cur->name != xmlStringTextNoenc)) {
1196 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1199 * Disable escaping, needed for XSLT
1201 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1207 if (cur->type == XML_PI_NODE) {
1208 if (cur->content != NULL) {
1209 xmlOutputBufferWrite(buf, 2, "<?");
1210 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1211 if (cur->content != NULL) {
1212 xmlOutputBufferWrite(buf, 1, " ");
1213 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1215 xmlOutputBufferWrite(buf, 2, "?>");
1217 xmlOutputBufferWrite(buf, 2, "<?");
1218 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1219 xmlOutputBufferWrite(buf, 2, "?>");
1223 if (cur->type == XML_COMMENT_NODE) {
1224 if (cur->content != NULL) {
1225 xmlOutputBufferWrite(buf, 4, "<!--");
1226 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1227 xmlOutputBufferWrite(buf, 3, "-->");
1231 if (cur->type == XML_ENTITY_REF_NODE) {
1232 xmlOutputBufferWrite(buf, 1, "&");
1233 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1234 xmlOutputBufferWrite(buf, 1, ";");
1237 if (cur->type == XML_CDATA_SECTION_NODE) {
1238 start = end = cur->content;
1239 while (*end != '\0') {
1240 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1242 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1243 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1244 xmlOutputBufferWrite(buf, 3, "]]>");
1250 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1251 xmlOutputBufferWriteString(buf, (const char *)start);
1252 xmlOutputBufferWrite(buf, 3, "]]>");
1257 format = ctxt->format;
1259 tmp = cur->children;
1260 while (tmp != NULL) {
1261 if ((tmp->type == XML_TEXT_NODE) ||
1262 (tmp->type == XML_ENTITY_REF_NODE)) {
1269 xmlOutputBufferWrite(buf, 1, "<");
1270 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1271 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1272 xmlOutputBufferWrite(buf, 1, ":");
1275 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1277 xmlNsListDumpOutput(buf, cur->nsDef);
1278 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1279 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1281 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1283 xmlOutputBufferWriteString(buf,
1284 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1286 if (cur->properties != NULL)
1287 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1289 if ((cur->type == XML_ELEMENT_NODE) &&
1290 (cur->parent != NULL) &&
1291 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1292 xmlStrEqual(cur->name, BAD_CAST"head") &&
1293 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1295 tmp = cur->children;
1296 while (tmp != NULL) {
1297 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1300 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1301 if (httpequiv != NULL) {
1302 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1315 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1316 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1317 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1319 * C.2. Empty Elements
1321 xmlOutputBufferWrite(buf, 3, " />");
1324 xmlOutputBufferWrite(buf, 1, ">");
1326 xmlOutputBufferWrite(buf, 1, "\n");
1327 if (xmlIndentTreeOutput)
1328 xmlOutputBufferWrite(buf, ctxt->indent_size *
1329 (ctxt->level + 1 > ctxt->indent_nr ?
1330 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1332 xmlOutputBufferWriteString(buf,
1333 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1334 if (ctxt->encoding) {
1335 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1337 xmlOutputBufferWrite(buf, 5, "UTF-8");
1339 xmlOutputBufferWrite(buf, 4, "\" />");
1341 xmlOutputBufferWrite(buf, 1, "\n");
1343 xmlOutputBufferWrite(buf, 1, ">");
1346 * C.3. Element Minimization and Empty Element Content
1348 xmlOutputBufferWrite(buf, 2, "</");
1349 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1350 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1351 xmlOutputBufferWrite(buf, 1, ":");
1353 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1354 xmlOutputBufferWrite(buf, 1, ">");
1358 xmlOutputBufferWrite(buf, 1, ">");
1361 xmlOutputBufferWrite(buf, 1, "\n");
1362 if (xmlIndentTreeOutput)
1363 xmlOutputBufferWrite(buf, ctxt->indent_size *
1364 (ctxt->level + 1 > ctxt->indent_nr ?
1365 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1367 xmlOutputBufferWriteString(buf,
1368 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1369 if (ctxt->encoding) {
1370 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1372 xmlOutputBufferWrite(buf, 5, "UTF-8");
1374 xmlOutputBufferWrite(buf, 4, "\" />");
1376 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1377 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1382 * This was removed due to problems with HTML processors.
1386 * 4.8. Script and Style elements
1388 if ((cur->type == XML_ELEMENT_NODE) &&
1389 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1390 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1391 ((cur->ns == NULL) ||
1392 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1393 xmlNodePtr child = cur->children;
1395 while (child != NULL) {
1396 if (child->type == XML_TEXT_NODE) {
1397 if ((xmlStrchr(child->content, '<') == NULL) &&
1398 (xmlStrchr(child->content, '&') == NULL) &&
1399 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
1400 /* Nothing to escape, so just output as is... */
1401 /* FIXME: Should we do something about "--" also? */
1402 int level = ctxt->level;
1403 int indent = ctxt->format;
1407 xmlOutputBufferWriteString(buf, (const char *) child->content);
1408 /* (We cannot use xhtmlNodeDumpOutput() here because
1409 * we wish to leave '>' unescaped!) */
1410 ctxt->level = level;
1411 ctxt->format = indent;
1413 /* We must use a CDATA section. Unfortunately,
1414 * this will break CSS and JavaScript when read by
1415 * a browser in HTML4-compliant mode. :-( */
1416 start = end = child->content;
1417 while (*end != '\0') {
1419 *(end + 1) == ']' &&
1420 *(end + 2) == '>') {
1422 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1423 xmlOutputBufferWrite(buf, end - start,
1424 (const char *)start);
1425 xmlOutputBufferWrite(buf, 3, "]]>");
1431 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1432 xmlOutputBufferWrite(buf, end - start,
1433 (const char *)start);
1434 xmlOutputBufferWrite(buf, 3, "]]>");
1438 int level = ctxt->level;
1439 int indent = ctxt->format;
1443 xhtmlNodeDumpOutput(ctxt, child);
1444 ctxt->level = level;
1445 ctxt->format = indent;
1447 child = child->next;
1452 if (cur->children != NULL) {
1453 int indent = ctxt->format;
1455 if (format) xmlOutputBufferWrite(buf, 1, "\n");
1456 if (ctxt->level >= 0) ctxt->level++;
1457 ctxt->format = format;
1458 xhtmlNodeListDumpOutput(ctxt, cur->children);
1459 if (ctxt->level > 0) ctxt->level--;
1460 ctxt->format = indent;
1461 if ((xmlIndentTreeOutput) && (format))
1462 xmlOutputBufferWrite(buf, ctxt->indent_size *
1463 (ctxt->level > ctxt->indent_nr ?
1464 ctxt->indent_nr : ctxt->level),
1467 xmlOutputBufferWrite(buf, 2, "</");
1468 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1469 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1470 xmlOutputBufferWrite(buf, 1, ":");
1473 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1474 xmlOutputBufferWrite(buf, 1, ">");
1478 /************************************************************************
1480 * Public entry points *
1482 ************************************************************************/
1486 * @fd: a file descriptor number
1487 * @encoding: the encoding name to use or NULL
1488 * @options: a set of xmlSaveOptions
1490 * Create a document saving context serializing to a file descriptor
1491 * with the encoding and the options given.
1493 * Returns a new serialization context or NULL in case of error.
1496 xmlSaveToFd(int fd, const char *encoding, int options)
1500 ret = xmlNewSaveCtxt(encoding, options);
1501 if (ret == NULL) return(NULL);
1502 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1503 if (ret->buf == NULL) {
1504 xmlFreeSaveCtxt(ret);
1511 * xmlSaveToFilename:
1512 * @filename: a file name or an URL
1513 * @encoding: the encoding name to use or NULL
1514 * @options: a set of xmlSaveOptions
1516 * Create a document saving context serializing to a filename or possibly
1517 * to an URL (but this is less reliable) with the encoding and the options
1520 * Returns a new serialization context or NULL in case of error.
1523 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1526 int compression = 0; /* TODO handle compression option */
1528 ret = xmlNewSaveCtxt(encoding, options);
1529 if (ret == NULL) return(NULL);
1530 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1532 if (ret->buf == NULL) {
1533 xmlFreeSaveCtxt(ret);
1542 * @encoding: the encoding name to use or NULL
1543 * @options: a set of xmlSaveOptions
1545 * Create a document saving context serializing to a buffer
1546 * with the encoding and the options given
1548 * Returns a new serialization context or NULL in case of error.
1552 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1555 xmlOutputBufferPtr out_buff;
1556 xmlCharEncodingHandlerPtr handler;
1558 ret = xmlNewSaveCtxt(encoding, options);
1559 if (ret == NULL) return(NULL);
1561 if (encoding != NULL) {
1562 handler = xmlFindCharEncodingHandler(encoding);
1563 if (handler == NULL) {
1569 out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
1570 if (out_buff == NULL) {
1572 if (handler) xmlCharEncCloseFunc(handler);
1576 ret->buf = out_buff;
1582 * @iowrite: an I/O write function
1583 * @ioclose: an I/O close function
1584 * @ioctx: an I/O handler
1585 * @encoding: the encoding name to use or NULL
1586 * @options: a set of xmlSaveOptions
1588 * Create a document saving context serializing to a file descriptor
1589 * with the encoding and the options given
1591 * Returns a new serialization context or NULL in case of error.
1594 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1595 xmlOutputCloseCallback ioclose,
1596 void *ioctx, const char *encoding, int options)
1600 ret = xmlNewSaveCtxt(encoding, options);
1601 if (ret == NULL) return(NULL);
1602 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1603 if (ret->buf == NULL) {
1604 xmlFreeSaveCtxt(ret);
1612 * @ctxt: a document saving context
1615 * Save a full document to a saving context
1616 * TODO: The function is not fully implemented yet as it does not return the
1617 * byte count but 0 instead
1619 * Returns the number of byte written or -1 in case of error
1622 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1626 if ((ctxt == NULL) || (doc == NULL)) return(-1);
1627 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1634 * @ctxt: a document saving context
1635 * @node: the top node of the subtree to save
1637 * Save a subtree starting at the node parameter to a saving context
1638 * TODO: The function is not fully implemented yet as it does not return the
1639 * byte count but 0 instead
1641 * Returns the number of byte written or -1 in case of error
1644 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1648 if ((ctxt == NULL) || (node == NULL)) return(-1);
1649 xmlNodeDumpOutputInternal(ctxt, node);
1655 * @ctxt: a document saving context
1657 * Flush a document saving context, i.e. make sure that all bytes have
1660 * Returns the number of byte written or -1 in case of error.
1663 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1665 if (ctxt == NULL) return(-1);
1666 if (ctxt->buf == NULL) return(-1);
1667 return(xmlOutputBufferFlush(ctxt->buf));
1672 * @ctxt: a document saving context
1674 * Close a document saving context, i.e. make sure that all bytes have
1675 * been output and free the associated data.
1677 * Returns the number of byte written or -1 in case of error.
1680 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1684 if (ctxt == NULL) return(-1);
1685 ret = xmlSaveFlush(ctxt);
1686 xmlFreeSaveCtxt(ctxt);
1692 * @ctxt: a document saving context
1693 * @escape: the escaping function
1695 * Set a custom escaping function to be used for text in element content
1697 * Returns 0 if successful or -1 in case of error.
1700 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1702 if (ctxt == NULL) return(-1);
1703 ctxt->escape = escape;
1708 * xmlSaveSetAttrEscape:
1709 * @ctxt: a document saving context
1710 * @escape: the escaping function
1712 * Set a custom escaping function to be used for text in attribute content
1714 * Returns 0 if successful or -1 in case of error.
1717 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1719 if (ctxt == NULL) return(-1);
1720 ctxt->escapeAttr = escape;
1724 /************************************************************************
1726 * Public entry points based on buffers *
1728 ************************************************************************/
1730 * xmlAttrSerializeTxtContent:
1731 * @buf: the XML buffer output
1732 * @doc: the document
1733 * @attr: the attribute node
1734 * @string: the text content
1736 * Serialize text attribute values to an xml simple buffer
1739 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1740 xmlAttrPtr attr, const xmlChar * string)
1742 xmlChar *base, *cur;
1746 base = cur = (xmlChar *) string;
1750 xmlBufferAdd(buf, base, cur - base);
1751 xmlBufferAdd(buf, BAD_CAST " ", 5);
1754 } else if (*cur == '\r') {
1756 xmlBufferAdd(buf, base, cur - base);
1757 xmlBufferAdd(buf, BAD_CAST " ", 5);
1760 } else if (*cur == '\t') {
1762 xmlBufferAdd(buf, base, cur - base);
1763 xmlBufferAdd(buf, BAD_CAST "	", 4);
1766 } else if (*cur == '"') {
1768 xmlBufferAdd(buf, base, cur - base);
1769 xmlBufferAdd(buf, BAD_CAST """, 6);
1772 } else if (*cur == '<') {
1774 xmlBufferAdd(buf, base, cur - base);
1775 xmlBufferAdd(buf, BAD_CAST "<", 4);
1778 } else if (*cur == '>') {
1780 xmlBufferAdd(buf, base, cur - base);
1781 xmlBufferAdd(buf, BAD_CAST ">", 4);
1784 } else if (*cur == '&') {
1786 xmlBufferAdd(buf, base, cur - base);
1787 xmlBufferAdd(buf, BAD_CAST "&", 5);
1790 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1791 (doc->encoding == NULL))) {
1793 * We assume we have UTF-8 content.
1795 unsigned char tmp[10];
1799 xmlBufferAdd(buf, base, cur - base);
1801 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1803 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1804 xmlSerializeHexCharRef(tmp, *cur);
1805 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1809 } else if (*cur < 0xE0) {
1810 val = (cur[0]) & 0x1F;
1812 val |= (cur[1]) & 0x3F;
1814 } else if (*cur < 0xF0) {
1815 val = (cur[0]) & 0x0F;
1817 val |= (cur[1]) & 0x3F;
1819 val |= (cur[2]) & 0x3F;
1821 } else if (*cur < 0xF8) {
1822 val = (cur[0]) & 0x07;
1824 val |= (cur[1]) & 0x3F;
1826 val |= (cur[2]) & 0x3F;
1828 val |= (cur[3]) & 0x3F;
1831 if ((l == 1) || (!IS_CHAR(val))) {
1832 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1834 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1836 xmlSerializeHexCharRef(tmp, *cur);
1837 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1843 * We could do multiple things here. Just save
1846 xmlSerializeHexCharRef(tmp, val);
1847 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1855 xmlBufferAdd(buf, base, cur - base);
1860 * @buf: the XML buffer output
1861 * @doc: the document
1862 * @cur: the current node
1863 * @level: the imbrication level for indenting
1864 * @format: is formatting allowed
1866 * Dump an XML node, recursive behaviour,children are printed too.
1867 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1868 * or xmlKeepBlanksDefault(0) was called
1870 * Returns the number of bytes written to the buffer or -1 in case of error
1873 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1878 xmlOutputBufferPtr outbuf;
1884 xmlGenericError(xmlGenericErrorContext,
1885 "xmlNodeDump : node == NULL\n");
1891 xmlGenericError(xmlGenericErrorContext,
1892 "xmlNodeDump : buf == NULL\n");
1896 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1897 if (outbuf == NULL) {
1898 xmlSaveErrMemory("creating buffer");
1901 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1902 outbuf->buffer = buf;
1903 outbuf->encoder = NULL;
1904 outbuf->writecallback = NULL;
1905 outbuf->closecallback = NULL;
1906 outbuf->context = NULL;
1907 outbuf->written = 0;
1910 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1912 ret = buf->use - use;
1918 * @f: the FILE * for the output
1919 * @doc: the document
1920 * @cur: the current node
1922 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1925 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1927 xmlOutputBufferPtr outbuf;
1933 xmlGenericError(xmlGenericErrorContext,
1934 "xmlElemDump : cur == NULL\n");
1940 xmlGenericError(xmlGenericErrorContext,
1941 "xmlElemDump : doc == NULL\n");
1945 outbuf = xmlOutputBufferCreateFile(f, NULL);
1948 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1949 #ifdef LIBXML_HTML_ENABLED
1950 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1952 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1953 #endif /* LIBXML_HTML_ENABLED */
1955 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1956 xmlOutputBufferClose(outbuf);
1959 /************************************************************************
1961 * Saving functions front-ends *
1963 ************************************************************************/
1966 * xmlNodeDumpOutput:
1967 * @buf: the XML buffer output
1968 * @doc: the document
1969 * @cur: the current node
1970 * @level: the imbrication level for indenting
1971 * @format: is formatting allowed
1972 * @encoding: an optional encoding string
1974 * Dump an XML node, recursive behaviour, children are printed too.
1975 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1976 * or xmlKeepBlanksDefault(0) was called
1979 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1980 int level, int format, const char *encoding)
1983 #ifdef LIBXML_HTML_ENABLED
1990 if ((buf == NULL) || (cur == NULL)) return;
1992 if (encoding == NULL)
1995 memset(&ctxt, 0, sizeof(ctxt));
1999 ctxt.format = format;
2000 ctxt.encoding = (const xmlChar *) encoding;
2001 xmlSaveCtxtInit(&ctxt);
2003 #ifdef LIBXML_HTML_ENABLED
2004 dtd = xmlGetIntSubset(doc);
2006 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2012 xhtmlNodeDumpOutput(&ctxt, cur);
2015 xmlNodeDumpOutputInternal(&ctxt, cur);
2019 * xmlDocDumpFormatMemoryEnc:
2020 * @out_doc: Document to generate XML text from
2021 * @doc_txt_ptr: Memory pointer for allocated XML text
2022 * @doc_txt_len: Length of the generated XML text
2023 * @txt_encoding: Character encoding to use when generating XML text
2024 * @format: should formatting spaces been added
2026 * Dump the current DOM tree into memory using the character encoding specified
2027 * by the caller. Note it is up to the caller of this function to free the
2028 * allocated memory with xmlFree().
2029 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2030 * or xmlKeepBlanksDefault(0) was called
2034 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2035 int * doc_txt_len, const char * txt_encoding,
2039 xmlOutputBufferPtr out_buff = NULL;
2040 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2042 if (doc_txt_len == NULL) {
2043 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2046 if (doc_txt_ptr == NULL) {
2051 *doc_txt_ptr = NULL;
2054 if (out_doc == NULL) {
2055 /* No document, no output */
2060 * Validate the encoding value, if provided.
2061 * This logic is copied from xmlSaveFileEnc.
2064 if (txt_encoding == NULL)
2065 txt_encoding = (const char *) out_doc->encoding;
2066 if (txt_encoding != NULL) {
2067 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2068 if ( conv_hdlr == NULL ) {
2069 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2075 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2076 xmlSaveErrMemory("creating buffer");
2080 memset(&ctxt, 0, sizeof(ctxt));
2082 ctxt.buf = out_buff;
2084 ctxt.format = format;
2085 ctxt.encoding = (const xmlChar *) txt_encoding;
2086 xmlSaveCtxtInit(&ctxt);
2087 xmlDocContentDumpOutput(&ctxt, out_doc);
2088 xmlOutputBufferFlush(out_buff);
2089 if (out_buff->conv != NULL) {
2090 *doc_txt_len = out_buff->conv->use;
2091 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
2093 *doc_txt_len = out_buff->buffer->use;
2094 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
2096 (void)xmlOutputBufferClose(out_buff);
2098 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2100 xmlSaveErrMemory("creating output");
2108 * @cur: the document
2109 * @mem: OUT: the memory pointer
2110 * @size: OUT: the memory length
2112 * Dump an XML document in memory and return the #xmlChar * and it's size
2113 * in bytes. It's up to the caller to free the memory with xmlFree().
2114 * The resulting byte array is zero terminated, though the last 0 is not
2115 * included in the returned size.
2118 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2119 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2123 * xmlDocDumpFormatMemory:
2124 * @cur: the document
2125 * @mem: OUT: the memory pointer
2126 * @size: OUT: the memory length
2127 * @format: should formatting spaces been added
2130 * Dump an XML document in memory and return the #xmlChar * and it's size.
2131 * It's up to the caller to free the memory with xmlFree().
2132 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2133 * or xmlKeepBlanksDefault(0) was called
2136 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2137 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2141 * xmlDocDumpMemoryEnc:
2142 * @out_doc: Document to generate XML text from
2143 * @doc_txt_ptr: Memory pointer for allocated XML text
2144 * @doc_txt_len: Length of the generated XML text
2145 * @txt_encoding: Character encoding to use when generating XML text
2147 * Dump the current DOM tree into memory using the character encoding specified
2148 * by the caller. Note it is up to the caller of this function to free the
2149 * allocated memory with xmlFree().
2153 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2154 int * doc_txt_len, const char * txt_encoding) {
2155 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2162 * @cur: the document
2163 * @format: should formatting spaces been added
2165 * Dump an XML document to an open FILE.
2167 * returns: the number of bytes written or -1 in case of failure.
2168 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2169 * or xmlKeepBlanksDefault(0) was called
2172 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2174 xmlOutputBufferPtr buf;
2175 const char * encoding;
2176 xmlCharEncodingHandlerPtr handler = NULL;
2181 xmlGenericError(xmlGenericErrorContext,
2182 "xmlDocDump : document == NULL\n");
2186 encoding = (const char *) cur->encoding;
2188 if (encoding != NULL) {
2189 handler = xmlFindCharEncodingHandler(encoding);
2190 if (handler == NULL) {
2191 xmlFree((char *) cur->encoding);
2192 cur->encoding = NULL;
2195 buf = xmlOutputBufferCreateFile(f, handler);
2196 if (buf == NULL) return(-1);
2197 memset(&ctxt, 0, sizeof(ctxt));
2201 ctxt.format = format;
2202 ctxt.encoding = (const xmlChar *) encoding;
2203 xmlSaveCtxtInit(&ctxt);
2204 xmlDocContentDumpOutput(&ctxt, cur);
2206 ret = xmlOutputBufferClose(buf);
2213 * @cur: the document
2215 * Dump an XML document to an open FILE.
2217 * returns: the number of bytes written or -1 in case of failure.
2220 xmlDocDump(FILE *f, xmlDocPtr cur) {
2221 return(xmlDocFormatDump (f, cur, 0));
2226 * @buf: an output I/O buffer
2227 * @cur: the document
2228 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2230 * Dump an XML document to an I/O buffer.
2231 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2234 * returns: the number of bytes written or -1 in case of failure.
2237 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2241 if (buf == NULL) return(-1);
2243 xmlOutputBufferClose(buf);
2246 memset(&ctxt, 0, sizeof(ctxt));
2251 ctxt.encoding = (const xmlChar *) encoding;
2252 xmlSaveCtxtInit(&ctxt);
2253 xmlDocContentDumpOutput(&ctxt, cur);
2254 ret = xmlOutputBufferClose(buf);
2259 * xmlSaveFormatFileTo:
2260 * @buf: an output I/O buffer
2261 * @cur: the document
2262 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2263 * @format: should formatting spaces been added
2265 * Dump an XML document to an I/O buffer.
2266 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2269 * returns: the number of bytes written or -1 in case of failure.
2272 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2273 const char *encoding, int format)
2278 if (buf == NULL) return(-1);
2279 if ((cur == NULL) ||
2280 ((cur->type != XML_DOCUMENT_NODE) &&
2281 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2282 xmlOutputBufferClose(buf);
2285 memset(&ctxt, 0, sizeof(ctxt));
2289 ctxt.format = format;
2290 ctxt.encoding = (const xmlChar *) encoding;
2291 xmlSaveCtxtInit(&ctxt);
2292 xmlDocContentDumpOutput(&ctxt, cur);
2293 ret = xmlOutputBufferClose(buf);
2298 * xmlSaveFormatFileEnc:
2299 * @filename: the filename or URL to output
2300 * @cur: the document being saved
2301 * @encoding: the name of the encoding to use or NULL.
2302 * @format: should formatting spaces be added.
2304 * Dump an XML document to a file or an URL.
2306 * Returns the number of bytes written or -1 in case of error.
2307 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2308 * or xmlKeepBlanksDefault(0) was called
2311 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2312 const char * encoding, int format ) {
2314 xmlOutputBufferPtr buf;
2315 xmlCharEncodingHandlerPtr handler = NULL;
2321 if (encoding == NULL)
2322 encoding = (const char *) cur->encoding;
2324 if (encoding != NULL) {
2326 handler = xmlFindCharEncodingHandler(encoding);
2327 if (handler == NULL)
2332 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2335 * save the content to a temp buffer.
2337 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2338 if (buf == NULL) return(-1);
2339 memset(&ctxt, 0, sizeof(ctxt));
2343 ctxt.format = format;
2344 ctxt.encoding = (const xmlChar *) encoding;
2345 xmlSaveCtxtInit(&ctxt);
2347 xmlDocContentDumpOutput(&ctxt, cur);
2349 ret = xmlOutputBufferClose(buf);
2356 * @filename: the filename (or URL)
2357 * @cur: the document
2358 * @encoding: the name of an encoding (or NULL)
2360 * Dump an XML document, converting it to the given encoding
2362 * returns: the number of bytes written or -1 in case of failure.
2365 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2366 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2370 * xmlSaveFormatFile:
2371 * @filename: the filename (or URL)
2372 * @cur: the document
2373 * @format: should formatting spaces been added
2375 * Dump an XML document to a file. Will use compression if
2376 * compiled in and enabled. If @filename is "-" the stdout file is
2377 * used. If @format is set then the document will be indented on output.
2378 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2379 * or xmlKeepBlanksDefault(0) was called
2381 * returns: the number of bytes written or -1 in case of failure.
2384 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2385 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2390 * @filename: the filename (or URL)
2391 * @cur: the document
2393 * Dump an XML document to a file. Will use compression if
2394 * compiled in and enabled. If @filename is "-" the stdout file is
2396 * returns: the number of bytes written or -1 in case of failure.
2399 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2400 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2403 #endif /* LIBXML_OUTPUT_ENABLED */
2405 #define bottom_xmlsave
2406 #include "elfgcchack.h"