blob: 2f8afcf695da951263ee25c3f9f47a15b0a81182 [file] [log] [blame]
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001/*
2 * xmlsave.c: Implemetation of the document serializer
3 *
4 * See Copyright for the status of this software.
5 *
6 * daniel@veillard.com
7 */
8
9#define IN_LIBXML
10#include "libxml.h"
11
12#include <string.h>
13#include <libxml/xmlmemory.h>
14#include <libxml/parserInternals.h>
15#include <libxml/tree.h>
16#include <libxml/xmlsave.h>
17#ifdef LIBXML_HTML_ENABLED
18#include <libxml/HTMLtree.h>
19
20/************************************************************************
21 * *
22 * XHTML detection *
23 * *
24 ************************************************************************/
25#define XHTML_STRICT_PUBLIC_ID BAD_CAST \
26 "-//W3C//DTD XHTML 1.0 Strict//EN"
27#define XHTML_STRICT_SYSTEM_ID BAD_CAST \
28 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
29#define XHTML_FRAME_PUBLIC_ID BAD_CAST \
30 "-//W3C//DTD XHTML 1.0 Frameset//EN"
31#define XHTML_FRAME_SYSTEM_ID BAD_CAST \
32 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
33#define XHTML_TRANS_PUBLIC_ID BAD_CAST \
34 "-//W3C//DTD XHTML 1.0 Transitional//EN"
35#define XHTML_TRANS_SYSTEM_ID BAD_CAST \
36 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
37
38#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
39/**
40 * xmlIsXHTML:
41 * @systemID: the system identifier
42 * @publicID: the public identifier
43 *
44 * Try to find if the document correspond to an XHTML DTD
45 *
46 * Returns 1 if true, 0 if not and -1 in case of error
47 */
48int
49xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
50 if ((systemID == NULL) && (publicID == NULL))
51 return(-1);
52 if (publicID != NULL) {
53 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
54 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
55 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
56 }
57 if (systemID != NULL) {
58 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
59 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
60 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
61 }
62 return(0);
63}
64#endif /* LIBXML_HTML_ENABLED */
65
66
67#ifdef LIBXML_OUTPUT_ENABLED
68
69#define TODO \
70 xmlGenericError(xmlGenericErrorContext, \
71 "Unimplemented block at %s:%d\n", \
72 __FILE__, __LINE__);
73
74struct _xmlSaveCtxt {
75 void *_private;
76 int type;
77 int fd;
78 const xmlChar *filename;
79 const xmlChar *encoding;
80 xmlCharEncodingHandlerPtr handler;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000081 xmlOutputBufferPtr buf;
82 xmlDocPtr doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000083 int options;
84 int level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000085 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000086};
87
88/************************************************************************
89 * *
90 * Output error handlers *
91 * *
92 ************************************************************************/
93/**
94 * xmlSaveErrMemory:
95 * @extra: extra informations
96 *
97 * Handle an out of memory condition
98 */
99static void
100xmlSaveErrMemory(const char *extra)
101{
102 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
103}
104
105/**
106 * xmlSaveErr:
107 * @code: the error number
108 * @node: the location of the error.
109 * @extra: extra informations
110 *
111 * Handle an out of memory condition
112 */
113static void
114xmlSaveErr(int code, xmlNodePtr node, const char *extra)
115{
116 const char *msg = NULL;
117
118 switch(code) {
119 case XML_SAVE_NOT_UTF8:
120 msg = "string is not in UTF-8";
121 break;
122 case XML_SAVE_CHAR_INVALID:
123 msg = "invalid character value";
124 break;
125 case XML_SAVE_UNKNOWN_ENCODING:
126 msg = "unknown encoding %s";
127 break;
128 case XML_SAVE_NO_DOCTYPE:
129 msg = "document has no DOCTYPE";
130 break;
131 default:
132 msg = "unexpected error number";
133 }
134 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
135}
136
137/************************************************************************
138 * *
139 * Allocation and deallocation *
140 * *
141 ************************************************************************/
142
143/**
144 * xmlFreeSaveCtxt:
145 *
146 * Free a saving context, destroying the ouptut in any remaining buffer
147 */
148static void
149xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
150{
151 if (ctxt == NULL) return;
152 if (ctxt->encoding != NULL)
153 xmlFree((char *) ctxt->encoding);
154 xmlFree(ctxt);
155}
156
157/**
158 * xmlNewSaveCtxt:
159 *
160 * Create a new saving context
161 *
162 * Returns the new structure or NULL in case of error
163 */
164static xmlSaveCtxtPtr
165xmlNewSaveCtxt(const char *encoding, int options)
166{
167 xmlSaveCtxtPtr ret;
168
169 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
170 if (ret == NULL) {
171 xmlSaveErrMemory("creating saving context");
172 return ( NULL );
173 }
174 memset(ret, 0, sizeof(xmlSaveCtxt));
175 ret->options = options;
176 if (encoding != NULL) {
177 ret->handler = xmlFindCharEncodingHandler(encoding);
178 if (ret->handler == NULL) {
179 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
180 xmlFreeSaveCtxt(ret);
181 return(NULL);
182 }
183 ret->encoding = xmlStrdup((const xmlChar *)encoding);
184 }
185 return(ret);
186}
187
188/************************************************************************
189 * *
190 * Dumping XML tree content to a simple buffer *
191 * *
192 ************************************************************************/
193/**
194 * xmlAttrSerializeContent:
195 * @buf: the XML buffer output
196 * @doc: the document
197 * @attr: the attribute pointer
198 *
199 * Serialize the attribute in the buffer
200 */
201static void
202xmlAttrSerializeContent(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr attr)
203{
204 xmlNodePtr children;
205
206 children = attr->children;
207 while (children != NULL) {
208 switch (children->type) {
209 case XML_TEXT_NODE:
210 xmlAttrSerializeTxtContent(buf, doc, attr, children->content);
211 break;
212 case XML_ENTITY_REF_NODE:
213 xmlBufferAdd(buf, BAD_CAST "&", 1);
214 xmlBufferAdd(buf, children->name,
215 xmlStrlen(children->name));
216 xmlBufferAdd(buf, BAD_CAST ";", 1);
217 break;
218 default:
219 /* should not happen unless we have a badly built tree */
220 break;
221 }
222 children = children->next;
223 }
224}
225
226/************************************************************************
227 * *
228 * Dumping XML tree content to an I/O output buffer *
229 * *
230 ************************************************************************/
231
232#ifdef LIBXML_HTML_ENABLED
233static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000234xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000235#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000236static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
237static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000238void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
239
240/**
241 * xmlNsDumpOutput:
242 * @buf: the XML buffer output
243 * @cur: a namespace
244 *
245 * Dump a local Namespace definition.
246 * Should be called in the context of attributes dumps.
247 */
248static void
249xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000250 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000251 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
252 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
253 return;
254
255 /* Within the context of an element attributes */
256 if (cur->prefix != NULL) {
257 xmlOutputBufferWriteString(buf, " xmlns:");
258 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
259 } else
260 xmlOutputBufferWriteString(buf, " xmlns");
261 xmlOutputBufferWriteString(buf, "=");
262 xmlBufferWriteQuotedString(buf->buffer, cur->href);
263 }
264}
265
266/**
267 * xmlNsListDumpOutput:
268 * @buf: the XML buffer output
269 * @cur: the first namespace
270 *
271 * Dump a list of local Namespace definitions.
272 * Should be called in the context of attributes dumps.
273 */
274void
275xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
276 while (cur != NULL) {
277 xmlNsDumpOutput(buf, cur);
278 cur = cur->next;
279 }
280}
281
282/**
283 * xmlDtdDumpOutput:
284 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000285 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000286 *
287 * Dump the XML document DTD, if any.
288 */
289static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000290xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
291 xmlOutputBufferPtr buf;
292 int format, level;
293 xmlDocPtr doc;
294
295 if (dtd == NULL) return;
296 if ((ctxt == NULL) || (ctxt->buf == NULL))
297 return;
298 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000299 xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
300 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
301 if (dtd->ExternalID != NULL) {
302 xmlOutputBufferWriteString(buf, " PUBLIC ");
303 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
304 xmlOutputBufferWriteString(buf, " ");
305 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
306 } else if (dtd->SystemID != NULL) {
307 xmlOutputBufferWriteString(buf, " SYSTEM ");
308 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
309 }
310 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
311 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
312 (dtd->pentities == NULL)) {
313 xmlOutputBufferWriteString(buf, ">");
314 return;
315 }
316 xmlOutputBufferWriteString(buf, " [\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000317 format = ctxt->format;
318 level = ctxt->level;
319 doc = ctxt->doc;
320 ctxt->format = 0;
321 ctxt->level = -1;
322 ctxt->doc = dtd->doc;
323 xmlNodeListDumpOutput(ctxt, dtd->children);
324 ctxt->format = format;
325 ctxt->level = level;
326 ctxt->doc = doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000327 xmlOutputBufferWriteString(buf, "]>");
328}
329
330/**
331 * xmlAttrDumpOutput:
332 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000333 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000334 *
335 * Dump an XML attribute
336 */
337static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000338xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
339 xmlOutputBufferPtr buf;
340 if (cur == NULL) return;
341 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000342 xmlOutputBufferWriteString(buf, " ");
343 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
344 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
345 xmlOutputBufferWriteString(buf, ":");
346 }
347 xmlOutputBufferWriteString(buf, (const char *)cur->name);
348 xmlOutputBufferWriteString(buf, "=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000349 xmlAttrSerializeContent(buf->buffer, ctxt->doc, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000350 xmlOutputBufferWriteString(buf, "\"");
351}
352
353/**
354 * xmlAttrListDumpOutput:
355 * @buf: the XML buffer output
356 * @doc: the document
357 * @cur: the first attribute pointer
358 * @encoding: an optional encoding string
359 *
360 * Dump a list of XML attributes
361 */
362static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000363xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
364 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000365 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000366 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000367 cur = cur->next;
368 }
369}
370
371
372
373/**
374 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000375 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000376 *
377 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000378 */
379static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000380xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000381 int i;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000382 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000383
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000384 if (cur == NULL) return;
385 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000386 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000387 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000388 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000389 for (i = 0;i < ctxt->level;i++)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000390 xmlOutputBufferWriteString(buf, xmlTreeIndentString);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000391 xmlNodeDumpOutputInternal(ctxt, cur);
392 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000393 xmlOutputBufferWriteString(buf, "\n");
394 }
395 cur = cur->next;
396 }
397}
398
399/**
400 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000401 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000402 *
403 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000404 */
405static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000406xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
407 int i, format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000408 xmlNodePtr tmp;
409 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000410 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000411
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000412 if (cur == NULL) return;
413 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000414 if (cur->type == XML_XINCLUDE_START)
415 return;
416 if (cur->type == XML_XINCLUDE_END)
417 return;
418 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000419 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000420 return;
421 }
422 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000423 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000424 return;
425 }
426 if (cur->type == XML_ELEMENT_DECL) {
427 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
428 return;
429 }
430 if (cur->type == XML_ATTRIBUTE_DECL) {
431 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
432 return;
433 }
434 if (cur->type == XML_ENTITY_DECL) {
435 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
436 return;
437 }
438 if (cur->type == XML_TEXT_NODE) {
439 if (cur->content != NULL) {
440 if ((cur->name == xmlStringText) ||
441 (cur->name != xmlStringTextNoenc)) {
442 xmlChar *buffer;
443
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000444 if (ctxt->encoding == NULL)
445 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
446 cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000447 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000448 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000449 if (buffer != NULL) {
450 xmlOutputBufferWriteString(buf, (const char *)buffer);
451 xmlFree(buffer);
452 }
453 } else {
454 /*
455 * Disable escaping, needed for XSLT
456 */
457 xmlOutputBufferWriteString(buf, (const char *) cur->content);
458 }
459 }
460
461 return;
462 }
463 if (cur->type == XML_PI_NODE) {
464 if (cur->content != NULL) {
465 xmlOutputBufferWriteString(buf, "<?");
466 xmlOutputBufferWriteString(buf, (const char *)cur->name);
467 if (cur->content != NULL) {
468 xmlOutputBufferWriteString(buf, " ");
469 xmlOutputBufferWriteString(buf, (const char *)cur->content);
470 }
471 xmlOutputBufferWriteString(buf, "?>");
472 } else {
473 xmlOutputBufferWriteString(buf, "<?");
474 xmlOutputBufferWriteString(buf, (const char *)cur->name);
475 xmlOutputBufferWriteString(buf, "?>");
476 }
477 return;
478 }
479 if (cur->type == XML_COMMENT_NODE) {
480 if (cur->content != NULL) {
481 xmlOutputBufferWriteString(buf, "<!--");
482 xmlOutputBufferWriteString(buf, (const char *)cur->content);
483 xmlOutputBufferWriteString(buf, "-->");
484 }
485 return;
486 }
487 if (cur->type == XML_ENTITY_REF_NODE) {
488 xmlOutputBufferWriteString(buf, "&");
489 xmlOutputBufferWriteString(buf, (const char *)cur->name);
490 xmlOutputBufferWriteString(buf, ";");
491 return;
492 }
493 if (cur->type == XML_CDATA_SECTION_NODE) {
494 start = end = cur->content;
495 while (*end != '\0') {
496 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
497 end = end + 2;
498 xmlOutputBufferWriteString(buf, "<![CDATA[");
499 xmlOutputBufferWrite(buf, end - start, (const char *)start);
500 xmlOutputBufferWriteString(buf, "]]>");
501 start = end;
502 }
503 end++;
504 }
505 if (start != end) {
506 xmlOutputBufferWriteString(buf, "<![CDATA[");
507 xmlOutputBufferWriteString(buf, (const char *)start);
508 xmlOutputBufferWriteString(buf, "]]>");
509 }
510 return;
511 }
512 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000513 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000514 return;
515 }
516 if (cur->type == XML_NAMESPACE_DECL) {
517 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
518 return;
519 }
520
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000521 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000522 if (format == 1) {
523 tmp = cur->children;
524 while (tmp != NULL) {
525 if ((tmp->type == XML_TEXT_NODE) ||
526 (tmp->type == XML_CDATA_SECTION_NODE) ||
527 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000528 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000529 break;
530 }
531 tmp = tmp->next;
532 }
533 }
534 xmlOutputBufferWriteString(buf, "<");
535 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
536 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
537 xmlOutputBufferWriteString(buf, ":");
538 }
539
540 xmlOutputBufferWriteString(buf, (const char *)cur->name);
541 if (cur->nsDef)
542 xmlNsListDumpOutput(buf, cur->nsDef);
543 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000544 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000545
546 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
547 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
548 xmlOutputBufferWriteString(buf, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000549 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000550 return;
551 }
552 xmlOutputBufferWriteString(buf, ">");
553 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
554 xmlChar *buffer;
555
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000556 if (ctxt->encoding == NULL)
557 buffer = xmlEncodeEntitiesReentrant(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000558 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000559 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000560 if (buffer != NULL) {
561 xmlOutputBufferWriteString(buf, (const char *)buffer);
562 xmlFree(buffer);
563 }
564 }
565 if (cur->children != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000566 if (ctxt->format) xmlOutputBufferWriteString(buf, "\n");
567 if (ctxt->level >= 0) ctxt->level++;
568 xmlNodeListDumpOutput(ctxt, cur->children);
569 if (ctxt->level > 0) ctxt->level--;
570 if ((xmlIndentTreeOutput) && (ctxt->format))
571 for (i = 0;i < ctxt->level;i++)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000572 xmlOutputBufferWriteString(buf, xmlTreeIndentString);
573 }
574 xmlOutputBufferWriteString(buf, "</");
575 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
576 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
577 xmlOutputBufferWriteString(buf, ":");
578 }
579
580 xmlOutputBufferWriteString(buf, (const char *)cur->name);
581 xmlOutputBufferWriteString(buf, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000582 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000583}
584
585/**
586 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000587 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000588 *
589 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000590 */
591static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000592xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000593#ifdef LIBXML_HTML_ENABLED
594 xmlDtdPtr dtd;
595 int is_xhtml = 0;
596#endif
597 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000598 const xmlChar *encoding = ctxt->encoding;
599 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000600
601 xmlInitParser();
602
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000603 if (ctxt->encoding != NULL)
604 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000605
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000606 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000607 xmlOutputBufferWriteString(buf, "<?xml version=");
608 if (cur->version != NULL)
609 xmlBufferWriteQuotedString(buf->buffer, cur->version);
610 else
611 xmlOutputBufferWriteString(buf, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000612 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000613 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000614 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000615 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000616 encoding = (const xmlChar *)
617 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000618 }
619 if (encoding != NULL) {
620 xmlOutputBufferWriteString(buf, " encoding=");
621 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
622 }
623 switch (cur->standalone) {
624 case 0:
625 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
626 break;
627 case 1:
628 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
629 break;
630 }
631 xmlOutputBufferWriteString(buf, "?>\n");
632
633#ifdef LIBXML_HTML_ENABLED
634 dtd = xmlGetIntSubset(cur);
635 if (dtd != NULL) {
636 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
637 if (is_xhtml < 0) is_xhtml = 0;
638 }
639 if (is_xhtml) {
640 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000641 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000642 else
643 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
644 }
645#endif
646 if (cur->children != NULL) {
647 xmlNodePtr child = cur->children;
648
649 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000650 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000651#ifdef LIBXML_HTML_ENABLED
652 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000653 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000654 else
655#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000656 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000657 xmlOutputBufferWriteString(buf, "\n");
658 child = child->next;
659 }
660 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000661 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000662 cur->encoding = oldenc;
663}
664
665#ifdef LIBXML_HTML_ENABLED
666/************************************************************************
667 * *
668 * Functions specific to XHTML serialization *
669 * *
670 ************************************************************************/
671
672/**
673 * xhtmlIsEmpty:
674 * @node: the node
675 *
676 * Check if a node is an empty xhtml node
677 *
678 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
679 */
680static int
681xhtmlIsEmpty(xmlNodePtr node) {
682 if (node == NULL)
683 return(-1);
684 if (node->type != XML_ELEMENT_NODE)
685 return(0);
686 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
687 return(0);
688 if (node->children != NULL)
689 return(0);
690 switch (node->name[0]) {
691 case 'a':
692 if (xmlStrEqual(node->name, BAD_CAST "area"))
693 return(1);
694 return(0);
695 case 'b':
696 if (xmlStrEqual(node->name, BAD_CAST "br"))
697 return(1);
698 if (xmlStrEqual(node->name, BAD_CAST "base"))
699 return(1);
700 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
701 return(1);
702 return(0);
703 case 'c':
704 if (xmlStrEqual(node->name, BAD_CAST "col"))
705 return(1);
706 return(0);
707 case 'f':
708 if (xmlStrEqual(node->name, BAD_CAST "frame"))
709 return(1);
710 return(0);
711 case 'h':
712 if (xmlStrEqual(node->name, BAD_CAST "hr"))
713 return(1);
714 return(0);
715 case 'i':
716 if (xmlStrEqual(node->name, BAD_CAST "img"))
717 return(1);
718 if (xmlStrEqual(node->name, BAD_CAST "input"))
719 return(1);
720 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
721 return(1);
722 return(0);
723 case 'l':
724 if (xmlStrEqual(node->name, BAD_CAST "link"))
725 return(1);
726 return(0);
727 case 'm':
728 if (xmlStrEqual(node->name, BAD_CAST "meta"))
729 return(1);
730 return(0);
731 case 'p':
732 if (xmlStrEqual(node->name, BAD_CAST "param"))
733 return(1);
734 return(0);
735 }
736 return(0);
737}
738
739/**
740 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000741 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000742 *
743 * Dump a list of XML attributes
744 */
745static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000746xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000747 xmlAttrPtr xml_lang = NULL;
748 xmlAttrPtr lang = NULL;
749 xmlAttrPtr name = NULL;
750 xmlAttrPtr id = NULL;
751 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000752 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000753
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000754 if (cur == NULL) return;
755 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000756 parent = cur->parent;
757 while (cur != NULL) {
758 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
759 id = cur;
760 else
761 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
762 name = cur;
763 else
764 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
765 lang = cur;
766 else
767 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
768 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
769 xml_lang = cur;
770 else if ((cur->ns == NULL) &&
771 ((cur->children == NULL) ||
772 (cur->children->content == NULL) ||
773 (cur->children->content[0] == 0)) &&
774 (htmlIsBooleanAttr(cur->name))) {
775 if (cur->children != NULL)
776 xmlFreeNode(cur->children);
777 cur->children = xmlNewText(cur->name);
778 if (cur->children != NULL)
779 cur->children->parent = (xmlNodePtr) cur;
780 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000781 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000782 cur = cur->next;
783 }
784 /*
785 * C.8
786 */
787 if ((name != NULL) && (id == NULL)) {
788 if ((parent != NULL) && (parent->name != NULL) &&
789 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
790 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
791 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
792 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
793 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
794 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
795 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
796 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
797 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
798 xmlOutputBufferWriteString(buf, " id=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000799 xmlAttrSerializeContent(buf->buffer, ctxt->doc, name);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000800 xmlOutputBufferWriteString(buf, "\"");
801 }
802 }
803 /*
804 * C.7.
805 */
806 if ((lang != NULL) && (xml_lang == NULL)) {
807 xmlOutputBufferWriteString(buf, " xml:lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000808 xmlAttrSerializeContent(buf->buffer, ctxt->doc, lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000809 xmlOutputBufferWriteString(buf, "\"");
810 } else
811 if ((xml_lang != NULL) && (lang == NULL)) {
812 xmlOutputBufferWriteString(buf, " lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000813 xmlAttrSerializeContent(buf->buffer, ctxt->doc, xml_lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000814 xmlOutputBufferWriteString(buf, "\"");
815 }
816}
817
818/**
819 * xhtmlNodeListDumpOutput:
820 * @buf: the XML buffer output
821 * @doc: the XHTML document
822 * @cur: the first node
823 * @level: the imbrication level for indenting
824 * @format: is formatting allowed
825 * @encoding: an optional encoding string
826 *
827 * Dump an XML node list, recursive behaviour, children are printed too.
828 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
829 * or xmlKeepBlanksDefault(0) was called
830 */
831static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000832xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000833 int i;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000834 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000835
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000836 if (cur == NULL) return;
837 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000838 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000839 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000840 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000841 for (i = 0;i < ctxt->level;i++)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000842 xmlOutputBufferWriteString(buf, xmlTreeIndentString);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000843 xhtmlNodeDumpOutput(ctxt, cur);
844 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000845 xmlOutputBufferWriteString(buf, "\n");
846 }
847 cur = cur->next;
848 }
849}
850
851/**
852 * xhtmlNodeDumpOutput:
853 * @buf: the XML buffer output
854 * @doc: the XHTML document
855 * @cur: the current node
856 * @level: the imbrication level for indenting
857 * @format: is formatting allowed
858 * @encoding: an optional encoding string
859 *
860 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000861 */
862static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000863xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
864 int i, format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000865 xmlNodePtr tmp;
866 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000867 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000868
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000869 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000870 if (cur->type == XML_XINCLUDE_START)
871 return;
872 if (cur->type == XML_XINCLUDE_END)
873 return;
874 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000875 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000876 return;
877 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000878 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000879 if (cur->type == XML_ELEMENT_DECL) {
880 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
881 return;
882 }
883 if (cur->type == XML_ATTRIBUTE_DECL) {
884 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
885 return;
886 }
887 if (cur->type == XML_ENTITY_DECL) {
888 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
889 return;
890 }
891 if (cur->type == XML_TEXT_NODE) {
892 if (cur->content != NULL) {
893 if ((cur->name == xmlStringText) ||
894 (cur->name != xmlStringTextNoenc)) {
895 xmlChar *buffer;
896
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000897 if (ctxt->encoding == NULL)
898 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
899 cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000900 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000901 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000902 if (buffer != NULL) {
903 xmlOutputBufferWriteString(buf, (const char *)buffer);
904 xmlFree(buffer);
905 }
906 } else {
907 /*
908 * Disable escaping, needed for XSLT
909 */
910 xmlOutputBufferWriteString(buf, (const char *) cur->content);
911 }
912 }
913
914 return;
915 }
916 if (cur->type == XML_PI_NODE) {
917 if (cur->content != NULL) {
918 xmlOutputBufferWriteString(buf, "<?");
919 xmlOutputBufferWriteString(buf, (const char *)cur->name);
920 if (cur->content != NULL) {
921 xmlOutputBufferWriteString(buf, " ");
922 xmlOutputBufferWriteString(buf, (const char *)cur->content);
923 }
924 xmlOutputBufferWriteString(buf, "?>");
925 } else {
926 xmlOutputBufferWriteString(buf, "<?");
927 xmlOutputBufferWriteString(buf, (const char *)cur->name);
928 xmlOutputBufferWriteString(buf, "?>");
929 }
930 return;
931 }
932 if (cur->type == XML_COMMENT_NODE) {
933 if (cur->content != NULL) {
934 xmlOutputBufferWriteString(buf, "<!--");
935 xmlOutputBufferWriteString(buf, (const char *)cur->content);
936 xmlOutputBufferWriteString(buf, "-->");
937 }
938 return;
939 }
940 if (cur->type == XML_ENTITY_REF_NODE) {
941 xmlOutputBufferWriteString(buf, "&");
942 xmlOutputBufferWriteString(buf, (const char *)cur->name);
943 xmlOutputBufferWriteString(buf, ";");
944 return;
945 }
946 if (cur->type == XML_CDATA_SECTION_NODE) {
947 start = end = cur->content;
948 while (*end != '\0') {
949 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
950 end = end + 2;
951 xmlOutputBufferWriteString(buf, "<![CDATA[");
952 xmlOutputBufferWrite(buf, end - start, (const char *)start);
953 xmlOutputBufferWriteString(buf, "]]>");
954 start = end;
955 }
956 end++;
957 }
958 if (start != end) {
959 xmlOutputBufferWriteString(buf, "<![CDATA[");
960 xmlOutputBufferWriteString(buf, (const char *)start);
961 xmlOutputBufferWriteString(buf, "]]>");
962 }
963 return;
964 }
965
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000966 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000967 if (format == 1) {
968 tmp = cur->children;
969 while (tmp != NULL) {
970 if ((tmp->type == XML_TEXT_NODE) ||
971 (tmp->type == XML_ENTITY_REF_NODE)) {
972 format = 0;
973 break;
974 }
975 tmp = tmp->next;
976 }
977 }
978 xmlOutputBufferWriteString(buf, "<");
979 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
980 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
981 xmlOutputBufferWriteString(buf, ":");
982 }
983
984 xmlOutputBufferWriteString(buf, (const char *)cur->name);
985 if (cur->nsDef)
986 xmlNsListDumpOutput(buf, cur->nsDef);
987 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
988 (cur->ns == NULL) && (cur->nsDef == NULL))) {
989 /*
990 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
991 */
992 xmlOutputBufferWriteString(buf,
993 " xmlns=\"http://www.w3.org/1999/xhtml\"");
994 }
995 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000996 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000997
998 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
999 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1000 (xhtmlIsEmpty(cur) == 1)) {
1001 /*
1002 * C.2. Empty Elements
1003 */
1004 xmlOutputBufferWriteString(buf, " />");
1005 } else {
1006 /*
1007 * C.3. Element Minimization and Empty Element Content
1008 */
1009 xmlOutputBufferWriteString(buf, "></");
1010 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1011 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1012 xmlOutputBufferWriteString(buf, ":");
1013 }
1014 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1015 xmlOutputBufferWriteString(buf, ">");
1016 }
1017 return;
1018 }
1019 xmlOutputBufferWriteString(buf, ">");
1020 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1021 xmlChar *buffer;
1022
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001023 if (ctxt->encoding == NULL)
1024 buffer = xmlEncodeEntitiesReentrant(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001025 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001026 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001027 if (buffer != NULL) {
1028 xmlOutputBufferWriteString(buf, (const char *)buffer);
1029 xmlFree(buffer);
1030 }
1031 }
1032
1033 /*
1034 * 4.8. Script and Style elements
1035 */
1036 if ((cur->type == XML_ELEMENT_NODE) &&
1037 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1038 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1039 ((cur->ns == NULL) ||
1040 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1041 xmlNodePtr child = cur->children;
1042
1043 while (child != NULL) {
1044 if ((child->type == XML_TEXT_NODE) ||
1045 (child->type == XML_CDATA_SECTION_NODE)) {
1046 /*
1047 * Apparently CDATA escaping for style just break on IE,
1048 * mozilla and galeon, so ...
1049 */
1050 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1051 (xmlStrchr(child->content, '<') == NULL) &&
1052 (xmlStrchr(child->content, '>') == NULL) &&
1053 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001054 int level = ctxt->level;
1055 int indent = ctxt->format;
1056
1057 ctxt->level = 0;
1058 ctxt->format = 0;
1059 xhtmlNodeDumpOutput(ctxt, child);
1060 ctxt->level = level;
1061 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001062 } else {
1063 start = end = child->content;
1064 while (*end != '\0') {
1065 if (*end == ']' &&
1066 *(end + 1) == ']' &&
1067 *(end + 2) == '>') {
1068 end = end + 2;
1069 xmlOutputBufferWriteString(buf, "<![CDATA[");
1070 xmlOutputBufferWrite(buf, end - start,
1071 (const char *)start);
1072 xmlOutputBufferWriteString(buf, "]]>");
1073 start = end;
1074 }
1075 end++;
1076 }
1077 if (start != end) {
1078 xmlOutputBufferWriteString(buf, "<![CDATA[");
1079 xmlOutputBufferWriteString(buf, (const char *)start);
1080 xmlOutputBufferWriteString(buf, "]]>");
1081 }
1082 }
1083 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001084 int level = ctxt->level;
1085 int indent = ctxt->format;
1086
1087 ctxt->level = 0;
1088 ctxt->format = 0;
1089 xhtmlNodeDumpOutput(ctxt, child);
1090 ctxt->level = level;
1091 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001092 }
1093 child = child->next;
1094 }
1095 } else if (cur->children != NULL) {
1096 if (format) xmlOutputBufferWriteString(buf, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001097 if (ctxt->level >= 0) ctxt->level++;
1098 xhtmlNodeListDumpOutput(ctxt, cur->children);
1099 if (ctxt->level > 0) ctxt->level--;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001100 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001101 for (i = 0;i < ctxt->level;i++)
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001102 xmlOutputBufferWriteString(buf, xmlTreeIndentString);
1103 }
1104 xmlOutputBufferWriteString(buf, "</");
1105 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1106 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1107 xmlOutputBufferWriteString(buf, ":");
1108 }
1109
1110 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1111 xmlOutputBufferWriteString(buf, ">");
1112}
1113#endif
1114
1115/************************************************************************
1116 * *
1117 * Public entry points *
1118 * *
1119 ************************************************************************/
1120
1121/**
1122 * xmlSaveToFd:
1123 * @fd: a file descriptor number
1124 * @encoding: the encoding name to use or NULL
1125 * @options: a set of xmlSaveOptions
1126 *
1127 * Create a document saving context serializing to a file descriptor
1128 * with the encoding and the options given.
1129 *
1130 * Returns a new serialization context or NULL in case of error.
1131 */
1132xmlSaveCtxtPtr
1133xmlSaveToFd(int fd, const char *encoding, int options)
1134{
1135 xmlSaveCtxtPtr ret;
1136
1137 ret = xmlNewSaveCtxt(encoding, options);
1138 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001139 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1140 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001141 xmlFreeSaveCtxt(ret);
1142 return(NULL);
1143 }
1144 return(ret);
1145}
1146
1147/**
1148 * xmlSaveToFilename:
1149 * @filename: a file name or an URL
1150 * @encoding: the encoding name to use or NULL
1151 * @options: a set of xmlSaveOptions
1152 *
1153 * Create a document saving context serializing to a filename or possibly
1154 * to an URL (but this is less reliable) with the encoding and the options
1155 * given.
1156 *
1157 * Returns a new serialization context or NULL in case of error.
1158 */
1159xmlSaveCtxtPtr
1160xmlSaveToFilename(const char *filename, const char *encoding, int options)
1161{
1162 xmlSaveCtxtPtr ret;
1163 int compression = 0; /* TODO handle compression option */
1164
1165 ret = xmlNewSaveCtxt(encoding, options);
1166 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001167 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001168 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001169 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001170 xmlFreeSaveCtxt(ret);
1171 return(NULL);
1172 }
1173 return(ret);
1174}
1175
1176#if 0
1177/**
1178 * xmlSaveToBuffer:
1179 * @buffer: a buffer
1180 * @encoding: the encoding name to use or NULL
1181 * @options: a set of xmlSaveOptions
1182 *
1183 * Create a document saving context serializing to a buffer
1184 * with the encoding and the options given
1185 *
1186 * Returns a new serialization context or NULL in case of error.
1187 */
1188xmlSaveCtxtPtr
1189xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1190{
1191 TODO
1192 return(NULL);
1193}
1194#endif
1195
1196/**
1197 * xmlSaveToIO:
1198 * @iowrite: an I/O write function
1199 * @ioclose: an I/O close function
1200 * @ioctx: an I/O handler
1201 * @encoding: the encoding name to use or NULL
1202 * @options: a set of xmlSaveOptions
1203 *
1204 * Create a document saving context serializing to a file descriptor
1205 * with the encoding and the options given
1206 *
1207 * Returns a new serialization context or NULL in case of error.
1208 */
1209xmlSaveCtxtPtr
1210xmlSaveToIO(xmlOutputWriteCallback iowrite,
1211 xmlOutputCloseCallback ioclose,
1212 void *ioctx, const char *encoding, int options)
1213{
1214 xmlSaveCtxtPtr ret;
1215
1216 ret = xmlNewSaveCtxt(encoding, options);
1217 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001218 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1219 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001220 xmlFreeSaveCtxt(ret);
1221 return(NULL);
1222 }
1223 return(ret);
1224}
1225
1226/**
1227 * xmlSaveDoc:
1228 * @ctxt: a document saving context
1229 * @doc: a document
1230 *
1231 * Save a full document to a saving context
1232 *
1233 * Returns the number of byte written or -1 in case of error
1234 */
1235long
1236xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1237{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001238 long ret = 0;
1239
1240 xmlDocContentDumpOutput(ctxt, doc);
1241 TODO /* compute ret */
1242 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001243}
1244
1245/**
1246 * xmlSaveTree:
1247 * @ctxt: a document saving context
1248 * @node: a document
1249 *
1250 * Save a subtree starting at the node parameter to a saving context
1251 *
1252 * Returns the number of byte written or -1 in case of error
1253 */
1254long
1255xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1256{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001257 long ret = 0;
1258
1259 xmlNodeDumpOutputInternal(ctxt, node);
1260 TODO /* compute ret */
1261 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001262}
1263
1264/**
1265 * xmlSaveFlush:
1266 * @ctxt: a document saving context
1267 *
1268 * Flush a document saving context, i.e. make sure that all bytes have
1269 * been output.
1270 *
1271 * Returns the number of byte written or -1 in case of error.
1272 */
1273int
1274xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1275{
1276 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001277 if (ctxt->buf == NULL) return(-1);
1278 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001279}
1280
1281/**
1282 * xmlSaveClose:
1283 * @ctxt: a document saving context
1284 *
1285 * Close a document saving context, i.e. make sure that all bytes have
1286 * been output and free the associated data.
1287 *
1288 * Returns the number of byte written or -1 in case of error.
1289 */
1290int
1291xmlSaveClose(xmlSaveCtxtPtr ctxt)
1292{
1293 int ret;
1294
1295 if (ctxt == NULL) return(-1);
1296 ret = xmlSaveFlush(ctxt);
1297 xmlFreeSaveCtxt(ctxt);
1298 return(ret);
1299}
1300
1301/************************************************************************
1302 * *
1303 * Public entry points based on buffers *
1304 * *
1305 ************************************************************************/
1306/**
1307 * xmlAttrSerializeTxtContent:
1308 * @buf: the XML buffer output
1309 * @doc: the document
1310 * @attr: the attribute node
1311 * @string: the text content
1312 *
1313 * Serialize text attribute values to an xml simple buffer
1314 */
1315void
1316xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1317 xmlAttrPtr attr, const xmlChar *string) {
1318 xmlChar *base, *cur;
1319
1320 if (string == NULL) return;
1321 base = cur = (xmlChar *)string;
1322 while (*cur != 0) {
1323 if (*cur == '\n') {
1324 if (base != cur)
1325 xmlBufferAdd(buf, base, cur - base);
1326 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1327 cur++;
1328 base = cur;
1329 } else if (*cur == '\r') {
1330 if (base != cur)
1331 xmlBufferAdd(buf, base, cur - base);
1332 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1333 cur++;
1334 base = cur;
1335 } else if (*cur == '\t') {
1336 if (base != cur)
1337 xmlBufferAdd(buf, base, cur - base);
1338 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1339 cur++;
1340 base = cur;
1341 } else if (*cur == '"') {
1342 if (base != cur)
1343 xmlBufferAdd(buf, base, cur - base);
1344 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1345 cur++;
1346 base = cur;
1347 } else if (*cur == '<') {
1348 if (base != cur)
1349 xmlBufferAdd(buf, base, cur - base);
1350 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1351 cur++;
1352 base = cur;
1353 } else if (*cur == '>') {
1354 if (base != cur)
1355 xmlBufferAdd(buf, base, cur - base);
1356 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1357 cur++;
1358 base = cur;
1359 } else if (*cur == '&') {
1360 if (base != cur)
1361 xmlBufferAdd(buf, base, cur - base);
1362 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1363 cur++;
1364 base = cur;
1365 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1366 (doc->encoding == NULL))) {
1367 /*
1368 * We assume we have UTF-8 content.
1369 */
1370 char tmp[10];
1371 int val = 0, l = 1;
1372
1373 if (base != cur)
1374 xmlBufferAdd(buf, base, cur - base);
1375 if (*cur < 0xC0) {
1376 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1377 if (doc != NULL)
1378 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1379 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1380 tmp[sizeof(tmp) - 1] = 0;
1381 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1382 cur++;
1383 base = cur;
1384 continue;
1385 } else if (*cur < 0xE0) {
1386 val = (cur[0]) & 0x1F;
1387 val <<= 6;
1388 val |= (cur[1]) & 0x3F;
1389 l = 2;
1390 } else if (*cur < 0xF0) {
1391 val = (cur[0]) & 0x0F;
1392 val <<= 6;
1393 val |= (cur[1]) & 0x3F;
1394 val <<= 6;
1395 val |= (cur[2]) & 0x3F;
1396 l = 3;
1397 } else if (*cur < 0xF8) {
1398 val = (cur[0]) & 0x07;
1399 val <<= 6;
1400 val |= (cur[1]) & 0x3F;
1401 val <<= 6;
1402 val |= (cur[2]) & 0x3F;
1403 val <<= 6;
1404 val |= (cur[3]) & 0x3F;
1405 l = 4;
1406 }
1407 if ((l == 1) || (!IS_CHAR(val))) {
1408 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1409 if (doc != NULL)
1410 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1411 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1412 tmp[sizeof(tmp) - 1] = 0;
1413 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1414 cur++;
1415 base = cur;
1416 continue;
1417 }
1418 /*
1419 * We could do multiple things here. Just save
1420 * as a char ref
1421 */
1422 snprintf(tmp, sizeof(tmp), "&#x%X;", val);
1423 tmp[sizeof(tmp) - 1] = 0;
1424 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1425 cur += l;
1426 base = cur;
1427 } else {
1428 cur++;
1429 }
1430 }
1431 if (base != cur)
1432 xmlBufferAdd(buf, base, cur - base);
1433}
1434
1435/**
1436 * xmlNodeDump:
1437 * @buf: the XML buffer output
1438 * @doc: the document
1439 * @cur: the current node
1440 * @level: the imbrication level for indenting
1441 * @format: is formatting allowed
1442 *
1443 * Dump an XML node, recursive behaviour,children are printed too.
1444 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1445 * or xmlKeepBlanksDefault(0) was called
1446 *
1447 * Returns the number of bytes written to the buffer or -1 in case of error
1448 */
1449int
1450xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1451 int format)
1452{
1453 unsigned int use;
1454 int ret;
1455 xmlOutputBufferPtr outbuf;
1456
1457 xmlInitParser();
1458
1459 if (cur == NULL) {
1460#ifdef DEBUG_TREE
1461 xmlGenericError(xmlGenericErrorContext,
1462 "xmlNodeDump : node == NULL\n");
1463#endif
1464 return (-1);
1465 }
1466 if (buf == NULL) {
1467#ifdef DEBUG_TREE
1468 xmlGenericError(xmlGenericErrorContext,
1469 "xmlNodeDump : buf == NULL\n");
1470#endif
1471 return (-1);
1472 }
1473 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1474 if (outbuf == NULL) {
1475 xmlSaveErrMemory("creating buffer");
1476 return (-1);
1477 }
1478 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1479 outbuf->buffer = buf;
1480 outbuf->encoder = NULL;
1481 outbuf->writecallback = NULL;
1482 outbuf->closecallback = NULL;
1483 outbuf->context = NULL;
1484 outbuf->written = 0;
1485
1486 use = buf->use;
1487 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1488 xmlFree(outbuf);
1489 ret = buf->use - use;
1490 return (ret);
1491}
1492
1493/**
1494 * xmlElemDump:
1495 * @f: the FILE * for the output
1496 * @doc: the document
1497 * @cur: the current node
1498 *
1499 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1500 */
1501void
1502xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1503{
1504 xmlOutputBufferPtr outbuf;
1505
1506 xmlInitParser();
1507
1508 if (cur == NULL) {
1509#ifdef DEBUG_TREE
1510 xmlGenericError(xmlGenericErrorContext,
1511 "xmlElemDump : cur == NULL\n");
1512#endif
1513 return;
1514 }
1515#ifdef DEBUG_TREE
1516 if (doc == NULL) {
1517 xmlGenericError(xmlGenericErrorContext,
1518 "xmlElemDump : doc == NULL\n");
1519 }
1520#endif
1521
1522 outbuf = xmlOutputBufferCreateFile(f, NULL);
1523 if (outbuf == NULL)
1524 return;
1525 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1526#ifdef LIBXML_HTML_ENABLED
1527 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1528#else
1529 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1530#endif /* LIBXML_HTML_ENABLED */
1531 } else
1532 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1533 xmlOutputBufferClose(outbuf);
1534}
1535
1536/************************************************************************
1537 * *
1538 * Saving functions front-ends *
1539 * *
1540 ************************************************************************/
1541
1542/**
1543 * xmlNodeDumpOutput:
1544 * @buf: the XML buffer output
1545 * @doc: the document
1546 * @cur: the current node
1547 * @level: the imbrication level for indenting
1548 * @format: is formatting allowed
1549 * @encoding: an optional encoding string
1550 *
1551 * Dump an XML node, recursive behaviour, children are printed too.
1552 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1553 * or xmlKeepBlanksDefault(0) was called
1554 */
1555void
1556xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1557 int level, int format, const char *encoding)
1558{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001559 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001560#ifdef LIBXML_HTML_ENABLED
1561 xmlDtdPtr dtd;
1562 int is_xhtml = 0;
1563#endif
1564
1565 xmlInitParser();
1566
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001567 memset(&ctxt, 0, sizeof(ctxt));
1568 ctxt.doc = doc;
1569 ctxt.buf = buf;
1570 ctxt.level = level;
1571 ctxt.format = format;
1572 ctxt.encoding = (const xmlChar *) encoding;
1573
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001574#ifdef LIBXML_HTML_ENABLED
1575 dtd = xmlGetIntSubset(doc);
1576 if (dtd != NULL) {
1577 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1578 if (is_xhtml < 0)
1579 is_xhtml = 0;
1580 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1581 (cur->type == XML_ELEMENT_NODE) &&
1582 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1583 if (encoding != NULL)
1584 htmlSetMetaEncoding((htmlDocPtr) doc,
1585 (const xmlChar *) encoding);
1586 else
1587 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1588 }
1589 }
1590
1591 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001592 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001593 else
1594#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001595 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001596}
1597
1598/**
1599 * xmlDocDumpFormatMemoryEnc:
1600 * @out_doc: Document to generate XML text from
1601 * @doc_txt_ptr: Memory pointer for allocated XML text
1602 * @doc_txt_len: Length of the generated XML text
1603 * @txt_encoding: Character encoding to use when generating XML text
1604 * @format: should formatting spaces been added
1605 *
1606 * Dump the current DOM tree into memory using the character encoding specified
1607 * by the caller. Note it is up to the caller of this function to free the
1608 * allocated memory with xmlFree().
1609 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1610 * or xmlKeepBlanksDefault(0) was called
1611 */
1612
1613void
1614xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1615 int * doc_txt_len, const char * txt_encoding,
1616 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001617 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001618 int dummy = 0;
1619 xmlOutputBufferPtr out_buff = NULL;
1620 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1621
1622 if (doc_txt_len == NULL) {
1623 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1624 }
1625
1626 if (doc_txt_ptr == NULL) {
1627 *doc_txt_len = 0;
1628 return;
1629 }
1630
1631 *doc_txt_ptr = NULL;
1632 *doc_txt_len = 0;
1633
1634 if (out_doc == NULL) {
1635 /* No document, no output */
1636 return;
1637 }
1638
1639 /*
1640 * Validate the encoding value, if provided.
1641 * This logic is copied from xmlSaveFileEnc.
1642 */
1643
1644 if (txt_encoding == NULL)
1645 txt_encoding = (const char *) out_doc->encoding;
1646 if (txt_encoding != NULL) {
1647 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1648 if ( conv_hdlr == NULL ) {
1649 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1650 txt_encoding);
1651 return;
1652 }
1653 }
1654
1655 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1656 xmlSaveErrMemory("creating buffer");
1657 return;
1658 }
1659
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001660 memset(&ctxt, 0, sizeof(ctxt));
1661 ctxt.doc = out_doc;
1662 ctxt.buf = out_buff;
1663 ctxt.level = 0;
1664 ctxt.format = format;
1665 ctxt.encoding = (const xmlChar *) txt_encoding;
1666 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001667 xmlOutputBufferFlush(out_buff);
1668 if (out_buff->conv != NULL) {
1669 *doc_txt_len = out_buff->conv->use;
1670 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1671 } else {
1672 *doc_txt_len = out_buff->buffer->use;
1673 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1674 }
1675 (void)xmlOutputBufferClose(out_buff);
1676
1677 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1678 *doc_txt_len = 0;
1679 xmlSaveErrMemory("creating output");
1680 }
1681
1682 return;
1683}
1684
1685/**
1686 * xmlDocDumpMemory:
1687 * @cur: the document
1688 * @mem: OUT: the memory pointer
1689 * @size: OUT: the memory length
1690 *
1691 * Dump an XML document in memory and return the #xmlChar * and it's size
1692 * in bytes. It's up to the caller to free the memory with xmlFree().
1693 * The resulting byte array is zero terminated, though the last 0 is not
1694 * included in the returned size.
1695 */
1696void
1697xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1698 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1699}
1700
1701/**
1702 * xmlDocDumpFormatMemory:
1703 * @cur: the document
1704 * @mem: OUT: the memory pointer
1705 * @size: OUT: the memory length
1706 * @format: should formatting spaces been added
1707 *
1708 *
1709 * Dump an XML document in memory and return the #xmlChar * and it's size.
1710 * It's up to the caller to free the memory with xmlFree().
1711 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1712 * or xmlKeepBlanksDefault(0) was called
1713 */
1714void
1715xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1716 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1717}
1718
1719/**
1720 * xmlDocDumpMemoryEnc:
1721 * @out_doc: Document to generate XML text from
1722 * @doc_txt_ptr: Memory pointer for allocated XML text
1723 * @doc_txt_len: Length of the generated XML text
1724 * @txt_encoding: Character encoding to use when generating XML text
1725 *
1726 * Dump the current DOM tree into memory using the character encoding specified
1727 * by the caller. Note it is up to the caller of this function to free the
1728 * allocated memory with xmlFree().
1729 */
1730
1731void
1732xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1733 int * doc_txt_len, const char * txt_encoding) {
1734 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1735 txt_encoding, 0);
1736}
1737
1738/**
1739 * xmlDocFormatDump:
1740 * @f: the FILE*
1741 * @cur: the document
1742 * @format: should formatting spaces been added
1743 *
1744 * Dump an XML document to an open FILE.
1745 *
1746 * returns: the number of bytes written or -1 in case of failure.
1747 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1748 * or xmlKeepBlanksDefault(0) was called
1749 */
1750int
1751xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001752 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001753 xmlOutputBufferPtr buf;
1754 const char * encoding;
1755 xmlCharEncodingHandlerPtr handler = NULL;
1756 int ret;
1757
1758 if (cur == NULL) {
1759#ifdef DEBUG_TREE
1760 xmlGenericError(xmlGenericErrorContext,
1761 "xmlDocDump : document == NULL\n");
1762#endif
1763 return(-1);
1764 }
1765 encoding = (const char *) cur->encoding;
1766
1767 if (encoding != NULL) {
1768 handler = xmlFindCharEncodingHandler(encoding);
1769 if (handler == NULL) {
1770 xmlFree((char *) cur->encoding);
1771 cur->encoding = NULL;
1772 }
1773 }
1774 buf = xmlOutputBufferCreateFile(f, handler);
1775 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001776 memset(&ctxt, 0, sizeof(ctxt));
1777 ctxt.doc = cur;
1778 ctxt.buf = buf;
1779 ctxt.level = 0;
1780 ctxt.format = format;
1781 ctxt.encoding = (const xmlChar *) encoding;
1782 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001783
1784 ret = xmlOutputBufferClose(buf);
1785 return(ret);
1786}
1787
1788/**
1789 * xmlDocDump:
1790 * @f: the FILE*
1791 * @cur: the document
1792 *
1793 * Dump an XML document to an open FILE.
1794 *
1795 * returns: the number of bytes written or -1 in case of failure.
1796 */
1797int
1798xmlDocDump(FILE *f, xmlDocPtr cur) {
1799 return(xmlDocFormatDump (f, cur, 0));
1800}
1801
1802/**
1803 * xmlSaveFileTo:
1804 * @buf: an output I/O buffer
1805 * @cur: the document
1806 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1807 *
1808 * Dump an XML document to an I/O buffer.
1809 *
1810 * returns: the number of bytes written or -1 in case of failure.
1811 */
1812int
1813xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001814 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001815 int ret;
1816
1817 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001818 memset(&ctxt, 0, sizeof(ctxt));
1819 ctxt.doc = cur;
1820 ctxt.buf = buf;
1821 ctxt.level = 0;
1822 ctxt.format = 0;
1823 ctxt.encoding = (const xmlChar *) encoding;
1824 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001825 ret = xmlOutputBufferClose(buf);
1826 return(ret);
1827}
1828
1829/**
1830 * xmlSaveFormatFileTo:
1831 * @buf: an output I/O buffer
1832 * @cur: the document
1833 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1834 * @format: should formatting spaces been added
1835 *
1836 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001837 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001838 *
1839 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001840 */
1841int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001842xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
1843 const char *encoding, int format)
1844{
1845 xmlSaveCtxt ctxt;
1846 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001847
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001848 if (buf == NULL)
1849 return (0);
1850 memset(&ctxt, 0, sizeof(ctxt));
1851 ctxt.doc = cur;
1852 ctxt.buf = buf;
1853 ctxt.level = 0;
1854 ctxt.format = format;
1855 ctxt.encoding = (const xmlChar *) encoding;
1856 xmlDocContentDumpOutput(&ctxt, cur);
1857 ret = xmlOutputBufferClose(buf);
1858 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001859}
1860
1861/**
1862 * xmlSaveFormatFileEnc:
1863 * @filename: the filename or URL to output
1864 * @cur: the document being saved
1865 * @encoding: the name of the encoding to use or NULL.
1866 * @format: should formatting spaces be added.
1867 *
1868 * Dump an XML document to a file or an URL.
1869 *
1870 * Returns the number of bytes written or -1 in case of error.
1871 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1872 * or xmlKeepBlanksDefault(0) was called
1873 */
1874int
1875xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
1876 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001877 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001878 xmlOutputBufferPtr buf;
1879 xmlCharEncodingHandlerPtr handler = NULL;
1880 int ret;
1881
1882 if (cur == NULL)
1883 return(-1);
1884
1885 if (encoding == NULL)
1886 encoding = (const char *) cur->encoding;
1887
1888 if (encoding != NULL) {
1889
1890 handler = xmlFindCharEncodingHandler(encoding);
1891 if (handler == NULL)
1892 return(-1);
1893 }
1894
1895#ifdef HAVE_ZLIB_H
1896 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
1897#endif
1898 /*
1899 * save the content to a temp buffer.
1900 */
1901 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
1902 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001903 memset(&ctxt, 0, sizeof(ctxt));
1904 ctxt.doc = cur;
1905 ctxt.buf = buf;
1906 ctxt.level = 0;
1907 ctxt.format = format;
1908 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001909
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001910 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001911
1912 ret = xmlOutputBufferClose(buf);
1913 return(ret);
1914}
1915
1916
1917/**
1918 * xmlSaveFileEnc:
1919 * @filename: the filename (or URL)
1920 * @cur: the document
1921 * @encoding: the name of an encoding (or NULL)
1922 *
1923 * Dump an XML document, converting it to the given encoding
1924 *
1925 * returns: the number of bytes written or -1 in case of failure.
1926 */
1927int
1928xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
1929 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
1930}
1931
1932/**
1933 * xmlSaveFormatFile:
1934 * @filename: the filename (or URL)
1935 * @cur: the document
1936 * @format: should formatting spaces been added
1937 *
1938 * Dump an XML document to a file. Will use compression if
1939 * compiled in and enabled. If @filename is "-" the stdout file is
1940 * used. If @format is set then the document will be indented on output.
1941 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1942 * or xmlKeepBlanksDefault(0) was called
1943 *
1944 * returns: the number of bytes written or -1 in case of failure.
1945 */
1946int
1947xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
1948 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
1949}
1950
1951/**
1952 * xmlSaveFile:
1953 * @filename: the filename (or URL)
1954 * @cur: the document
1955 *
1956 * Dump an XML document to a file. Will use compression if
1957 * compiled in and enabled. If @filename is "-" the stdout file is
1958 * used.
1959 * returns: the number of bytes written or -1 in case of failure.
1960 */
1961int
1962xmlSaveFile(const char *filename, xmlDocPtr cur) {
1963 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
1964}
1965
1966#endif /* LIBXML_OUTPUT_ENABLED */
1967