blob: 6e3141640f8aa14a0f67167974d2a909e0127b76 [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
Daniel Veillard753086a2004-03-28 16:12:44 +000020#define MAX_INDENT 60
21
Daniel Veillard1a8741c2004-03-04 13:40:59 +000022/************************************************************************
23 * *
24 * XHTML detection *
25 * *
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"
39
40#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
41/**
42 * xmlIsXHTML:
43 * @systemID: the system identifier
44 * @publicID: the public identifier
45 *
46 * Try to find if the document correspond to an XHTML DTD
47 *
48 * Returns 1 if true, 0 if not and -1 in case of error
49 */
50int
51xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
52 if ((systemID == NULL) && (publicID == NULL))
53 return(-1);
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);
58 }
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);
63 }
64 return(0);
65}
66#endif /* LIBXML_HTML_ENABLED */
67
68
69#ifdef LIBXML_OUTPUT_ENABLED
70
71#define TODO \
72 xmlGenericError(xmlGenericErrorContext, \
73 "Unimplemented block at %s:%d\n", \
74 __FILE__, __LINE__);
75
76struct _xmlSaveCtxt {
77 void *_private;
78 int type;
79 int fd;
80 const xmlChar *filename;
81 const xmlChar *encoding;
82 xmlCharEncodingHandlerPtr handler;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000083 xmlOutputBufferPtr buf;
84 xmlDocPtr doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000085 int options;
86 int level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000087 int format;
Daniel Veillard753086a2004-03-28 16:12:44 +000088 char indent[MAX_INDENT + 1];
89 int indent_nr;
90 int indent_size;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000091};
92
93/************************************************************************
94 * *
95 * Output error handlers *
96 * *
97 ************************************************************************/
98/**
99 * xmlSaveErrMemory:
100 * @extra: extra informations
101 *
102 * Handle an out of memory condition
103 */
104static void
105xmlSaveErrMemory(const char *extra)
106{
107 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
108}
109
110/**
111 * xmlSaveErr:
112 * @code: the error number
113 * @node: the location of the error.
114 * @extra: extra informations
115 *
116 * Handle an out of memory condition
117 */
118static void
119xmlSaveErr(int code, xmlNodePtr node, const char *extra)
120{
121 const char *msg = NULL;
122
123 switch(code) {
124 case XML_SAVE_NOT_UTF8:
125 msg = "string is not in UTF-8";
126 break;
127 case XML_SAVE_CHAR_INVALID:
128 msg = "invalid character value";
129 break;
130 case XML_SAVE_UNKNOWN_ENCODING:
131 msg = "unknown encoding %s";
132 break;
133 case XML_SAVE_NO_DOCTYPE:
134 msg = "document has no DOCTYPE";
135 break;
136 default:
137 msg = "unexpected error number";
138 }
139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
140}
141
142/************************************************************************
143 * *
144 * Allocation and deallocation *
145 * *
146 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000147/**
148 * xmlSaveCtxtInit:
149 * @ctxt: the saving context
150 *
151 * Initialize a saving context
152 */
153static void
154xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
155{
156 int i;
157
158 if (ctxt == NULL) return;
159 if (xmlTreeIndentString == NULL) {
160 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
161 } else {
162 ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
163 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
164 for (i = 0;i < ctxt->indent_nr;i++)
165 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
166 ctxt->indent_size);
167 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
168 }
169}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000170
171/**
172 * xmlFreeSaveCtxt:
173 *
174 * Free a saving context, destroying the ouptut in any remaining buffer
175 */
176static void
177xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
178{
179 if (ctxt == NULL) return;
180 if (ctxt->encoding != NULL)
181 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000182 if (ctxt->buf != NULL)
183 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000184 xmlFree(ctxt);
185}
186
187/**
188 * xmlNewSaveCtxt:
189 *
190 * Create a new saving context
191 *
192 * Returns the new structure or NULL in case of error
193 */
194static xmlSaveCtxtPtr
195xmlNewSaveCtxt(const char *encoding, int options)
196{
197 xmlSaveCtxtPtr ret;
198
199 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
200 if (ret == NULL) {
201 xmlSaveErrMemory("creating saving context");
202 return ( NULL );
203 }
204 memset(ret, 0, sizeof(xmlSaveCtxt));
205 ret->options = options;
206 if (encoding != NULL) {
207 ret->handler = xmlFindCharEncodingHandler(encoding);
208 if (ret->handler == NULL) {
209 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
210 xmlFreeSaveCtxt(ret);
211 return(NULL);
212 }
213 ret->encoding = xmlStrdup((const xmlChar *)encoding);
214 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000215 xmlSaveCtxtInit(ret);
216
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000217 return(ret);
218}
219
220/************************************************************************
221 * *
222 * Dumping XML tree content to a simple buffer *
223 * *
224 ************************************************************************/
225/**
226 * xmlAttrSerializeContent:
227 * @buf: the XML buffer output
228 * @doc: the document
229 * @attr: the attribute pointer
230 *
231 * Serialize the attribute in the buffer
232 */
233static void
234xmlAttrSerializeContent(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr attr)
235{
236 xmlNodePtr children;
237
238 children = attr->children;
239 while (children != NULL) {
240 switch (children->type) {
241 case XML_TEXT_NODE:
242 xmlAttrSerializeTxtContent(buf, doc, attr, children->content);
243 break;
244 case XML_ENTITY_REF_NODE:
245 xmlBufferAdd(buf, BAD_CAST "&", 1);
246 xmlBufferAdd(buf, children->name,
247 xmlStrlen(children->name));
248 xmlBufferAdd(buf, BAD_CAST ";", 1);
249 break;
250 default:
251 /* should not happen unless we have a badly built tree */
252 break;
253 }
254 children = children->next;
255 }
256}
257
258/************************************************************************
259 * *
260 * Dumping XML tree content to an I/O output buffer *
261 * *
262 ************************************************************************/
263
264#ifdef LIBXML_HTML_ENABLED
265static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000266xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000267#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000268static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
269static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000270void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
271
272/**
273 * xmlNsDumpOutput:
274 * @buf: the XML buffer output
275 * @cur: a namespace
276 *
277 * Dump a local Namespace definition.
278 * Should be called in the context of attributes dumps.
279 */
280static void
281xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000282 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000283 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
284 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
285 return;
286
287 /* Within the context of an element attributes */
288 if (cur->prefix != NULL) {
289 xmlOutputBufferWriteString(buf, " xmlns:");
290 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
291 } else
292 xmlOutputBufferWriteString(buf, " xmlns");
293 xmlOutputBufferWriteString(buf, "=");
294 xmlBufferWriteQuotedString(buf->buffer, cur->href);
295 }
296}
297
298/**
299 * xmlNsListDumpOutput:
300 * @buf: the XML buffer output
301 * @cur: the first namespace
302 *
303 * Dump a list of local Namespace definitions.
304 * Should be called in the context of attributes dumps.
305 */
306void
307xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
308 while (cur != NULL) {
309 xmlNsDumpOutput(buf, cur);
310 cur = cur->next;
311 }
312}
313
314/**
315 * xmlDtdDumpOutput:
316 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000317 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000318 *
319 * Dump the XML document DTD, if any.
320 */
321static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000322xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
323 xmlOutputBufferPtr buf;
324 int format, level;
325 xmlDocPtr doc;
326
327 if (dtd == NULL) return;
328 if ((ctxt == NULL) || (ctxt->buf == NULL))
329 return;
330 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000331 xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
332 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
333 if (dtd->ExternalID != NULL) {
334 xmlOutputBufferWriteString(buf, " PUBLIC ");
335 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
336 xmlOutputBufferWriteString(buf, " ");
337 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
338 } else if (dtd->SystemID != NULL) {
339 xmlOutputBufferWriteString(buf, " SYSTEM ");
340 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
341 }
342 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
343 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
344 (dtd->pentities == NULL)) {
345 xmlOutputBufferWriteString(buf, ">");
346 return;
347 }
348 xmlOutputBufferWriteString(buf, " [\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000349 format = ctxt->format;
350 level = ctxt->level;
351 doc = ctxt->doc;
352 ctxt->format = 0;
353 ctxt->level = -1;
354 ctxt->doc = dtd->doc;
355 xmlNodeListDumpOutput(ctxt, dtd->children);
356 ctxt->format = format;
357 ctxt->level = level;
358 ctxt->doc = doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000359 xmlOutputBufferWriteString(buf, "]>");
360}
361
362/**
363 * xmlAttrDumpOutput:
364 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000365 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000366 *
367 * Dump an XML attribute
368 */
369static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000370xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
371 xmlOutputBufferPtr buf;
372 if (cur == NULL) return;
373 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000374 xmlOutputBufferWriteString(buf, " ");
375 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
376 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
377 xmlOutputBufferWriteString(buf, ":");
378 }
379 xmlOutputBufferWriteString(buf, (const char *)cur->name);
380 xmlOutputBufferWriteString(buf, "=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000381 xmlAttrSerializeContent(buf->buffer, ctxt->doc, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000382 xmlOutputBufferWriteString(buf, "\"");
383}
384
385/**
386 * xmlAttrListDumpOutput:
387 * @buf: the XML buffer output
388 * @doc: the document
389 * @cur: the first attribute pointer
390 * @encoding: an optional encoding string
391 *
392 * Dump a list of XML attributes
393 */
394static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000395xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
396 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000397 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000398 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000399 cur = cur->next;
400 }
401}
402
403
404
405/**
406 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000407 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000408 *
409 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000410 */
411static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000412xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000413 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000414
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000415 if (cur == NULL) return;
416 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000417 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000418 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000419 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000420 xmlOutputBufferWrite(buf, ctxt->indent_size *
421 (ctxt->level > ctxt->indent_nr ?
422 ctxt->indent_nr : ctxt->level),
423 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000424 xmlNodeDumpOutputInternal(ctxt, cur);
425 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000426 xmlOutputBufferWriteString(buf, "\n");
427 }
428 cur = cur->next;
429 }
430}
431
432/**
433 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000434 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000435 *
436 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000437 */
438static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000439xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000440 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000441 xmlNodePtr tmp;
442 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000443 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000444
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000445 if (cur == NULL) return;
446 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000447 if (cur->type == XML_XINCLUDE_START)
448 return;
449 if (cur->type == XML_XINCLUDE_END)
450 return;
451 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000452 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000453 return;
454 }
455 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000456 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000457 return;
458 }
459 if (cur->type == XML_ELEMENT_DECL) {
460 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
461 return;
462 }
463 if (cur->type == XML_ATTRIBUTE_DECL) {
464 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
465 return;
466 }
467 if (cur->type == XML_ENTITY_DECL) {
468 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
469 return;
470 }
471 if (cur->type == XML_TEXT_NODE) {
472 if (cur->content != NULL) {
473 if ((cur->name == xmlStringText) ||
474 (cur->name != xmlStringTextNoenc)) {
475 xmlChar *buffer;
476
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000477 if (ctxt->encoding == NULL)
478 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
479 cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000480 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000481 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000482 if (buffer != NULL) {
483 xmlOutputBufferWriteString(buf, (const char *)buffer);
484 xmlFree(buffer);
485 }
486 } else {
487 /*
488 * Disable escaping, needed for XSLT
489 */
490 xmlOutputBufferWriteString(buf, (const char *) cur->content);
491 }
492 }
493
494 return;
495 }
496 if (cur->type == XML_PI_NODE) {
497 if (cur->content != NULL) {
498 xmlOutputBufferWriteString(buf, "<?");
499 xmlOutputBufferWriteString(buf, (const char *)cur->name);
500 if (cur->content != NULL) {
501 xmlOutputBufferWriteString(buf, " ");
502 xmlOutputBufferWriteString(buf, (const char *)cur->content);
503 }
504 xmlOutputBufferWriteString(buf, "?>");
505 } else {
506 xmlOutputBufferWriteString(buf, "<?");
507 xmlOutputBufferWriteString(buf, (const char *)cur->name);
508 xmlOutputBufferWriteString(buf, "?>");
509 }
510 return;
511 }
512 if (cur->type == XML_COMMENT_NODE) {
513 if (cur->content != NULL) {
514 xmlOutputBufferWriteString(buf, "<!--");
515 xmlOutputBufferWriteString(buf, (const char *)cur->content);
516 xmlOutputBufferWriteString(buf, "-->");
517 }
518 return;
519 }
520 if (cur->type == XML_ENTITY_REF_NODE) {
521 xmlOutputBufferWriteString(buf, "&");
522 xmlOutputBufferWriteString(buf, (const char *)cur->name);
523 xmlOutputBufferWriteString(buf, ";");
524 return;
525 }
526 if (cur->type == XML_CDATA_SECTION_NODE) {
527 start = end = cur->content;
528 while (*end != '\0') {
529 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
530 end = end + 2;
531 xmlOutputBufferWriteString(buf, "<![CDATA[");
532 xmlOutputBufferWrite(buf, end - start, (const char *)start);
533 xmlOutputBufferWriteString(buf, "]]>");
534 start = end;
535 }
536 end++;
537 }
538 if (start != end) {
539 xmlOutputBufferWriteString(buf, "<![CDATA[");
540 xmlOutputBufferWriteString(buf, (const char *)start);
541 xmlOutputBufferWriteString(buf, "]]>");
542 }
543 return;
544 }
545 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000546 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000547 return;
548 }
549 if (cur->type == XML_NAMESPACE_DECL) {
550 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
551 return;
552 }
553
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000554 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000555 if (format == 1) {
556 tmp = cur->children;
557 while (tmp != NULL) {
558 if ((tmp->type == XML_TEXT_NODE) ||
559 (tmp->type == XML_CDATA_SECTION_NODE) ||
560 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000561 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000562 break;
563 }
564 tmp = tmp->next;
565 }
566 }
567 xmlOutputBufferWriteString(buf, "<");
568 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
569 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
570 xmlOutputBufferWriteString(buf, ":");
571 }
572
573 xmlOutputBufferWriteString(buf, (const char *)cur->name);
574 if (cur->nsDef)
575 xmlNsListDumpOutput(buf, cur->nsDef);
576 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000577 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000578
579 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
580 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
581 xmlOutputBufferWriteString(buf, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000582 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000583 return;
584 }
585 xmlOutputBufferWriteString(buf, ">");
586 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
587 xmlChar *buffer;
588
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000589 if (ctxt->encoding == NULL)
590 buffer = xmlEncodeEntitiesReentrant(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000591 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000592 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000593 if (buffer != NULL) {
594 xmlOutputBufferWriteString(buf, (const char *)buffer);
595 xmlFree(buffer);
596 }
597 }
598 if (cur->children != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000599 if (ctxt->format) xmlOutputBufferWriteString(buf, "\n");
600 if (ctxt->level >= 0) ctxt->level++;
601 xmlNodeListDumpOutput(ctxt, cur->children);
602 if (ctxt->level > 0) ctxt->level--;
603 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000604 xmlOutputBufferWrite(buf, ctxt->indent_size *
605 (ctxt->level > ctxt->indent_nr ?
606 ctxt->indent_nr : ctxt->level),
607 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000608 }
609 xmlOutputBufferWriteString(buf, "</");
610 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
611 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
612 xmlOutputBufferWriteString(buf, ":");
613 }
614
615 xmlOutputBufferWriteString(buf, (const char *)cur->name);
616 xmlOutputBufferWriteString(buf, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000617 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000618}
619
620/**
621 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000622 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000623 *
624 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000625 */
626static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000627xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000628#ifdef LIBXML_HTML_ENABLED
629 xmlDtdPtr dtd;
630 int is_xhtml = 0;
631#endif
632 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000633 const xmlChar *encoding = ctxt->encoding;
634 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000635
636 xmlInitParser();
637
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000638 if (ctxt->encoding != NULL)
639 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000640
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000641 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000642 xmlOutputBufferWriteString(buf, "<?xml version=");
643 if (cur->version != NULL)
644 xmlBufferWriteQuotedString(buf->buffer, cur->version);
645 else
646 xmlOutputBufferWriteString(buf, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000647 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000648 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000649 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000650 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000651 encoding = (const xmlChar *)
652 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000653 }
654 if (encoding != NULL) {
655 xmlOutputBufferWriteString(buf, " encoding=");
656 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
657 }
658 switch (cur->standalone) {
659 case 0:
660 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
661 break;
662 case 1:
663 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
664 break;
665 }
666 xmlOutputBufferWriteString(buf, "?>\n");
667
668#ifdef LIBXML_HTML_ENABLED
669 dtd = xmlGetIntSubset(cur);
670 if (dtd != NULL) {
671 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
672 if (is_xhtml < 0) is_xhtml = 0;
673 }
674 if (is_xhtml) {
675 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000676 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000677 else
678 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
679 }
680#endif
681 if (cur->children != NULL) {
682 xmlNodePtr child = cur->children;
683
684 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000685 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000686#ifdef LIBXML_HTML_ENABLED
687 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000688 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000689 else
690#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000691 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000692 xmlOutputBufferWriteString(buf, "\n");
693 child = child->next;
694 }
695 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000696 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000697 cur->encoding = oldenc;
698}
699
700#ifdef LIBXML_HTML_ENABLED
701/************************************************************************
702 * *
703 * Functions specific to XHTML serialization *
704 * *
705 ************************************************************************/
706
707/**
708 * xhtmlIsEmpty:
709 * @node: the node
710 *
711 * Check if a node is an empty xhtml node
712 *
713 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
714 */
715static int
716xhtmlIsEmpty(xmlNodePtr node) {
717 if (node == NULL)
718 return(-1);
719 if (node->type != XML_ELEMENT_NODE)
720 return(0);
721 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
722 return(0);
723 if (node->children != NULL)
724 return(0);
725 switch (node->name[0]) {
726 case 'a':
727 if (xmlStrEqual(node->name, BAD_CAST "area"))
728 return(1);
729 return(0);
730 case 'b':
731 if (xmlStrEqual(node->name, BAD_CAST "br"))
732 return(1);
733 if (xmlStrEqual(node->name, BAD_CAST "base"))
734 return(1);
735 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
736 return(1);
737 return(0);
738 case 'c':
739 if (xmlStrEqual(node->name, BAD_CAST "col"))
740 return(1);
741 return(0);
742 case 'f':
743 if (xmlStrEqual(node->name, BAD_CAST "frame"))
744 return(1);
745 return(0);
746 case 'h':
747 if (xmlStrEqual(node->name, BAD_CAST "hr"))
748 return(1);
749 return(0);
750 case 'i':
751 if (xmlStrEqual(node->name, BAD_CAST "img"))
752 return(1);
753 if (xmlStrEqual(node->name, BAD_CAST "input"))
754 return(1);
755 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
756 return(1);
757 return(0);
758 case 'l':
759 if (xmlStrEqual(node->name, BAD_CAST "link"))
760 return(1);
761 return(0);
762 case 'm':
763 if (xmlStrEqual(node->name, BAD_CAST "meta"))
764 return(1);
765 return(0);
766 case 'p':
767 if (xmlStrEqual(node->name, BAD_CAST "param"))
768 return(1);
769 return(0);
770 }
771 return(0);
772}
773
774/**
775 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000776 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000777 *
778 * Dump a list of XML attributes
779 */
780static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000781xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000782 xmlAttrPtr xml_lang = NULL;
783 xmlAttrPtr lang = NULL;
784 xmlAttrPtr name = NULL;
785 xmlAttrPtr id = NULL;
786 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000787 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000788
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000789 if (cur == NULL) return;
790 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000791 parent = cur->parent;
792 while (cur != NULL) {
793 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
794 id = cur;
795 else
796 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
797 name = cur;
798 else
799 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
800 lang = cur;
801 else
802 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
803 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
804 xml_lang = cur;
805 else if ((cur->ns == NULL) &&
806 ((cur->children == NULL) ||
807 (cur->children->content == NULL) ||
808 (cur->children->content[0] == 0)) &&
809 (htmlIsBooleanAttr(cur->name))) {
810 if (cur->children != NULL)
811 xmlFreeNode(cur->children);
812 cur->children = xmlNewText(cur->name);
813 if (cur->children != NULL)
814 cur->children->parent = (xmlNodePtr) cur;
815 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000816 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000817 cur = cur->next;
818 }
819 /*
820 * C.8
821 */
822 if ((name != NULL) && (id == NULL)) {
823 if ((parent != NULL) && (parent->name != NULL) &&
824 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
825 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
826 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
827 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
828 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
829 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
830 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
831 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
832 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
833 xmlOutputBufferWriteString(buf, " id=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000834 xmlAttrSerializeContent(buf->buffer, ctxt->doc, name);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000835 xmlOutputBufferWriteString(buf, "\"");
836 }
837 }
838 /*
839 * C.7.
840 */
841 if ((lang != NULL) && (xml_lang == NULL)) {
842 xmlOutputBufferWriteString(buf, " xml:lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000843 xmlAttrSerializeContent(buf->buffer, ctxt->doc, lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000844 xmlOutputBufferWriteString(buf, "\"");
845 } else
846 if ((xml_lang != NULL) && (lang == NULL)) {
847 xmlOutputBufferWriteString(buf, " lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000848 xmlAttrSerializeContent(buf->buffer, ctxt->doc, xml_lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000849 xmlOutputBufferWriteString(buf, "\"");
850 }
851}
852
853/**
854 * xhtmlNodeListDumpOutput:
855 * @buf: the XML buffer output
856 * @doc: the XHTML document
857 * @cur: the first node
858 * @level: the imbrication level for indenting
859 * @format: is formatting allowed
860 * @encoding: an optional encoding string
861 *
862 * Dump an XML node list, recursive behaviour, children are printed too.
863 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
864 * or xmlKeepBlanksDefault(0) was called
865 */
866static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000867xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000868 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000869
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000870 if (cur == NULL) return;
871 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000872 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000873 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000874 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000875 xmlOutputBufferWrite(buf, ctxt->indent_size *
876 (ctxt->level > ctxt->indent_nr ?
877 ctxt->indent_nr : ctxt->level),
878 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000879 xhtmlNodeDumpOutput(ctxt, cur);
880 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000881 xmlOutputBufferWriteString(buf, "\n");
882 }
883 cur = cur->next;
884 }
885}
886
887/**
888 * xhtmlNodeDumpOutput:
889 * @buf: the XML buffer output
890 * @doc: the XHTML document
891 * @cur: the current node
892 * @level: the imbrication level for indenting
893 * @format: is formatting allowed
894 * @encoding: an optional encoding string
895 *
896 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000897 */
898static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000899xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000900 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000901 xmlNodePtr tmp;
902 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000903 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000904
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000905 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000906 if (cur->type == XML_XINCLUDE_START)
907 return;
908 if (cur->type == XML_XINCLUDE_END)
909 return;
910 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000911 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000912 return;
913 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000914 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000915 if (cur->type == XML_ELEMENT_DECL) {
916 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
917 return;
918 }
919 if (cur->type == XML_ATTRIBUTE_DECL) {
920 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
921 return;
922 }
923 if (cur->type == XML_ENTITY_DECL) {
924 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
925 return;
926 }
927 if (cur->type == XML_TEXT_NODE) {
928 if (cur->content != NULL) {
929 if ((cur->name == xmlStringText) ||
930 (cur->name != xmlStringTextNoenc)) {
931 xmlChar *buffer;
932
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000933 if (ctxt->encoding == NULL)
934 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
935 cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000936 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000937 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000938 if (buffer != NULL) {
939 xmlOutputBufferWriteString(buf, (const char *)buffer);
940 xmlFree(buffer);
941 }
942 } else {
943 /*
944 * Disable escaping, needed for XSLT
945 */
946 xmlOutputBufferWriteString(buf, (const char *) cur->content);
947 }
948 }
949
950 return;
951 }
952 if (cur->type == XML_PI_NODE) {
953 if (cur->content != NULL) {
954 xmlOutputBufferWriteString(buf, "<?");
955 xmlOutputBufferWriteString(buf, (const char *)cur->name);
956 if (cur->content != NULL) {
957 xmlOutputBufferWriteString(buf, " ");
958 xmlOutputBufferWriteString(buf, (const char *)cur->content);
959 }
960 xmlOutputBufferWriteString(buf, "?>");
961 } else {
962 xmlOutputBufferWriteString(buf, "<?");
963 xmlOutputBufferWriteString(buf, (const char *)cur->name);
964 xmlOutputBufferWriteString(buf, "?>");
965 }
966 return;
967 }
968 if (cur->type == XML_COMMENT_NODE) {
969 if (cur->content != NULL) {
970 xmlOutputBufferWriteString(buf, "<!--");
971 xmlOutputBufferWriteString(buf, (const char *)cur->content);
972 xmlOutputBufferWriteString(buf, "-->");
973 }
974 return;
975 }
976 if (cur->type == XML_ENTITY_REF_NODE) {
977 xmlOutputBufferWriteString(buf, "&");
978 xmlOutputBufferWriteString(buf, (const char *)cur->name);
979 xmlOutputBufferWriteString(buf, ";");
980 return;
981 }
982 if (cur->type == XML_CDATA_SECTION_NODE) {
983 start = end = cur->content;
984 while (*end != '\0') {
985 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
986 end = end + 2;
987 xmlOutputBufferWriteString(buf, "<![CDATA[");
988 xmlOutputBufferWrite(buf, end - start, (const char *)start);
989 xmlOutputBufferWriteString(buf, "]]>");
990 start = end;
991 }
992 end++;
993 }
994 if (start != end) {
995 xmlOutputBufferWriteString(buf, "<![CDATA[");
996 xmlOutputBufferWriteString(buf, (const char *)start);
997 xmlOutputBufferWriteString(buf, "]]>");
998 }
999 return;
1000 }
1001
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001002 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001003 if (format == 1) {
1004 tmp = cur->children;
1005 while (tmp != NULL) {
1006 if ((tmp->type == XML_TEXT_NODE) ||
1007 (tmp->type == XML_ENTITY_REF_NODE)) {
1008 format = 0;
1009 break;
1010 }
1011 tmp = tmp->next;
1012 }
1013 }
1014 xmlOutputBufferWriteString(buf, "<");
1015 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1016 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1017 xmlOutputBufferWriteString(buf, ":");
1018 }
1019
1020 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1021 if (cur->nsDef)
1022 xmlNsListDumpOutput(buf, cur->nsDef);
1023 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1024 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1025 /*
1026 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1027 */
1028 xmlOutputBufferWriteString(buf,
1029 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1030 }
1031 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001032 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001033
1034 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1035 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1036 (xhtmlIsEmpty(cur) == 1)) {
1037 /*
1038 * C.2. Empty Elements
1039 */
1040 xmlOutputBufferWriteString(buf, " />");
1041 } else {
1042 /*
1043 * C.3. Element Minimization and Empty Element Content
1044 */
1045 xmlOutputBufferWriteString(buf, "></");
1046 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1047 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1048 xmlOutputBufferWriteString(buf, ":");
1049 }
1050 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1051 xmlOutputBufferWriteString(buf, ">");
1052 }
1053 return;
1054 }
1055 xmlOutputBufferWriteString(buf, ">");
1056 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1057 xmlChar *buffer;
1058
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001059 if (ctxt->encoding == NULL)
1060 buffer = xmlEncodeEntitiesReentrant(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001061 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001062 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001063 if (buffer != NULL) {
1064 xmlOutputBufferWriteString(buf, (const char *)buffer);
1065 xmlFree(buffer);
1066 }
1067 }
1068
1069 /*
1070 * 4.8. Script and Style elements
1071 */
1072 if ((cur->type == XML_ELEMENT_NODE) &&
1073 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1074 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1075 ((cur->ns == NULL) ||
1076 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1077 xmlNodePtr child = cur->children;
1078
1079 while (child != NULL) {
1080 if ((child->type == XML_TEXT_NODE) ||
1081 (child->type == XML_CDATA_SECTION_NODE)) {
1082 /*
1083 * Apparently CDATA escaping for style just break on IE,
1084 * mozilla and galeon, so ...
1085 */
1086 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1087 (xmlStrchr(child->content, '<') == NULL) &&
1088 (xmlStrchr(child->content, '>') == NULL) &&
1089 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001090 int level = ctxt->level;
1091 int indent = ctxt->format;
1092
1093 ctxt->level = 0;
1094 ctxt->format = 0;
1095 xhtmlNodeDumpOutput(ctxt, child);
1096 ctxt->level = level;
1097 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001098 } else {
1099 start = end = child->content;
1100 while (*end != '\0') {
1101 if (*end == ']' &&
1102 *(end + 1) == ']' &&
1103 *(end + 2) == '>') {
1104 end = end + 2;
1105 xmlOutputBufferWriteString(buf, "<![CDATA[");
1106 xmlOutputBufferWrite(buf, end - start,
1107 (const char *)start);
1108 xmlOutputBufferWriteString(buf, "]]>");
1109 start = end;
1110 }
1111 end++;
1112 }
1113 if (start != end) {
1114 xmlOutputBufferWriteString(buf, "<![CDATA[");
1115 xmlOutputBufferWriteString(buf, (const char *)start);
1116 xmlOutputBufferWriteString(buf, "]]>");
1117 }
1118 }
1119 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001120 int level = ctxt->level;
1121 int indent = ctxt->format;
1122
1123 ctxt->level = 0;
1124 ctxt->format = 0;
1125 xhtmlNodeDumpOutput(ctxt, child);
1126 ctxt->level = level;
1127 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001128 }
1129 child = child->next;
1130 }
1131 } else if (cur->children != NULL) {
1132 if (format) xmlOutputBufferWriteString(buf, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001133 if (ctxt->level >= 0) ctxt->level++;
1134 xhtmlNodeListDumpOutput(ctxt, cur->children);
1135 if (ctxt->level > 0) ctxt->level--;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001136 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001137 xmlOutputBufferWrite(buf, ctxt->indent_size *
1138 (ctxt->level > ctxt->indent_nr ?
1139 ctxt->indent_nr : ctxt->level),
1140 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001141 }
1142 xmlOutputBufferWriteString(buf, "</");
1143 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1144 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1145 xmlOutputBufferWriteString(buf, ":");
1146 }
1147
1148 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1149 xmlOutputBufferWriteString(buf, ">");
1150}
1151#endif
1152
1153/************************************************************************
1154 * *
1155 * Public entry points *
1156 * *
1157 ************************************************************************/
1158
1159/**
1160 * xmlSaveToFd:
1161 * @fd: a file descriptor number
1162 * @encoding: the encoding name to use or NULL
1163 * @options: a set of xmlSaveOptions
1164 *
1165 * Create a document saving context serializing to a file descriptor
1166 * with the encoding and the options given.
1167 *
1168 * Returns a new serialization context or NULL in case of error.
1169 */
1170xmlSaveCtxtPtr
1171xmlSaveToFd(int fd, const char *encoding, int options)
1172{
1173 xmlSaveCtxtPtr ret;
1174
1175 ret = xmlNewSaveCtxt(encoding, options);
1176 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001177 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1178 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001179 xmlFreeSaveCtxt(ret);
1180 return(NULL);
1181 }
1182 return(ret);
1183}
1184
1185/**
1186 * xmlSaveToFilename:
1187 * @filename: a file name or an URL
1188 * @encoding: the encoding name to use or NULL
1189 * @options: a set of xmlSaveOptions
1190 *
1191 * Create a document saving context serializing to a filename or possibly
1192 * to an URL (but this is less reliable) with the encoding and the options
1193 * given.
1194 *
1195 * Returns a new serialization context or NULL in case of error.
1196 */
1197xmlSaveCtxtPtr
1198xmlSaveToFilename(const char *filename, const char *encoding, int options)
1199{
1200 xmlSaveCtxtPtr ret;
1201 int compression = 0; /* TODO handle compression option */
1202
1203 ret = xmlNewSaveCtxt(encoding, options);
1204 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001205 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001206 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001207 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001208 xmlFreeSaveCtxt(ret);
1209 return(NULL);
1210 }
1211 return(ret);
1212}
1213
1214#if 0
1215/**
1216 * xmlSaveToBuffer:
1217 * @buffer: a buffer
1218 * @encoding: the encoding name to use or NULL
1219 * @options: a set of xmlSaveOptions
1220 *
1221 * Create a document saving context serializing to a buffer
1222 * with the encoding and the options given
1223 *
1224 * Returns a new serialization context or NULL in case of error.
1225 */
1226xmlSaveCtxtPtr
1227xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1228{
1229 TODO
1230 return(NULL);
1231}
1232#endif
1233
1234/**
1235 * xmlSaveToIO:
1236 * @iowrite: an I/O write function
1237 * @ioclose: an I/O close function
1238 * @ioctx: an I/O handler
1239 * @encoding: the encoding name to use or NULL
1240 * @options: a set of xmlSaveOptions
1241 *
1242 * Create a document saving context serializing to a file descriptor
1243 * with the encoding and the options given
1244 *
1245 * Returns a new serialization context or NULL in case of error.
1246 */
1247xmlSaveCtxtPtr
1248xmlSaveToIO(xmlOutputWriteCallback iowrite,
1249 xmlOutputCloseCallback ioclose,
1250 void *ioctx, const char *encoding, int options)
1251{
1252 xmlSaveCtxtPtr ret;
1253
1254 ret = xmlNewSaveCtxt(encoding, options);
1255 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001256 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1257 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001258 xmlFreeSaveCtxt(ret);
1259 return(NULL);
1260 }
1261 return(ret);
1262}
1263
1264/**
1265 * xmlSaveDoc:
1266 * @ctxt: a document saving context
1267 * @doc: a document
1268 *
1269 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001270 * TODO: The function is not fully implemented yet as it does not return the
1271 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001272 *
1273 * Returns the number of byte written or -1 in case of error
1274 */
1275long
1276xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1277{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001278 long ret = 0;
1279
1280 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001281 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001282}
1283
1284/**
1285 * xmlSaveTree:
1286 * @ctxt: a document saving context
1287 * @node: a document
1288 *
1289 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001290 * TODO: The function is not fully implemented yet as it does not return the
1291 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001292 *
1293 * Returns the number of byte written or -1 in case of error
1294 */
1295long
1296xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1297{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001298 long ret = 0;
1299
1300 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001301 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001302}
1303
1304/**
1305 * xmlSaveFlush:
1306 * @ctxt: a document saving context
1307 *
1308 * Flush a document saving context, i.e. make sure that all bytes have
1309 * been output.
1310 *
1311 * Returns the number of byte written or -1 in case of error.
1312 */
1313int
1314xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1315{
1316 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001317 if (ctxt->buf == NULL) return(-1);
1318 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001319}
1320
1321/**
1322 * xmlSaveClose:
1323 * @ctxt: a document saving context
1324 *
1325 * Close a document saving context, i.e. make sure that all bytes have
1326 * been output and free the associated data.
1327 *
1328 * Returns the number of byte written or -1 in case of error.
1329 */
1330int
1331xmlSaveClose(xmlSaveCtxtPtr ctxt)
1332{
1333 int ret;
1334
1335 if (ctxt == NULL) return(-1);
1336 ret = xmlSaveFlush(ctxt);
1337 xmlFreeSaveCtxt(ctxt);
1338 return(ret);
1339}
1340
1341/************************************************************************
1342 * *
1343 * Public entry points based on buffers *
1344 * *
1345 ************************************************************************/
1346/**
1347 * xmlAttrSerializeTxtContent:
1348 * @buf: the XML buffer output
1349 * @doc: the document
1350 * @attr: the attribute node
1351 * @string: the text content
1352 *
1353 * Serialize text attribute values to an xml simple buffer
1354 */
1355void
1356xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1357 xmlAttrPtr attr, const xmlChar *string) {
1358 xmlChar *base, *cur;
1359
1360 if (string == NULL) return;
1361 base = cur = (xmlChar *)string;
1362 while (*cur != 0) {
1363 if (*cur == '\n') {
1364 if (base != cur)
1365 xmlBufferAdd(buf, base, cur - base);
1366 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1367 cur++;
1368 base = cur;
1369 } else if (*cur == '\r') {
1370 if (base != cur)
1371 xmlBufferAdd(buf, base, cur - base);
1372 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1373 cur++;
1374 base = cur;
1375 } else if (*cur == '\t') {
1376 if (base != cur)
1377 xmlBufferAdd(buf, base, cur - base);
1378 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1379 cur++;
1380 base = cur;
1381 } else if (*cur == '"') {
1382 if (base != cur)
1383 xmlBufferAdd(buf, base, cur - base);
1384 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1385 cur++;
1386 base = cur;
1387 } else if (*cur == '<') {
1388 if (base != cur)
1389 xmlBufferAdd(buf, base, cur - base);
1390 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1391 cur++;
1392 base = cur;
1393 } else if (*cur == '>') {
1394 if (base != cur)
1395 xmlBufferAdd(buf, base, cur - base);
1396 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1397 cur++;
1398 base = cur;
1399 } else if (*cur == '&') {
1400 if (base != cur)
1401 xmlBufferAdd(buf, base, cur - base);
1402 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1403 cur++;
1404 base = cur;
1405 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1406 (doc->encoding == NULL))) {
1407 /*
1408 * We assume we have UTF-8 content.
1409 */
1410 char tmp[10];
1411 int val = 0, l = 1;
1412
1413 if (base != cur)
1414 xmlBufferAdd(buf, base, cur - base);
1415 if (*cur < 0xC0) {
1416 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1417 if (doc != NULL)
1418 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1419 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1420 tmp[sizeof(tmp) - 1] = 0;
1421 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1422 cur++;
1423 base = cur;
1424 continue;
1425 } else if (*cur < 0xE0) {
1426 val = (cur[0]) & 0x1F;
1427 val <<= 6;
1428 val |= (cur[1]) & 0x3F;
1429 l = 2;
1430 } else if (*cur < 0xF0) {
1431 val = (cur[0]) & 0x0F;
1432 val <<= 6;
1433 val |= (cur[1]) & 0x3F;
1434 val <<= 6;
1435 val |= (cur[2]) & 0x3F;
1436 l = 3;
1437 } else if (*cur < 0xF8) {
1438 val = (cur[0]) & 0x07;
1439 val <<= 6;
1440 val |= (cur[1]) & 0x3F;
1441 val <<= 6;
1442 val |= (cur[2]) & 0x3F;
1443 val <<= 6;
1444 val |= (cur[3]) & 0x3F;
1445 l = 4;
1446 }
1447 if ((l == 1) || (!IS_CHAR(val))) {
1448 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1449 if (doc != NULL)
1450 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1451 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1452 tmp[sizeof(tmp) - 1] = 0;
1453 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1454 cur++;
1455 base = cur;
1456 continue;
1457 }
1458 /*
1459 * We could do multiple things here. Just save
1460 * as a char ref
1461 */
1462 snprintf(tmp, sizeof(tmp), "&#x%X;", val);
1463 tmp[sizeof(tmp) - 1] = 0;
1464 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1465 cur += l;
1466 base = cur;
1467 } else {
1468 cur++;
1469 }
1470 }
1471 if (base != cur)
1472 xmlBufferAdd(buf, base, cur - base);
1473}
1474
1475/**
1476 * xmlNodeDump:
1477 * @buf: the XML buffer output
1478 * @doc: the document
1479 * @cur: the current node
1480 * @level: the imbrication level for indenting
1481 * @format: is formatting allowed
1482 *
1483 * Dump an XML node, recursive behaviour,children are printed too.
1484 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1485 * or xmlKeepBlanksDefault(0) was called
1486 *
1487 * Returns the number of bytes written to the buffer or -1 in case of error
1488 */
1489int
1490xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1491 int format)
1492{
1493 unsigned int use;
1494 int ret;
1495 xmlOutputBufferPtr outbuf;
1496
1497 xmlInitParser();
1498
1499 if (cur == NULL) {
1500#ifdef DEBUG_TREE
1501 xmlGenericError(xmlGenericErrorContext,
1502 "xmlNodeDump : node == NULL\n");
1503#endif
1504 return (-1);
1505 }
1506 if (buf == NULL) {
1507#ifdef DEBUG_TREE
1508 xmlGenericError(xmlGenericErrorContext,
1509 "xmlNodeDump : buf == NULL\n");
1510#endif
1511 return (-1);
1512 }
1513 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1514 if (outbuf == NULL) {
1515 xmlSaveErrMemory("creating buffer");
1516 return (-1);
1517 }
1518 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1519 outbuf->buffer = buf;
1520 outbuf->encoder = NULL;
1521 outbuf->writecallback = NULL;
1522 outbuf->closecallback = NULL;
1523 outbuf->context = NULL;
1524 outbuf->written = 0;
1525
1526 use = buf->use;
1527 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1528 xmlFree(outbuf);
1529 ret = buf->use - use;
1530 return (ret);
1531}
1532
1533/**
1534 * xmlElemDump:
1535 * @f: the FILE * for the output
1536 * @doc: the document
1537 * @cur: the current node
1538 *
1539 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1540 */
1541void
1542xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1543{
1544 xmlOutputBufferPtr outbuf;
1545
1546 xmlInitParser();
1547
1548 if (cur == NULL) {
1549#ifdef DEBUG_TREE
1550 xmlGenericError(xmlGenericErrorContext,
1551 "xmlElemDump : cur == NULL\n");
1552#endif
1553 return;
1554 }
1555#ifdef DEBUG_TREE
1556 if (doc == NULL) {
1557 xmlGenericError(xmlGenericErrorContext,
1558 "xmlElemDump : doc == NULL\n");
1559 }
1560#endif
1561
1562 outbuf = xmlOutputBufferCreateFile(f, NULL);
1563 if (outbuf == NULL)
1564 return;
1565 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1566#ifdef LIBXML_HTML_ENABLED
1567 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1568#else
1569 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1570#endif /* LIBXML_HTML_ENABLED */
1571 } else
1572 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1573 xmlOutputBufferClose(outbuf);
1574}
1575
1576/************************************************************************
1577 * *
1578 * Saving functions front-ends *
1579 * *
1580 ************************************************************************/
1581
1582/**
1583 * xmlNodeDumpOutput:
1584 * @buf: the XML buffer output
1585 * @doc: the document
1586 * @cur: the current node
1587 * @level: the imbrication level for indenting
1588 * @format: is formatting allowed
1589 * @encoding: an optional encoding string
1590 *
1591 * Dump an XML node, recursive behaviour, children are printed too.
1592 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1593 * or xmlKeepBlanksDefault(0) was called
1594 */
1595void
1596xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1597 int level, int format, const char *encoding)
1598{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001599 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001600#ifdef LIBXML_HTML_ENABLED
1601 xmlDtdPtr dtd;
1602 int is_xhtml = 0;
1603#endif
1604
1605 xmlInitParser();
1606
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001607 memset(&ctxt, 0, sizeof(ctxt));
1608 ctxt.doc = doc;
1609 ctxt.buf = buf;
1610 ctxt.level = level;
1611 ctxt.format = format;
1612 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001613 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001614
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001615#ifdef LIBXML_HTML_ENABLED
1616 dtd = xmlGetIntSubset(doc);
1617 if (dtd != NULL) {
1618 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1619 if (is_xhtml < 0)
1620 is_xhtml = 0;
1621 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1622 (cur->type == XML_ELEMENT_NODE) &&
1623 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1624 if (encoding != NULL)
1625 htmlSetMetaEncoding((htmlDocPtr) doc,
1626 (const xmlChar *) encoding);
1627 else
1628 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1629 }
1630 }
1631
1632 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001633 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001634 else
1635#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001636 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001637}
1638
1639/**
1640 * xmlDocDumpFormatMemoryEnc:
1641 * @out_doc: Document to generate XML text from
1642 * @doc_txt_ptr: Memory pointer for allocated XML text
1643 * @doc_txt_len: Length of the generated XML text
1644 * @txt_encoding: Character encoding to use when generating XML text
1645 * @format: should formatting spaces been added
1646 *
1647 * Dump the current DOM tree into memory using the character encoding specified
1648 * by the caller. Note it is up to the caller of this function to free the
1649 * allocated memory with xmlFree().
1650 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1651 * or xmlKeepBlanksDefault(0) was called
1652 */
1653
1654void
1655xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1656 int * doc_txt_len, const char * txt_encoding,
1657 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001658 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001659 int dummy = 0;
1660 xmlOutputBufferPtr out_buff = NULL;
1661 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1662
1663 if (doc_txt_len == NULL) {
1664 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1665 }
1666
1667 if (doc_txt_ptr == NULL) {
1668 *doc_txt_len = 0;
1669 return;
1670 }
1671
1672 *doc_txt_ptr = NULL;
1673 *doc_txt_len = 0;
1674
1675 if (out_doc == NULL) {
1676 /* No document, no output */
1677 return;
1678 }
1679
1680 /*
1681 * Validate the encoding value, if provided.
1682 * This logic is copied from xmlSaveFileEnc.
1683 */
1684
1685 if (txt_encoding == NULL)
1686 txt_encoding = (const char *) out_doc->encoding;
1687 if (txt_encoding != NULL) {
1688 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1689 if ( conv_hdlr == NULL ) {
1690 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1691 txt_encoding);
1692 return;
1693 }
1694 }
1695
1696 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1697 xmlSaveErrMemory("creating buffer");
1698 return;
1699 }
1700
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001701 memset(&ctxt, 0, sizeof(ctxt));
1702 ctxt.doc = out_doc;
1703 ctxt.buf = out_buff;
1704 ctxt.level = 0;
1705 ctxt.format = format;
1706 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001707 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001708 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001709 xmlOutputBufferFlush(out_buff);
1710 if (out_buff->conv != NULL) {
1711 *doc_txt_len = out_buff->conv->use;
1712 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1713 } else {
1714 *doc_txt_len = out_buff->buffer->use;
1715 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1716 }
1717 (void)xmlOutputBufferClose(out_buff);
1718
1719 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1720 *doc_txt_len = 0;
1721 xmlSaveErrMemory("creating output");
1722 }
1723
1724 return;
1725}
1726
1727/**
1728 * xmlDocDumpMemory:
1729 * @cur: the document
1730 * @mem: OUT: the memory pointer
1731 * @size: OUT: the memory length
1732 *
1733 * Dump an XML document in memory and return the #xmlChar * and it's size
1734 * in bytes. It's up to the caller to free the memory with xmlFree().
1735 * The resulting byte array is zero terminated, though the last 0 is not
1736 * included in the returned size.
1737 */
1738void
1739xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1740 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1741}
1742
1743/**
1744 * xmlDocDumpFormatMemory:
1745 * @cur: the document
1746 * @mem: OUT: the memory pointer
1747 * @size: OUT: the memory length
1748 * @format: should formatting spaces been added
1749 *
1750 *
1751 * Dump an XML document in memory and return the #xmlChar * and it's size.
1752 * It's up to the caller to free the memory with xmlFree().
1753 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1754 * or xmlKeepBlanksDefault(0) was called
1755 */
1756void
1757xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1758 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1759}
1760
1761/**
1762 * xmlDocDumpMemoryEnc:
1763 * @out_doc: Document to generate XML text from
1764 * @doc_txt_ptr: Memory pointer for allocated XML text
1765 * @doc_txt_len: Length of the generated XML text
1766 * @txt_encoding: Character encoding to use when generating XML text
1767 *
1768 * Dump the current DOM tree into memory using the character encoding specified
1769 * by the caller. Note it is up to the caller of this function to free the
1770 * allocated memory with xmlFree().
1771 */
1772
1773void
1774xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1775 int * doc_txt_len, const char * txt_encoding) {
1776 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1777 txt_encoding, 0);
1778}
1779
1780/**
1781 * xmlDocFormatDump:
1782 * @f: the FILE*
1783 * @cur: the document
1784 * @format: should formatting spaces been added
1785 *
1786 * Dump an XML document to an open FILE.
1787 *
1788 * returns: the number of bytes written or -1 in case of failure.
1789 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1790 * or xmlKeepBlanksDefault(0) was called
1791 */
1792int
1793xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001794 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001795 xmlOutputBufferPtr buf;
1796 const char * encoding;
1797 xmlCharEncodingHandlerPtr handler = NULL;
1798 int ret;
1799
1800 if (cur == NULL) {
1801#ifdef DEBUG_TREE
1802 xmlGenericError(xmlGenericErrorContext,
1803 "xmlDocDump : document == NULL\n");
1804#endif
1805 return(-1);
1806 }
1807 encoding = (const char *) cur->encoding;
1808
1809 if (encoding != NULL) {
1810 handler = xmlFindCharEncodingHandler(encoding);
1811 if (handler == NULL) {
1812 xmlFree((char *) cur->encoding);
1813 cur->encoding = NULL;
1814 }
1815 }
1816 buf = xmlOutputBufferCreateFile(f, handler);
1817 if (buf == NULL) return(-1);
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 = format;
1823 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001824 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001825 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001826
1827 ret = xmlOutputBufferClose(buf);
1828 return(ret);
1829}
1830
1831/**
1832 * xmlDocDump:
1833 * @f: the FILE*
1834 * @cur: the document
1835 *
1836 * Dump an XML document to an open FILE.
1837 *
1838 * returns: the number of bytes written or -1 in case of failure.
1839 */
1840int
1841xmlDocDump(FILE *f, xmlDocPtr cur) {
1842 return(xmlDocFormatDump (f, cur, 0));
1843}
1844
1845/**
1846 * xmlSaveFileTo:
1847 * @buf: an output I/O buffer
1848 * @cur: the document
1849 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1850 *
1851 * Dump an XML document to an I/O buffer.
1852 *
1853 * returns: the number of bytes written or -1 in case of failure.
1854 */
1855int
1856xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001857 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001858 int ret;
1859
1860 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001861 memset(&ctxt, 0, sizeof(ctxt));
1862 ctxt.doc = cur;
1863 ctxt.buf = buf;
1864 ctxt.level = 0;
1865 ctxt.format = 0;
1866 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001867 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001868 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001869 ret = xmlOutputBufferClose(buf);
1870 return(ret);
1871}
1872
1873/**
1874 * xmlSaveFormatFileTo:
1875 * @buf: an output I/O buffer
1876 * @cur: the document
1877 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1878 * @format: should formatting spaces been added
1879 *
1880 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001881 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001882 *
1883 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001884 */
1885int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001886xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
1887 const char *encoding, int format)
1888{
1889 xmlSaveCtxt ctxt;
1890 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001891
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001892 if (buf == NULL)
1893 return (0);
1894 memset(&ctxt, 0, sizeof(ctxt));
1895 ctxt.doc = cur;
1896 ctxt.buf = buf;
1897 ctxt.level = 0;
1898 ctxt.format = format;
1899 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001900 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001901 xmlDocContentDumpOutput(&ctxt, cur);
1902 ret = xmlOutputBufferClose(buf);
1903 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001904}
1905
1906/**
1907 * xmlSaveFormatFileEnc:
1908 * @filename: the filename or URL to output
1909 * @cur: the document being saved
1910 * @encoding: the name of the encoding to use or NULL.
1911 * @format: should formatting spaces be added.
1912 *
1913 * Dump an XML document to a file or an URL.
1914 *
1915 * Returns the number of bytes written or -1 in case of error.
1916 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1917 * or xmlKeepBlanksDefault(0) was called
1918 */
1919int
1920xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
1921 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001922 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001923 xmlOutputBufferPtr buf;
1924 xmlCharEncodingHandlerPtr handler = NULL;
1925 int ret;
1926
1927 if (cur == NULL)
1928 return(-1);
1929
1930 if (encoding == NULL)
1931 encoding = (const char *) cur->encoding;
1932
1933 if (encoding != NULL) {
1934
1935 handler = xmlFindCharEncodingHandler(encoding);
1936 if (handler == NULL)
1937 return(-1);
1938 }
1939
1940#ifdef HAVE_ZLIB_H
1941 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
1942#endif
1943 /*
1944 * save the content to a temp buffer.
1945 */
1946 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
1947 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001948 memset(&ctxt, 0, sizeof(ctxt));
1949 ctxt.doc = cur;
1950 ctxt.buf = buf;
1951 ctxt.level = 0;
1952 ctxt.format = format;
1953 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001954 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001955
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001956 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001957
1958 ret = xmlOutputBufferClose(buf);
1959 return(ret);
1960}
1961
1962
1963/**
1964 * xmlSaveFileEnc:
1965 * @filename: the filename (or URL)
1966 * @cur: the document
1967 * @encoding: the name of an encoding (or NULL)
1968 *
1969 * Dump an XML document, converting it to the given encoding
1970 *
1971 * returns: the number of bytes written or -1 in case of failure.
1972 */
1973int
1974xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
1975 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
1976}
1977
1978/**
1979 * xmlSaveFormatFile:
1980 * @filename: the filename (or URL)
1981 * @cur: the document
1982 * @format: should formatting spaces been added
1983 *
1984 * Dump an XML document to a file. Will use compression if
1985 * compiled in and enabled. If @filename is "-" the stdout file is
1986 * used. If @format is set then the document will be indented on output.
1987 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1988 * or xmlKeepBlanksDefault(0) was called
1989 *
1990 * returns: the number of bytes written or -1 in case of failure.
1991 */
1992int
1993xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
1994 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
1995}
1996
1997/**
1998 * xmlSaveFile:
1999 * @filename: the filename (or URL)
2000 * @cur: the document
2001 *
2002 * Dump an XML document to a file. Will use compression if
2003 * compiled in and enabled. If @filename is "-" the stdout file is
2004 * used.
2005 * returns: the number of bytes written or -1 in case of failure.
2006 */
2007int
2008xmlSaveFile(const char *filename, xmlDocPtr cur) {
2009 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2010}
2011
2012#endif /* LIBXML_OUTPUT_ENABLED */
2013