blob: aaa5da8af6daa6e045ac83014ec9dc314358a959 [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 Veillard3995bc32004-05-15 18:57:31 +000086 char indent[MAX_INDENT + 1]; /* array for indenting output */
Daniel Veillard753086a2004-03-28 16:12:44 +000087 int indent_nr;
88 int indent_size;
Daniel Veillard3995bc32004-05-15 18:57:31 +000089 xmlCharEncodingOutputFunc escape; /* used for element content */
90 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
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:
Rob Richards417b74d2006-08-15 23:14:24 +0000125 msg = "string is not in UTF-8\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000126 break;
127 case XML_SAVE_CHAR_INVALID:
Rob Richards417b74d2006-08-15 23:14:24 +0000128 msg = "invalid character value\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000129 break;
130 case XML_SAVE_UNKNOWN_ENCODING:
Rob Richards417b74d2006-08-15 23:14:24 +0000131 msg = "unknown encoding %s\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000132 break;
133 case XML_SAVE_NO_DOCTYPE:
Rob Richards417b74d2006-08-15 23:14:24 +0000134 msg = "document has no DOCTYPE\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000135 break;
136 default:
Rob Richards417b74d2006-08-15 23:14:24 +0000137 msg = "unexpected error number\n";
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000138 }
139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
140}
141
142/************************************************************************
143 * *
Daniel Veillard83a75e02004-05-14 21:50:42 +0000144 * Special escaping routines *
145 * *
146 ************************************************************************/
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000147static unsigned char *
148xmlSerializeHexCharRef(unsigned char *out, int val) {
149 unsigned char *ptr;
150
151 *out++ = '&';
152 *out++ = '#';
153 *out++ = 'x';
154 if (val < 0x10) ptr = out;
155 else if (val < 0x100) ptr = out + 1;
156 else if (val < 0x1000) ptr = out + 2;
157 else if (val < 0x10000) ptr = out + 3;
158 else if (val < 0x100000) ptr = out + 4;
159 else ptr = out + 5;
160 out = ptr + 1;
161 while (val > 0) {
162 switch (val & 0xF) {
163 case 0: *ptr-- = '0'; break;
164 case 1: *ptr-- = '1'; break;
165 case 2: *ptr-- = '2'; break;
166 case 3: *ptr-- = '3'; break;
167 case 4: *ptr-- = '4'; break;
168 case 5: *ptr-- = '5'; break;
169 case 6: *ptr-- = '6'; break;
170 case 7: *ptr-- = '7'; break;
171 case 8: *ptr-- = '8'; break;
172 case 9: *ptr-- = '9'; break;
173 case 0xA: *ptr-- = 'A'; break;
174 case 0xB: *ptr-- = 'B'; break;
175 case 0xC: *ptr-- = 'C'; break;
176 case 0xD: *ptr-- = 'D'; break;
177 case 0xE: *ptr-- = 'E'; break;
178 case 0xF: *ptr-- = 'F'; break;
179 default: *ptr-- = '0'; break;
180 }
181 val >>= 4;
182 }
183 *out++ = ';';
184 *out = 0;
185 return(out);
186}
187
Daniel Veillard83a75e02004-05-14 21:50:42 +0000188/**
189 * xmlEscapeEntities:
190 * @out: a pointer to an array of bytes to store the result
191 * @outlen: the length of @out
192 * @in: a pointer to an array of unescaped UTF-8 bytes
193 * @inlen: the length of @in
194 *
195 * Take a block of UTF-8 chars in and escape them. Used when there is no
196 * encoding specified.
197 *
198 * Returns 0 if success, or -1 otherwise
199 * The value of @inlen after return is the number of octets consumed
200 * if the return value is positive, else unpredictable.
201 * The value of @outlen after return is the number of octets consumed.
202 */
203static int
204xmlEscapeEntities(unsigned char* out, int *outlen,
205 const xmlChar* in, int *inlen) {
206 unsigned char* outstart = out;
207 const unsigned char* base = in;
208 unsigned char* outend = out + *outlen;
209 const unsigned char* inend;
210 int val;
211
212 inend = in + (*inlen);
213
214 while ((in < inend) && (out < outend)) {
215 if (*in == '<') {
216 if (outend - out < 4) break;
217 *out++ = '&';
218 *out++ = 'l';
219 *out++ = 't';
220 *out++ = ';';
221 in++;
222 continue;
223 } else if (*in == '>') {
224 if (outend - out < 4) break;
225 *out++ = '&';
226 *out++ = 'g';
227 *out++ = 't';
228 *out++ = ';';
229 in++;
230 continue;
231 } else if (*in == '&') {
232 if (outend - out < 5) break;
233 *out++ = '&';
234 *out++ = 'a';
235 *out++ = 'm';
236 *out++ = 'p';
237 *out++ = ';';
238 in++;
239 continue;
240 } else if (((*in >= 0x20) && (*in < 0x80)) ||
241 (*in == '\n') || (*in == '\t')) {
242 /*
243 * default case, just copy !
244 */
245 *out++ = *in++;
246 continue;
247 } else if (*in >= 0x80) {
248 /*
249 * We assume we have UTF-8 input.
250 */
Daniel Veillard83a75e02004-05-14 21:50:42 +0000251 if (outend - out < 10) break;
252
253 if (*in < 0xC0) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000254 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000255 in++;
256 goto error;
257 } else if (*in < 0xE0) {
258 if (inend - in < 2) break;
259 val = (in[0]) & 0x1F;
260 val <<= 6;
261 val |= (in[1]) & 0x3F;
262 in += 2;
263 } else if (*in < 0xF0) {
264 if (inend - in < 3) break;
265 val = (in[0]) & 0x0F;
266 val <<= 6;
267 val |= (in[1]) & 0x3F;
268 val <<= 6;
269 val |= (in[2]) & 0x3F;
270 in += 3;
271 } else if (*in < 0xF8) {
272 if (inend - in < 4) break;
273 val = (in[0]) & 0x07;
274 val <<= 6;
275 val |= (in[1]) & 0x3F;
276 val <<= 6;
277 val |= (in[2]) & 0x3F;
278 val <<= 6;
279 val |= (in[3]) & 0x3F;
280 in += 4;
281 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000282 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000283 in++;
284 goto error;
285 }
286 if (!IS_CHAR(val)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000287 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000288 in++;
289 goto error;
290 }
291
292 /*
293 * We could do multiple things here. Just save as a char ref
294 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000295 out = xmlSerializeHexCharRef(out, val);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000296 } else if (IS_BYTE_CHAR(*in)) {
297 if (outend - out < 6) break;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000298 out = xmlSerializeHexCharRef(out, *in++);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000299 } else {
300 xmlGenericError(xmlGenericErrorContext,
301 "xmlEscapeEntities : char out of range\n");
302 in++;
303 goto error;
304 }
305 }
306 *outlen = out - outstart;
307 *inlen = in - base;
308 return(0);
309error:
310 *outlen = out - outstart;
311 *inlen = in - base;
312 return(-1);
313}
314
315/************************************************************************
316 * *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000317 * Allocation and deallocation *
318 * *
319 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000320/**
321 * xmlSaveCtxtInit:
322 * @ctxt: the saving context
323 *
324 * Initialize a saving context
325 */
326static void
327xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
328{
329 int i;
William M. Brack12d37ab2005-02-21 13:54:07 +0000330 int len;
Daniel Veillard753086a2004-03-28 16:12:44 +0000331
332 if (ctxt == NULL) return;
Daniel Veillard3995bc32004-05-15 18:57:31 +0000333 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
334 ctxt->escape = xmlEscapeEntities;
William M. Brack12d37ab2005-02-21 13:54:07 +0000335 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
336 if ((xmlTreeIndentString == NULL) || (len == 0)) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000337 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
338 } else {
William M. Brack12d37ab2005-02-21 13:54:07 +0000339 ctxt->indent_size = len;
Daniel Veillard753086a2004-03-28 16:12:44 +0000340 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
341 for (i = 0;i < ctxt->indent_nr;i++)
342 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
343 ctxt->indent_size);
344 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
345 }
Rob Richards2ce51c02005-09-12 12:16:35 +0000346
Daniel Veillard9a00fd22005-11-09 08:56:26 +0000347 if (xmlSaveNoEmptyTags) {
348 ctxt->options |= XML_SAVE_NO_EMPTY;
349 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000350}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000351
352/**
353 * xmlFreeSaveCtxt:
354 *
355 * Free a saving context, destroying the ouptut in any remaining buffer
356 */
357static void
358xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
359{
360 if (ctxt == NULL) return;
361 if (ctxt->encoding != NULL)
362 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000363 if (ctxt->buf != NULL)
364 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000365 xmlFree(ctxt);
366}
367
368/**
369 * xmlNewSaveCtxt:
370 *
371 * Create a new saving context
372 *
373 * Returns the new structure or NULL in case of error
374 */
375static xmlSaveCtxtPtr
376xmlNewSaveCtxt(const char *encoding, int options)
377{
378 xmlSaveCtxtPtr ret;
379
380 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
381 if (ret == NULL) {
382 xmlSaveErrMemory("creating saving context");
383 return ( NULL );
384 }
385 memset(ret, 0, sizeof(xmlSaveCtxt));
Daniel Veillard6fc5db02005-01-16 00:05:58 +0000386
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000387 if (encoding != NULL) {
388 ret->handler = xmlFindCharEncodingHandler(encoding);
389 if (ret->handler == NULL) {
390 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
391 xmlFreeSaveCtxt(ret);
392 return(NULL);
393 }
394 ret->encoding = xmlStrdup((const xmlChar *)encoding);
Daniel Veillarddab39b52006-10-16 23:22:10 +0000395 ret->escape = NULL;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000396 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000397 xmlSaveCtxtInit(ret);
398
Rob Richards2ce51c02005-09-12 12:16:35 +0000399 /*
400 * Use the options
401 */
402
Daniel Veillard9a00fd22005-11-09 08:56:26 +0000403 /* Re-check this option as it may already have been set */
404 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
405 options |= XML_SAVE_NO_EMPTY;
406 }
Rob Richards2ce51c02005-09-12 12:16:35 +0000407
408 ret->options = options;
409 if (options & XML_SAVE_FORMAT)
410 ret->format = 1;
411
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000412 return(ret);
413}
414
415/************************************************************************
416 * *
417 * Dumping XML tree content to a simple buffer *
418 * *
419 ************************************************************************/
420/**
421 * xmlAttrSerializeContent:
422 * @buf: the XML buffer output
423 * @doc: the document
424 * @attr: the attribute pointer
425 *
426 * Serialize the attribute in the buffer
427 */
428static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000429xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000430{
431 xmlNodePtr children;
432
433 children = attr->children;
434 while (children != NULL) {
435 switch (children->type) {
436 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000437 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
438 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000439 break;
440 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000441 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
442 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000443 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000444 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000445 break;
446 default:
447 /* should not happen unless we have a badly built tree */
448 break;
449 }
450 children = children->next;
451 }
452}
453
454/************************************************************************
455 * *
456 * Dumping XML tree content to an I/O output buffer *
457 * *
458 ************************************************************************/
459
Daniel Veillardda3fee42008-09-01 13:08:57 +0000460static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
461 xmlOutputBufferPtr buf = ctxt->buf;
462
463 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
464 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
465 if (buf->encoder == NULL) {
466 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
467 (const char *)encoding);
468 return(-1);
469 }
470 buf->conv = xmlBufferCreate();
471 if (buf->conv == NULL) {
472 xmlCharEncCloseFunc(buf->encoder);
473 xmlSaveErrMemory("creating encoding buffer");
474 return(-1);
475 }
476 /*
477 * initialize the state, e.g. if outputting a BOM
478 */
479 xmlCharEncOutFunc(buf->encoder, buf->conv, NULL);
480 }
481 return(0);
482}
483
484static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
485 xmlOutputBufferPtr buf = ctxt->buf;
486 xmlOutputBufferFlush(buf);
487 xmlCharEncCloseFunc(buf->encoder);
488 xmlBufferFree(buf->conv);
489 buf->encoder = NULL;
490 buf->conv = NULL;
491 return(0);
492}
493
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000494#ifdef LIBXML_HTML_ENABLED
495static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000496xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000497#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000498static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
499static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000500void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
Daniel Veillarddab39b52006-10-16 23:22:10 +0000501static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000502
503/**
504 * xmlNsDumpOutput:
505 * @buf: the XML buffer output
506 * @cur: a namespace
507 *
508 * Dump a local Namespace definition.
509 * Should be called in the context of attributes dumps.
510 */
511static void
512xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillardce244ad2004-11-05 10:03:46 +0000513 if ((cur == NULL) || (buf == NULL)) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000514 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
515 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
516 return;
517
518 /* Within the context of an element attributes */
519 if (cur->prefix != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000520 xmlOutputBufferWrite(buf, 7, " xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000521 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
522 } else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000523 xmlOutputBufferWrite(buf, 6, " xmlns");
524 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000525 xmlBufferWriteQuotedString(buf->buffer, cur->href);
526 }
527}
528
529/**
530 * xmlNsListDumpOutput:
531 * @buf: the XML buffer output
532 * @cur: the first namespace
533 *
534 * Dump a list of local Namespace definitions.
535 * Should be called in the context of attributes dumps.
536 */
537void
538xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
539 while (cur != NULL) {
540 xmlNsDumpOutput(buf, cur);
541 cur = cur->next;
542 }
543}
544
545/**
546 * xmlDtdDumpOutput:
547 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000548 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000549 *
550 * Dump the XML document DTD, if any.
551 */
552static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000553xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
554 xmlOutputBufferPtr buf;
555 int format, level;
556 xmlDocPtr doc;
557
558 if (dtd == NULL) return;
559 if ((ctxt == NULL) || (ctxt->buf == NULL))
560 return;
561 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000562 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000563 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
564 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000565 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000566 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000567 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000568 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
569 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000570 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000571 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
572 }
573 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
Daniel Veillard41c4a752004-09-08 20:55:38 +0000574 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
575 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000576 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000577 return;
578 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000579 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard41c4a752004-09-08 20:55:38 +0000580 /*
581 * Dump the notations first they are not in the DTD children list
582 * Do this only on a standalone DTD or on the internal subset though.
583 */
584 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
585 (dtd->doc->intSubset == dtd))) {
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000586 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
587 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000588 format = ctxt->format;
589 level = ctxt->level;
590 doc = ctxt->doc;
591 ctxt->format = 0;
592 ctxt->level = -1;
593 ctxt->doc = dtd->doc;
594 xmlNodeListDumpOutput(ctxt, dtd->children);
595 ctxt->format = format;
596 ctxt->level = level;
597 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000598 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000599}
600
601/**
602 * xmlAttrDumpOutput:
603 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000604 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000605 *
606 * Dump an XML attribute
607 */
608static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000609xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
610 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000611
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000612 if (cur == NULL) return;
613 buf = ctxt->buf;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000614 if (buf == NULL) return;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000615 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000616 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
617 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000618 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000619 }
620 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000621 xmlOutputBufferWrite(buf, 2, "=\"");
622 xmlAttrSerializeContent(buf, cur);
623 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000624}
625
626/**
627 * xmlAttrListDumpOutput:
628 * @buf: the XML buffer output
629 * @doc: the document
630 * @cur: the first attribute pointer
631 * @encoding: an optional encoding string
632 *
633 * Dump a list of XML attributes
634 */
635static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000636xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
637 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000638 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000639 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000640 cur = cur->next;
641 }
642}
643
644
645
646/**
647 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000648 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000649 *
650 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000651 */
652static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000653xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000654 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000655
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000656 if (cur == NULL) return;
657 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000658 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000659 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillardbd444842007-03-20 08:47:29 +0000660 ((cur->type == XML_ELEMENT_NODE) ||
661 (cur->type == XML_COMMENT_NODE) ||
662 (cur->type == XML_PI_NODE)))
Daniel Veillard753086a2004-03-28 16:12:44 +0000663 xmlOutputBufferWrite(buf, ctxt->indent_size *
664 (ctxt->level > ctxt->indent_nr ?
665 ctxt->indent_nr : ctxt->level),
666 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000667 xmlNodeDumpOutputInternal(ctxt, cur);
668 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000669 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000670 }
671 cur = cur->next;
672 }
673}
674
Daniel Veillardda3fee42008-09-01 13:08:57 +0000675#ifdef LIBXML_HTML_ENABLED
676/**
677 * xmlNodeDumpOutputInternal:
678 * @cur: the current node
679 *
680 * Dump an HTML node, recursive behaviour, children are printed too.
681 */
682static int
683htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
684 const xmlChar *oldenc = NULL;
685 const xmlChar *oldctxtenc = ctxt->encoding;
686 const xmlChar *encoding = ctxt->encoding;
687 xmlOutputBufferPtr buf = ctxt->buf;
688 int switched_encoding = 0;
689 xmlDocPtr doc;
690
691 xmlInitParser();
692
Daniel Veillard141ebfa2009-09-02 14:58:13 +0200693 doc = cur->doc;
694 if (doc != NULL) {
Daniel Veillardda3fee42008-09-01 13:08:57 +0000695 oldenc = doc->encoding;
696 if (ctxt->encoding != NULL) {
697 doc->encoding = BAD_CAST ctxt->encoding;
698 } else if (doc->encoding != NULL) {
699 encoding = doc->encoding;
700 }
701 }
702
703 if ((encoding != NULL) && (doc != NULL))
704 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
705 if ((encoding == NULL) && (doc != NULL))
706 encoding = htmlGetMetaEncoding(doc);
707 if (encoding == NULL)
708 encoding = BAD_CAST "HTML";
709 if ((encoding != NULL) && (oldctxtenc == NULL) &&
710 (buf->encoder == NULL) && (buf->conv == NULL)) {
711 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
712 doc->encoding = oldenc;
713 return(-1);
714 }
715 switched_encoding = 1;
716 }
717 if (ctxt->options & XML_SAVE_FORMAT)
718 htmlNodeDumpFormatOutput(buf, doc, cur,
719 (const char *)encoding, 1);
720 else
721 htmlNodeDumpFormatOutput(buf, doc, cur,
722 (const char *)encoding, 0);
723 /*
724 * Restore the state of the saving context at the end of the document
725 */
726 if ((switched_encoding) && (oldctxtenc == NULL)) {
727 xmlSaveClearEncoding(ctxt);
728 }
729 if (doc != NULL)
730 doc->encoding = oldenc;
731 return(0);
732}
733#endif
734
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000735/**
736 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000737 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000738 *
739 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000740 */
741static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000742xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000743 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000744 xmlNodePtr tmp;
745 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000746 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000747
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000748 if (cur == NULL) return;
749 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000750 if (cur->type == XML_XINCLUDE_START)
751 return;
752 if (cur->type == XML_XINCLUDE_END)
753 return;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000754 if ((cur->type == XML_DOCUMENT_NODE) ||
755 (cur->type == XML_HTML_DOCUMENT_NODE)) {
756 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
757 return;
758 }
Daniel Veillardda3fee42008-09-01 13:08:57 +0000759#ifdef LIBXML_HTML_ENABLED
Daniel Veillard856d9282008-09-25 14:31:40 +0000760 if (ctxt->options & XML_SAVE_XHTML) {
761 xhtmlNodeDumpOutput(ctxt, cur);
762 return;
763 }
764 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
765 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
766 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
767 (ctxt->options & XML_SAVE_AS_HTML)) {
Daniel Veillardda3fee42008-09-01 13:08:57 +0000768 htmlNodeDumpOutputInternal(ctxt, cur);
769 return;
770 }
771#endif
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000772 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000773 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000774 return;
775 }
776 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000777 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000778 return;
779 }
780 if (cur->type == XML_ELEMENT_DECL) {
781 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
782 return;
783 }
784 if (cur->type == XML_ATTRIBUTE_DECL) {
785 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
786 return;
787 }
788 if (cur->type == XML_ENTITY_DECL) {
789 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
790 return;
791 }
792 if (cur->type == XML_TEXT_NODE) {
793 if (cur->content != NULL) {
William M. Brack4e1c2db2005-02-11 10:58:55 +0000794 if (cur->name != xmlStringTextNoenc) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000795 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000796 } else {
797 /*
798 * Disable escaping, needed for XSLT
799 */
800 xmlOutputBufferWriteString(buf, (const char *) cur->content);
801 }
802 }
803
804 return;
805 }
806 if (cur->type == XML_PI_NODE) {
807 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000808 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000809 xmlOutputBufferWriteString(buf, (const char *)cur->name);
810 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000811 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000812 xmlOutputBufferWriteString(buf, (const char *)cur->content);
813 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000814 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000815 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000816 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000817 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000818 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000819 }
820 return;
821 }
822 if (cur->type == XML_COMMENT_NODE) {
823 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000824 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000825 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000826 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000827 }
828 return;
829 }
830 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000831 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000832 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000833 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000834 return;
835 }
836 if (cur->type == XML_CDATA_SECTION_NODE) {
Daniel Veillardd0d2f092008-03-07 16:50:21 +0000837 if (cur->content == NULL || *cur->content == '\0') {
838 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000839 } else {
840 start = end = cur->content;
841 while (*end != '\0') {
842 if ((*end == ']') && (*(end + 1) == ']') &&
843 (*(end + 2) == '>')) {
844 end = end + 2;
845 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
846 xmlOutputBufferWrite(buf, end - start, (const char *)start);
847 xmlOutputBufferWrite(buf, 3, "]]>");
848 start = end;
849 }
850 end++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000851 }
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000852 if (start != end) {
853 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
854 xmlOutputBufferWriteString(buf, (const char *)start);
855 xmlOutputBufferWrite(buf, 3, "]]>");
856 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000857 }
858 return;
859 }
860 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000861 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000862 return;
863 }
864 if (cur->type == XML_NAMESPACE_DECL) {
865 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
866 return;
867 }
868
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000869 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000870 if (format == 1) {
871 tmp = cur->children;
872 while (tmp != NULL) {
873 if ((tmp->type == XML_TEXT_NODE) ||
874 (tmp->type == XML_CDATA_SECTION_NODE) ||
875 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000876 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000877 break;
878 }
879 tmp = tmp->next;
880 }
881 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000882 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000883 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
884 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000885 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000886 }
887
888 xmlOutputBufferWriteString(buf, (const char *)cur->name);
889 if (cur->nsDef)
890 xmlNsListDumpOutput(buf, cur->nsDef);
891 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000892 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000893
894 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
Rob Richards2ce51c02005-09-12 12:16:35 +0000895 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000896 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000897 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000898 return;
899 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000900 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000901 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000902 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000903 }
904 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000905 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000906 if (ctxt->level >= 0) ctxt->level++;
907 xmlNodeListDumpOutput(ctxt, cur->children);
908 if (ctxt->level > 0) ctxt->level--;
909 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000910 xmlOutputBufferWrite(buf, ctxt->indent_size *
911 (ctxt->level > ctxt->indent_nr ?
912 ctxt->indent_nr : ctxt->level),
913 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000914 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000915 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000916 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
917 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000918 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000919 }
920
921 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000922 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000923 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000924}
925
926/**
927 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000928 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000929 *
930 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000931 */
Daniel Veillarddab39b52006-10-16 23:22:10 +0000932static int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000933xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000934#ifdef LIBXML_HTML_ENABLED
935 xmlDtdPtr dtd;
936 int is_xhtml = 0;
937#endif
938 const xmlChar *oldenc = cur->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000939 const xmlChar *oldctxtenc = ctxt->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000940 const xmlChar *encoding = ctxt->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000941 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
942 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
943 xmlOutputBufferPtr buf = ctxt->buf;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000944 xmlCharEncoding enc;
Daniel Veillardda3fee42008-09-01 13:08:57 +0000945 int switched_encoding = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000946
947 xmlInitParser();
948
Daniel Veillardda3fee42008-09-01 13:08:57 +0000949 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
950 (cur->type != XML_DOCUMENT_NODE))
951 return(-1);
952
Daniel Veillarddab39b52006-10-16 23:22:10 +0000953 if (ctxt->encoding != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000954 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000955 } else if (cur->encoding != NULL) {
956 encoding = cur->encoding;
957 } else if (cur->charset != XML_CHAR_ENCODING_UTF8) {
958 encoding = (const xmlChar *)
959 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
960 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000961
Daniel Veillard856d9282008-09-25 14:31:40 +0000962 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
963 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
964 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
965 (ctxt->options & XML_SAVE_AS_HTML)) {
Daniel Veillardda3fee42008-09-01 13:08:57 +0000966#ifdef LIBXML_HTML_ENABLED
967 if (encoding != NULL)
968 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
969 if (encoding == NULL)
970 encoding = htmlGetMetaEncoding(cur);
971 if (encoding == NULL)
972 encoding = BAD_CAST "HTML";
973 if ((encoding != NULL) && (oldctxtenc == NULL) &&
974 (buf->encoder == NULL) && (buf->conv == NULL)) {
975 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
976 cur->encoding = oldenc;
Daniel Veillarddab39b52006-10-16 23:22:10 +0000977 return(-1);
978 }
Daniel Veillarddab39b52006-10-16 23:22:10 +0000979 }
Daniel Veillardda3fee42008-09-01 13:08:57 +0000980 if (ctxt->options & XML_SAVE_FORMAT)
981 htmlDocContentDumpFormatOutput(buf, cur,
982 (const char *)encoding, 1);
Daniel Veillard100e1802005-08-08 14:44:11 +0000983 else
Daniel Veillardda3fee42008-09-01 13:08:57 +0000984 htmlDocContentDumpFormatOutput(buf, cur,
985 (const char *)encoding, 0);
986 if (ctxt->encoding != NULL)
987 cur->encoding = oldenc;
988 return(0);
989#else
990 return(-1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000991#endif
Daniel Veillard856d9282008-09-25 14:31:40 +0000992 } else if ((cur->type == XML_DOCUMENT_NODE) ||
993 (ctxt->options & XML_SAVE_AS_XML) ||
994 (ctxt->options & XML_SAVE_XHTML)) {
Daniel Veillardda3fee42008-09-01 13:08:57 +0000995 enc = xmlParseCharEncoding((const char*) encoding);
996 if ((encoding != NULL) && (oldctxtenc == NULL) &&
997 (buf->encoder == NULL) && (buf->conv == NULL) &&
998 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
999 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1000 (enc != XML_CHAR_ENCODING_NONE) &&
1001 (enc != XML_CHAR_ENCODING_ASCII)) {
1002 /*
1003 * we need to switch to this encoding but just for this
1004 * document since we output the XMLDecl the conversion
1005 * must be done to not generate not well formed documents.
1006 */
1007 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1008 cur->encoding = oldenc;
1009 return(-1);
1010 }
1011 switched_encoding = 1;
1012 }
1013 if (ctxt->escape == xmlEscapeEntities)
1014 ctxt->escape = NULL;
1015 if (ctxt->escapeAttr == xmlEscapeEntities)
1016 ctxt->escapeAttr = NULL;
1017 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001018
Daniel Veillardda3fee42008-09-01 13:08:57 +00001019
1020 /*
1021 * Save the XML declaration
1022 */
1023 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1024 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1025 if (cur->version != NULL)
1026 xmlBufferWriteQuotedString(buf->buffer, cur->version);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001027 else
Daniel Veillardda3fee42008-09-01 13:08:57 +00001028 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1029 if (encoding != NULL) {
1030 xmlOutputBufferWrite(buf, 10, " encoding=");
1031 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1032 }
1033 switch (cur->standalone) {
1034 case 0:
1035 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1036 break;
1037 case 1:
1038 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1039 break;
1040 }
1041 xmlOutputBufferWrite(buf, 3, "?>\n");
1042 }
1043
1044#ifdef LIBXML_HTML_ENABLED
Daniel Veillard856d9282008-09-25 14:31:40 +00001045 if (ctxt->options & XML_SAVE_XHTML)
1046 is_xhtml = 1;
Daniel Veillardda3fee42008-09-01 13:08:57 +00001047 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1048 dtd = xmlGetIntSubset(cur);
1049 if (dtd != NULL) {
1050 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1051 if (is_xhtml < 0) is_xhtml = 0;
1052 }
1053 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001054#endif
Daniel Veillardda3fee42008-09-01 13:08:57 +00001055 if (cur->children != NULL) {
1056 xmlNodePtr child = cur->children;
1057
1058 while (child != NULL) {
1059 ctxt->level = 0;
1060#ifdef LIBXML_HTML_ENABLED
1061 if (is_xhtml)
1062 xhtmlNodeDumpOutput(ctxt, child);
1063 else
1064#endif
1065 xmlNodeDumpOutputInternal(ctxt, child);
1066 xmlOutputBufferWrite(buf, 1, "\n");
1067 child = child->next;
1068 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001069 }
1070 }
Daniel Veillardda3fee42008-09-01 13:08:57 +00001071
Daniel Veillarddab39b52006-10-16 23:22:10 +00001072 /*
1073 * Restore the state of the saving context at the end of the document
1074 */
Daniel Veillardda3fee42008-09-01 13:08:57 +00001075 if ((switched_encoding) && (oldctxtenc == NULL)) {
1076 xmlSaveClearEncoding(ctxt);
Daniel Veillarddab39b52006-10-16 23:22:10 +00001077 ctxt->escape = oldescape;
1078 ctxt->escapeAttr = oldescapeAttr;
1079 }
Daniel Veillardda3fee42008-09-01 13:08:57 +00001080 cur->encoding = oldenc;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001081 return(0);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001082}
1083
1084#ifdef LIBXML_HTML_ENABLED
1085/************************************************************************
1086 * *
1087 * Functions specific to XHTML serialization *
1088 * *
1089 ************************************************************************/
1090
1091/**
1092 * xhtmlIsEmpty:
1093 * @node: the node
1094 *
1095 * Check if a node is an empty xhtml node
1096 *
1097 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1098 */
1099static int
1100xhtmlIsEmpty(xmlNodePtr node) {
1101 if (node == NULL)
1102 return(-1);
1103 if (node->type != XML_ELEMENT_NODE)
1104 return(0);
1105 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1106 return(0);
1107 if (node->children != NULL)
1108 return(0);
1109 switch (node->name[0]) {
1110 case 'a':
1111 if (xmlStrEqual(node->name, BAD_CAST "area"))
1112 return(1);
1113 return(0);
1114 case 'b':
1115 if (xmlStrEqual(node->name, BAD_CAST "br"))
1116 return(1);
1117 if (xmlStrEqual(node->name, BAD_CAST "base"))
1118 return(1);
1119 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1120 return(1);
1121 return(0);
1122 case 'c':
1123 if (xmlStrEqual(node->name, BAD_CAST "col"))
1124 return(1);
1125 return(0);
1126 case 'f':
1127 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1128 return(1);
1129 return(0);
1130 case 'h':
1131 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1132 return(1);
1133 return(0);
1134 case 'i':
1135 if (xmlStrEqual(node->name, BAD_CAST "img"))
1136 return(1);
1137 if (xmlStrEqual(node->name, BAD_CAST "input"))
1138 return(1);
1139 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1140 return(1);
1141 return(0);
1142 case 'l':
1143 if (xmlStrEqual(node->name, BAD_CAST "link"))
1144 return(1);
1145 return(0);
1146 case 'm':
1147 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1148 return(1);
1149 return(0);
1150 case 'p':
1151 if (xmlStrEqual(node->name, BAD_CAST "param"))
1152 return(1);
1153 return(0);
1154 }
1155 return(0);
1156}
1157
1158/**
1159 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001160 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001161 *
1162 * Dump a list of XML attributes
1163 */
1164static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001165xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001166 xmlAttrPtr xml_lang = NULL;
1167 xmlAttrPtr lang = NULL;
1168 xmlAttrPtr name = NULL;
1169 xmlAttrPtr id = NULL;
1170 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001171 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001172
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001173 if (cur == NULL) return;
1174 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001175 parent = cur->parent;
1176 while (cur != NULL) {
1177 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1178 id = cur;
1179 else
1180 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1181 name = cur;
1182 else
1183 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1184 lang = cur;
1185 else
1186 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1187 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1188 xml_lang = cur;
1189 else if ((cur->ns == NULL) &&
1190 ((cur->children == NULL) ||
1191 (cur->children->content == NULL) ||
1192 (cur->children->content[0] == 0)) &&
1193 (htmlIsBooleanAttr(cur->name))) {
1194 if (cur->children != NULL)
1195 xmlFreeNode(cur->children);
1196 cur->children = xmlNewText(cur->name);
1197 if (cur->children != NULL)
1198 cur->children->parent = (xmlNodePtr) cur;
1199 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001200 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001201 cur = cur->next;
1202 }
1203 /*
1204 * C.8
1205 */
1206 if ((name != NULL) && (id == NULL)) {
1207 if ((parent != NULL) && (parent->name != NULL) &&
1208 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1209 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1210 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1211 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1212 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1213 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1214 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1215 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1216 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001217 xmlOutputBufferWrite(buf, 5, " id=\"");
1218 xmlAttrSerializeContent(buf, name);
1219 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001220 }
1221 }
1222 /*
1223 * C.7.
1224 */
1225 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001226 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1227 xmlAttrSerializeContent(buf, lang);
1228 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001229 } else
1230 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001231 xmlOutputBufferWrite(buf, 7, " lang=\"");
1232 xmlAttrSerializeContent(buf, xml_lang);
1233 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001234 }
1235}
1236
1237/**
1238 * xhtmlNodeListDumpOutput:
1239 * @buf: the XML buffer output
1240 * @doc: the XHTML document
1241 * @cur: the first node
1242 * @level: the imbrication level for indenting
1243 * @format: is formatting allowed
1244 * @encoding: an optional encoding string
1245 *
1246 * Dump an XML node list, recursive behaviour, children are printed too.
1247 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1248 * or xmlKeepBlanksDefault(0) was called
1249 */
1250static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001251xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001252 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001253
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001254 if (cur == NULL) return;
1255 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001256 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001257 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001258 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001259 xmlOutputBufferWrite(buf, ctxt->indent_size *
1260 (ctxt->level > ctxt->indent_nr ?
1261 ctxt->indent_nr : ctxt->level),
1262 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001263 xhtmlNodeDumpOutput(ctxt, cur);
1264 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001265 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001266 }
1267 cur = cur->next;
1268 }
1269}
1270
1271/**
1272 * xhtmlNodeDumpOutput:
1273 * @buf: the XML buffer output
1274 * @doc: the XHTML document
1275 * @cur: the current node
1276 * @level: the imbrication level for indenting
1277 * @format: is formatting allowed
1278 * @encoding: an optional encoding string
1279 *
1280 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001281 */
1282static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001283xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Rob Richards31f73022005-08-26 15:33:26 +00001284 int format, addmeta = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001285 xmlNodePtr tmp;
1286 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001287 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001288
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001289 if (cur == NULL) return;
Daniel Veillard60071ae2005-09-12 00:03:43 +00001290 if ((cur->type == XML_DOCUMENT_NODE) ||
1291 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1292 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1293 return;
1294 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001295 if (cur->type == XML_XINCLUDE_START)
1296 return;
1297 if (cur->type == XML_XINCLUDE_END)
1298 return;
1299 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001300 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001301 return;
1302 }
Rob Richards2e2691b2005-10-21 14:45:16 +00001303 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
1304 xhtmlNodeListDumpOutput(ctxt, cur->children);
1305 return;
1306 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001307 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001308 if (cur->type == XML_ELEMENT_DECL) {
1309 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1310 return;
1311 }
1312 if (cur->type == XML_ATTRIBUTE_DECL) {
1313 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1314 return;
1315 }
1316 if (cur->type == XML_ENTITY_DECL) {
1317 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1318 return;
1319 }
1320 if (cur->type == XML_TEXT_NODE) {
1321 if (cur->content != NULL) {
1322 if ((cur->name == xmlStringText) ||
1323 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001324 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001325 } else {
1326 /*
1327 * Disable escaping, needed for XSLT
1328 */
1329 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1330 }
1331 }
1332
1333 return;
1334 }
1335 if (cur->type == XML_PI_NODE) {
1336 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001337 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001338 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1339 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001340 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001341 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1342 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001343 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001344 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001345 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001346 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001347 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001348 }
1349 return;
1350 }
1351 if (cur->type == XML_COMMENT_NODE) {
1352 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001353 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001354 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001355 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001356 }
1357 return;
1358 }
1359 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001360 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001361 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001362 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001363 return;
1364 }
1365 if (cur->type == XML_CDATA_SECTION_NODE) {
Daniel Veillardd0d2f092008-03-07 16:50:21 +00001366 if (cur->content == NULL || *cur->content == '\0') {
1367 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1368 } else {
1369 start = end = cur->content;
1370 while (*end != '\0') {
1371 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1372 end = end + 2;
1373 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1374 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1375 xmlOutputBufferWrite(buf, 3, "]]>");
1376 start = end;
1377 }
1378 end++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001379 }
Daniel Veillardd0d2f092008-03-07 16:50:21 +00001380 if (start != end) {
1381 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1382 xmlOutputBufferWriteString(buf, (const char *)start);
1383 xmlOutputBufferWrite(buf, 3, "]]>");
1384 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001385 }
1386 return;
1387 }
Daniel Veillarda76a81f2007-10-10 08:28:18 +00001388 if (cur->type == XML_ATTRIBUTE_NODE) {
1389 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1390 return;
1391 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001392
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001393 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001394 if (format == 1) {
1395 tmp = cur->children;
1396 while (tmp != NULL) {
1397 if ((tmp->type == XML_TEXT_NODE) ||
1398 (tmp->type == XML_ENTITY_REF_NODE)) {
1399 format = 0;
1400 break;
1401 }
1402 tmp = tmp->next;
1403 }
1404 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001405 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001406 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1407 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001408 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001409 }
1410
1411 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1412 if (cur->nsDef)
1413 xmlNsListDumpOutput(buf, cur->nsDef);
1414 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1415 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1416 /*
1417 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1418 */
1419 xmlOutputBufferWriteString(buf,
1420 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1421 }
1422 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001423 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001424
Rob Richards31f73022005-08-26 15:33:26 +00001425 if ((cur->type == XML_ELEMENT_NODE) &&
1426 (cur->parent != NULL) &&
1427 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1428 xmlStrEqual(cur->name, BAD_CAST"head") &&
1429 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1430
1431 tmp = cur->children;
1432 while (tmp != NULL) {
1433 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1434 xmlChar *httpequiv;
1435
1436 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
Rob Richards07b72002005-09-03 14:56:36 +00001437 if (httpequiv != NULL) {
1438 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1439 xmlFree(httpequiv);
1440 break;
1441 }
Rob Richards31f73022005-08-26 15:33:26 +00001442 xmlFree(httpequiv);
Rob Richards31f73022005-08-26 15:33:26 +00001443 }
Rob Richards31f73022005-08-26 15:33:26 +00001444 }
1445 tmp = tmp->next;
1446 }
1447 if (tmp == NULL)
1448 addmeta = 1;
1449 }
1450
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001451 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1452 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
Rob Richards31f73022005-08-26 15:33:26 +00001453 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001454 /*
1455 * C.2. Empty Elements
1456 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001457 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001458 } else {
Rob Richards31f73022005-08-26 15:33:26 +00001459 if (addmeta == 1) {
1460 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards2ce51c02005-09-12 12:16:35 +00001461 if (ctxt->format) {
1462 xmlOutputBufferWrite(buf, 1, "\n");
1463 if (xmlIndentTreeOutput)
1464 xmlOutputBufferWrite(buf, ctxt->indent_size *
1465 (ctxt->level + 1 > ctxt->indent_nr ?
1466 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1467 }
Rob Richards31f73022005-08-26 15:33:26 +00001468 xmlOutputBufferWriteString(buf,
1469 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1470 if (ctxt->encoding) {
1471 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1472 } else {
1473 xmlOutputBufferWrite(buf, 5, "UTF-8");
1474 }
Rob Richards2ce51c02005-09-12 12:16:35 +00001475 xmlOutputBufferWrite(buf, 4, "\" />");
1476 if (ctxt->format)
1477 xmlOutputBufferWrite(buf, 1, "\n");
1478 } else {
1479 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001480 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001481 /*
1482 * C.3. Element Minimization and Empty Element Content
1483 */
Rob Richards2ce51c02005-09-12 12:16:35 +00001484 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001485 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1486 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001487 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001488 }
1489 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001490 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001491 }
1492 return;
1493 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001494 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001495 if (addmeta == 1) {
Rob Richards2ce51c02005-09-12 12:16:35 +00001496 if (ctxt->format) {
1497 xmlOutputBufferWrite(buf, 1, "\n");
1498 if (xmlIndentTreeOutput)
1499 xmlOutputBufferWrite(buf, ctxt->indent_size *
1500 (ctxt->level + 1 > ctxt->indent_nr ?
1501 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1502 }
Rob Richards31f73022005-08-26 15:33:26 +00001503 xmlOutputBufferWriteString(buf,
1504 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1505 if (ctxt->encoding) {
1506 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1507 } else {
1508 xmlOutputBufferWrite(buf, 5, "UTF-8");
1509 }
1510 xmlOutputBufferWrite(buf, 4, "\" />");
1511 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001512 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001513 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001514 }
1515
Kasimier T. Buchcik7b4e2e22006-07-13 13:07:11 +00001516#if 0
1517 /*
1518 * This was removed due to problems with HTML processors.
1519 * See bug #345147.
Daniel Veillarddab39b52006-10-16 23:22:10 +00001520 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001521 /*
1522 * 4.8. Script and Style elements
1523 */
1524 if ((cur->type == XML_ELEMENT_NODE) &&
1525 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1526 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1527 ((cur->ns == NULL) ||
1528 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1529 xmlNodePtr child = cur->children;
1530
1531 while (child != NULL) {
Daniel Veillarddbd61052005-09-12 14:03:26 +00001532 if (child->type == XML_TEXT_NODE) {
1533 if ((xmlStrchr(child->content, '<') == NULL) &&
1534 (xmlStrchr(child->content, '&') == NULL) &&
1535 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
1536 /* Nothing to escape, so just output as is... */
1537 /* FIXME: Should we do something about "--" also? */
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001538 int level = ctxt->level;
1539 int indent = ctxt->format;
1540
1541 ctxt->level = 0;
1542 ctxt->format = 0;
Daniel Veillarddbd61052005-09-12 14:03:26 +00001543 xmlOutputBufferWriteString(buf, (const char *) child->content);
1544 /* (We cannot use xhtmlNodeDumpOutput() here because
1545 * we wish to leave '>' unescaped!) */
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001546 ctxt->level = level;
1547 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001548 } else {
Daniel Veillarddbd61052005-09-12 14:03:26 +00001549 /* We must use a CDATA section. Unfortunately,
1550 * this will break CSS and JavaScript when read by
1551 * a browser in HTML4-compliant mode. :-( */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001552 start = end = child->content;
1553 while (*end != '\0') {
1554 if (*end == ']' &&
1555 *(end + 1) == ']' &&
1556 *(end + 2) == '>') {
1557 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001558 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001559 xmlOutputBufferWrite(buf, end - start,
1560 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001561 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001562 start = end;
1563 }
1564 end++;
1565 }
1566 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001567 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1568 xmlOutputBufferWrite(buf, end - start,
1569 (const char *)start);
1570 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001571 }
1572 }
1573 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001574 int level = ctxt->level;
1575 int indent = ctxt->format;
1576
1577 ctxt->level = 0;
1578 ctxt->format = 0;
1579 xhtmlNodeDumpOutput(ctxt, child);
1580 ctxt->level = level;
1581 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001582 }
1583 child = child->next;
1584 }
Kasimier T. Buchcik7b4e2e22006-07-13 13:07:11 +00001585 }
1586#endif
1587
1588 if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001589 int indent = ctxt->format;
1590
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001591 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001592 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001593 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001594 xhtmlNodeListDumpOutput(ctxt, cur->children);
1595 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001596 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001597 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001598 xmlOutputBufferWrite(buf, ctxt->indent_size *
1599 (ctxt->level > ctxt->indent_nr ?
1600 ctxt->indent_nr : ctxt->level),
1601 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001602 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001603 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001604 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1605 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001606 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001607 }
1608
1609 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001610 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001611}
1612#endif
1613
1614/************************************************************************
1615 * *
1616 * Public entry points *
1617 * *
1618 ************************************************************************/
1619
1620/**
1621 * xmlSaveToFd:
1622 * @fd: a file descriptor number
1623 * @encoding: the encoding name to use or NULL
1624 * @options: a set of xmlSaveOptions
1625 *
1626 * Create a document saving context serializing to a file descriptor
1627 * with the encoding and the options given.
1628 *
1629 * Returns a new serialization context or NULL in case of error.
1630 */
1631xmlSaveCtxtPtr
1632xmlSaveToFd(int fd, const char *encoding, int options)
1633{
1634 xmlSaveCtxtPtr ret;
1635
1636 ret = xmlNewSaveCtxt(encoding, options);
1637 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001638 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1639 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001640 xmlFreeSaveCtxt(ret);
1641 return(NULL);
1642 }
1643 return(ret);
1644}
1645
1646/**
1647 * xmlSaveToFilename:
1648 * @filename: a file name or an URL
1649 * @encoding: the encoding name to use or NULL
1650 * @options: a set of xmlSaveOptions
1651 *
1652 * Create a document saving context serializing to a filename or possibly
1653 * to an URL (but this is less reliable) with the encoding and the options
1654 * given.
1655 *
1656 * Returns a new serialization context or NULL in case of error.
1657 */
1658xmlSaveCtxtPtr
1659xmlSaveToFilename(const char *filename, const char *encoding, int options)
1660{
1661 xmlSaveCtxtPtr ret;
1662 int compression = 0; /* TODO handle compression option */
1663
1664 ret = xmlNewSaveCtxt(encoding, options);
1665 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001666 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001667 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001668 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001669 xmlFreeSaveCtxt(ret);
1670 return(NULL);
1671 }
1672 return(ret);
1673}
1674
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001675/**
1676 * xmlSaveToBuffer:
1677 * @buffer: a buffer
1678 * @encoding: the encoding name to use or NULL
1679 * @options: a set of xmlSaveOptions
1680 *
1681 * Create a document saving context serializing to a buffer
1682 * with the encoding and the options given
1683 *
1684 * Returns a new serialization context or NULL in case of error.
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001685 */
1686
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001687xmlSaveCtxtPtr
1688xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1689{
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001690 xmlSaveCtxtPtr ret;
1691 xmlOutputBufferPtr out_buff;
1692 xmlCharEncodingHandlerPtr handler;
1693
1694 ret = xmlNewSaveCtxt(encoding, options);
1695 if (ret == NULL) return(NULL);
1696
1697 if (encoding != NULL) {
1698 handler = xmlFindCharEncodingHandler(encoding);
1699 if (handler == NULL) {
1700 xmlFree(ret);
1701 return(NULL);
1702 }
1703 } else
1704 handler = NULL;
1705 out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
1706 if (out_buff == NULL) {
1707 xmlFree(ret);
1708 if (handler) xmlCharEncCloseFunc(handler);
1709 return(NULL);
1710 }
1711
1712 ret->buf = out_buff;
1713 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001714}
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001715
1716/**
1717 * xmlSaveToIO:
1718 * @iowrite: an I/O write function
1719 * @ioclose: an I/O close function
1720 * @ioctx: an I/O handler
1721 * @encoding: the encoding name to use or NULL
1722 * @options: a set of xmlSaveOptions
1723 *
1724 * Create a document saving context serializing to a file descriptor
1725 * with the encoding and the options given
1726 *
1727 * Returns a new serialization context or NULL in case of error.
1728 */
1729xmlSaveCtxtPtr
1730xmlSaveToIO(xmlOutputWriteCallback iowrite,
1731 xmlOutputCloseCallback ioclose,
1732 void *ioctx, const char *encoding, int options)
1733{
1734 xmlSaveCtxtPtr ret;
1735
1736 ret = xmlNewSaveCtxt(encoding, options);
1737 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001738 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1739 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001740 xmlFreeSaveCtxt(ret);
1741 return(NULL);
1742 }
1743 return(ret);
1744}
1745
1746/**
1747 * xmlSaveDoc:
1748 * @ctxt: a document saving context
1749 * @doc: a document
1750 *
1751 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001752 * TODO: The function is not fully implemented yet as it does not return the
1753 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001754 *
1755 * Returns the number of byte written or -1 in case of error
1756 */
1757long
1758xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1759{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001760 long ret = 0;
1761
Daniel Veillardce682bc2004-11-05 17:22:25 +00001762 if ((ctxt == NULL) || (doc == NULL)) return(-1);
Daniel Veillarddab39b52006-10-16 23:22:10 +00001763 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1764 return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001765 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001766}
1767
1768/**
1769 * xmlSaveTree:
1770 * @ctxt: a document saving context
Daniel Veillard681e9042006-09-29 09:16:00 +00001771 * @node: the top node of the subtree to save
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001772 *
1773 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001774 * TODO: The function is not fully implemented yet as it does not return the
1775 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001776 *
1777 * Returns the number of byte written or -1 in case of error
1778 */
1779long
1780xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1781{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001782 long ret = 0;
1783
Daniel Veillardce682bc2004-11-05 17:22:25 +00001784 if ((ctxt == NULL) || (node == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001785 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001786 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001787}
1788
1789/**
1790 * xmlSaveFlush:
1791 * @ctxt: a document saving context
1792 *
1793 * Flush a document saving context, i.e. make sure that all bytes have
1794 * been output.
1795 *
1796 * Returns the number of byte written or -1 in case of error.
1797 */
1798int
1799xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1800{
1801 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001802 if (ctxt->buf == NULL) return(-1);
1803 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001804}
1805
1806/**
1807 * xmlSaveClose:
1808 * @ctxt: a document saving context
1809 *
1810 * Close a document saving context, i.e. make sure that all bytes have
1811 * been output and free the associated data.
1812 *
1813 * Returns the number of byte written or -1 in case of error.
1814 */
1815int
1816xmlSaveClose(xmlSaveCtxtPtr ctxt)
1817{
1818 int ret;
1819
1820 if (ctxt == NULL) return(-1);
1821 ret = xmlSaveFlush(ctxt);
1822 xmlFreeSaveCtxt(ctxt);
1823 return(ret);
1824}
1825
Daniel Veillard3995bc32004-05-15 18:57:31 +00001826/**
1827 * xmlSaveSetEscape:
1828 * @ctxt: a document saving context
1829 * @escape: the escaping function
1830 *
1831 * Set a custom escaping function to be used for text in element content
1832 *
1833 * Returns 0 if successful or -1 in case of error.
1834 */
1835int
1836xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1837{
1838 if (ctxt == NULL) return(-1);
1839 ctxt->escape = escape;
1840 return(0);
1841}
1842
1843/**
1844 * xmlSaveSetAttrEscape:
1845 * @ctxt: a document saving context
1846 * @escape: the escaping function
1847 *
1848 * Set a custom escaping function to be used for text in attribute content
1849 *
1850 * Returns 0 if successful or -1 in case of error.
1851 */
1852int
1853xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1854{
1855 if (ctxt == NULL) return(-1);
1856 ctxt->escapeAttr = escape;
1857 return(0);
1858}
1859
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001860/************************************************************************
1861 * *
1862 * Public entry points based on buffers *
1863 * *
1864 ************************************************************************/
1865/**
1866 * xmlAttrSerializeTxtContent:
1867 * @buf: the XML buffer output
1868 * @doc: the document
1869 * @attr: the attribute node
1870 * @string: the text content
1871 *
1872 * Serialize text attribute values to an xml simple buffer
1873 */
1874void
1875xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001876 xmlAttrPtr attr, const xmlChar * string)
1877{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001878 xmlChar *base, *cur;
1879
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001880 if (string == NULL)
1881 return;
1882 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001883 while (*cur != 0) {
1884 if (*cur == '\n') {
1885 if (base != cur)
1886 xmlBufferAdd(buf, base, cur - base);
1887 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1888 cur++;
1889 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001890 } else if (*cur == '\r') {
1891 if (base != cur)
1892 xmlBufferAdd(buf, base, cur - base);
1893 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1894 cur++;
1895 base = cur;
1896 } else if (*cur == '\t') {
1897 if (base != cur)
1898 xmlBufferAdd(buf, base, cur - base);
1899 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1900 cur++;
1901 base = cur;
1902 } else if (*cur == '"') {
1903 if (base != cur)
1904 xmlBufferAdd(buf, base, cur - base);
1905 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1906 cur++;
1907 base = cur;
1908 } else if (*cur == '<') {
1909 if (base != cur)
1910 xmlBufferAdd(buf, base, cur - base);
1911 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1912 cur++;
1913 base = cur;
1914 } else if (*cur == '>') {
1915 if (base != cur)
1916 xmlBufferAdd(buf, base, cur - base);
1917 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1918 cur++;
1919 base = cur;
1920 } else if (*cur == '&') {
1921 if (base != cur)
1922 xmlBufferAdd(buf, base, cur - base);
1923 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1924 cur++;
1925 base = cur;
1926 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1927 (doc->encoding == NULL))) {
1928 /*
1929 * We assume we have UTF-8 content.
1930 */
1931 unsigned char tmp[10];
1932 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001933
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001934 if (base != cur)
1935 xmlBufferAdd(buf, base, cur - base);
1936 if (*cur < 0xC0) {
1937 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1938 if (doc != NULL)
1939 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1940 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001941 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001942 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001943 base = cur;
1944 continue;
1945 } else if (*cur < 0xE0) {
1946 val = (cur[0]) & 0x1F;
1947 val <<= 6;
1948 val |= (cur[1]) & 0x3F;
1949 l = 2;
1950 } else if (*cur < 0xF0) {
1951 val = (cur[0]) & 0x0F;
1952 val <<= 6;
1953 val |= (cur[1]) & 0x3F;
1954 val <<= 6;
1955 val |= (cur[2]) & 0x3F;
1956 l = 3;
1957 } else if (*cur < 0xF8) {
1958 val = (cur[0]) & 0x07;
1959 val <<= 6;
1960 val |= (cur[1]) & 0x3F;
1961 val <<= 6;
1962 val |= (cur[2]) & 0x3F;
1963 val <<= 6;
1964 val |= (cur[3]) & 0x3F;
1965 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001966 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001967 if ((l == 1) || (!IS_CHAR(val))) {
1968 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1969 if (doc != NULL)
1970 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1971
1972 xmlSerializeHexCharRef(tmp, *cur);
1973 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1974 cur++;
1975 base = cur;
1976 continue;
1977 }
1978 /*
1979 * We could do multiple things here. Just save
1980 * as a char ref
1981 */
1982 xmlSerializeHexCharRef(tmp, val);
1983 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1984 cur += l;
1985 base = cur;
1986 } else {
1987 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001988 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001989 }
1990 if (base != cur)
1991 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001992}
1993
1994/**
1995 * xmlNodeDump:
1996 * @buf: the XML buffer output
1997 * @doc: the document
1998 * @cur: the current node
1999 * @level: the imbrication level for indenting
2000 * @format: is formatting allowed
2001 *
2002 * Dump an XML node, recursive behaviour,children are printed too.
2003 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2004 * or xmlKeepBlanksDefault(0) was called
2005 *
2006 * Returns the number of bytes written to the buffer or -1 in case of error
2007 */
2008int
2009xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2010 int format)
2011{
2012 unsigned int use;
2013 int ret;
2014 xmlOutputBufferPtr outbuf;
2015
2016 xmlInitParser();
2017
2018 if (cur == NULL) {
2019#ifdef DEBUG_TREE
2020 xmlGenericError(xmlGenericErrorContext,
2021 "xmlNodeDump : node == NULL\n");
2022#endif
2023 return (-1);
2024 }
2025 if (buf == NULL) {
2026#ifdef DEBUG_TREE
2027 xmlGenericError(xmlGenericErrorContext,
2028 "xmlNodeDump : buf == NULL\n");
2029#endif
2030 return (-1);
2031 }
2032 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2033 if (outbuf == NULL) {
2034 xmlSaveErrMemory("creating buffer");
2035 return (-1);
2036 }
2037 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2038 outbuf->buffer = buf;
2039 outbuf->encoder = NULL;
2040 outbuf->writecallback = NULL;
2041 outbuf->closecallback = NULL;
2042 outbuf->context = NULL;
2043 outbuf->written = 0;
2044
2045 use = buf->use;
2046 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2047 xmlFree(outbuf);
2048 ret = buf->use - use;
2049 return (ret);
2050}
2051
2052/**
2053 * xmlElemDump:
2054 * @f: the FILE * for the output
2055 * @doc: the document
2056 * @cur: the current node
2057 *
2058 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2059 */
2060void
2061xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2062{
2063 xmlOutputBufferPtr outbuf;
2064
2065 xmlInitParser();
2066
2067 if (cur == NULL) {
2068#ifdef DEBUG_TREE
2069 xmlGenericError(xmlGenericErrorContext,
2070 "xmlElemDump : cur == NULL\n");
2071#endif
2072 return;
2073 }
2074#ifdef DEBUG_TREE
2075 if (doc == NULL) {
2076 xmlGenericError(xmlGenericErrorContext,
2077 "xmlElemDump : doc == NULL\n");
2078 }
2079#endif
2080
2081 outbuf = xmlOutputBufferCreateFile(f, NULL);
2082 if (outbuf == NULL)
2083 return;
2084 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2085#ifdef LIBXML_HTML_ENABLED
2086 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2087#else
2088 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2089#endif /* LIBXML_HTML_ENABLED */
2090 } else
2091 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2092 xmlOutputBufferClose(outbuf);
2093}
2094
2095/************************************************************************
2096 * *
2097 * Saving functions front-ends *
2098 * *
2099 ************************************************************************/
2100
2101/**
2102 * xmlNodeDumpOutput:
2103 * @buf: the XML buffer output
2104 * @doc: the document
2105 * @cur: the current node
2106 * @level: the imbrication level for indenting
2107 * @format: is formatting allowed
2108 * @encoding: an optional encoding string
2109 *
2110 * Dump an XML node, recursive behaviour, children are printed too.
2111 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2112 * or xmlKeepBlanksDefault(0) was called
2113 */
2114void
2115xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2116 int level, int format, const char *encoding)
2117{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002118 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002119#ifdef LIBXML_HTML_ENABLED
2120 xmlDtdPtr dtd;
2121 int is_xhtml = 0;
2122#endif
2123
2124 xmlInitParser();
2125
Daniel Veillardce244ad2004-11-05 10:03:46 +00002126 if ((buf == NULL) || (cur == NULL)) return;
2127
Daniel Veillard64354ea2005-03-31 15:22:56 +00002128 if (encoding == NULL)
2129 encoding = "UTF-8";
2130
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002131 memset(&ctxt, 0, sizeof(ctxt));
2132 ctxt.doc = doc;
2133 ctxt.buf = buf;
2134 ctxt.level = level;
2135 ctxt.format = format;
2136 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002137 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002138 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002139
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002140#ifdef LIBXML_HTML_ENABLED
2141 dtd = xmlGetIntSubset(doc);
2142 if (dtd != NULL) {
Daniel Veillard33b20b72005-09-12 21:43:20 +00002143 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2144 if (is_xhtml < 0)
2145 is_xhtml = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002146 }
2147
2148 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002149 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002150 else
2151#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002152 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002153}
2154
2155/**
2156 * xmlDocDumpFormatMemoryEnc:
2157 * @out_doc: Document to generate XML text from
2158 * @doc_txt_ptr: Memory pointer for allocated XML text
2159 * @doc_txt_len: Length of the generated XML text
2160 * @txt_encoding: Character encoding to use when generating XML text
2161 * @format: should formatting spaces been added
2162 *
2163 * Dump the current DOM tree into memory using the character encoding specified
2164 * by the caller. Note it is up to the caller of this function to free the
2165 * allocated memory with xmlFree().
2166 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2167 * or xmlKeepBlanksDefault(0) was called
2168 */
2169
2170void
2171xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2172 int * doc_txt_len, const char * txt_encoding,
2173 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002174 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002175 int dummy = 0;
2176 xmlOutputBufferPtr out_buff = NULL;
2177 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2178
2179 if (doc_txt_len == NULL) {
2180 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2181 }
2182
2183 if (doc_txt_ptr == NULL) {
2184 *doc_txt_len = 0;
2185 return;
2186 }
2187
2188 *doc_txt_ptr = NULL;
2189 *doc_txt_len = 0;
2190
2191 if (out_doc == NULL) {
2192 /* No document, no output */
2193 return;
2194 }
2195
2196 /*
2197 * Validate the encoding value, if provided.
2198 * This logic is copied from xmlSaveFileEnc.
2199 */
2200
2201 if (txt_encoding == NULL)
2202 txt_encoding = (const char *) out_doc->encoding;
2203 if (txt_encoding != NULL) {
2204 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2205 if ( conv_hdlr == NULL ) {
2206 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2207 txt_encoding);
2208 return;
2209 }
2210 }
2211
2212 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2213 xmlSaveErrMemory("creating buffer");
2214 return;
2215 }
2216
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002217 memset(&ctxt, 0, sizeof(ctxt));
2218 ctxt.doc = out_doc;
2219 ctxt.buf = out_buff;
2220 ctxt.level = 0;
2221 ctxt.format = format;
2222 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002223 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002224 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002225 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002226 xmlOutputBufferFlush(out_buff);
2227 if (out_buff->conv != NULL) {
2228 *doc_txt_len = out_buff->conv->use;
2229 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
2230 } else {
2231 *doc_txt_len = out_buff->buffer->use;
2232 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
2233 }
2234 (void)xmlOutputBufferClose(out_buff);
2235
2236 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2237 *doc_txt_len = 0;
2238 xmlSaveErrMemory("creating output");
2239 }
2240
2241 return;
2242}
2243
2244/**
2245 * xmlDocDumpMemory:
2246 * @cur: the document
2247 * @mem: OUT: the memory pointer
2248 * @size: OUT: the memory length
2249 *
2250 * Dump an XML document in memory and return the #xmlChar * and it's size
2251 * in bytes. It's up to the caller to free the memory with xmlFree().
2252 * The resulting byte array is zero terminated, though the last 0 is not
2253 * included in the returned size.
2254 */
2255void
2256xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2257 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2258}
2259
2260/**
2261 * xmlDocDumpFormatMemory:
2262 * @cur: the document
2263 * @mem: OUT: the memory pointer
2264 * @size: OUT: the memory length
2265 * @format: should formatting spaces been added
2266 *
2267 *
2268 * Dump an XML document in memory and return the #xmlChar * and it's size.
2269 * It's up to the caller to free the memory with xmlFree().
2270 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2271 * or xmlKeepBlanksDefault(0) was called
2272 */
2273void
2274xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2275 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2276}
2277
2278/**
2279 * xmlDocDumpMemoryEnc:
2280 * @out_doc: Document to generate XML text from
2281 * @doc_txt_ptr: Memory pointer for allocated XML text
2282 * @doc_txt_len: Length of the generated XML text
2283 * @txt_encoding: Character encoding to use when generating XML text
2284 *
2285 * Dump the current DOM tree into memory using the character encoding specified
2286 * by the caller. Note it is up to the caller of this function to free the
2287 * allocated memory with xmlFree().
2288 */
2289
2290void
2291xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2292 int * doc_txt_len, const char * txt_encoding) {
2293 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2294 txt_encoding, 0);
2295}
2296
2297/**
2298 * xmlDocFormatDump:
2299 * @f: the FILE*
2300 * @cur: the document
2301 * @format: should formatting spaces been added
2302 *
2303 * Dump an XML document to an open FILE.
2304 *
2305 * returns: the number of bytes written or -1 in case of failure.
2306 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2307 * or xmlKeepBlanksDefault(0) was called
2308 */
2309int
2310xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002311 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002312 xmlOutputBufferPtr buf;
2313 const char * encoding;
2314 xmlCharEncodingHandlerPtr handler = NULL;
2315 int ret;
2316
2317 if (cur == NULL) {
2318#ifdef DEBUG_TREE
2319 xmlGenericError(xmlGenericErrorContext,
2320 "xmlDocDump : document == NULL\n");
2321#endif
2322 return(-1);
2323 }
2324 encoding = (const char *) cur->encoding;
2325
2326 if (encoding != NULL) {
Daniel Veillard3814a362007-07-26 11:41:46 +00002327 handler = xmlFindCharEncodingHandler(encoding);
2328 if (handler == NULL) {
2329 xmlFree((char *) cur->encoding);
2330 cur->encoding = NULL;
2331 encoding = NULL;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002332 }
Daniel Veillard3814a362007-07-26 11:41:46 +00002333 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002334 buf = xmlOutputBufferCreateFile(f, handler);
2335 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002336 memset(&ctxt, 0, sizeof(ctxt));
2337 ctxt.doc = cur;
2338 ctxt.buf = buf;
2339 ctxt.level = 0;
2340 ctxt.format = format;
2341 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002342 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002343 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002344 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002345
2346 ret = xmlOutputBufferClose(buf);
2347 return(ret);
2348}
2349
2350/**
2351 * xmlDocDump:
2352 * @f: the FILE*
2353 * @cur: the document
2354 *
2355 * Dump an XML document to an open FILE.
2356 *
2357 * returns: the number of bytes written or -1 in case of failure.
2358 */
2359int
2360xmlDocDump(FILE *f, xmlDocPtr cur) {
2361 return(xmlDocFormatDump (f, cur, 0));
2362}
2363
2364/**
2365 * xmlSaveFileTo:
2366 * @buf: an output I/O buffer
2367 * @cur: the document
2368 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2369 *
2370 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002371 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2372 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002373 *
2374 * returns: the number of bytes written or -1 in case of failure.
2375 */
2376int
2377xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002378 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002379 int ret;
2380
Daniel Veillard3d97e662004-11-04 10:49:00 +00002381 if (buf == NULL) return(-1);
2382 if (cur == NULL) {
2383 xmlOutputBufferClose(buf);
2384 return(-1);
2385 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002386 memset(&ctxt, 0, sizeof(ctxt));
2387 ctxt.doc = cur;
2388 ctxt.buf = buf;
2389 ctxt.level = 0;
2390 ctxt.format = 0;
2391 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002392 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002393 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002394 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002395 ret = xmlOutputBufferClose(buf);
2396 return(ret);
2397}
2398
2399/**
2400 * xmlSaveFormatFileTo:
2401 * @buf: an output I/O buffer
2402 * @cur: the document
2403 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2404 * @format: should formatting spaces been added
2405 *
2406 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002407 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2408 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002409 *
2410 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002411 */
2412int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002413xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2414 const char *encoding, int format)
2415{
2416 xmlSaveCtxt ctxt;
2417 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002418
Daniel Veillard3d97e662004-11-04 10:49:00 +00002419 if (buf == NULL) return(-1);
Daniel Veillardce244ad2004-11-05 10:03:46 +00002420 if ((cur == NULL) ||
2421 ((cur->type != XML_DOCUMENT_NODE) &&
2422 (cur->type != XML_HTML_DOCUMENT_NODE))) {
Daniel Veillard3d97e662004-11-04 10:49:00 +00002423 xmlOutputBufferClose(buf);
2424 return(-1);
2425 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002426 memset(&ctxt, 0, sizeof(ctxt));
2427 ctxt.doc = cur;
2428 ctxt.buf = buf;
2429 ctxt.level = 0;
2430 ctxt.format = format;
2431 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002432 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002433 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002434 xmlDocContentDumpOutput(&ctxt, cur);
2435 ret = xmlOutputBufferClose(buf);
2436 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002437}
2438
2439/**
2440 * xmlSaveFormatFileEnc:
2441 * @filename: the filename or URL to output
2442 * @cur: the document being saved
2443 * @encoding: the name of the encoding to use or NULL.
2444 * @format: should formatting spaces be added.
2445 *
2446 * Dump an XML document to a file or an URL.
2447 *
2448 * Returns the number of bytes written or -1 in case of error.
2449 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2450 * or xmlKeepBlanksDefault(0) was called
2451 */
2452int
2453xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2454 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002455 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002456 xmlOutputBufferPtr buf;
2457 xmlCharEncodingHandlerPtr handler = NULL;
2458 int ret;
2459
2460 if (cur == NULL)
2461 return(-1);
2462
2463 if (encoding == NULL)
2464 encoding = (const char *) cur->encoding;
2465
2466 if (encoding != NULL) {
2467
2468 handler = xmlFindCharEncodingHandler(encoding);
2469 if (handler == NULL)
2470 return(-1);
2471 }
2472
2473#ifdef HAVE_ZLIB_H
2474 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2475#endif
2476 /*
2477 * save the content to a temp buffer.
2478 */
2479 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2480 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002481 memset(&ctxt, 0, sizeof(ctxt));
2482 ctxt.doc = cur;
2483 ctxt.buf = buf;
2484 ctxt.level = 0;
2485 ctxt.format = format;
2486 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002487 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002488 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002489
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002490 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002491
2492 ret = xmlOutputBufferClose(buf);
2493 return(ret);
2494}
2495
2496
2497/**
2498 * xmlSaveFileEnc:
2499 * @filename: the filename (or URL)
2500 * @cur: the document
2501 * @encoding: the name of an encoding (or NULL)
2502 *
2503 * Dump an XML document, converting it to the given encoding
2504 *
2505 * returns: the number of bytes written or -1 in case of failure.
2506 */
2507int
2508xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2509 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2510}
2511
2512/**
2513 * xmlSaveFormatFile:
2514 * @filename: the filename (or URL)
2515 * @cur: the document
2516 * @format: should formatting spaces been added
2517 *
2518 * Dump an XML document to a file. Will use compression if
2519 * compiled in and enabled. If @filename is "-" the stdout file is
2520 * used. If @format is set then the document will be indented on output.
2521 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2522 * or xmlKeepBlanksDefault(0) was called
2523 *
2524 * returns: the number of bytes written or -1 in case of failure.
2525 */
2526int
2527xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2528 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2529}
2530
2531/**
2532 * xmlSaveFile:
2533 * @filename: the filename (or URL)
2534 * @cur: the document
2535 *
2536 * Dump an XML document to a file. Will use compression if
2537 * compiled in and enabled. If @filename is "-" the stdout file is
2538 * used.
2539 * returns: the number of bytes written or -1 in case of failure.
2540 */
2541int
2542xmlSaveFile(const char *filename, xmlDocPtr cur) {
2543 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2544}
2545
2546#endif /* LIBXML_OUTPUT_ENABLED */
2547
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002548#define bottom_xmlsave
2549#include "elfgcchack.h"