blob: 65b96d1bea3797f95d3b3bae20aa162459b04a7b [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>
Daniel Veillard1a8741c2004-03-04 13:40:59 +000017
Daniel Veillard753086a2004-03-28 16:12:44 +000018#define MAX_INDENT 60
19
Daniel Veillard656ce942004-04-30 23:11:45 +000020#include <libxml/HTMLtree.h>
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}
Daniel Veillard1a8741c2004-03-04 13:40:59 +000066
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 Veillard753086a2004-03-28 16:12:44 +000086 char indent[MAX_INDENT + 1];
87 int indent_nr;
88 int indent_size;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000089};
90
91/************************************************************************
92 * *
93 * Output error handlers *
94 * *
95 ************************************************************************/
96/**
97 * xmlSaveErrMemory:
98 * @extra: extra informations
99 *
100 * Handle an out of memory condition
101 */
102static void
103xmlSaveErrMemory(const char *extra)
104{
105 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
106}
107
108/**
109 * xmlSaveErr:
110 * @code: the error number
111 * @node: the location of the error.
112 * @extra: extra informations
113 *
114 * Handle an out of memory condition
115 */
116static void
117xmlSaveErr(int code, xmlNodePtr node, const char *extra)
118{
119 const char *msg = NULL;
120
121 switch(code) {
122 case XML_SAVE_NOT_UTF8:
123 msg = "string is not in UTF-8";
124 break;
125 case XML_SAVE_CHAR_INVALID:
126 msg = "invalid character value";
127 break;
128 case XML_SAVE_UNKNOWN_ENCODING:
129 msg = "unknown encoding %s";
130 break;
131 case XML_SAVE_NO_DOCTYPE:
132 msg = "document has no DOCTYPE";
133 break;
134 default:
135 msg = "unexpected error number";
136 }
137 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
138}
139
140/************************************************************************
141 * *
142 * Allocation and deallocation *
143 * *
144 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000145/**
146 * xmlSaveCtxtInit:
147 * @ctxt: the saving context
148 *
149 * Initialize a saving context
150 */
151static void
152xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
153{
154 int i;
155
156 if (ctxt == NULL) return;
157 if (xmlTreeIndentString == NULL) {
158 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
159 } else {
160 ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
161 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
162 for (i = 0;i < ctxt->indent_nr;i++)
163 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
164 ctxt->indent_size);
165 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
166 }
167}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000168
169/**
170 * xmlFreeSaveCtxt:
171 *
172 * Free a saving context, destroying the ouptut in any remaining buffer
173 */
174static void
175xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
176{
177 if (ctxt == NULL) return;
178 if (ctxt->encoding != NULL)
179 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000180 if (ctxt->buf != NULL)
181 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000182 xmlFree(ctxt);
183}
184
185/**
186 * xmlNewSaveCtxt:
187 *
188 * Create a new saving context
189 *
190 * Returns the new structure or NULL in case of error
191 */
192static xmlSaveCtxtPtr
193xmlNewSaveCtxt(const char *encoding, int options)
194{
195 xmlSaveCtxtPtr ret;
196
197 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
198 if (ret == NULL) {
199 xmlSaveErrMemory("creating saving context");
200 return ( NULL );
201 }
202 memset(ret, 0, sizeof(xmlSaveCtxt));
203 ret->options = options;
204 if (encoding != NULL) {
205 ret->handler = xmlFindCharEncodingHandler(encoding);
206 if (ret->handler == NULL) {
207 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
208 xmlFreeSaveCtxt(ret);
209 return(NULL);
210 }
211 ret->encoding = xmlStrdup((const xmlChar *)encoding);
212 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000213 xmlSaveCtxtInit(ret);
214
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000215 return(ret);
216}
217
218/************************************************************************
219 * *
220 * Dumping XML tree content to a simple buffer *
221 * *
222 ************************************************************************/
223/**
224 * xmlAttrSerializeContent:
225 * @buf: the XML buffer output
226 * @doc: the document
227 * @attr: the attribute pointer
228 *
229 * Serialize the attribute in the buffer
230 */
231static void
232xmlAttrSerializeContent(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr attr)
233{
234 xmlNodePtr children;
235
236 children = attr->children;
237 while (children != NULL) {
238 switch (children->type) {
239 case XML_TEXT_NODE:
240 xmlAttrSerializeTxtContent(buf, doc, attr, children->content);
241 break;
242 case XML_ENTITY_REF_NODE:
243 xmlBufferAdd(buf, BAD_CAST "&", 1);
244 xmlBufferAdd(buf, children->name,
245 xmlStrlen(children->name));
246 xmlBufferAdd(buf, BAD_CAST ";", 1);
247 break;
248 default:
249 /* should not happen unless we have a badly built tree */
250 break;
251 }
252 children = children->next;
253 }
254}
255
256/************************************************************************
257 * *
258 * Dumping XML tree content to an I/O output buffer *
259 * *
260 ************************************************************************/
261
262#ifdef LIBXML_HTML_ENABLED
263static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000264xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000265#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000266static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
267static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000268void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
269
270/**
271 * xmlNsDumpOutput:
272 * @buf: the XML buffer output
273 * @cur: a namespace
274 *
275 * Dump a local Namespace definition.
276 * Should be called in the context of attributes dumps.
277 */
278static void
279xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000280 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000281 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
282 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
283 return;
284
285 /* Within the context of an element attributes */
286 if (cur->prefix != NULL) {
287 xmlOutputBufferWriteString(buf, " xmlns:");
288 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
289 } else
290 xmlOutputBufferWriteString(buf, " xmlns");
291 xmlOutputBufferWriteString(buf, "=");
292 xmlBufferWriteQuotedString(buf->buffer, cur->href);
293 }
294}
295
296/**
297 * xmlNsListDumpOutput:
298 * @buf: the XML buffer output
299 * @cur: the first namespace
300 *
301 * Dump a list of local Namespace definitions.
302 * Should be called in the context of attributes dumps.
303 */
304void
305xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
306 while (cur != NULL) {
307 xmlNsDumpOutput(buf, cur);
308 cur = cur->next;
309 }
310}
311
312/**
313 * xmlDtdDumpOutput:
314 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000315 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000316 *
317 * Dump the XML document DTD, if any.
318 */
319static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000320xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
321 xmlOutputBufferPtr buf;
322 int format, level;
323 xmlDocPtr doc;
324
325 if (dtd == NULL) return;
326 if ((ctxt == NULL) || (ctxt->buf == NULL))
327 return;
328 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000329 xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
330 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
331 if (dtd->ExternalID != NULL) {
332 xmlOutputBufferWriteString(buf, " PUBLIC ");
333 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
334 xmlOutputBufferWriteString(buf, " ");
335 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
336 } else if (dtd->SystemID != NULL) {
337 xmlOutputBufferWriteString(buf, " SYSTEM ");
338 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
339 }
340 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
341 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
342 (dtd->pentities == NULL)) {
343 xmlOutputBufferWriteString(buf, ">");
344 return;
345 }
346 xmlOutputBufferWriteString(buf, " [\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000347 format = ctxt->format;
348 level = ctxt->level;
349 doc = ctxt->doc;
350 ctxt->format = 0;
351 ctxt->level = -1;
352 ctxt->doc = dtd->doc;
353 xmlNodeListDumpOutput(ctxt, dtd->children);
354 ctxt->format = format;
355 ctxt->level = level;
356 ctxt->doc = doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000357 xmlOutputBufferWriteString(buf, "]>");
358}
359
360/**
361 * xmlAttrDumpOutput:
362 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000363 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000364 *
365 * Dump an XML attribute
366 */
367static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000368xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
369 xmlOutputBufferPtr buf;
370 if (cur == NULL) return;
371 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000372 xmlOutputBufferWriteString(buf, " ");
373 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
374 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
375 xmlOutputBufferWriteString(buf, ":");
376 }
377 xmlOutputBufferWriteString(buf, (const char *)cur->name);
378 xmlOutputBufferWriteString(buf, "=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000379 xmlAttrSerializeContent(buf->buffer, ctxt->doc, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000380 xmlOutputBufferWriteString(buf, "\"");
381}
382
383/**
384 * xmlAttrListDumpOutput:
385 * @buf: the XML buffer output
386 * @doc: the document
387 * @cur: the first attribute pointer
388 * @encoding: an optional encoding string
389 *
390 * Dump a list of XML attributes
391 */
392static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000393xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
394 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000395 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000396 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000397 cur = cur->next;
398 }
399}
400
401
402
403/**
404 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000405 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000406 *
407 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000408 */
409static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000410xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000411 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000412
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000413 if (cur == NULL) return;
414 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000415 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000416 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000417 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000418 xmlOutputBufferWrite(buf, ctxt->indent_size *
419 (ctxt->level > ctxt->indent_nr ?
420 ctxt->indent_nr : ctxt->level),
421 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000422 xmlNodeDumpOutputInternal(ctxt, cur);
423 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000424 xmlOutputBufferWriteString(buf, "\n");
425 }
426 cur = cur->next;
427 }
428}
429
430/**
431 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000432 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000433 *
434 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000435 */
436static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000437xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000438 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000439 xmlNodePtr tmp;
440 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000441 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000442
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000443 if (cur == NULL) return;
444 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000445 if (cur->type == XML_XINCLUDE_START)
446 return;
447 if (cur->type == XML_XINCLUDE_END)
448 return;
449 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000450 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000451 return;
452 }
453 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000454 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000455 return;
456 }
457 if (cur->type == XML_ELEMENT_DECL) {
458 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
459 return;
460 }
461 if (cur->type == XML_ATTRIBUTE_DECL) {
462 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
463 return;
464 }
465 if (cur->type == XML_ENTITY_DECL) {
466 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
467 return;
468 }
469 if (cur->type == XML_TEXT_NODE) {
470 if (cur->content != NULL) {
471 if ((cur->name == xmlStringText) ||
472 (cur->name != xmlStringTextNoenc)) {
473 xmlChar *buffer;
474
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000475 if (ctxt->encoding == NULL)
476 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
477 cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000478 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000479 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000480 if (buffer != NULL) {
481 xmlOutputBufferWriteString(buf, (const char *)buffer);
482 xmlFree(buffer);
483 }
484 } else {
485 /*
486 * Disable escaping, needed for XSLT
487 */
488 xmlOutputBufferWriteString(buf, (const char *) cur->content);
489 }
490 }
491
492 return;
493 }
494 if (cur->type == XML_PI_NODE) {
495 if (cur->content != NULL) {
496 xmlOutputBufferWriteString(buf, "<?");
497 xmlOutputBufferWriteString(buf, (const char *)cur->name);
498 if (cur->content != NULL) {
499 xmlOutputBufferWriteString(buf, " ");
500 xmlOutputBufferWriteString(buf, (const char *)cur->content);
501 }
502 xmlOutputBufferWriteString(buf, "?>");
503 } else {
504 xmlOutputBufferWriteString(buf, "<?");
505 xmlOutputBufferWriteString(buf, (const char *)cur->name);
506 xmlOutputBufferWriteString(buf, "?>");
507 }
508 return;
509 }
510 if (cur->type == XML_COMMENT_NODE) {
511 if (cur->content != NULL) {
512 xmlOutputBufferWriteString(buf, "<!--");
513 xmlOutputBufferWriteString(buf, (const char *)cur->content);
514 xmlOutputBufferWriteString(buf, "-->");
515 }
516 return;
517 }
518 if (cur->type == XML_ENTITY_REF_NODE) {
519 xmlOutputBufferWriteString(buf, "&");
520 xmlOutputBufferWriteString(buf, (const char *)cur->name);
521 xmlOutputBufferWriteString(buf, ";");
522 return;
523 }
524 if (cur->type == XML_CDATA_SECTION_NODE) {
525 start = end = cur->content;
526 while (*end != '\0') {
527 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
528 end = end + 2;
529 xmlOutputBufferWriteString(buf, "<![CDATA[");
530 xmlOutputBufferWrite(buf, end - start, (const char *)start);
531 xmlOutputBufferWriteString(buf, "]]>");
532 start = end;
533 }
534 end++;
535 }
536 if (start != end) {
537 xmlOutputBufferWriteString(buf, "<![CDATA[");
538 xmlOutputBufferWriteString(buf, (const char *)start);
539 xmlOutputBufferWriteString(buf, "]]>");
540 }
541 return;
542 }
543 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000544 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000545 return;
546 }
547 if (cur->type == XML_NAMESPACE_DECL) {
548 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
549 return;
550 }
551
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000552 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000553 if (format == 1) {
554 tmp = cur->children;
555 while (tmp != NULL) {
556 if ((tmp->type == XML_TEXT_NODE) ||
557 (tmp->type == XML_CDATA_SECTION_NODE) ||
558 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000559 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000560 break;
561 }
562 tmp = tmp->next;
563 }
564 }
565 xmlOutputBufferWriteString(buf, "<");
566 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
567 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
568 xmlOutputBufferWriteString(buf, ":");
569 }
570
571 xmlOutputBufferWriteString(buf, (const char *)cur->name);
572 if (cur->nsDef)
573 xmlNsListDumpOutput(buf, cur->nsDef);
574 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000575 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000576
577 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
578 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
579 xmlOutputBufferWriteString(buf, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000580 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000581 return;
582 }
583 xmlOutputBufferWriteString(buf, ">");
584 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
585 xmlChar *buffer;
586
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000587 if (ctxt->encoding == NULL)
588 buffer = xmlEncodeEntitiesReentrant(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000589 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000590 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000591 if (buffer != NULL) {
592 xmlOutputBufferWriteString(buf, (const char *)buffer);
593 xmlFree(buffer);
594 }
595 }
596 if (cur->children != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000597 if (ctxt->format) xmlOutputBufferWriteString(buf, "\n");
598 if (ctxt->level >= 0) ctxt->level++;
599 xmlNodeListDumpOutput(ctxt, cur->children);
600 if (ctxt->level > 0) ctxt->level--;
601 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000602 xmlOutputBufferWrite(buf, ctxt->indent_size *
603 (ctxt->level > ctxt->indent_nr ?
604 ctxt->indent_nr : ctxt->level),
605 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000606 }
607 xmlOutputBufferWriteString(buf, "</");
608 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
609 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
610 xmlOutputBufferWriteString(buf, ":");
611 }
612
613 xmlOutputBufferWriteString(buf, (const char *)cur->name);
614 xmlOutputBufferWriteString(buf, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000615 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000616}
617
618/**
619 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000620 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000621 *
622 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000623 */
624static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000625xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000626#ifdef LIBXML_HTML_ENABLED
627 xmlDtdPtr dtd;
628 int is_xhtml = 0;
629#endif
630 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000631 const xmlChar *encoding = ctxt->encoding;
632 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000633
634 xmlInitParser();
635
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000636 if (ctxt->encoding != NULL)
637 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000638
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000639 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000640 xmlOutputBufferWriteString(buf, "<?xml version=");
641 if (cur->version != NULL)
642 xmlBufferWriteQuotedString(buf->buffer, cur->version);
643 else
644 xmlOutputBufferWriteString(buf, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000645 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000646 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000647 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000648 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000649 encoding = (const xmlChar *)
650 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000651 }
652 if (encoding != NULL) {
653 xmlOutputBufferWriteString(buf, " encoding=");
654 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
655 }
656 switch (cur->standalone) {
657 case 0:
658 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
659 break;
660 case 1:
661 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
662 break;
663 }
664 xmlOutputBufferWriteString(buf, "?>\n");
665
666#ifdef LIBXML_HTML_ENABLED
667 dtd = xmlGetIntSubset(cur);
668 if (dtd != NULL) {
669 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
670 if (is_xhtml < 0) is_xhtml = 0;
671 }
672 if (is_xhtml) {
673 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000674 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000675 else
676 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
677 }
678#endif
679 if (cur->children != NULL) {
680 xmlNodePtr child = cur->children;
681
682 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000683 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000684#ifdef LIBXML_HTML_ENABLED
685 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000686 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000687 else
688#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000689 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000690 xmlOutputBufferWriteString(buf, "\n");
691 child = child->next;
692 }
693 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000694 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000695 cur->encoding = oldenc;
696}
697
698#ifdef LIBXML_HTML_ENABLED
699/************************************************************************
700 * *
701 * Functions specific to XHTML serialization *
702 * *
703 ************************************************************************/
704
705/**
706 * xhtmlIsEmpty:
707 * @node: the node
708 *
709 * Check if a node is an empty xhtml node
710 *
711 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
712 */
713static int
714xhtmlIsEmpty(xmlNodePtr node) {
715 if (node == NULL)
716 return(-1);
717 if (node->type != XML_ELEMENT_NODE)
718 return(0);
719 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
720 return(0);
721 if (node->children != NULL)
722 return(0);
723 switch (node->name[0]) {
724 case 'a':
725 if (xmlStrEqual(node->name, BAD_CAST "area"))
726 return(1);
727 return(0);
728 case 'b':
729 if (xmlStrEqual(node->name, BAD_CAST "br"))
730 return(1);
731 if (xmlStrEqual(node->name, BAD_CAST "base"))
732 return(1);
733 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
734 return(1);
735 return(0);
736 case 'c':
737 if (xmlStrEqual(node->name, BAD_CAST "col"))
738 return(1);
739 return(0);
740 case 'f':
741 if (xmlStrEqual(node->name, BAD_CAST "frame"))
742 return(1);
743 return(0);
744 case 'h':
745 if (xmlStrEqual(node->name, BAD_CAST "hr"))
746 return(1);
747 return(0);
748 case 'i':
749 if (xmlStrEqual(node->name, BAD_CAST "img"))
750 return(1);
751 if (xmlStrEqual(node->name, BAD_CAST "input"))
752 return(1);
753 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
754 return(1);
755 return(0);
756 case 'l':
757 if (xmlStrEqual(node->name, BAD_CAST "link"))
758 return(1);
759 return(0);
760 case 'm':
761 if (xmlStrEqual(node->name, BAD_CAST "meta"))
762 return(1);
763 return(0);
764 case 'p':
765 if (xmlStrEqual(node->name, BAD_CAST "param"))
766 return(1);
767 return(0);
768 }
769 return(0);
770}
771
772/**
773 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000774 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000775 *
776 * Dump a list of XML attributes
777 */
778static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000779xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000780 xmlAttrPtr xml_lang = NULL;
781 xmlAttrPtr lang = NULL;
782 xmlAttrPtr name = NULL;
783 xmlAttrPtr id = NULL;
784 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000785 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000786
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000787 if (cur == NULL) return;
788 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000789 parent = cur->parent;
790 while (cur != NULL) {
791 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
792 id = cur;
793 else
794 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
795 name = cur;
796 else
797 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
798 lang = cur;
799 else
800 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
801 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
802 xml_lang = cur;
803 else if ((cur->ns == NULL) &&
804 ((cur->children == NULL) ||
805 (cur->children->content == NULL) ||
806 (cur->children->content[0] == 0)) &&
807 (htmlIsBooleanAttr(cur->name))) {
808 if (cur->children != NULL)
809 xmlFreeNode(cur->children);
810 cur->children = xmlNewText(cur->name);
811 if (cur->children != NULL)
812 cur->children->parent = (xmlNodePtr) cur;
813 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000814 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000815 cur = cur->next;
816 }
817 /*
818 * C.8
819 */
820 if ((name != NULL) && (id == NULL)) {
821 if ((parent != NULL) && (parent->name != NULL) &&
822 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
823 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
824 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
825 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
826 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
827 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
828 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
829 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
830 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
831 xmlOutputBufferWriteString(buf, " id=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000832 xmlAttrSerializeContent(buf->buffer, ctxt->doc, name);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000833 xmlOutputBufferWriteString(buf, "\"");
834 }
835 }
836 /*
837 * C.7.
838 */
839 if ((lang != NULL) && (xml_lang == NULL)) {
840 xmlOutputBufferWriteString(buf, " xml:lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000841 xmlAttrSerializeContent(buf->buffer, ctxt->doc, lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000842 xmlOutputBufferWriteString(buf, "\"");
843 } else
844 if ((xml_lang != NULL) && (lang == NULL)) {
845 xmlOutputBufferWriteString(buf, " lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000846 xmlAttrSerializeContent(buf->buffer, ctxt->doc, xml_lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000847 xmlOutputBufferWriteString(buf, "\"");
848 }
849}
850
851/**
852 * xhtmlNodeListDumpOutput:
853 * @buf: the XML buffer output
854 * @doc: the XHTML document
855 * @cur: the first node
856 * @level: the imbrication level for indenting
857 * @format: is formatting allowed
858 * @encoding: an optional encoding string
859 *
860 * Dump an XML node list, recursive behaviour, children are printed too.
861 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
862 * or xmlKeepBlanksDefault(0) was called
863 */
864static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000865xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000866 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000867
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000868 if (cur == NULL) return;
869 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000870 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000871 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000872 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000873 xmlOutputBufferWrite(buf, ctxt->indent_size *
874 (ctxt->level > ctxt->indent_nr ?
875 ctxt->indent_nr : ctxt->level),
876 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000877 xhtmlNodeDumpOutput(ctxt, cur);
878 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000879 xmlOutputBufferWriteString(buf, "\n");
880 }
881 cur = cur->next;
882 }
883}
884
885/**
886 * xhtmlNodeDumpOutput:
887 * @buf: the XML buffer output
888 * @doc: the XHTML document
889 * @cur: the current node
890 * @level: the imbrication level for indenting
891 * @format: is formatting allowed
892 * @encoding: an optional encoding string
893 *
894 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000895 */
896static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000897xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000898 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000899 xmlNodePtr tmp;
900 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000901 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000902
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000903 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000904 if (cur->type == XML_XINCLUDE_START)
905 return;
906 if (cur->type == XML_XINCLUDE_END)
907 return;
908 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000909 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000910 return;
911 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000912 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000913 if (cur->type == XML_ELEMENT_DECL) {
914 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
915 return;
916 }
917 if (cur->type == XML_ATTRIBUTE_DECL) {
918 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
919 return;
920 }
921 if (cur->type == XML_ENTITY_DECL) {
922 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
923 return;
924 }
925 if (cur->type == XML_TEXT_NODE) {
926 if (cur->content != NULL) {
927 if ((cur->name == xmlStringText) ||
928 (cur->name != xmlStringTextNoenc)) {
929 xmlChar *buffer;
930
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000931 if (ctxt->encoding == NULL)
932 buffer = xmlEncodeEntitiesReentrant(ctxt->doc,
933 cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000934 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000935 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000936 if (buffer != NULL) {
937 xmlOutputBufferWriteString(buf, (const char *)buffer);
938 xmlFree(buffer);
939 }
940 } else {
941 /*
942 * Disable escaping, needed for XSLT
943 */
944 xmlOutputBufferWriteString(buf, (const char *) cur->content);
945 }
946 }
947
948 return;
949 }
950 if (cur->type == XML_PI_NODE) {
951 if (cur->content != NULL) {
952 xmlOutputBufferWriteString(buf, "<?");
953 xmlOutputBufferWriteString(buf, (const char *)cur->name);
954 if (cur->content != NULL) {
955 xmlOutputBufferWriteString(buf, " ");
956 xmlOutputBufferWriteString(buf, (const char *)cur->content);
957 }
958 xmlOutputBufferWriteString(buf, "?>");
959 } else {
960 xmlOutputBufferWriteString(buf, "<?");
961 xmlOutputBufferWriteString(buf, (const char *)cur->name);
962 xmlOutputBufferWriteString(buf, "?>");
963 }
964 return;
965 }
966 if (cur->type == XML_COMMENT_NODE) {
967 if (cur->content != NULL) {
968 xmlOutputBufferWriteString(buf, "<!--");
969 xmlOutputBufferWriteString(buf, (const char *)cur->content);
970 xmlOutputBufferWriteString(buf, "-->");
971 }
972 return;
973 }
974 if (cur->type == XML_ENTITY_REF_NODE) {
975 xmlOutputBufferWriteString(buf, "&");
976 xmlOutputBufferWriteString(buf, (const char *)cur->name);
977 xmlOutputBufferWriteString(buf, ";");
978 return;
979 }
980 if (cur->type == XML_CDATA_SECTION_NODE) {
981 start = end = cur->content;
982 while (*end != '\0') {
983 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
984 end = end + 2;
985 xmlOutputBufferWriteString(buf, "<![CDATA[");
986 xmlOutputBufferWrite(buf, end - start, (const char *)start);
987 xmlOutputBufferWriteString(buf, "]]>");
988 start = end;
989 }
990 end++;
991 }
992 if (start != end) {
993 xmlOutputBufferWriteString(buf, "<![CDATA[");
994 xmlOutputBufferWriteString(buf, (const char *)start);
995 xmlOutputBufferWriteString(buf, "]]>");
996 }
997 return;
998 }
999
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001000 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001001 if (format == 1) {
1002 tmp = cur->children;
1003 while (tmp != NULL) {
1004 if ((tmp->type == XML_TEXT_NODE) ||
1005 (tmp->type == XML_ENTITY_REF_NODE)) {
1006 format = 0;
1007 break;
1008 }
1009 tmp = tmp->next;
1010 }
1011 }
1012 xmlOutputBufferWriteString(buf, "<");
1013 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1014 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1015 xmlOutputBufferWriteString(buf, ":");
1016 }
1017
1018 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1019 if (cur->nsDef)
1020 xmlNsListDumpOutput(buf, cur->nsDef);
1021 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1022 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1023 /*
1024 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1025 */
1026 xmlOutputBufferWriteString(buf,
1027 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1028 }
1029 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001030 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001031
1032 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1033 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1034 (xhtmlIsEmpty(cur) == 1)) {
1035 /*
1036 * C.2. Empty Elements
1037 */
1038 xmlOutputBufferWriteString(buf, " />");
1039 } else {
1040 /*
1041 * C.3. Element Minimization and Empty Element Content
1042 */
1043 xmlOutputBufferWriteString(buf, "></");
1044 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1045 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1046 xmlOutputBufferWriteString(buf, ":");
1047 }
1048 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1049 xmlOutputBufferWriteString(buf, ">");
1050 }
1051 return;
1052 }
1053 xmlOutputBufferWriteString(buf, ">");
1054 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1055 xmlChar *buffer;
1056
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001057 if (ctxt->encoding == NULL)
1058 buffer = xmlEncodeEntitiesReentrant(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001059 else
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001060 buffer = xmlEncodeSpecialChars(ctxt->doc, cur->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001061 if (buffer != NULL) {
1062 xmlOutputBufferWriteString(buf, (const char *)buffer);
1063 xmlFree(buffer);
1064 }
1065 }
1066
1067 /*
1068 * 4.8. Script and Style elements
1069 */
1070 if ((cur->type == XML_ELEMENT_NODE) &&
1071 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1072 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1073 ((cur->ns == NULL) ||
1074 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1075 xmlNodePtr child = cur->children;
1076
1077 while (child != NULL) {
1078 if ((child->type == XML_TEXT_NODE) ||
1079 (child->type == XML_CDATA_SECTION_NODE)) {
1080 /*
1081 * Apparently CDATA escaping for style just break on IE,
1082 * mozilla and galeon, so ...
1083 */
1084 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1085 (xmlStrchr(child->content, '<') == NULL) &&
1086 (xmlStrchr(child->content, '>') == NULL) &&
1087 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001088 int level = ctxt->level;
1089 int indent = ctxt->format;
1090
1091 ctxt->level = 0;
1092 ctxt->format = 0;
1093 xhtmlNodeDumpOutput(ctxt, child);
1094 ctxt->level = level;
1095 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001096 } else {
1097 start = end = child->content;
1098 while (*end != '\0') {
1099 if (*end == ']' &&
1100 *(end + 1) == ']' &&
1101 *(end + 2) == '>') {
1102 end = end + 2;
1103 xmlOutputBufferWriteString(buf, "<![CDATA[");
1104 xmlOutputBufferWrite(buf, end - start,
1105 (const char *)start);
1106 xmlOutputBufferWriteString(buf, "]]>");
1107 start = end;
1108 }
1109 end++;
1110 }
1111 if (start != end) {
1112 xmlOutputBufferWriteString(buf, "<![CDATA[");
1113 xmlOutputBufferWriteString(buf, (const char *)start);
1114 xmlOutputBufferWriteString(buf, "]]>");
1115 }
1116 }
1117 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001118 int level = ctxt->level;
1119 int indent = ctxt->format;
1120
1121 ctxt->level = 0;
1122 ctxt->format = 0;
1123 xhtmlNodeDumpOutput(ctxt, child);
1124 ctxt->level = level;
1125 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001126 }
1127 child = child->next;
1128 }
1129 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001130 int indent = ctxt->format;
1131
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001132 if (format) xmlOutputBufferWriteString(buf, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001133 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001134 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001135 xhtmlNodeListDumpOutput(ctxt, cur->children);
1136 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001137 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001138 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001139 xmlOutputBufferWrite(buf, ctxt->indent_size *
1140 (ctxt->level > ctxt->indent_nr ?
1141 ctxt->indent_nr : ctxt->level),
1142 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001143 }
1144 xmlOutputBufferWriteString(buf, "</");
1145 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1146 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1147 xmlOutputBufferWriteString(buf, ":");
1148 }
1149
1150 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1151 xmlOutputBufferWriteString(buf, ">");
1152}
1153#endif
1154
1155/************************************************************************
1156 * *
1157 * Public entry points *
1158 * *
1159 ************************************************************************/
1160
1161/**
1162 * xmlSaveToFd:
1163 * @fd: a file descriptor number
1164 * @encoding: the encoding name to use or NULL
1165 * @options: a set of xmlSaveOptions
1166 *
1167 * Create a document saving context serializing to a file descriptor
1168 * with the encoding and the options given.
1169 *
1170 * Returns a new serialization context or NULL in case of error.
1171 */
1172xmlSaveCtxtPtr
1173xmlSaveToFd(int fd, const char *encoding, int options)
1174{
1175 xmlSaveCtxtPtr ret;
1176
1177 ret = xmlNewSaveCtxt(encoding, options);
1178 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001179 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1180 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001181 xmlFreeSaveCtxt(ret);
1182 return(NULL);
1183 }
1184 return(ret);
1185}
1186
1187/**
1188 * xmlSaveToFilename:
1189 * @filename: a file name or an URL
1190 * @encoding: the encoding name to use or NULL
1191 * @options: a set of xmlSaveOptions
1192 *
1193 * Create a document saving context serializing to a filename or possibly
1194 * to an URL (but this is less reliable) with the encoding and the options
1195 * given.
1196 *
1197 * Returns a new serialization context or NULL in case of error.
1198 */
1199xmlSaveCtxtPtr
1200xmlSaveToFilename(const char *filename, const char *encoding, int options)
1201{
1202 xmlSaveCtxtPtr ret;
1203 int compression = 0; /* TODO handle compression option */
1204
1205 ret = xmlNewSaveCtxt(encoding, options);
1206 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001207 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001208 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001209 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001210 xmlFreeSaveCtxt(ret);
1211 return(NULL);
1212 }
1213 return(ret);
1214}
1215
1216#if 0
1217/**
1218 * xmlSaveToBuffer:
1219 * @buffer: a buffer
1220 * @encoding: the encoding name to use or NULL
1221 * @options: a set of xmlSaveOptions
1222 *
1223 * Create a document saving context serializing to a buffer
1224 * with the encoding and the options given
1225 *
1226 * Returns a new serialization context or NULL in case of error.
1227 */
1228xmlSaveCtxtPtr
1229xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1230{
1231 TODO
1232 return(NULL);
1233}
1234#endif
1235
1236/**
1237 * xmlSaveToIO:
1238 * @iowrite: an I/O write function
1239 * @ioclose: an I/O close function
1240 * @ioctx: an I/O handler
1241 * @encoding: the encoding name to use or NULL
1242 * @options: a set of xmlSaveOptions
1243 *
1244 * Create a document saving context serializing to a file descriptor
1245 * with the encoding and the options given
1246 *
1247 * Returns a new serialization context or NULL in case of error.
1248 */
1249xmlSaveCtxtPtr
1250xmlSaveToIO(xmlOutputWriteCallback iowrite,
1251 xmlOutputCloseCallback ioclose,
1252 void *ioctx, const char *encoding, int options)
1253{
1254 xmlSaveCtxtPtr ret;
1255
1256 ret = xmlNewSaveCtxt(encoding, options);
1257 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001258 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1259 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001260 xmlFreeSaveCtxt(ret);
1261 return(NULL);
1262 }
1263 return(ret);
1264}
1265
1266/**
1267 * xmlSaveDoc:
1268 * @ctxt: a document saving context
1269 * @doc: a document
1270 *
1271 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001272 * TODO: The function is not fully implemented yet as it does not return the
1273 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001274 *
1275 * Returns the number of byte written or -1 in case of error
1276 */
1277long
1278xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1279{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001280 long ret = 0;
1281
1282 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001283 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001284}
1285
1286/**
1287 * xmlSaveTree:
1288 * @ctxt: a document saving context
1289 * @node: a document
1290 *
1291 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001292 * TODO: The function is not fully implemented yet as it does not return the
1293 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001294 *
1295 * Returns the number of byte written or -1 in case of error
1296 */
1297long
1298xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1299{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001300 long ret = 0;
1301
1302 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001303 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001304}
1305
1306/**
1307 * xmlSaveFlush:
1308 * @ctxt: a document saving context
1309 *
1310 * Flush a document saving context, i.e. make sure that all bytes have
1311 * been output.
1312 *
1313 * Returns the number of byte written or -1 in case of error.
1314 */
1315int
1316xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1317{
1318 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001319 if (ctxt->buf == NULL) return(-1);
1320 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001321}
1322
1323/**
1324 * xmlSaveClose:
1325 * @ctxt: a document saving context
1326 *
1327 * Close a document saving context, i.e. make sure that all bytes have
1328 * been output and free the associated data.
1329 *
1330 * Returns the number of byte written or -1 in case of error.
1331 */
1332int
1333xmlSaveClose(xmlSaveCtxtPtr ctxt)
1334{
1335 int ret;
1336
1337 if (ctxt == NULL) return(-1);
1338 ret = xmlSaveFlush(ctxt);
1339 xmlFreeSaveCtxt(ctxt);
1340 return(ret);
1341}
1342
1343/************************************************************************
1344 * *
1345 * Public entry points based on buffers *
1346 * *
1347 ************************************************************************/
1348/**
1349 * xmlAttrSerializeTxtContent:
1350 * @buf: the XML buffer output
1351 * @doc: the document
1352 * @attr: the attribute node
1353 * @string: the text content
1354 *
1355 * Serialize text attribute values to an xml simple buffer
1356 */
1357void
1358xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1359 xmlAttrPtr attr, const xmlChar *string) {
1360 xmlChar *base, *cur;
1361
1362 if (string == NULL) return;
1363 base = cur = (xmlChar *)string;
1364 while (*cur != 0) {
1365 if (*cur == '\n') {
1366 if (base != cur)
1367 xmlBufferAdd(buf, base, cur - base);
1368 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1369 cur++;
1370 base = cur;
1371 } else if (*cur == '\r') {
1372 if (base != cur)
1373 xmlBufferAdd(buf, base, cur - base);
1374 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1375 cur++;
1376 base = cur;
1377 } else if (*cur == '\t') {
1378 if (base != cur)
1379 xmlBufferAdd(buf, base, cur - base);
1380 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1381 cur++;
1382 base = cur;
1383 } else if (*cur == '"') {
1384 if (base != cur)
1385 xmlBufferAdd(buf, base, cur - base);
1386 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1387 cur++;
1388 base = cur;
1389 } else if (*cur == '<') {
1390 if (base != cur)
1391 xmlBufferAdd(buf, base, cur - base);
1392 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1393 cur++;
1394 base = cur;
1395 } else if (*cur == '>') {
1396 if (base != cur)
1397 xmlBufferAdd(buf, base, cur - base);
1398 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1399 cur++;
1400 base = cur;
1401 } else if (*cur == '&') {
1402 if (base != cur)
1403 xmlBufferAdd(buf, base, cur - base);
1404 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1405 cur++;
1406 base = cur;
1407 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1408 (doc->encoding == NULL))) {
1409 /*
1410 * We assume we have UTF-8 content.
1411 */
1412 char tmp[10];
1413 int val = 0, l = 1;
1414
1415 if (base != cur)
1416 xmlBufferAdd(buf, base, cur - base);
1417 if (*cur < 0xC0) {
1418 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1419 if (doc != NULL)
1420 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1421 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1422 tmp[sizeof(tmp) - 1] = 0;
1423 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1424 cur++;
1425 base = cur;
1426 continue;
1427 } else if (*cur < 0xE0) {
1428 val = (cur[0]) & 0x1F;
1429 val <<= 6;
1430 val |= (cur[1]) & 0x3F;
1431 l = 2;
1432 } else if (*cur < 0xF0) {
1433 val = (cur[0]) & 0x0F;
1434 val <<= 6;
1435 val |= (cur[1]) & 0x3F;
1436 val <<= 6;
1437 val |= (cur[2]) & 0x3F;
1438 l = 3;
1439 } else if (*cur < 0xF8) {
1440 val = (cur[0]) & 0x07;
1441 val <<= 6;
1442 val |= (cur[1]) & 0x3F;
1443 val <<= 6;
1444 val |= (cur[2]) & 0x3F;
1445 val <<= 6;
1446 val |= (cur[3]) & 0x3F;
1447 l = 4;
1448 }
1449 if ((l == 1) || (!IS_CHAR(val))) {
1450 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1451 if (doc != NULL)
1452 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1453 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1454 tmp[sizeof(tmp) - 1] = 0;
1455 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1456 cur++;
1457 base = cur;
1458 continue;
1459 }
1460 /*
1461 * We could do multiple things here. Just save
1462 * as a char ref
1463 */
1464 snprintf(tmp, sizeof(tmp), "&#x%X;", val);
1465 tmp[sizeof(tmp) - 1] = 0;
1466 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1467 cur += l;
1468 base = cur;
1469 } else {
1470 cur++;
1471 }
1472 }
1473 if (base != cur)
1474 xmlBufferAdd(buf, base, cur - base);
1475}
1476
1477/**
1478 * xmlNodeDump:
1479 * @buf: the XML buffer output
1480 * @doc: the document
1481 * @cur: the current node
1482 * @level: the imbrication level for indenting
1483 * @format: is formatting allowed
1484 *
1485 * Dump an XML node, recursive behaviour,children are printed too.
1486 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1487 * or xmlKeepBlanksDefault(0) was called
1488 *
1489 * Returns the number of bytes written to the buffer or -1 in case of error
1490 */
1491int
1492xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1493 int format)
1494{
1495 unsigned int use;
1496 int ret;
1497 xmlOutputBufferPtr outbuf;
1498
1499 xmlInitParser();
1500
1501 if (cur == NULL) {
1502#ifdef DEBUG_TREE
1503 xmlGenericError(xmlGenericErrorContext,
1504 "xmlNodeDump : node == NULL\n");
1505#endif
1506 return (-1);
1507 }
1508 if (buf == NULL) {
1509#ifdef DEBUG_TREE
1510 xmlGenericError(xmlGenericErrorContext,
1511 "xmlNodeDump : buf == NULL\n");
1512#endif
1513 return (-1);
1514 }
1515 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1516 if (outbuf == NULL) {
1517 xmlSaveErrMemory("creating buffer");
1518 return (-1);
1519 }
1520 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1521 outbuf->buffer = buf;
1522 outbuf->encoder = NULL;
1523 outbuf->writecallback = NULL;
1524 outbuf->closecallback = NULL;
1525 outbuf->context = NULL;
1526 outbuf->written = 0;
1527
1528 use = buf->use;
1529 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1530 xmlFree(outbuf);
1531 ret = buf->use - use;
1532 return (ret);
1533}
1534
1535/**
1536 * xmlElemDump:
1537 * @f: the FILE * for the output
1538 * @doc: the document
1539 * @cur: the current node
1540 *
1541 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1542 */
1543void
1544xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1545{
1546 xmlOutputBufferPtr outbuf;
1547
1548 xmlInitParser();
1549
1550 if (cur == NULL) {
1551#ifdef DEBUG_TREE
1552 xmlGenericError(xmlGenericErrorContext,
1553 "xmlElemDump : cur == NULL\n");
1554#endif
1555 return;
1556 }
1557#ifdef DEBUG_TREE
1558 if (doc == NULL) {
1559 xmlGenericError(xmlGenericErrorContext,
1560 "xmlElemDump : doc == NULL\n");
1561 }
1562#endif
1563
1564 outbuf = xmlOutputBufferCreateFile(f, NULL);
1565 if (outbuf == NULL)
1566 return;
1567 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1568#ifdef LIBXML_HTML_ENABLED
1569 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1570#else
1571 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1572#endif /* LIBXML_HTML_ENABLED */
1573 } else
1574 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1575 xmlOutputBufferClose(outbuf);
1576}
1577
1578/************************************************************************
1579 * *
1580 * Saving functions front-ends *
1581 * *
1582 ************************************************************************/
1583
1584/**
1585 * xmlNodeDumpOutput:
1586 * @buf: the XML buffer output
1587 * @doc: the document
1588 * @cur: the current node
1589 * @level: the imbrication level for indenting
1590 * @format: is formatting allowed
1591 * @encoding: an optional encoding string
1592 *
1593 * Dump an XML node, recursive behaviour, children are printed too.
1594 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1595 * or xmlKeepBlanksDefault(0) was called
1596 */
1597void
1598xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1599 int level, int format, const char *encoding)
1600{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001601 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001602#ifdef LIBXML_HTML_ENABLED
1603 xmlDtdPtr dtd;
1604 int is_xhtml = 0;
1605#endif
1606
1607 xmlInitParser();
1608
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001609 memset(&ctxt, 0, sizeof(ctxt));
1610 ctxt.doc = doc;
1611 ctxt.buf = buf;
1612 ctxt.level = level;
1613 ctxt.format = format;
1614 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001615 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001616
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001617#ifdef LIBXML_HTML_ENABLED
1618 dtd = xmlGetIntSubset(doc);
1619 if (dtd != NULL) {
1620 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1621 if (is_xhtml < 0)
1622 is_xhtml = 0;
1623 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1624 (cur->type == XML_ELEMENT_NODE) &&
1625 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1626 if (encoding != NULL)
1627 htmlSetMetaEncoding((htmlDocPtr) doc,
1628 (const xmlChar *) encoding);
1629 else
1630 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1631 }
1632 }
1633
1634 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001635 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001636 else
1637#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001638 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001639}
1640
1641/**
1642 * xmlDocDumpFormatMemoryEnc:
1643 * @out_doc: Document to generate XML text from
1644 * @doc_txt_ptr: Memory pointer for allocated XML text
1645 * @doc_txt_len: Length of the generated XML text
1646 * @txt_encoding: Character encoding to use when generating XML text
1647 * @format: should formatting spaces been added
1648 *
1649 * Dump the current DOM tree into memory using the character encoding specified
1650 * by the caller. Note it is up to the caller of this function to free the
1651 * allocated memory with xmlFree().
1652 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1653 * or xmlKeepBlanksDefault(0) was called
1654 */
1655
1656void
1657xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1658 int * doc_txt_len, const char * txt_encoding,
1659 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001660 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001661 int dummy = 0;
1662 xmlOutputBufferPtr out_buff = NULL;
1663 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1664
1665 if (doc_txt_len == NULL) {
1666 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1667 }
1668
1669 if (doc_txt_ptr == NULL) {
1670 *doc_txt_len = 0;
1671 return;
1672 }
1673
1674 *doc_txt_ptr = NULL;
1675 *doc_txt_len = 0;
1676
1677 if (out_doc == NULL) {
1678 /* No document, no output */
1679 return;
1680 }
1681
1682 /*
1683 * Validate the encoding value, if provided.
1684 * This logic is copied from xmlSaveFileEnc.
1685 */
1686
1687 if (txt_encoding == NULL)
1688 txt_encoding = (const char *) out_doc->encoding;
1689 if (txt_encoding != NULL) {
1690 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1691 if ( conv_hdlr == NULL ) {
1692 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1693 txt_encoding);
1694 return;
1695 }
1696 }
1697
1698 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1699 xmlSaveErrMemory("creating buffer");
1700 return;
1701 }
1702
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001703 memset(&ctxt, 0, sizeof(ctxt));
1704 ctxt.doc = out_doc;
1705 ctxt.buf = out_buff;
1706 ctxt.level = 0;
1707 ctxt.format = format;
1708 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001709 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001710 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001711 xmlOutputBufferFlush(out_buff);
1712 if (out_buff->conv != NULL) {
1713 *doc_txt_len = out_buff->conv->use;
1714 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1715 } else {
1716 *doc_txt_len = out_buff->buffer->use;
1717 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1718 }
1719 (void)xmlOutputBufferClose(out_buff);
1720
1721 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1722 *doc_txt_len = 0;
1723 xmlSaveErrMemory("creating output");
1724 }
1725
1726 return;
1727}
1728
1729/**
1730 * xmlDocDumpMemory:
1731 * @cur: the document
1732 * @mem: OUT: the memory pointer
1733 * @size: OUT: the memory length
1734 *
1735 * Dump an XML document in memory and return the #xmlChar * and it's size
1736 * in bytes. It's up to the caller to free the memory with xmlFree().
1737 * The resulting byte array is zero terminated, though the last 0 is not
1738 * included in the returned size.
1739 */
1740void
1741xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1742 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1743}
1744
1745/**
1746 * xmlDocDumpFormatMemory:
1747 * @cur: the document
1748 * @mem: OUT: the memory pointer
1749 * @size: OUT: the memory length
1750 * @format: should formatting spaces been added
1751 *
1752 *
1753 * Dump an XML document in memory and return the #xmlChar * and it's size.
1754 * It's up to the caller to free the memory with xmlFree().
1755 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1756 * or xmlKeepBlanksDefault(0) was called
1757 */
1758void
1759xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1760 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1761}
1762
1763/**
1764 * xmlDocDumpMemoryEnc:
1765 * @out_doc: Document to generate XML text from
1766 * @doc_txt_ptr: Memory pointer for allocated XML text
1767 * @doc_txt_len: Length of the generated XML text
1768 * @txt_encoding: Character encoding to use when generating XML text
1769 *
1770 * Dump the current DOM tree into memory using the character encoding specified
1771 * by the caller. Note it is up to the caller of this function to free the
1772 * allocated memory with xmlFree().
1773 */
1774
1775void
1776xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1777 int * doc_txt_len, const char * txt_encoding) {
1778 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1779 txt_encoding, 0);
1780}
1781
1782/**
1783 * xmlDocFormatDump:
1784 * @f: the FILE*
1785 * @cur: the document
1786 * @format: should formatting spaces been added
1787 *
1788 * Dump an XML document to an open FILE.
1789 *
1790 * returns: the number of bytes written or -1 in case of failure.
1791 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1792 * or xmlKeepBlanksDefault(0) was called
1793 */
1794int
1795xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001796 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001797 xmlOutputBufferPtr buf;
1798 const char * encoding;
1799 xmlCharEncodingHandlerPtr handler = NULL;
1800 int ret;
1801
1802 if (cur == NULL) {
1803#ifdef DEBUG_TREE
1804 xmlGenericError(xmlGenericErrorContext,
1805 "xmlDocDump : document == NULL\n");
1806#endif
1807 return(-1);
1808 }
1809 encoding = (const char *) cur->encoding;
1810
1811 if (encoding != NULL) {
1812 handler = xmlFindCharEncodingHandler(encoding);
1813 if (handler == NULL) {
1814 xmlFree((char *) cur->encoding);
1815 cur->encoding = NULL;
1816 }
1817 }
1818 buf = xmlOutputBufferCreateFile(f, handler);
1819 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001820 memset(&ctxt, 0, sizeof(ctxt));
1821 ctxt.doc = cur;
1822 ctxt.buf = buf;
1823 ctxt.level = 0;
1824 ctxt.format = format;
1825 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001826 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001827 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001828
1829 ret = xmlOutputBufferClose(buf);
1830 return(ret);
1831}
1832
1833/**
1834 * xmlDocDump:
1835 * @f: the FILE*
1836 * @cur: the document
1837 *
1838 * Dump an XML document to an open FILE.
1839 *
1840 * returns: the number of bytes written or -1 in case of failure.
1841 */
1842int
1843xmlDocDump(FILE *f, xmlDocPtr cur) {
1844 return(xmlDocFormatDump (f, cur, 0));
1845}
1846
1847/**
1848 * xmlSaveFileTo:
1849 * @buf: an output I/O buffer
1850 * @cur: the document
1851 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1852 *
1853 * Dump an XML document to an I/O buffer.
1854 *
1855 * returns: the number of bytes written or -1 in case of failure.
1856 */
1857int
1858xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001859 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001860 int ret;
1861
1862 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001863 memset(&ctxt, 0, sizeof(ctxt));
1864 ctxt.doc = cur;
1865 ctxt.buf = buf;
1866 ctxt.level = 0;
1867 ctxt.format = 0;
1868 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001869 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001870 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001871 ret = xmlOutputBufferClose(buf);
1872 return(ret);
1873}
1874
1875/**
1876 * xmlSaveFormatFileTo:
1877 * @buf: an output I/O buffer
1878 * @cur: the document
1879 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
1880 * @format: should formatting spaces been added
1881 *
1882 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001883 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001884 *
1885 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001886 */
1887int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001888xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
1889 const char *encoding, int format)
1890{
1891 xmlSaveCtxt ctxt;
1892 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001893
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001894 if (buf == NULL)
1895 return (0);
1896 memset(&ctxt, 0, sizeof(ctxt));
1897 ctxt.doc = cur;
1898 ctxt.buf = buf;
1899 ctxt.level = 0;
1900 ctxt.format = format;
1901 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001902 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001903 xmlDocContentDumpOutput(&ctxt, cur);
1904 ret = xmlOutputBufferClose(buf);
1905 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001906}
1907
1908/**
1909 * xmlSaveFormatFileEnc:
1910 * @filename: the filename or URL to output
1911 * @cur: the document being saved
1912 * @encoding: the name of the encoding to use or NULL.
1913 * @format: should formatting spaces be added.
1914 *
1915 * Dump an XML document to a file or an URL.
1916 *
1917 * Returns the number of bytes written or -1 in case of error.
1918 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1919 * or xmlKeepBlanksDefault(0) was called
1920 */
1921int
1922xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
1923 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001924 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001925 xmlOutputBufferPtr buf;
1926 xmlCharEncodingHandlerPtr handler = NULL;
1927 int ret;
1928
1929 if (cur == NULL)
1930 return(-1);
1931
1932 if (encoding == NULL)
1933 encoding = (const char *) cur->encoding;
1934
1935 if (encoding != NULL) {
1936
1937 handler = xmlFindCharEncodingHandler(encoding);
1938 if (handler == NULL)
1939 return(-1);
1940 }
1941
1942#ifdef HAVE_ZLIB_H
1943 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
1944#endif
1945 /*
1946 * save the content to a temp buffer.
1947 */
1948 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
1949 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001950 memset(&ctxt, 0, sizeof(ctxt));
1951 ctxt.doc = cur;
1952 ctxt.buf = buf;
1953 ctxt.level = 0;
1954 ctxt.format = format;
1955 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001956 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001957
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001958 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001959
1960 ret = xmlOutputBufferClose(buf);
1961 return(ret);
1962}
1963
1964
1965/**
1966 * xmlSaveFileEnc:
1967 * @filename: the filename (or URL)
1968 * @cur: the document
1969 * @encoding: the name of an encoding (or NULL)
1970 *
1971 * Dump an XML document, converting it to the given encoding
1972 *
1973 * returns: the number of bytes written or -1 in case of failure.
1974 */
1975int
1976xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
1977 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
1978}
1979
1980/**
1981 * xmlSaveFormatFile:
1982 * @filename: the filename (or URL)
1983 * @cur: the document
1984 * @format: should formatting spaces been added
1985 *
1986 * Dump an XML document to a file. Will use compression if
1987 * compiled in and enabled. If @filename is "-" the stdout file is
1988 * used. If @format is set then the document will be indented on output.
1989 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1990 * or xmlKeepBlanksDefault(0) was called
1991 *
1992 * returns: the number of bytes written or -1 in case of failure.
1993 */
1994int
1995xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
1996 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
1997}
1998
1999/**
2000 * xmlSaveFile:
2001 * @filename: the filename (or URL)
2002 * @cur: the document
2003 *
2004 * Dump an XML document to a file. Will use compression if
2005 * compiled in and enabled. If @filename is "-" the stdout file is
2006 * used.
2007 * returns: the number of bytes written or -1 in case of failure.
2008 */
2009int
2010xmlSaveFile(const char *filename, xmlDocPtr cur) {
2011 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2012}
2013
2014#endif /* LIBXML_OUTPUT_ENABLED */
2015