blob: 23c3bcc1a9306ead0a50e17500382d092721e6ff [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 Veillard07953482012-01-22 17:42:35 +0800251 if (outend - out < 11) break;
Daniel Veillard83a75e02004-05-14 21:50:42 +0000252
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;
Adam Spraggd2e62312010-11-03 15:33:40 +0100411 else if (options & XML_SAVE_WSNONSIG)
412 ret->format = 2;
Rob Richards2ce51c02005-09-12 12:16:35 +0000413
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000414 return(ret);
415}
416
417/************************************************************************
418 * *
419 * Dumping XML tree content to a simple buffer *
420 * *
421 ************************************************************************/
422/**
423 * xmlAttrSerializeContent:
424 * @buf: the XML buffer output
425 * @doc: the document
426 * @attr: the attribute pointer
427 *
428 * Serialize the attribute in the buffer
429 */
430static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000431xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000432{
433 xmlNodePtr children;
434
435 children = attr->children;
436 while (children != NULL) {
437 switch (children->type) {
438 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000439 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
440 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000441 break;
442 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000443 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
444 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000445 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000446 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000447 break;
448 default:
449 /* should not happen unless we have a badly built tree */
450 break;
451 }
452 children = children->next;
453 }
454}
455
456/************************************************************************
457 * *
458 * Dumping XML tree content to an I/O output buffer *
459 * *
460 ************************************************************************/
461
Daniel Veillardda3fee42008-09-01 13:08:57 +0000462static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
463 xmlOutputBufferPtr buf = ctxt->buf;
464
465 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
466 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
467 if (buf->encoder == NULL) {
468 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
469 (const char *)encoding);
470 return(-1);
471 }
472 buf->conv = xmlBufferCreate();
473 if (buf->conv == NULL) {
474 xmlCharEncCloseFunc(buf->encoder);
475 xmlSaveErrMemory("creating encoding buffer");
476 return(-1);
477 }
478 /*
479 * initialize the state, e.g. if outputting a BOM
480 */
481 xmlCharEncOutFunc(buf->encoder, buf->conv, NULL);
482 }
483 return(0);
484}
485
486static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
487 xmlOutputBufferPtr buf = ctxt->buf;
488 xmlOutputBufferFlush(buf);
489 xmlCharEncCloseFunc(buf->encoder);
490 xmlBufferFree(buf->conv);
491 buf->encoder = NULL;
492 buf->conv = NULL;
493 return(0);
494}
495
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000496#ifdef LIBXML_HTML_ENABLED
497static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000498xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000499#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000500static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
501static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000502void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
Daniel Veillarddab39b52006-10-16 23:22:10 +0000503static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000504
505/**
Adam Spraggd2e62312010-11-03 15:33:40 +0100506 * xmlOutputBufferWriteWSNonSig:
507 * @ctxt: The save context
508 * @extra: Number of extra indents to apply to ctxt->level
509 *
510 * Write out formatting for non-significant whitespace output.
511 */
512static void
513xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
514{
515 int i;
516 if ((ctxt == NULL) || (ctxt->buf == NULL))
517 return;
518 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
519 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
520 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
521 ((ctxt->level + extra - i) > ctxt->indent_nr ?
522 ctxt->indent_nr : (ctxt->level + extra - i)),
523 ctxt->indent);
524 }
525}
526
527/**
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000528 * xmlNsDumpOutput:
529 * @buf: the XML buffer output
530 * @cur: a namespace
Adam Spraggd2e62312010-11-03 15:33:40 +0100531 * @ctxt: the output save context. Optional.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000532 *
533 * Dump a local Namespace definition.
534 * Should be called in the context of attributes dumps.
Adam Spraggd2e62312010-11-03 15:33:40 +0100535 * If @ctxt is supplied, @buf should be its buffer.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000536 */
537static void
Adam Spraggd2e62312010-11-03 15:33:40 +0100538xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
Daniel Veillardce244ad2004-11-05 10:03:46 +0000539 if ((cur == NULL) || (buf == NULL)) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000540 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
541 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
542 return;
543
Adam Spraggd2e62312010-11-03 15:33:40 +0100544 if (ctxt != NULL && ctxt->format == 2)
545 xmlOutputBufferWriteWSNonSig(ctxt, 2);
546 else
547 xmlOutputBufferWrite(buf, 1, " ");
548
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000549 /* Within the context of an element attributes */
550 if (cur->prefix != NULL) {
Adam Spraggd2e62312010-11-03 15:33:40 +0100551 xmlOutputBufferWrite(buf, 6, "xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000552 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
553 } else
Adam Spraggd2e62312010-11-03 15:33:40 +0100554 xmlOutputBufferWrite(buf, 5, "xmlns");
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000555 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000556 xmlBufferWriteQuotedString(buf->buffer, cur->href);
557 }
558}
559
560/**
Adam Spraggd2e62312010-11-03 15:33:40 +0100561 * xmlNsDumpOutputCtxt
562 * @ctxt: the save context
563 * @cur: a namespace
564 *
565 * Dump a local Namespace definition to a save context.
566 * Should be called in the context of attribute dumps.
567 */
568static void
569xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
570 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
571}
572
573/**
574 * xmlNsListDumpOutputCtxt
575 * @ctxt: the save context
576 * @cur: the first namespace
577 *
578 * Dump a list of local namespace definitions to a save context.
579 * Should be called in the context of attribute dumps.
580 */
581static void
582xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
583 while (cur != NULL) {
584 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
585 cur = cur->next;
586 }
587}
588
589/**
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000590 * xmlNsListDumpOutput:
591 * @buf: the XML buffer output
592 * @cur: the first namespace
593 *
594 * Dump a list of local Namespace definitions.
595 * Should be called in the context of attributes dumps.
596 */
597void
598xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
599 while (cur != NULL) {
Adam Spraggd2e62312010-11-03 15:33:40 +0100600 xmlNsDumpOutput(buf, cur, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000601 cur = cur->next;
602 }
603}
604
605/**
606 * xmlDtdDumpOutput:
607 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000608 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000609 *
610 * Dump the XML document DTD, if any.
611 */
612static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000613xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
614 xmlOutputBufferPtr buf;
615 int format, level;
616 xmlDocPtr doc;
617
618 if (dtd == NULL) return;
619 if ((ctxt == NULL) || (ctxt->buf == NULL))
620 return;
621 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000622 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000623 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
624 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000625 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000626 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000627 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000628 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
629 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000630 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000631 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
632 }
633 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
Daniel Veillard41c4a752004-09-08 20:55:38 +0000634 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
635 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000636 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000637 return;
638 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000639 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard41c4a752004-09-08 20:55:38 +0000640 /*
641 * Dump the notations first they are not in the DTD children list
642 * Do this only on a standalone DTD or on the internal subset though.
643 */
644 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
645 (dtd->doc->intSubset == dtd))) {
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000646 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
647 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000648 format = ctxt->format;
649 level = ctxt->level;
650 doc = ctxt->doc;
651 ctxt->format = 0;
652 ctxt->level = -1;
653 ctxt->doc = dtd->doc;
654 xmlNodeListDumpOutput(ctxt, dtd->children);
655 ctxt->format = format;
656 ctxt->level = level;
657 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000658 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000659}
660
661/**
662 * xmlAttrDumpOutput:
663 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000664 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000665 *
666 * Dump an XML attribute
667 */
668static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000669xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
670 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000671
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000672 if (cur == NULL) return;
673 buf = ctxt->buf;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000674 if (buf == NULL) return;
Adam Spraggd2e62312010-11-03 15:33:40 +0100675 if (ctxt->format == 2)
676 xmlOutputBufferWriteWSNonSig(ctxt, 2);
677 else
678 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000679 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
680 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000681 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000682 }
683 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000684 xmlOutputBufferWrite(buf, 2, "=\"");
685 xmlAttrSerializeContent(buf, cur);
686 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000687}
688
689/**
690 * xmlAttrListDumpOutput:
691 * @buf: the XML buffer output
692 * @doc: the document
693 * @cur: the first attribute pointer
694 * @encoding: an optional encoding string
695 *
696 * Dump a list of XML attributes
697 */
698static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000699xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
700 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000701 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000702 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000703 cur = cur->next;
704 }
705}
706
707
708
709/**
710 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000711 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000712 *
713 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000714 */
715static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000716xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000717 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000718
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000719 if (cur == NULL) return;
720 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000721 while (cur != NULL) {
Adam Spragg8b877132010-11-01 14:24:56 +0100722 if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
Daniel Veillardbd444842007-03-20 08:47:29 +0000723 ((cur->type == XML_ELEMENT_NODE) ||
724 (cur->type == XML_COMMENT_NODE) ||
725 (cur->type == XML_PI_NODE)))
Daniel Veillard753086a2004-03-28 16:12:44 +0000726 xmlOutputBufferWrite(buf, ctxt->indent_size *
727 (ctxt->level > ctxt->indent_nr ?
728 ctxt->indent_nr : ctxt->level),
729 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000730 xmlNodeDumpOutputInternal(ctxt, cur);
Adam Spragg8b877132010-11-01 14:24:56 +0100731 if (ctxt->format == 1) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000732 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000733 }
734 cur = cur->next;
735 }
736}
737
Daniel Veillardda3fee42008-09-01 13:08:57 +0000738#ifdef LIBXML_HTML_ENABLED
739/**
740 * xmlNodeDumpOutputInternal:
741 * @cur: the current node
742 *
743 * Dump an HTML node, recursive behaviour, children are printed too.
744 */
745static int
746htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
747 const xmlChar *oldenc = NULL;
748 const xmlChar *oldctxtenc = ctxt->encoding;
749 const xmlChar *encoding = ctxt->encoding;
750 xmlOutputBufferPtr buf = ctxt->buf;
751 int switched_encoding = 0;
752 xmlDocPtr doc;
753
754 xmlInitParser();
755
Daniel Veillard141ebfa2009-09-02 14:58:13 +0200756 doc = cur->doc;
757 if (doc != NULL) {
Daniel Veillardda3fee42008-09-01 13:08:57 +0000758 oldenc = doc->encoding;
759 if (ctxt->encoding != NULL) {
760 doc->encoding = BAD_CAST ctxt->encoding;
761 } else if (doc->encoding != NULL) {
762 encoding = doc->encoding;
763 }
764 }
765
766 if ((encoding != NULL) && (doc != NULL))
767 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
768 if ((encoding == NULL) && (doc != NULL))
769 encoding = htmlGetMetaEncoding(doc);
770 if (encoding == NULL)
771 encoding = BAD_CAST "HTML";
772 if ((encoding != NULL) && (oldctxtenc == NULL) &&
773 (buf->encoder == NULL) && (buf->conv == NULL)) {
774 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
775 doc->encoding = oldenc;
776 return(-1);
777 }
778 switched_encoding = 1;
779 }
780 if (ctxt->options & XML_SAVE_FORMAT)
781 htmlNodeDumpFormatOutput(buf, doc, cur,
782 (const char *)encoding, 1);
783 else
784 htmlNodeDumpFormatOutput(buf, doc, cur,
785 (const char *)encoding, 0);
786 /*
787 * Restore the state of the saving context at the end of the document
788 */
789 if ((switched_encoding) && (oldctxtenc == NULL)) {
790 xmlSaveClearEncoding(ctxt);
791 }
792 if (doc != NULL)
793 doc->encoding = oldenc;
794 return(0);
795}
796#endif
797
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000798/**
799 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000800 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000801 *
802 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000803 */
804static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000805xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000806 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000807 xmlNodePtr tmp;
808 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000809 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000810
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000811 if (cur == NULL) return;
812 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000813 if (cur->type == XML_XINCLUDE_START)
814 return;
815 if (cur->type == XML_XINCLUDE_END)
816 return;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000817 if ((cur->type == XML_DOCUMENT_NODE) ||
818 (cur->type == XML_HTML_DOCUMENT_NODE)) {
819 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
820 return;
821 }
Daniel Veillardda3fee42008-09-01 13:08:57 +0000822#ifdef LIBXML_HTML_ENABLED
Daniel Veillard856d9282008-09-25 14:31:40 +0000823 if (ctxt->options & XML_SAVE_XHTML) {
824 xhtmlNodeDumpOutput(ctxt, cur);
825 return;
826 }
827 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
828 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
829 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
830 (ctxt->options & XML_SAVE_AS_HTML)) {
Daniel Veillardda3fee42008-09-01 13:08:57 +0000831 htmlNodeDumpOutputInternal(ctxt, cur);
832 return;
833 }
834#endif
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000835 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000836 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000837 return;
838 }
839 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000840 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000841 return;
842 }
843 if (cur->type == XML_ELEMENT_DECL) {
844 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
845 return;
846 }
847 if (cur->type == XML_ATTRIBUTE_DECL) {
848 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
849 return;
850 }
851 if (cur->type == XML_ENTITY_DECL) {
852 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
853 return;
854 }
855 if (cur->type == XML_TEXT_NODE) {
856 if (cur->content != NULL) {
William M. Brack4e1c2db2005-02-11 10:58:55 +0000857 if (cur->name != xmlStringTextNoenc) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000858 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000859 } else {
860 /*
861 * Disable escaping, needed for XSLT
862 */
863 xmlOutputBufferWriteString(buf, (const char *) cur->content);
864 }
865 }
866
867 return;
868 }
869 if (cur->type == XML_PI_NODE) {
870 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000871 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000872 xmlOutputBufferWriteString(buf, (const char *)cur->name);
873 if (cur->content != NULL) {
Adam Spraggd2e62312010-11-03 15:33:40 +0100874 if (ctxt->format == 2)
875 xmlOutputBufferWriteWSNonSig(ctxt, 0);
876 else
877 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000878 xmlOutputBufferWriteString(buf, (const char *)cur->content);
879 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000880 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000881 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000882 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000883 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Adam Spraggd2e62312010-11-03 15:33:40 +0100884 if (ctxt->format == 2)
885 xmlOutputBufferWriteWSNonSig(ctxt, 0);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000886 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000887 }
888 return;
889 }
890 if (cur->type == XML_COMMENT_NODE) {
891 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000892 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000893 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000894 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000895 }
896 return;
897 }
898 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000899 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000900 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000901 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000902 return;
903 }
904 if (cur->type == XML_CDATA_SECTION_NODE) {
Daniel Veillardd0d2f092008-03-07 16:50:21 +0000905 if (cur->content == NULL || *cur->content == '\0') {
906 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000907 } else {
908 start = end = cur->content;
909 while (*end != '\0') {
910 if ((*end == ']') && (*(end + 1) == ']') &&
911 (*(end + 2) == '>')) {
912 end = end + 2;
913 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
914 xmlOutputBufferWrite(buf, end - start, (const char *)start);
915 xmlOutputBufferWrite(buf, 3, "]]>");
916 start = end;
917 }
918 end++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000919 }
Daniel Veillard7cd517c2005-05-20 18:47:22 +0000920 if (start != end) {
921 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
922 xmlOutputBufferWriteString(buf, (const char *)start);
923 xmlOutputBufferWrite(buf, 3, "]]>");
924 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000925 }
926 return;
927 }
928 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000929 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000930 return;
931 }
932 if (cur->type == XML_NAMESPACE_DECL) {
Adam Spraggd2e62312010-11-03 15:33:40 +0100933 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000934 return;
935 }
936
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000937 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000938 if (format == 1) {
939 tmp = cur->children;
940 while (tmp != NULL) {
941 if ((tmp->type == XML_TEXT_NODE) ||
942 (tmp->type == XML_CDATA_SECTION_NODE) ||
943 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000944 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000945 break;
946 }
947 tmp = tmp->next;
948 }
949 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000950 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000951 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
952 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000953 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000954 }
955
956 xmlOutputBufferWriteString(buf, (const char *)cur->name);
957 if (cur->nsDef)
Adam Spraggd2e62312010-11-03 15:33:40 +0100958 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000959 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000960 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000961
962 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
Rob Richards2ce51c02005-09-12 12:16:35 +0000963 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
Adam Spraggd2e62312010-11-03 15:33:40 +0100964 if (ctxt->format == 2)
965 xmlOutputBufferWriteWSNonSig(ctxt, 0);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000966 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000967 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000968 return;
969 }
Adam Spraggd2e62312010-11-03 15:33:40 +0100970 if (ctxt->format == 2)
971 xmlOutputBufferWriteWSNonSig(ctxt, 1);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000972 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000973 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000974 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000975 }
976 if (cur->children != NULL) {
Adam Spragg8b877132010-11-01 14:24:56 +0100977 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000978 if (ctxt->level >= 0) ctxt->level++;
979 xmlNodeListDumpOutput(ctxt, cur->children);
980 if (ctxt->level > 0) ctxt->level--;
Adam Spragg8b877132010-11-01 14:24:56 +0100981 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
Daniel Veillard753086a2004-03-28 16:12:44 +0000982 xmlOutputBufferWrite(buf, ctxt->indent_size *
983 (ctxt->level > ctxt->indent_nr ?
984 ctxt->indent_nr : ctxt->level),
985 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000986 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000987 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000988 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
989 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000990 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000991 }
992
993 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Adam Spraggd2e62312010-11-03 15:33:40 +0100994 if (ctxt->format == 2)
995 xmlOutputBufferWriteWSNonSig(ctxt, 0);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000996 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000997 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000998}
999
1000/**
1001 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001002 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001003 *
1004 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001005 */
Daniel Veillarddab39b52006-10-16 23:22:10 +00001006static int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001007xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001008#ifdef LIBXML_HTML_ENABLED
1009 xmlDtdPtr dtd;
1010 int is_xhtml = 0;
1011#endif
1012 const xmlChar *oldenc = cur->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001013 const xmlChar *oldctxtenc = ctxt->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001014 const xmlChar *encoding = ctxt->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001015 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1016 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1017 xmlOutputBufferPtr buf = ctxt->buf;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001018 xmlCharEncoding enc;
Daniel Veillardda3fee42008-09-01 13:08:57 +00001019 int switched_encoding = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001020
1021 xmlInitParser();
1022
Daniel Veillardda3fee42008-09-01 13:08:57 +00001023 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1024 (cur->type != XML_DOCUMENT_NODE))
1025 return(-1);
1026
Daniel Veillarddab39b52006-10-16 23:22:10 +00001027 if (ctxt->encoding != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001028 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001029 } else if (cur->encoding != NULL) {
1030 encoding = cur->encoding;
1031 } else if (cur->charset != XML_CHAR_ENCODING_UTF8) {
1032 encoding = (const xmlChar *)
1033 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
1034 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001035
Daniel Veillard856d9282008-09-25 14:31:40 +00001036 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1037 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1038 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1039 (ctxt->options & XML_SAVE_AS_HTML)) {
Daniel Veillardda3fee42008-09-01 13:08:57 +00001040#ifdef LIBXML_HTML_ENABLED
1041 if (encoding != NULL)
1042 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1043 if (encoding == NULL)
1044 encoding = htmlGetMetaEncoding(cur);
1045 if (encoding == NULL)
1046 encoding = BAD_CAST "HTML";
1047 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1048 (buf->encoder == NULL) && (buf->conv == NULL)) {
1049 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1050 cur->encoding = oldenc;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001051 return(-1);
1052 }
Daniel Veillarddab39b52006-10-16 23:22:10 +00001053 }
Daniel Veillardda3fee42008-09-01 13:08:57 +00001054 if (ctxt->options & XML_SAVE_FORMAT)
1055 htmlDocContentDumpFormatOutput(buf, cur,
1056 (const char *)encoding, 1);
Daniel Veillard100e1802005-08-08 14:44:11 +00001057 else
Daniel Veillardda3fee42008-09-01 13:08:57 +00001058 htmlDocContentDumpFormatOutput(buf, cur,
1059 (const char *)encoding, 0);
1060 if (ctxt->encoding != NULL)
1061 cur->encoding = oldenc;
1062 return(0);
1063#else
1064 return(-1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001065#endif
Daniel Veillard856d9282008-09-25 14:31:40 +00001066 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1067 (ctxt->options & XML_SAVE_AS_XML) ||
1068 (ctxt->options & XML_SAVE_XHTML)) {
Daniel Veillardda3fee42008-09-01 13:08:57 +00001069 enc = xmlParseCharEncoding((const char*) encoding);
1070 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1071 (buf->encoder == NULL) && (buf->conv == NULL) &&
1072 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1073 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1074 (enc != XML_CHAR_ENCODING_NONE) &&
1075 (enc != XML_CHAR_ENCODING_ASCII)) {
1076 /*
1077 * we need to switch to this encoding but just for this
1078 * document since we output the XMLDecl the conversion
1079 * must be done to not generate not well formed documents.
1080 */
1081 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1082 cur->encoding = oldenc;
1083 return(-1);
1084 }
1085 switched_encoding = 1;
1086 }
1087 if (ctxt->escape == xmlEscapeEntities)
1088 ctxt->escape = NULL;
1089 if (ctxt->escapeAttr == xmlEscapeEntities)
1090 ctxt->escapeAttr = NULL;
1091 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001092
Daniel Veillardda3fee42008-09-01 13:08:57 +00001093
1094 /*
1095 * Save the XML declaration
1096 */
1097 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1098 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1099 if (cur->version != NULL)
1100 xmlBufferWriteQuotedString(buf->buffer, cur->version);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001101 else
Daniel Veillardda3fee42008-09-01 13:08:57 +00001102 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1103 if (encoding != NULL) {
1104 xmlOutputBufferWrite(buf, 10, " encoding=");
1105 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1106 }
1107 switch (cur->standalone) {
1108 case 0:
1109 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1110 break;
1111 case 1:
1112 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1113 break;
1114 }
1115 xmlOutputBufferWrite(buf, 3, "?>\n");
1116 }
1117
1118#ifdef LIBXML_HTML_ENABLED
Daniel Veillard856d9282008-09-25 14:31:40 +00001119 if (ctxt->options & XML_SAVE_XHTML)
1120 is_xhtml = 1;
Daniel Veillardda3fee42008-09-01 13:08:57 +00001121 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1122 dtd = xmlGetIntSubset(cur);
1123 if (dtd != NULL) {
1124 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1125 if (is_xhtml < 0) is_xhtml = 0;
1126 }
1127 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001128#endif
Daniel Veillardda3fee42008-09-01 13:08:57 +00001129 if (cur->children != NULL) {
1130 xmlNodePtr child = cur->children;
1131
1132 while (child != NULL) {
1133 ctxt->level = 0;
1134#ifdef LIBXML_HTML_ENABLED
1135 if (is_xhtml)
1136 xhtmlNodeDumpOutput(ctxt, child);
1137 else
1138#endif
1139 xmlNodeDumpOutputInternal(ctxt, child);
1140 xmlOutputBufferWrite(buf, 1, "\n");
1141 child = child->next;
1142 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001143 }
1144 }
Daniel Veillardda3fee42008-09-01 13:08:57 +00001145
Daniel Veillarddab39b52006-10-16 23:22:10 +00001146 /*
1147 * Restore the state of the saving context at the end of the document
1148 */
Daniel Veillardda3fee42008-09-01 13:08:57 +00001149 if ((switched_encoding) && (oldctxtenc == NULL)) {
1150 xmlSaveClearEncoding(ctxt);
Daniel Veillarddab39b52006-10-16 23:22:10 +00001151 ctxt->escape = oldescape;
1152 ctxt->escapeAttr = oldescapeAttr;
1153 }
Daniel Veillardda3fee42008-09-01 13:08:57 +00001154 cur->encoding = oldenc;
Daniel Veillarddab39b52006-10-16 23:22:10 +00001155 return(0);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001156}
1157
1158#ifdef LIBXML_HTML_ENABLED
1159/************************************************************************
1160 * *
1161 * Functions specific to XHTML serialization *
1162 * *
1163 ************************************************************************/
1164
1165/**
1166 * xhtmlIsEmpty:
1167 * @node: the node
1168 *
1169 * Check if a node is an empty xhtml node
1170 *
1171 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1172 */
1173static int
1174xhtmlIsEmpty(xmlNodePtr node) {
1175 if (node == NULL)
1176 return(-1);
1177 if (node->type != XML_ELEMENT_NODE)
1178 return(0);
1179 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1180 return(0);
1181 if (node->children != NULL)
1182 return(0);
1183 switch (node->name[0]) {
1184 case 'a':
1185 if (xmlStrEqual(node->name, BAD_CAST "area"))
1186 return(1);
1187 return(0);
1188 case 'b':
1189 if (xmlStrEqual(node->name, BAD_CAST "br"))
1190 return(1);
1191 if (xmlStrEqual(node->name, BAD_CAST "base"))
1192 return(1);
1193 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1194 return(1);
1195 return(0);
1196 case 'c':
1197 if (xmlStrEqual(node->name, BAD_CAST "col"))
1198 return(1);
1199 return(0);
1200 case 'f':
1201 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1202 return(1);
1203 return(0);
1204 case 'h':
1205 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1206 return(1);
1207 return(0);
1208 case 'i':
1209 if (xmlStrEqual(node->name, BAD_CAST "img"))
1210 return(1);
1211 if (xmlStrEqual(node->name, BAD_CAST "input"))
1212 return(1);
1213 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1214 return(1);
1215 return(0);
1216 case 'l':
1217 if (xmlStrEqual(node->name, BAD_CAST "link"))
1218 return(1);
1219 return(0);
1220 case 'm':
1221 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1222 return(1);
1223 return(0);
1224 case 'p':
1225 if (xmlStrEqual(node->name, BAD_CAST "param"))
1226 return(1);
1227 return(0);
1228 }
1229 return(0);
1230}
1231
1232/**
1233 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001234 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001235 *
1236 * Dump a list of XML attributes
1237 */
1238static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001239xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001240 xmlAttrPtr xml_lang = NULL;
1241 xmlAttrPtr lang = NULL;
1242 xmlAttrPtr name = NULL;
1243 xmlAttrPtr id = NULL;
1244 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001245 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001246
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001247 if (cur == NULL) return;
1248 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001249 parent = cur->parent;
1250 while (cur != NULL) {
1251 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1252 id = cur;
1253 else
1254 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1255 name = cur;
1256 else
1257 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1258 lang = cur;
1259 else
1260 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1261 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1262 xml_lang = cur;
1263 else if ((cur->ns == NULL) &&
1264 ((cur->children == NULL) ||
1265 (cur->children->content == NULL) ||
1266 (cur->children->content[0] == 0)) &&
1267 (htmlIsBooleanAttr(cur->name))) {
1268 if (cur->children != NULL)
1269 xmlFreeNode(cur->children);
1270 cur->children = xmlNewText(cur->name);
1271 if (cur->children != NULL)
1272 cur->children->parent = (xmlNodePtr) cur;
1273 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001274 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001275 cur = cur->next;
1276 }
1277 /*
1278 * C.8
1279 */
1280 if ((name != NULL) && (id == NULL)) {
1281 if ((parent != NULL) && (parent->name != NULL) &&
1282 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1283 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1284 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1285 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1286 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1287 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1288 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1289 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1290 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001291 xmlOutputBufferWrite(buf, 5, " id=\"");
1292 xmlAttrSerializeContent(buf, name);
1293 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001294 }
1295 }
1296 /*
1297 * C.7.
1298 */
1299 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001300 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1301 xmlAttrSerializeContent(buf, lang);
1302 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001303 } else
1304 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001305 xmlOutputBufferWrite(buf, 7, " lang=\"");
1306 xmlAttrSerializeContent(buf, xml_lang);
1307 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001308 }
1309}
1310
1311/**
1312 * xhtmlNodeListDumpOutput:
1313 * @buf: the XML buffer output
1314 * @doc: the XHTML document
1315 * @cur: the first node
1316 * @level: the imbrication level for indenting
1317 * @format: is formatting allowed
1318 * @encoding: an optional encoding string
1319 *
1320 * Dump an XML node list, recursive behaviour, children are printed too.
1321 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1322 * or xmlKeepBlanksDefault(0) was called
1323 */
1324static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001325xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001326 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001327
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001328 if (cur == NULL) return;
1329 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001330 while (cur != NULL) {
Adam Spragg8b877132010-11-01 14:24:56 +01001331 if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001332 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001333 xmlOutputBufferWrite(buf, ctxt->indent_size *
1334 (ctxt->level > ctxt->indent_nr ?
1335 ctxt->indent_nr : ctxt->level),
1336 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001337 xhtmlNodeDumpOutput(ctxt, cur);
Adam Spragg8b877132010-11-01 14:24:56 +01001338 if (ctxt->format == 1) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001339 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001340 }
1341 cur = cur->next;
1342 }
1343}
1344
1345/**
1346 * xhtmlNodeDumpOutput:
1347 * @buf: the XML buffer output
1348 * @doc: the XHTML document
1349 * @cur: the current node
1350 * @level: the imbrication level for indenting
1351 * @format: is formatting allowed
1352 * @encoding: an optional encoding string
1353 *
1354 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001355 */
1356static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001357xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Rob Richards31f73022005-08-26 15:33:26 +00001358 int format, addmeta = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001359 xmlNodePtr tmp;
1360 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001361 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001362
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001363 if (cur == NULL) return;
Daniel Veillard60071ae2005-09-12 00:03:43 +00001364 if ((cur->type == XML_DOCUMENT_NODE) ||
1365 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1366 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1367 return;
1368 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001369 if (cur->type == XML_XINCLUDE_START)
1370 return;
1371 if (cur->type == XML_XINCLUDE_END)
1372 return;
1373 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001374 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001375 return;
1376 }
Rob Richards2e2691b2005-10-21 14:45:16 +00001377 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
1378 xhtmlNodeListDumpOutput(ctxt, cur->children);
1379 return;
1380 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001381 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001382 if (cur->type == XML_ELEMENT_DECL) {
1383 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1384 return;
1385 }
1386 if (cur->type == XML_ATTRIBUTE_DECL) {
1387 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1388 return;
1389 }
1390 if (cur->type == XML_ENTITY_DECL) {
1391 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1392 return;
1393 }
1394 if (cur->type == XML_TEXT_NODE) {
1395 if (cur->content != NULL) {
1396 if ((cur->name == xmlStringText) ||
1397 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001398 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001399 } else {
1400 /*
1401 * Disable escaping, needed for XSLT
1402 */
1403 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1404 }
1405 }
1406
1407 return;
1408 }
1409 if (cur->type == XML_PI_NODE) {
1410 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001411 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001412 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1413 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001414 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001415 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1416 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001417 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001418 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001419 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001420 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001421 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001422 }
1423 return;
1424 }
1425 if (cur->type == XML_COMMENT_NODE) {
1426 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001427 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001428 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001429 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001430 }
1431 return;
1432 }
1433 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001434 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001435 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001436 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001437 return;
1438 }
1439 if (cur->type == XML_CDATA_SECTION_NODE) {
Daniel Veillardd0d2f092008-03-07 16:50:21 +00001440 if (cur->content == NULL || *cur->content == '\0') {
1441 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1442 } else {
1443 start = end = cur->content;
1444 while (*end != '\0') {
1445 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1446 end = end + 2;
1447 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1448 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1449 xmlOutputBufferWrite(buf, 3, "]]>");
1450 start = end;
1451 }
1452 end++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001453 }
Daniel Veillardd0d2f092008-03-07 16:50:21 +00001454 if (start != end) {
1455 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1456 xmlOutputBufferWriteString(buf, (const char *)start);
1457 xmlOutputBufferWrite(buf, 3, "]]>");
1458 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001459 }
1460 return;
1461 }
Daniel Veillarda76a81f2007-10-10 08:28:18 +00001462 if (cur->type == XML_ATTRIBUTE_NODE) {
1463 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1464 return;
1465 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001466
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001467 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001468 if (format == 1) {
1469 tmp = cur->children;
1470 while (tmp != NULL) {
1471 if ((tmp->type == XML_TEXT_NODE) ||
1472 (tmp->type == XML_ENTITY_REF_NODE)) {
1473 format = 0;
1474 break;
1475 }
1476 tmp = tmp->next;
1477 }
1478 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001479 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001480 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1481 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001482 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001483 }
1484
1485 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1486 if (cur->nsDef)
Adam Spraggd2e62312010-11-03 15:33:40 +01001487 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001488 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1489 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1490 /*
1491 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1492 */
1493 xmlOutputBufferWriteString(buf,
1494 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1495 }
1496 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001497 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001498
Rob Richards31f73022005-08-26 15:33:26 +00001499 if ((cur->type == XML_ELEMENT_NODE) &&
1500 (cur->parent != NULL) &&
1501 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1502 xmlStrEqual(cur->name, BAD_CAST"head") &&
1503 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1504
1505 tmp = cur->children;
1506 while (tmp != NULL) {
1507 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1508 xmlChar *httpequiv;
1509
1510 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
Rob Richards07b72002005-09-03 14:56:36 +00001511 if (httpequiv != NULL) {
1512 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1513 xmlFree(httpequiv);
1514 break;
1515 }
Rob Richards31f73022005-08-26 15:33:26 +00001516 xmlFree(httpequiv);
Rob Richards31f73022005-08-26 15:33:26 +00001517 }
Rob Richards31f73022005-08-26 15:33:26 +00001518 }
1519 tmp = tmp->next;
1520 }
1521 if (tmp == NULL)
1522 addmeta = 1;
1523 }
1524
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001525 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1526 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
Rob Richards31f73022005-08-26 15:33:26 +00001527 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001528 /*
1529 * C.2. Empty Elements
1530 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001531 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001532 } else {
Rob Richards31f73022005-08-26 15:33:26 +00001533 if (addmeta == 1) {
1534 xmlOutputBufferWrite(buf, 1, ">");
Adam Spragg8b877132010-11-01 14:24:56 +01001535 if (ctxt->format == 1) {
Rob Richards2ce51c02005-09-12 12:16:35 +00001536 xmlOutputBufferWrite(buf, 1, "\n");
1537 if (xmlIndentTreeOutput)
1538 xmlOutputBufferWrite(buf, ctxt->indent_size *
1539 (ctxt->level + 1 > ctxt->indent_nr ?
1540 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1541 }
Rob Richards31f73022005-08-26 15:33:26 +00001542 xmlOutputBufferWriteString(buf,
1543 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1544 if (ctxt->encoding) {
1545 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1546 } else {
1547 xmlOutputBufferWrite(buf, 5, "UTF-8");
1548 }
Rob Richards2ce51c02005-09-12 12:16:35 +00001549 xmlOutputBufferWrite(buf, 4, "\" />");
Adam Spragg8b877132010-11-01 14:24:56 +01001550 if (ctxt->format == 1)
Rob Richards2ce51c02005-09-12 12:16:35 +00001551 xmlOutputBufferWrite(buf, 1, "\n");
1552 } else {
1553 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001554 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001555 /*
1556 * C.3. Element Minimization and Empty Element Content
1557 */
Rob Richards2ce51c02005-09-12 12:16:35 +00001558 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001559 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1560 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001561 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001562 }
1563 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001564 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001565 }
1566 return;
1567 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001568 xmlOutputBufferWrite(buf, 1, ">");
Rob Richards31f73022005-08-26 15:33:26 +00001569 if (addmeta == 1) {
Adam Spragg8b877132010-11-01 14:24:56 +01001570 if (ctxt->format == 1) {
Rob Richards2ce51c02005-09-12 12:16:35 +00001571 xmlOutputBufferWrite(buf, 1, "\n");
1572 if (xmlIndentTreeOutput)
1573 xmlOutputBufferWrite(buf, ctxt->indent_size *
1574 (ctxt->level + 1 > ctxt->indent_nr ?
1575 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1576 }
Rob Richards31f73022005-08-26 15:33:26 +00001577 xmlOutputBufferWriteString(buf,
1578 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1579 if (ctxt->encoding) {
1580 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1581 } else {
1582 xmlOutputBufferWrite(buf, 5, "UTF-8");
1583 }
1584 xmlOutputBufferWrite(buf, 4, "\" />");
1585 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001586 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001587 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001588 }
1589
Kasimier T. Buchcik7b4e2e22006-07-13 13:07:11 +00001590#if 0
1591 /*
1592 * This was removed due to problems with HTML processors.
1593 * See bug #345147.
Daniel Veillarddab39b52006-10-16 23:22:10 +00001594 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001595 /*
1596 * 4.8. Script and Style elements
1597 */
1598 if ((cur->type == XML_ELEMENT_NODE) &&
1599 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1600 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1601 ((cur->ns == NULL) ||
1602 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1603 xmlNodePtr child = cur->children;
1604
1605 while (child != NULL) {
Daniel Veillarddbd61052005-09-12 14:03:26 +00001606 if (child->type == XML_TEXT_NODE) {
1607 if ((xmlStrchr(child->content, '<') == NULL) &&
1608 (xmlStrchr(child->content, '&') == NULL) &&
1609 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
1610 /* Nothing to escape, so just output as is... */
1611 /* FIXME: Should we do something about "--" also? */
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001612 int level = ctxt->level;
1613 int indent = ctxt->format;
1614
1615 ctxt->level = 0;
1616 ctxt->format = 0;
Daniel Veillarddbd61052005-09-12 14:03:26 +00001617 xmlOutputBufferWriteString(buf, (const char *) child->content);
1618 /* (We cannot use xhtmlNodeDumpOutput() here because
1619 * we wish to leave '>' unescaped!) */
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001620 ctxt->level = level;
1621 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001622 } else {
Daniel Veillarddbd61052005-09-12 14:03:26 +00001623 /* We must use a CDATA section. Unfortunately,
1624 * this will break CSS and JavaScript when read by
1625 * a browser in HTML4-compliant mode. :-( */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001626 start = end = child->content;
1627 while (*end != '\0') {
1628 if (*end == ']' &&
1629 *(end + 1) == ']' &&
1630 *(end + 2) == '>') {
1631 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001632 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001633 xmlOutputBufferWrite(buf, end - start,
1634 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001635 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001636 start = end;
1637 }
1638 end++;
1639 }
1640 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001641 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1642 xmlOutputBufferWrite(buf, end - start,
1643 (const char *)start);
1644 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001645 }
1646 }
1647 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001648 int level = ctxt->level;
1649 int indent = ctxt->format;
1650
1651 ctxt->level = 0;
1652 ctxt->format = 0;
1653 xhtmlNodeDumpOutput(ctxt, child);
1654 ctxt->level = level;
1655 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001656 }
1657 child = child->next;
1658 }
Kasimier T. Buchcik7b4e2e22006-07-13 13:07:11 +00001659 }
1660#endif
1661
1662 if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001663 int indent = ctxt->format;
1664
Adam Spragg8b877132010-11-01 14:24:56 +01001665 if (format == 1) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001666 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001667 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001668 xhtmlNodeListDumpOutput(ctxt, cur->children);
1669 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001670 ctxt->format = indent;
Adam Spragg8b877132010-11-01 14:24:56 +01001671 if ((xmlIndentTreeOutput) && (format == 1))
Daniel Veillard753086a2004-03-28 16:12:44 +00001672 xmlOutputBufferWrite(buf, ctxt->indent_size *
1673 (ctxt->level > ctxt->indent_nr ?
1674 ctxt->indent_nr : ctxt->level),
1675 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001676 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001677 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001678 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1679 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001680 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001681 }
1682
1683 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001684 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001685}
1686#endif
1687
1688/************************************************************************
1689 * *
1690 * Public entry points *
1691 * *
1692 ************************************************************************/
1693
1694/**
1695 * xmlSaveToFd:
1696 * @fd: a file descriptor number
1697 * @encoding: the encoding name to use or NULL
1698 * @options: a set of xmlSaveOptions
1699 *
1700 * Create a document saving context serializing to a file descriptor
1701 * with the encoding and the options given.
1702 *
1703 * Returns a new serialization context or NULL in case of error.
1704 */
1705xmlSaveCtxtPtr
1706xmlSaveToFd(int fd, const char *encoding, int options)
1707{
1708 xmlSaveCtxtPtr ret;
1709
1710 ret = xmlNewSaveCtxt(encoding, options);
1711 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001712 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1713 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001714 xmlFreeSaveCtxt(ret);
1715 return(NULL);
1716 }
1717 return(ret);
1718}
1719
1720/**
1721 * xmlSaveToFilename:
1722 * @filename: a file name or an URL
1723 * @encoding: the encoding name to use or NULL
1724 * @options: a set of xmlSaveOptions
1725 *
1726 * Create a document saving context serializing to a filename or possibly
1727 * to an URL (but this is less reliable) with the encoding and the options
1728 * given.
1729 *
1730 * Returns a new serialization context or NULL in case of error.
1731 */
1732xmlSaveCtxtPtr
1733xmlSaveToFilename(const char *filename, const char *encoding, int options)
1734{
1735 xmlSaveCtxtPtr ret;
1736 int compression = 0; /* TODO handle compression option */
1737
1738 ret = xmlNewSaveCtxt(encoding, options);
1739 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001740 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001741 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001742 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001743 xmlFreeSaveCtxt(ret);
1744 return(NULL);
1745 }
1746 return(ret);
1747}
1748
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001749/**
1750 * xmlSaveToBuffer:
1751 * @buffer: a buffer
1752 * @encoding: the encoding name to use or NULL
1753 * @options: a set of xmlSaveOptions
1754 *
1755 * Create a document saving context serializing to a buffer
1756 * with the encoding and the options given
1757 *
1758 * Returns a new serialization context or NULL in case of error.
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001759 */
1760
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001761xmlSaveCtxtPtr
1762xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1763{
Daniel Veillard9a00fd22005-11-09 08:56:26 +00001764 xmlSaveCtxtPtr ret;
1765 xmlOutputBufferPtr out_buff;
1766 xmlCharEncodingHandlerPtr handler;
1767
1768 ret = xmlNewSaveCtxt(encoding, options);
1769 if (ret == NULL) return(NULL);
1770
1771 if (encoding != NULL) {
1772 handler = xmlFindCharEncodingHandler(encoding);
1773 if (handler == NULL) {
1774 xmlFree(ret);
1775 return(NULL);
1776 }
1777 } else
1778 handler = NULL;
1779 out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
1780 if (out_buff == NULL) {
1781 xmlFree(ret);
1782 if (handler) xmlCharEncCloseFunc(handler);
1783 return(NULL);
1784 }
1785
1786 ret->buf = out_buff;
1787 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001788}
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001789
1790/**
1791 * xmlSaveToIO:
1792 * @iowrite: an I/O write function
1793 * @ioclose: an I/O close function
1794 * @ioctx: an I/O handler
1795 * @encoding: the encoding name to use or NULL
1796 * @options: a set of xmlSaveOptions
1797 *
1798 * Create a document saving context serializing to a file descriptor
1799 * with the encoding and the options given
1800 *
1801 * Returns a new serialization context or NULL in case of error.
1802 */
1803xmlSaveCtxtPtr
1804xmlSaveToIO(xmlOutputWriteCallback iowrite,
1805 xmlOutputCloseCallback ioclose,
1806 void *ioctx, const char *encoding, int options)
1807{
1808 xmlSaveCtxtPtr ret;
1809
1810 ret = xmlNewSaveCtxt(encoding, options);
1811 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001812 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1813 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001814 xmlFreeSaveCtxt(ret);
1815 return(NULL);
1816 }
1817 return(ret);
1818}
1819
1820/**
1821 * xmlSaveDoc:
1822 * @ctxt: a document saving context
1823 * @doc: a document
1824 *
1825 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001826 * TODO: The function is not fully implemented yet as it does not return the
1827 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001828 *
1829 * Returns the number of byte written or -1 in case of error
1830 */
1831long
1832xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1833{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001834 long ret = 0;
1835
Daniel Veillardce682bc2004-11-05 17:22:25 +00001836 if ((ctxt == NULL) || (doc == NULL)) return(-1);
Daniel Veillarddab39b52006-10-16 23:22:10 +00001837 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1838 return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001839 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001840}
1841
1842/**
1843 * xmlSaveTree:
1844 * @ctxt: a document saving context
Daniel Veillard681e9042006-09-29 09:16:00 +00001845 * @node: the top node of the subtree to save
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001846 *
1847 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001848 * TODO: The function is not fully implemented yet as it does not return the
1849 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001850 *
1851 * Returns the number of byte written or -1 in case of error
1852 */
1853long
1854xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1855{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001856 long ret = 0;
1857
Daniel Veillardce682bc2004-11-05 17:22:25 +00001858 if ((ctxt == NULL) || (node == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001859 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001860 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001861}
1862
1863/**
1864 * xmlSaveFlush:
1865 * @ctxt: a document saving context
1866 *
1867 * Flush a document saving context, i.e. make sure that all bytes have
1868 * been output.
1869 *
1870 * Returns the number of byte written or -1 in case of error.
1871 */
1872int
1873xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1874{
1875 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001876 if (ctxt->buf == NULL) return(-1);
1877 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001878}
1879
1880/**
1881 * xmlSaveClose:
1882 * @ctxt: a document saving context
1883 *
1884 * Close a document saving context, i.e. make sure that all bytes have
1885 * been output and free the associated data.
1886 *
1887 * Returns the number of byte written or -1 in case of error.
1888 */
1889int
1890xmlSaveClose(xmlSaveCtxtPtr ctxt)
1891{
1892 int ret;
1893
1894 if (ctxt == NULL) return(-1);
1895 ret = xmlSaveFlush(ctxt);
1896 xmlFreeSaveCtxt(ctxt);
1897 return(ret);
1898}
1899
Daniel Veillard3995bc32004-05-15 18:57:31 +00001900/**
1901 * xmlSaveSetEscape:
1902 * @ctxt: a document saving context
1903 * @escape: the escaping function
1904 *
1905 * Set a custom escaping function to be used for text in element content
1906 *
1907 * Returns 0 if successful or -1 in case of error.
1908 */
1909int
1910xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1911{
1912 if (ctxt == NULL) return(-1);
1913 ctxt->escape = escape;
1914 return(0);
1915}
1916
1917/**
1918 * xmlSaveSetAttrEscape:
1919 * @ctxt: a document saving context
1920 * @escape: the escaping function
1921 *
1922 * Set a custom escaping function to be used for text in attribute content
1923 *
1924 * Returns 0 if successful or -1 in case of error.
1925 */
1926int
1927xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1928{
1929 if (ctxt == NULL) return(-1);
1930 ctxt->escapeAttr = escape;
1931 return(0);
1932}
1933
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001934/************************************************************************
1935 * *
1936 * Public entry points based on buffers *
1937 * *
1938 ************************************************************************/
1939/**
1940 * xmlAttrSerializeTxtContent:
1941 * @buf: the XML buffer output
1942 * @doc: the document
1943 * @attr: the attribute node
1944 * @string: the text content
1945 *
1946 * Serialize text attribute values to an xml simple buffer
1947 */
1948void
1949xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001950 xmlAttrPtr attr, const xmlChar * string)
1951{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001952 xmlChar *base, *cur;
1953
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001954 if (string == NULL)
1955 return;
1956 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001957 while (*cur != 0) {
1958 if (*cur == '\n') {
1959 if (base != cur)
1960 xmlBufferAdd(buf, base, cur - base);
1961 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1962 cur++;
1963 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001964 } else if (*cur == '\r') {
1965 if (base != cur)
1966 xmlBufferAdd(buf, base, cur - base);
1967 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1968 cur++;
1969 base = cur;
1970 } else if (*cur == '\t') {
1971 if (base != cur)
1972 xmlBufferAdd(buf, base, cur - base);
1973 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1974 cur++;
1975 base = cur;
1976 } else if (*cur == '"') {
1977 if (base != cur)
1978 xmlBufferAdd(buf, base, cur - base);
1979 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1980 cur++;
1981 base = cur;
1982 } else if (*cur == '<') {
1983 if (base != cur)
1984 xmlBufferAdd(buf, base, cur - base);
1985 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1986 cur++;
1987 base = cur;
1988 } else if (*cur == '>') {
1989 if (base != cur)
1990 xmlBufferAdd(buf, base, cur - base);
1991 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1992 cur++;
1993 base = cur;
1994 } else if (*cur == '&') {
1995 if (base != cur)
1996 xmlBufferAdd(buf, base, cur - base);
1997 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1998 cur++;
1999 base = cur;
2000 } else if ((*cur >= 0x80) && ((doc == NULL) ||
2001 (doc->encoding == NULL))) {
2002 /*
2003 * We assume we have UTF-8 content.
2004 */
Daniel Veillard07953482012-01-22 17:42:35 +08002005 unsigned char tmp[12];
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002006 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002007
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002008 if (base != cur)
2009 xmlBufferAdd(buf, base, cur - base);
2010 if (*cur < 0xC0) {
2011 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2012 if (doc != NULL)
2013 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
2014 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002015 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002016 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002017 base = cur;
2018 continue;
2019 } else if (*cur < 0xE0) {
2020 val = (cur[0]) & 0x1F;
2021 val <<= 6;
2022 val |= (cur[1]) & 0x3F;
2023 l = 2;
2024 } else if (*cur < 0xF0) {
2025 val = (cur[0]) & 0x0F;
2026 val <<= 6;
2027 val |= (cur[1]) & 0x3F;
2028 val <<= 6;
2029 val |= (cur[2]) & 0x3F;
2030 l = 3;
2031 } else if (*cur < 0xF8) {
2032 val = (cur[0]) & 0x07;
2033 val <<= 6;
2034 val |= (cur[1]) & 0x3F;
2035 val <<= 6;
2036 val |= (cur[2]) & 0x3F;
2037 val <<= 6;
2038 val |= (cur[3]) & 0x3F;
2039 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002040 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002041 if ((l == 1) || (!IS_CHAR(val))) {
2042 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2043 if (doc != NULL)
2044 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
2045
2046 xmlSerializeHexCharRef(tmp, *cur);
2047 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
2048 cur++;
2049 base = cur;
2050 continue;
2051 }
2052 /*
2053 * We could do multiple things here. Just save
2054 * as a char ref
2055 */
2056 xmlSerializeHexCharRef(tmp, val);
2057 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
2058 cur += l;
2059 base = cur;
2060 } else {
2061 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002062 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00002063 }
2064 if (base != cur)
2065 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002066}
2067
2068/**
2069 * xmlNodeDump:
2070 * @buf: the XML buffer output
2071 * @doc: the document
2072 * @cur: the current node
2073 * @level: the imbrication level for indenting
2074 * @format: is formatting allowed
2075 *
2076 * Dump an XML node, recursive behaviour,children are printed too.
2077 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2078 * or xmlKeepBlanksDefault(0) was called
2079 *
2080 * Returns the number of bytes written to the buffer or -1 in case of error
2081 */
2082int
2083xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2084 int format)
2085{
2086 unsigned int use;
2087 int ret;
2088 xmlOutputBufferPtr outbuf;
2089
2090 xmlInitParser();
2091
2092 if (cur == NULL) {
2093#ifdef DEBUG_TREE
2094 xmlGenericError(xmlGenericErrorContext,
2095 "xmlNodeDump : node == NULL\n");
2096#endif
2097 return (-1);
2098 }
2099 if (buf == NULL) {
2100#ifdef DEBUG_TREE
2101 xmlGenericError(xmlGenericErrorContext,
2102 "xmlNodeDump : buf == NULL\n");
2103#endif
2104 return (-1);
2105 }
2106 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2107 if (outbuf == NULL) {
2108 xmlSaveErrMemory("creating buffer");
2109 return (-1);
2110 }
2111 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2112 outbuf->buffer = buf;
2113 outbuf->encoder = NULL;
2114 outbuf->writecallback = NULL;
2115 outbuf->closecallback = NULL;
2116 outbuf->context = NULL;
2117 outbuf->written = 0;
2118
2119 use = buf->use;
2120 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2121 xmlFree(outbuf);
2122 ret = buf->use - use;
2123 return (ret);
2124}
2125
2126/**
2127 * xmlElemDump:
2128 * @f: the FILE * for the output
2129 * @doc: the document
2130 * @cur: the current node
2131 *
2132 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2133 */
2134void
2135xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2136{
2137 xmlOutputBufferPtr outbuf;
2138
2139 xmlInitParser();
2140
2141 if (cur == NULL) {
2142#ifdef DEBUG_TREE
2143 xmlGenericError(xmlGenericErrorContext,
2144 "xmlElemDump : cur == NULL\n");
2145#endif
2146 return;
2147 }
2148#ifdef DEBUG_TREE
2149 if (doc == NULL) {
2150 xmlGenericError(xmlGenericErrorContext,
2151 "xmlElemDump : doc == NULL\n");
2152 }
2153#endif
2154
2155 outbuf = xmlOutputBufferCreateFile(f, NULL);
2156 if (outbuf == NULL)
2157 return;
2158 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2159#ifdef LIBXML_HTML_ENABLED
2160 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2161#else
2162 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2163#endif /* LIBXML_HTML_ENABLED */
2164 } else
2165 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2166 xmlOutputBufferClose(outbuf);
2167}
2168
2169/************************************************************************
2170 * *
2171 * Saving functions front-ends *
2172 * *
2173 ************************************************************************/
2174
2175/**
2176 * xmlNodeDumpOutput:
2177 * @buf: the XML buffer output
2178 * @doc: the document
2179 * @cur: the current node
2180 * @level: the imbrication level for indenting
2181 * @format: is formatting allowed
2182 * @encoding: an optional encoding string
2183 *
2184 * Dump an XML node, recursive behaviour, children are printed too.
2185 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2186 * or xmlKeepBlanksDefault(0) was called
2187 */
2188void
2189xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2190 int level, int format, const char *encoding)
2191{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002192 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002193#ifdef LIBXML_HTML_ENABLED
2194 xmlDtdPtr dtd;
2195 int is_xhtml = 0;
2196#endif
2197
2198 xmlInitParser();
2199
Daniel Veillardce244ad2004-11-05 10:03:46 +00002200 if ((buf == NULL) || (cur == NULL)) return;
2201
Daniel Veillard64354ea2005-03-31 15:22:56 +00002202 if (encoding == NULL)
2203 encoding = "UTF-8";
2204
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002205 memset(&ctxt, 0, sizeof(ctxt));
2206 ctxt.doc = doc;
2207 ctxt.buf = buf;
2208 ctxt.level = level;
Adam Spragg8b877132010-11-01 14:24:56 +01002209 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002210 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002211 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002212 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002213
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002214#ifdef LIBXML_HTML_ENABLED
2215 dtd = xmlGetIntSubset(doc);
2216 if (dtd != NULL) {
Daniel Veillard33b20b72005-09-12 21:43:20 +00002217 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2218 if (is_xhtml < 0)
2219 is_xhtml = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002220 }
2221
2222 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002223 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002224 else
2225#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002226 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002227}
2228
2229/**
2230 * xmlDocDumpFormatMemoryEnc:
2231 * @out_doc: Document to generate XML text from
2232 * @doc_txt_ptr: Memory pointer for allocated XML text
2233 * @doc_txt_len: Length of the generated XML text
2234 * @txt_encoding: Character encoding to use when generating XML text
2235 * @format: should formatting spaces been added
2236 *
2237 * Dump the current DOM tree into memory using the character encoding specified
2238 * by the caller. Note it is up to the caller of this function to free the
2239 * allocated memory with xmlFree().
2240 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2241 * or xmlKeepBlanksDefault(0) was called
2242 */
2243
2244void
2245xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2246 int * doc_txt_len, const char * txt_encoding,
2247 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002248 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002249 int dummy = 0;
2250 xmlOutputBufferPtr out_buff = NULL;
2251 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2252
2253 if (doc_txt_len == NULL) {
2254 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2255 }
2256
2257 if (doc_txt_ptr == NULL) {
2258 *doc_txt_len = 0;
2259 return;
2260 }
2261
2262 *doc_txt_ptr = NULL;
2263 *doc_txt_len = 0;
2264
2265 if (out_doc == NULL) {
2266 /* No document, no output */
2267 return;
2268 }
2269
2270 /*
2271 * Validate the encoding value, if provided.
2272 * This logic is copied from xmlSaveFileEnc.
2273 */
2274
2275 if (txt_encoding == NULL)
2276 txt_encoding = (const char *) out_doc->encoding;
2277 if (txt_encoding != NULL) {
2278 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2279 if ( conv_hdlr == NULL ) {
2280 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2281 txt_encoding);
2282 return;
2283 }
2284 }
2285
2286 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2287 xmlSaveErrMemory("creating buffer");
2288 return;
2289 }
2290
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002291 memset(&ctxt, 0, sizeof(ctxt));
2292 ctxt.doc = out_doc;
2293 ctxt.buf = out_buff;
2294 ctxt.level = 0;
Adam Spragg8b877132010-11-01 14:24:56 +01002295 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002296 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002297 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002298 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002299 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002300 xmlOutputBufferFlush(out_buff);
2301 if (out_buff->conv != NULL) {
2302 *doc_txt_len = out_buff->conv->use;
2303 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
2304 } else {
2305 *doc_txt_len = out_buff->buffer->use;
2306 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
2307 }
2308 (void)xmlOutputBufferClose(out_buff);
2309
2310 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2311 *doc_txt_len = 0;
2312 xmlSaveErrMemory("creating output");
2313 }
2314
2315 return;
2316}
2317
2318/**
2319 * xmlDocDumpMemory:
2320 * @cur: the document
2321 * @mem: OUT: the memory pointer
2322 * @size: OUT: the memory length
2323 *
2324 * Dump an XML document in memory and return the #xmlChar * and it's size
2325 * in bytes. It's up to the caller to free the memory with xmlFree().
2326 * The resulting byte array is zero terminated, though the last 0 is not
2327 * included in the returned size.
2328 */
2329void
2330xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2331 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2332}
2333
2334/**
2335 * xmlDocDumpFormatMemory:
2336 * @cur: the document
2337 * @mem: OUT: the memory pointer
2338 * @size: OUT: the memory length
2339 * @format: should formatting spaces been added
2340 *
2341 *
2342 * Dump an XML document in memory and return the #xmlChar * and it's size.
2343 * It's up to the caller to free the memory with xmlFree().
2344 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2345 * or xmlKeepBlanksDefault(0) was called
2346 */
2347void
2348xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2349 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2350}
2351
2352/**
2353 * xmlDocDumpMemoryEnc:
2354 * @out_doc: Document to generate XML text from
2355 * @doc_txt_ptr: Memory pointer for allocated XML text
2356 * @doc_txt_len: Length of the generated XML text
2357 * @txt_encoding: Character encoding to use when generating XML text
2358 *
2359 * Dump the current DOM tree into memory using the character encoding specified
2360 * by the caller. Note it is up to the caller of this function to free the
2361 * allocated memory with xmlFree().
2362 */
2363
2364void
2365xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2366 int * doc_txt_len, const char * txt_encoding) {
2367 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2368 txt_encoding, 0);
2369}
2370
2371/**
2372 * xmlDocFormatDump:
2373 * @f: the FILE*
2374 * @cur: the document
2375 * @format: should formatting spaces been added
2376 *
2377 * Dump an XML document to an open FILE.
2378 *
2379 * returns: the number of bytes written or -1 in case of failure.
2380 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2381 * or xmlKeepBlanksDefault(0) was called
2382 */
2383int
2384xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002385 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002386 xmlOutputBufferPtr buf;
2387 const char * encoding;
2388 xmlCharEncodingHandlerPtr handler = NULL;
2389 int ret;
2390
2391 if (cur == NULL) {
2392#ifdef DEBUG_TREE
2393 xmlGenericError(xmlGenericErrorContext,
2394 "xmlDocDump : document == NULL\n");
2395#endif
2396 return(-1);
2397 }
2398 encoding = (const char *) cur->encoding;
2399
2400 if (encoding != NULL) {
Daniel Veillard3814a362007-07-26 11:41:46 +00002401 handler = xmlFindCharEncodingHandler(encoding);
2402 if (handler == NULL) {
2403 xmlFree((char *) cur->encoding);
2404 cur->encoding = NULL;
2405 encoding = NULL;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002406 }
Daniel Veillard3814a362007-07-26 11:41:46 +00002407 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002408 buf = xmlOutputBufferCreateFile(f, handler);
2409 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002410 memset(&ctxt, 0, sizeof(ctxt));
2411 ctxt.doc = cur;
2412 ctxt.buf = buf;
2413 ctxt.level = 0;
Adam Spragg8b877132010-11-01 14:24:56 +01002414 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002415 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002416 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002417 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002418 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002419
2420 ret = xmlOutputBufferClose(buf);
2421 return(ret);
2422}
2423
2424/**
2425 * xmlDocDump:
2426 * @f: the FILE*
2427 * @cur: the document
2428 *
2429 * Dump an XML document to an open FILE.
2430 *
2431 * returns: the number of bytes written or -1 in case of failure.
2432 */
2433int
2434xmlDocDump(FILE *f, xmlDocPtr cur) {
2435 return(xmlDocFormatDump (f, cur, 0));
2436}
2437
2438/**
2439 * xmlSaveFileTo:
2440 * @buf: an output I/O buffer
2441 * @cur: the document
2442 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2443 *
2444 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002445 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2446 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002447 *
2448 * returns: the number of bytes written or -1 in case of failure.
2449 */
2450int
2451xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002452 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002453 int ret;
2454
Daniel Veillard3d97e662004-11-04 10:49:00 +00002455 if (buf == NULL) return(-1);
2456 if (cur == NULL) {
2457 xmlOutputBufferClose(buf);
2458 return(-1);
2459 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002460 memset(&ctxt, 0, sizeof(ctxt));
2461 ctxt.doc = cur;
2462 ctxt.buf = buf;
2463 ctxt.level = 0;
2464 ctxt.format = 0;
2465 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002466 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002467 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002468 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002469 ret = xmlOutputBufferClose(buf);
2470 return(ret);
2471}
2472
2473/**
2474 * xmlSaveFormatFileTo:
2475 * @buf: an output I/O buffer
2476 * @cur: the document
2477 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2478 * @format: should formatting spaces been added
2479 *
2480 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002481 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2482 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002483 *
2484 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002485 */
2486int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002487xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2488 const char *encoding, int format)
2489{
2490 xmlSaveCtxt ctxt;
2491 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002492
Daniel Veillard3d97e662004-11-04 10:49:00 +00002493 if (buf == NULL) return(-1);
Daniel Veillardce244ad2004-11-05 10:03:46 +00002494 if ((cur == NULL) ||
2495 ((cur->type != XML_DOCUMENT_NODE) &&
2496 (cur->type != XML_HTML_DOCUMENT_NODE))) {
Daniel Veillard3d97e662004-11-04 10:49:00 +00002497 xmlOutputBufferClose(buf);
2498 return(-1);
2499 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002500 memset(&ctxt, 0, sizeof(ctxt));
2501 ctxt.doc = cur;
2502 ctxt.buf = buf;
2503 ctxt.level = 0;
Adam Spragg8b877132010-11-01 14:24:56 +01002504 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002505 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002506 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002507 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002508 xmlDocContentDumpOutput(&ctxt, cur);
2509 ret = xmlOutputBufferClose(buf);
2510 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002511}
2512
2513/**
2514 * xmlSaveFormatFileEnc:
2515 * @filename: the filename or URL to output
2516 * @cur: the document being saved
2517 * @encoding: the name of the encoding to use or NULL.
2518 * @format: should formatting spaces be added.
2519 *
2520 * Dump an XML document to a file or an URL.
2521 *
2522 * Returns the number of bytes written or -1 in case of error.
2523 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2524 * or xmlKeepBlanksDefault(0) was called
2525 */
2526int
2527xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2528 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002529 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002530 xmlOutputBufferPtr buf;
2531 xmlCharEncodingHandlerPtr handler = NULL;
2532 int ret;
2533
2534 if (cur == NULL)
2535 return(-1);
2536
2537 if (encoding == NULL)
2538 encoding = (const char *) cur->encoding;
2539
2540 if (encoding != NULL) {
2541
2542 handler = xmlFindCharEncodingHandler(encoding);
2543 if (handler == NULL)
2544 return(-1);
2545 }
2546
2547#ifdef HAVE_ZLIB_H
2548 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2549#endif
2550 /*
2551 * save the content to a temp buffer.
2552 */
2553 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2554 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002555 memset(&ctxt, 0, sizeof(ctxt));
2556 ctxt.doc = cur;
2557 ctxt.buf = buf;
2558 ctxt.level = 0;
Adam Spragg8b877132010-11-01 14:24:56 +01002559 ctxt.format = format ? 1 : 0;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002560 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002561 xmlSaveCtxtInit(&ctxt);
Daniel Veillard856d9282008-09-25 14:31:40 +00002562 ctxt.options |= XML_SAVE_AS_XML;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002563
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002564 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002565
2566 ret = xmlOutputBufferClose(buf);
2567 return(ret);
2568}
2569
2570
2571/**
2572 * xmlSaveFileEnc:
2573 * @filename: the filename (or URL)
2574 * @cur: the document
2575 * @encoding: the name of an encoding (or NULL)
2576 *
2577 * Dump an XML document, converting it to the given encoding
2578 *
2579 * returns: the number of bytes written or -1 in case of failure.
2580 */
2581int
2582xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2583 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2584}
2585
2586/**
2587 * xmlSaveFormatFile:
2588 * @filename: the filename (or URL)
2589 * @cur: the document
2590 * @format: should formatting spaces been added
2591 *
2592 * Dump an XML document to a file. Will use compression if
2593 * compiled in and enabled. If @filename is "-" the stdout file is
2594 * used. If @format is set then the document will be indented on output.
2595 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2596 * or xmlKeepBlanksDefault(0) was called
2597 *
2598 * returns: the number of bytes written or -1 in case of failure.
2599 */
2600int
2601xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2602 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2603}
2604
2605/**
2606 * xmlSaveFile:
2607 * @filename: the filename (or URL)
2608 * @cur: the document
2609 *
2610 * Dump an XML document to a file. Will use compression if
2611 * compiled in and enabled. If @filename is "-" the stdout file is
2612 * used.
2613 * returns: the number of bytes written or -1 in case of failure.
2614 */
2615int
2616xmlSaveFile(const char *filename, xmlDocPtr cur) {
2617 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2618}
2619
2620#endif /* LIBXML_OUTPUT_ENABLED */
2621
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002622#define bottom_xmlsave
2623#include "elfgcchack.h"