blob: 398f62a20fc6b4565defcee1807f98d8d1597b7e [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:
125 msg = "string is not in UTF-8";
126 break;
127 case XML_SAVE_CHAR_INVALID:
128 msg = "invalid character value";
129 break;
130 case XML_SAVE_UNKNOWN_ENCODING:
131 msg = "unknown encoding %s";
132 break;
133 case XML_SAVE_NO_DOCTYPE:
134 msg = "document has no DOCTYPE";
135 break;
136 default:
137 msg = "unexpected error number";
138 }
139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
140}
141
142/************************************************************************
143 * *
Daniel Veillard83a75e02004-05-14 21:50:42 +0000144 * Special escaping routines *
145 * *
146 ************************************************************************/
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000147static unsigned char *
148xmlSerializeHexCharRef(unsigned char *out, int val) {
149 unsigned char *ptr;
150
151 *out++ = '&';
152 *out++ = '#';
153 *out++ = 'x';
154 if (val < 0x10) ptr = out;
155 else if (val < 0x100) ptr = out + 1;
156 else if (val < 0x1000) ptr = out + 2;
157 else if (val < 0x10000) ptr = out + 3;
158 else if (val < 0x100000) ptr = out + 4;
159 else ptr = out + 5;
160 out = ptr + 1;
161 while (val > 0) {
162 switch (val & 0xF) {
163 case 0: *ptr-- = '0'; break;
164 case 1: *ptr-- = '1'; break;
165 case 2: *ptr-- = '2'; break;
166 case 3: *ptr-- = '3'; break;
167 case 4: *ptr-- = '4'; break;
168 case 5: *ptr-- = '5'; break;
169 case 6: *ptr-- = '6'; break;
170 case 7: *ptr-- = '7'; break;
171 case 8: *ptr-- = '8'; break;
172 case 9: *ptr-- = '9'; break;
173 case 0xA: *ptr-- = 'A'; break;
174 case 0xB: *ptr-- = 'B'; break;
175 case 0xC: *ptr-- = 'C'; break;
176 case 0xD: *ptr-- = 'D'; break;
177 case 0xE: *ptr-- = 'E'; break;
178 case 0xF: *ptr-- = 'F'; break;
179 default: *ptr-- = '0'; break;
180 }
181 val >>= 4;
182 }
183 *out++ = ';';
184 *out = 0;
185 return(out);
186}
187
Daniel Veillard83a75e02004-05-14 21:50:42 +0000188/**
189 * xmlEscapeEntities:
190 * @out: a pointer to an array of bytes to store the result
191 * @outlen: the length of @out
192 * @in: a pointer to an array of unescaped UTF-8 bytes
193 * @inlen: the length of @in
194 *
195 * Take a block of UTF-8 chars in and escape them. Used when there is no
196 * encoding specified.
197 *
198 * Returns 0 if success, or -1 otherwise
199 * The value of @inlen after return is the number of octets consumed
200 * if the return value is positive, else unpredictable.
201 * The value of @outlen after return is the number of octets consumed.
202 */
203static int
204xmlEscapeEntities(unsigned char* out, int *outlen,
205 const xmlChar* in, int *inlen) {
206 unsigned char* outstart = out;
207 const unsigned char* base = in;
208 unsigned char* outend = out + *outlen;
209 const unsigned char* inend;
210 int val;
211
212 inend = in + (*inlen);
213
214 while ((in < inend) && (out < outend)) {
215 if (*in == '<') {
216 if (outend - out < 4) break;
217 *out++ = '&';
218 *out++ = 'l';
219 *out++ = 't';
220 *out++ = ';';
221 in++;
222 continue;
223 } else if (*in == '>') {
224 if (outend - out < 4) break;
225 *out++ = '&';
226 *out++ = 'g';
227 *out++ = 't';
228 *out++ = ';';
229 in++;
230 continue;
231 } else if (*in == '&') {
232 if (outend - out < 5) break;
233 *out++ = '&';
234 *out++ = 'a';
235 *out++ = 'm';
236 *out++ = 'p';
237 *out++ = ';';
238 in++;
239 continue;
240 } else if (((*in >= 0x20) && (*in < 0x80)) ||
241 (*in == '\n') || (*in == '\t')) {
242 /*
243 * default case, just copy !
244 */
245 *out++ = *in++;
246 continue;
247 } else if (*in >= 0x80) {
248 /*
249 * We assume we have UTF-8 input.
250 */
Daniel Veillard83a75e02004-05-14 21:50:42 +0000251 if (outend - out < 10) break;
252
253 if (*in < 0xC0) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000254 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000255 in++;
256 goto error;
257 } else if (*in < 0xE0) {
258 if (inend - in < 2) break;
259 val = (in[0]) & 0x1F;
260 val <<= 6;
261 val |= (in[1]) & 0x3F;
262 in += 2;
263 } else if (*in < 0xF0) {
264 if (inend - in < 3) break;
265 val = (in[0]) & 0x0F;
266 val <<= 6;
267 val |= (in[1]) & 0x3F;
268 val <<= 6;
269 val |= (in[2]) & 0x3F;
270 in += 3;
271 } else if (*in < 0xF8) {
272 if (inend - in < 4) break;
273 val = (in[0]) & 0x07;
274 val <<= 6;
275 val |= (in[1]) & 0x3F;
276 val <<= 6;
277 val |= (in[2]) & 0x3F;
278 val <<= 6;
279 val |= (in[3]) & 0x3F;
280 in += 4;
281 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000282 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000283 in++;
284 goto error;
285 }
286 if (!IS_CHAR(val)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000287 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000288 in++;
289 goto error;
290 }
291
292 /*
293 * We could do multiple things here. Just save as a char ref
294 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000295 out = xmlSerializeHexCharRef(out, val);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000296 } else if (IS_BYTE_CHAR(*in)) {
297 if (outend - out < 6) break;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000298 out = xmlSerializeHexCharRef(out, *in++);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000299 } else {
300 xmlGenericError(xmlGenericErrorContext,
301 "xmlEscapeEntities : char out of range\n");
302 in++;
303 goto error;
304 }
305 }
306 *outlen = out - outstart;
307 *inlen = in - base;
308 return(0);
309error:
310 *outlen = out - outstart;
311 *inlen = in - base;
312 return(-1);
313}
314
315/************************************************************************
316 * *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000317 * Allocation and deallocation *
318 * *
319 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000320/**
321 * xmlSaveCtxtInit:
322 * @ctxt: the saving context
323 *
324 * Initialize a saving context
325 */
326static void
327xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
328{
329 int i;
330
331 if (ctxt == NULL) return;
Daniel Veillard3995bc32004-05-15 18:57:31 +0000332 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
333 ctxt->escape = xmlEscapeEntities;
Daniel Veillard753086a2004-03-28 16:12:44 +0000334 if (xmlTreeIndentString == NULL) {
335 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
336 } else {
337 ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
338 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
339 for (i = 0;i < ctxt->indent_nr;i++)
340 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
341 ctxt->indent_size);
342 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
343 }
344}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000345
346/**
347 * xmlFreeSaveCtxt:
348 *
349 * Free a saving context, destroying the ouptut in any remaining buffer
350 */
351static void
352xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
353{
354 if (ctxt == NULL) return;
355 if (ctxt->encoding != NULL)
356 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000357 if (ctxt->buf != NULL)
358 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000359 xmlFree(ctxt);
360}
361
362/**
363 * xmlNewSaveCtxt:
364 *
365 * Create a new saving context
366 *
367 * Returns the new structure or NULL in case of error
368 */
369static xmlSaveCtxtPtr
370xmlNewSaveCtxt(const char *encoding, int options)
371{
372 xmlSaveCtxtPtr ret;
373
374 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
375 if (ret == NULL) {
376 xmlSaveErrMemory("creating saving context");
377 return ( NULL );
378 }
379 memset(ret, 0, sizeof(xmlSaveCtxt));
380 ret->options = options;
381 if (encoding != NULL) {
382 ret->handler = xmlFindCharEncodingHandler(encoding);
383 if (ret->handler == NULL) {
384 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
385 xmlFreeSaveCtxt(ret);
386 return(NULL);
387 }
388 ret->encoding = xmlStrdup((const xmlChar *)encoding);
Daniel Veillard3995bc32004-05-15 18:57:31 +0000389 ret->escape = xmlEscapeEntities;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000390 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000391 xmlSaveCtxtInit(ret);
392
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000393 return(ret);
394}
395
396/************************************************************************
397 * *
398 * Dumping XML tree content to a simple buffer *
399 * *
400 ************************************************************************/
401/**
402 * xmlAttrSerializeContent:
403 * @buf: the XML buffer output
404 * @doc: the document
405 * @attr: the attribute pointer
406 *
407 * Serialize the attribute in the buffer
408 */
409static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000410xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000411{
412 xmlNodePtr children;
413
414 children = attr->children;
415 while (children != NULL) {
416 switch (children->type) {
417 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000418 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
419 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000420 break;
421 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000422 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
423 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000424 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000425 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000426 break;
427 default:
428 /* should not happen unless we have a badly built tree */
429 break;
430 }
431 children = children->next;
432 }
433}
434
435/************************************************************************
436 * *
437 * Dumping XML tree content to an I/O output buffer *
438 * *
439 ************************************************************************/
440
441#ifdef LIBXML_HTML_ENABLED
442static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000443xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000444#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000445static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
446static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000447void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
448
449/**
450 * xmlNsDumpOutput:
451 * @buf: the XML buffer output
452 * @cur: a namespace
453 *
454 * Dump a local Namespace definition.
455 * Should be called in the context of attributes dumps.
456 */
457static void
458xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000459 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000460 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
461 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
462 return;
463
464 /* Within the context of an element attributes */
465 if (cur->prefix != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000466 xmlOutputBufferWrite(buf, 7, " xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000467 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
468 } else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000469 xmlOutputBufferWrite(buf, 6, " xmlns");
470 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000471 xmlBufferWriteQuotedString(buf->buffer, cur->href);
472 }
473}
474
475/**
476 * xmlNsListDumpOutput:
477 * @buf: the XML buffer output
478 * @cur: the first namespace
479 *
480 * Dump a list of local Namespace definitions.
481 * Should be called in the context of attributes dumps.
482 */
483void
484xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
485 while (cur != NULL) {
486 xmlNsDumpOutput(buf, cur);
487 cur = cur->next;
488 }
489}
490
491/**
492 * xmlDtdDumpOutput:
493 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000494 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000495 *
496 * Dump the XML document DTD, if any.
497 */
498static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000499xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
500 xmlOutputBufferPtr buf;
501 int format, level;
502 xmlDocPtr doc;
503
504 if (dtd == NULL) return;
505 if ((ctxt == NULL) || (ctxt->buf == NULL))
506 return;
507 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000508 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000509 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
510 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000511 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000512 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000513 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000514 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
515 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000516 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000517 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
518 }
519 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
Daniel Veillard41c4a752004-09-08 20:55:38 +0000520 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
521 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000522 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000523 return;
524 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000525 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard41c4a752004-09-08 20:55:38 +0000526 /*
527 * Dump the notations first they are not in the DTD children list
528 * Do this only on a standalone DTD or on the internal subset though.
529 */
530 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
531 (dtd->doc->intSubset == dtd))) {
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000532 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
533 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000534 format = ctxt->format;
535 level = ctxt->level;
536 doc = ctxt->doc;
537 ctxt->format = 0;
538 ctxt->level = -1;
539 ctxt->doc = dtd->doc;
540 xmlNodeListDumpOutput(ctxt, dtd->children);
541 ctxt->format = format;
542 ctxt->level = level;
543 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000544 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000545}
546
547/**
548 * xmlAttrDumpOutput:
549 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000550 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000551 *
552 * Dump an XML attribute
553 */
554static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000555xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
556 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000557
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000558 if (cur == NULL) return;
559 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000560 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000561 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
562 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000563 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000564 }
565 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000566 xmlOutputBufferWrite(buf, 2, "=\"");
567 xmlAttrSerializeContent(buf, cur);
568 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000569}
570
571/**
572 * xmlAttrListDumpOutput:
573 * @buf: the XML buffer output
574 * @doc: the document
575 * @cur: the first attribute pointer
576 * @encoding: an optional encoding string
577 *
578 * Dump a list of XML attributes
579 */
580static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000581xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
582 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000583 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000584 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000585 cur = cur->next;
586 }
587}
588
589
590
591/**
592 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000593 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000594 *
595 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000596 */
597static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000598xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000599 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000600
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000601 if (cur == NULL) return;
602 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000603 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000604 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000605 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000606 xmlOutputBufferWrite(buf, ctxt->indent_size *
607 (ctxt->level > ctxt->indent_nr ?
608 ctxt->indent_nr : ctxt->level),
609 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000610 xmlNodeDumpOutputInternal(ctxt, cur);
611 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000612 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000613 }
614 cur = cur->next;
615 }
616}
617
618/**
619 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000620 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000621 *
622 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000623 */
624static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000625xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000626 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000627 xmlNodePtr tmp;
628 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000629 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000630
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000631 if (cur == NULL) return;
632 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000633 if (cur->type == XML_XINCLUDE_START)
634 return;
635 if (cur->type == XML_XINCLUDE_END)
636 return;
637 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000638 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000639 return;
640 }
641 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000642 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000643 return;
644 }
645 if (cur->type == XML_ELEMENT_DECL) {
646 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
647 return;
648 }
649 if (cur->type == XML_ATTRIBUTE_DECL) {
650 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
651 return;
652 }
653 if (cur->type == XML_ENTITY_DECL) {
654 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
655 return;
656 }
657 if (cur->type == XML_TEXT_NODE) {
658 if (cur->content != NULL) {
659 if ((cur->name == xmlStringText) ||
660 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000661
Daniel Veillard3995bc32004-05-15 18:57:31 +0000662 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000663 } else {
664 /*
665 * Disable escaping, needed for XSLT
666 */
667 xmlOutputBufferWriteString(buf, (const char *) cur->content);
668 }
669 }
670
671 return;
672 }
673 if (cur->type == XML_PI_NODE) {
674 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000675 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000676 xmlOutputBufferWriteString(buf, (const char *)cur->name);
677 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000678 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000679 xmlOutputBufferWriteString(buf, (const char *)cur->content);
680 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000681 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000682 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000683 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000684 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000685 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000686 }
687 return;
688 }
689 if (cur->type == XML_COMMENT_NODE) {
690 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000691 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000692 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000693 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000694 }
695 return;
696 }
697 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000698 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000699 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000700 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000701 return;
702 }
703 if (cur->type == XML_CDATA_SECTION_NODE) {
704 start = end = cur->content;
705 while (*end != '\0') {
706 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
707 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000708 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000709 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000710 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000711 start = end;
712 }
713 end++;
714 }
715 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000716 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000717 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000718 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000719 }
720 return;
721 }
722 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000723 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000724 return;
725 }
726 if (cur->type == XML_NAMESPACE_DECL) {
727 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
728 return;
729 }
730
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000731 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000732 if (format == 1) {
733 tmp = cur->children;
734 while (tmp != NULL) {
735 if ((tmp->type == XML_TEXT_NODE) ||
736 (tmp->type == XML_CDATA_SECTION_NODE) ||
737 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000738 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000739 break;
740 }
741 tmp = tmp->next;
742 }
743 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000744 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000745 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
746 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000747 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000748 }
749
750 xmlOutputBufferWriteString(buf, (const char *)cur->name);
751 if (cur->nsDef)
752 xmlNsListDumpOutput(buf, cur->nsDef);
753 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000754 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000755
756 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
757 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000758 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000759 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000760 return;
761 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000762 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000763 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000764 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000765 }
766 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000767 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000768 if (ctxt->level >= 0) ctxt->level++;
769 xmlNodeListDumpOutput(ctxt, cur->children);
770 if (ctxt->level > 0) ctxt->level--;
771 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000772 xmlOutputBufferWrite(buf, ctxt->indent_size *
773 (ctxt->level > ctxt->indent_nr ?
774 ctxt->indent_nr : ctxt->level),
775 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000776 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000777 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000778 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
779 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000780 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000781 }
782
783 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000784 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000785 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000786}
787
788/**
789 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000790 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000791 *
792 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000793 */
794static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000795xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000796#ifdef LIBXML_HTML_ENABLED
797 xmlDtdPtr dtd;
798 int is_xhtml = 0;
799#endif
800 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000801 const xmlChar *encoding = ctxt->encoding;
802 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000803
804 xmlInitParser();
805
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000806 if (ctxt->encoding != NULL)
807 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000808
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000809 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000810 xmlOutputBufferWrite(buf, 14, "<?xml version=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000811 if (cur->version != NULL)
812 xmlBufferWriteQuotedString(buf->buffer, cur->version);
813 else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000814 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000815 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000816 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000817 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000818 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000819 encoding = (const xmlChar *)
820 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000821 }
822 if (encoding != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000823 xmlOutputBufferWrite(buf, 10, " encoding=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000824 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
825 }
826 switch (cur->standalone) {
827 case 0:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000828 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000829 break;
830 case 1:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000831 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000832 break;
833 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000834 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000835
836#ifdef LIBXML_HTML_ENABLED
837 dtd = xmlGetIntSubset(cur);
838 if (dtd != NULL) {
839 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
840 if (is_xhtml < 0) is_xhtml = 0;
841 }
842 if (is_xhtml) {
843 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000844 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000845 else
846 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
847 }
848#endif
849 if (cur->children != NULL) {
850 xmlNodePtr child = cur->children;
851
852 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000853 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000854#ifdef LIBXML_HTML_ENABLED
855 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000856 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000857 else
858#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000859 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000860 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000861 child = child->next;
862 }
863 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000864 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000865 cur->encoding = oldenc;
866}
867
868#ifdef LIBXML_HTML_ENABLED
869/************************************************************************
870 * *
871 * Functions specific to XHTML serialization *
872 * *
873 ************************************************************************/
874
875/**
876 * xhtmlIsEmpty:
877 * @node: the node
878 *
879 * Check if a node is an empty xhtml node
880 *
881 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
882 */
883static int
884xhtmlIsEmpty(xmlNodePtr node) {
885 if (node == NULL)
886 return(-1);
887 if (node->type != XML_ELEMENT_NODE)
888 return(0);
889 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
890 return(0);
891 if (node->children != NULL)
892 return(0);
893 switch (node->name[0]) {
894 case 'a':
895 if (xmlStrEqual(node->name, BAD_CAST "area"))
896 return(1);
897 return(0);
898 case 'b':
899 if (xmlStrEqual(node->name, BAD_CAST "br"))
900 return(1);
901 if (xmlStrEqual(node->name, BAD_CAST "base"))
902 return(1);
903 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
904 return(1);
905 return(0);
906 case 'c':
907 if (xmlStrEqual(node->name, BAD_CAST "col"))
908 return(1);
909 return(0);
910 case 'f':
911 if (xmlStrEqual(node->name, BAD_CAST "frame"))
912 return(1);
913 return(0);
914 case 'h':
915 if (xmlStrEqual(node->name, BAD_CAST "hr"))
916 return(1);
917 return(0);
918 case 'i':
919 if (xmlStrEqual(node->name, BAD_CAST "img"))
920 return(1);
921 if (xmlStrEqual(node->name, BAD_CAST "input"))
922 return(1);
923 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
924 return(1);
925 return(0);
926 case 'l':
927 if (xmlStrEqual(node->name, BAD_CAST "link"))
928 return(1);
929 return(0);
930 case 'm':
931 if (xmlStrEqual(node->name, BAD_CAST "meta"))
932 return(1);
933 return(0);
934 case 'p':
935 if (xmlStrEqual(node->name, BAD_CAST "param"))
936 return(1);
937 return(0);
938 }
939 return(0);
940}
941
942/**
943 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000944 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000945 *
946 * Dump a list of XML attributes
947 */
948static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000949xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000950 xmlAttrPtr xml_lang = NULL;
951 xmlAttrPtr lang = NULL;
952 xmlAttrPtr name = NULL;
953 xmlAttrPtr id = NULL;
954 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000955 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000956
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000957 if (cur == NULL) return;
958 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000959 parent = cur->parent;
960 while (cur != NULL) {
961 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
962 id = cur;
963 else
964 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
965 name = cur;
966 else
967 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
968 lang = cur;
969 else
970 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
971 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
972 xml_lang = cur;
973 else if ((cur->ns == NULL) &&
974 ((cur->children == NULL) ||
975 (cur->children->content == NULL) ||
976 (cur->children->content[0] == 0)) &&
977 (htmlIsBooleanAttr(cur->name))) {
978 if (cur->children != NULL)
979 xmlFreeNode(cur->children);
980 cur->children = xmlNewText(cur->name);
981 if (cur->children != NULL)
982 cur->children->parent = (xmlNodePtr) cur;
983 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000984 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000985 cur = cur->next;
986 }
987 /*
988 * C.8
989 */
990 if ((name != NULL) && (id == NULL)) {
991 if ((parent != NULL) && (parent->name != NULL) &&
992 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
993 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
994 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
995 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
996 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
997 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
998 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
999 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1000 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001001 xmlOutputBufferWrite(buf, 5, " id=\"");
1002 xmlAttrSerializeContent(buf, name);
1003 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001004 }
1005 }
1006 /*
1007 * C.7.
1008 */
1009 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001010 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1011 xmlAttrSerializeContent(buf, lang);
1012 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001013 } else
1014 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001015 xmlOutputBufferWrite(buf, 7, " lang=\"");
1016 xmlAttrSerializeContent(buf, xml_lang);
1017 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001018 }
1019}
1020
1021/**
1022 * xhtmlNodeListDumpOutput:
1023 * @buf: the XML buffer output
1024 * @doc: the XHTML document
1025 * @cur: the first node
1026 * @level: the imbrication level for indenting
1027 * @format: is formatting allowed
1028 * @encoding: an optional encoding string
1029 *
1030 * Dump an XML node list, recursive behaviour, children are printed too.
1031 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1032 * or xmlKeepBlanksDefault(0) was called
1033 */
1034static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001035xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001036 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001037
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001038 if (cur == NULL) return;
1039 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001040 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001041 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001042 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001043 xmlOutputBufferWrite(buf, ctxt->indent_size *
1044 (ctxt->level > ctxt->indent_nr ?
1045 ctxt->indent_nr : ctxt->level),
1046 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001047 xhtmlNodeDumpOutput(ctxt, cur);
1048 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001049 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001050 }
1051 cur = cur->next;
1052 }
1053}
1054
1055/**
1056 * xhtmlNodeDumpOutput:
1057 * @buf: the XML buffer output
1058 * @doc: the XHTML document
1059 * @cur: the current node
1060 * @level: the imbrication level for indenting
1061 * @format: is formatting allowed
1062 * @encoding: an optional encoding string
1063 *
1064 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001065 */
1066static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001067xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +00001068 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001069 xmlNodePtr tmp;
1070 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001071 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001072
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001073 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001074 if (cur->type == XML_XINCLUDE_START)
1075 return;
1076 if (cur->type == XML_XINCLUDE_END)
1077 return;
1078 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001079 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001080 return;
1081 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001082 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001083 if (cur->type == XML_ELEMENT_DECL) {
1084 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1085 return;
1086 }
1087 if (cur->type == XML_ATTRIBUTE_DECL) {
1088 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1089 return;
1090 }
1091 if (cur->type == XML_ENTITY_DECL) {
1092 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1093 return;
1094 }
1095 if (cur->type == XML_TEXT_NODE) {
1096 if (cur->content != NULL) {
1097 if ((cur->name == xmlStringText) ||
1098 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001099 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001100 } else {
1101 /*
1102 * Disable escaping, needed for XSLT
1103 */
1104 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1105 }
1106 }
1107
1108 return;
1109 }
1110 if (cur->type == XML_PI_NODE) {
1111 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001112 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001113 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1114 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001115 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001116 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1117 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001118 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001119 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001120 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001121 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001122 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001123 }
1124 return;
1125 }
1126 if (cur->type == XML_COMMENT_NODE) {
1127 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001128 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001129 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001130 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001131 }
1132 return;
1133 }
1134 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001135 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001136 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001137 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001138 return;
1139 }
1140 if (cur->type == XML_CDATA_SECTION_NODE) {
1141 start = end = cur->content;
1142 while (*end != '\0') {
1143 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1144 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001145 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001146 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001147 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001148 start = end;
1149 }
1150 end++;
1151 }
1152 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001153 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001154 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001155 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001156 }
1157 return;
1158 }
1159
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001160 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001161 if (format == 1) {
1162 tmp = cur->children;
1163 while (tmp != NULL) {
1164 if ((tmp->type == XML_TEXT_NODE) ||
1165 (tmp->type == XML_ENTITY_REF_NODE)) {
1166 format = 0;
1167 break;
1168 }
1169 tmp = tmp->next;
1170 }
1171 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001172 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001173 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1174 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001175 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001176 }
1177
1178 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1179 if (cur->nsDef)
1180 xmlNsListDumpOutput(buf, cur->nsDef);
1181 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1182 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1183 /*
1184 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1185 */
1186 xmlOutputBufferWriteString(buf,
1187 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1188 }
1189 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001190 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001191
1192 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1193 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1194 (xhtmlIsEmpty(cur) == 1)) {
1195 /*
1196 * C.2. Empty Elements
1197 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001198 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001199 } else {
1200 /*
1201 * C.3. Element Minimization and Empty Element Content
1202 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001203 xmlOutputBufferWrite(buf, 3, "></");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001204 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1205 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001206 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001207 }
1208 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001209 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001210 }
1211 return;
1212 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001213 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001214 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001215 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001216 }
1217
1218 /*
1219 * 4.8. Script and Style elements
1220 */
1221 if ((cur->type == XML_ELEMENT_NODE) &&
1222 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1223 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1224 ((cur->ns == NULL) ||
1225 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1226 xmlNodePtr child = cur->children;
1227
1228 while (child != NULL) {
1229 if ((child->type == XML_TEXT_NODE) ||
1230 (child->type == XML_CDATA_SECTION_NODE)) {
1231 /*
1232 * Apparently CDATA escaping for style just break on IE,
1233 * mozilla and galeon, so ...
1234 */
1235 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1236 (xmlStrchr(child->content, '<') == NULL) &&
1237 (xmlStrchr(child->content, '>') == NULL) &&
1238 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001239 int level = ctxt->level;
1240 int indent = ctxt->format;
1241
1242 ctxt->level = 0;
1243 ctxt->format = 0;
1244 xhtmlNodeDumpOutput(ctxt, child);
1245 ctxt->level = level;
1246 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001247 } else {
1248 start = end = child->content;
1249 while (*end != '\0') {
1250 if (*end == ']' &&
1251 *(end + 1) == ']' &&
1252 *(end + 2) == '>') {
1253 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001254 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001255 xmlOutputBufferWrite(buf, end - start,
1256 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001257 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001258 start = end;
1259 }
1260 end++;
1261 }
1262 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001263 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1264 xmlOutputBufferWrite(buf, end - start,
1265 (const char *)start);
1266 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001267 }
1268 }
1269 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001270 int level = ctxt->level;
1271 int indent = ctxt->format;
1272
1273 ctxt->level = 0;
1274 ctxt->format = 0;
1275 xhtmlNodeDumpOutput(ctxt, child);
1276 ctxt->level = level;
1277 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001278 }
1279 child = child->next;
1280 }
1281 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001282 int indent = ctxt->format;
1283
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001284 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001285 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001286 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001287 xhtmlNodeListDumpOutput(ctxt, cur->children);
1288 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001289 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001290 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001291 xmlOutputBufferWrite(buf, ctxt->indent_size *
1292 (ctxt->level > ctxt->indent_nr ?
1293 ctxt->indent_nr : ctxt->level),
1294 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001295 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001296 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001297 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1298 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001299 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001300 }
1301
1302 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001303 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001304}
1305#endif
1306
1307/************************************************************************
1308 * *
1309 * Public entry points *
1310 * *
1311 ************************************************************************/
1312
1313/**
1314 * xmlSaveToFd:
1315 * @fd: a file descriptor number
1316 * @encoding: the encoding name to use or NULL
1317 * @options: a set of xmlSaveOptions
1318 *
1319 * Create a document saving context serializing to a file descriptor
1320 * with the encoding and the options given.
1321 *
1322 * Returns a new serialization context or NULL in case of error.
1323 */
1324xmlSaveCtxtPtr
1325xmlSaveToFd(int fd, const char *encoding, int options)
1326{
1327 xmlSaveCtxtPtr ret;
1328
1329 ret = xmlNewSaveCtxt(encoding, options);
1330 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001331 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1332 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001333 xmlFreeSaveCtxt(ret);
1334 return(NULL);
1335 }
1336 return(ret);
1337}
1338
1339/**
1340 * xmlSaveToFilename:
1341 * @filename: a file name or an URL
1342 * @encoding: the encoding name to use or NULL
1343 * @options: a set of xmlSaveOptions
1344 *
1345 * Create a document saving context serializing to a filename or possibly
1346 * to an URL (but this is less reliable) with the encoding and the options
1347 * given.
1348 *
1349 * Returns a new serialization context or NULL in case of error.
1350 */
1351xmlSaveCtxtPtr
1352xmlSaveToFilename(const char *filename, const char *encoding, int options)
1353{
1354 xmlSaveCtxtPtr ret;
1355 int compression = 0; /* TODO handle compression option */
1356
1357 ret = xmlNewSaveCtxt(encoding, options);
1358 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001359 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001360 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001361 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001362 xmlFreeSaveCtxt(ret);
1363 return(NULL);
1364 }
1365 return(ret);
1366}
1367
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001368/**
1369 * xmlSaveToBuffer:
1370 * @buffer: a buffer
1371 * @encoding: the encoding name to use or NULL
1372 * @options: a set of xmlSaveOptions
1373 *
1374 * Create a document saving context serializing to a buffer
1375 * with the encoding and the options given
1376 *
1377 * Returns a new serialization context or NULL in case of error.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001378xmlSaveCtxtPtr
1379xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1380{
1381 TODO
1382 return(NULL);
1383}
Daniel Veillarda2351322004-06-27 12:08:10 +00001384 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001385
1386/**
1387 * xmlSaveToIO:
1388 * @iowrite: an I/O write function
1389 * @ioclose: an I/O close function
1390 * @ioctx: an I/O handler
1391 * @encoding: the encoding name to use or NULL
1392 * @options: a set of xmlSaveOptions
1393 *
1394 * Create a document saving context serializing to a file descriptor
1395 * with the encoding and the options given
1396 *
1397 * Returns a new serialization context or NULL in case of error.
1398 */
1399xmlSaveCtxtPtr
1400xmlSaveToIO(xmlOutputWriteCallback iowrite,
1401 xmlOutputCloseCallback ioclose,
1402 void *ioctx, const char *encoding, int options)
1403{
1404 xmlSaveCtxtPtr ret;
1405
1406 ret = xmlNewSaveCtxt(encoding, options);
1407 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001408 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1409 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001410 xmlFreeSaveCtxt(ret);
1411 return(NULL);
1412 }
1413 return(ret);
1414}
1415
1416/**
1417 * xmlSaveDoc:
1418 * @ctxt: a document saving context
1419 * @doc: a document
1420 *
1421 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001422 * TODO: The function is not fully implemented yet as it does not return the
1423 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001424 *
1425 * Returns the number of byte written or -1 in case of error
1426 */
1427long
1428xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1429{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001430 long ret = 0;
1431
1432 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001433 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001434}
1435
1436/**
1437 * xmlSaveTree:
1438 * @ctxt: a document saving context
1439 * @node: a document
1440 *
1441 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001442 * TODO: The function is not fully implemented yet as it does not return the
1443 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001444 *
1445 * Returns the number of byte written or -1 in case of error
1446 */
1447long
1448xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1449{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001450 long ret = 0;
1451
1452 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001453 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001454}
1455
1456/**
1457 * xmlSaveFlush:
1458 * @ctxt: a document saving context
1459 *
1460 * Flush a document saving context, i.e. make sure that all bytes have
1461 * been output.
1462 *
1463 * Returns the number of byte written or -1 in case of error.
1464 */
1465int
1466xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1467{
1468 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001469 if (ctxt->buf == NULL) return(-1);
1470 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001471}
1472
1473/**
1474 * xmlSaveClose:
1475 * @ctxt: a document saving context
1476 *
1477 * Close a document saving context, i.e. make sure that all bytes have
1478 * been output and free the associated data.
1479 *
1480 * Returns the number of byte written or -1 in case of error.
1481 */
1482int
1483xmlSaveClose(xmlSaveCtxtPtr ctxt)
1484{
1485 int ret;
1486
1487 if (ctxt == NULL) return(-1);
1488 ret = xmlSaveFlush(ctxt);
1489 xmlFreeSaveCtxt(ctxt);
1490 return(ret);
1491}
1492
Daniel Veillard3995bc32004-05-15 18:57:31 +00001493/**
1494 * xmlSaveSetEscape:
1495 * @ctxt: a document saving context
1496 * @escape: the escaping function
1497 *
1498 * Set a custom escaping function to be used for text in element content
1499 *
1500 * Returns 0 if successful or -1 in case of error.
1501 */
1502int
1503xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1504{
1505 if (ctxt == NULL) return(-1);
1506 ctxt->escape = escape;
1507 return(0);
1508}
1509
1510/**
1511 * xmlSaveSetAttrEscape:
1512 * @ctxt: a document saving context
1513 * @escape: the escaping function
1514 *
1515 * Set a custom escaping function to be used for text in attribute content
1516 *
1517 * Returns 0 if successful or -1 in case of error.
1518 */
1519int
1520xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1521{
1522 if (ctxt == NULL) return(-1);
1523 ctxt->escapeAttr = escape;
1524 return(0);
1525}
1526
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001527/************************************************************************
1528 * *
1529 * Public entry points based on buffers *
1530 * *
1531 ************************************************************************/
1532/**
1533 * xmlAttrSerializeTxtContent:
1534 * @buf: the XML buffer output
1535 * @doc: the document
1536 * @attr: the attribute node
1537 * @string: the text content
1538 *
1539 * Serialize text attribute values to an xml simple buffer
1540 */
1541void
1542xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001543 xmlAttrPtr attr, const xmlChar * string)
1544{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001545 xmlChar *base, *cur;
1546
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001547 if (string == NULL)
1548 return;
1549 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001550 while (*cur != 0) {
1551 if (*cur == '\n') {
1552 if (base != cur)
1553 xmlBufferAdd(buf, base, cur - base);
1554 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1555 cur++;
1556 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001557 } else if (*cur == '\r') {
1558 if (base != cur)
1559 xmlBufferAdd(buf, base, cur - base);
1560 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1561 cur++;
1562 base = cur;
1563 } else if (*cur == '\t') {
1564 if (base != cur)
1565 xmlBufferAdd(buf, base, cur - base);
1566 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1567 cur++;
1568 base = cur;
1569 } else if (*cur == '"') {
1570 if (base != cur)
1571 xmlBufferAdd(buf, base, cur - base);
1572 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1573 cur++;
1574 base = cur;
1575 } else if (*cur == '<') {
1576 if (base != cur)
1577 xmlBufferAdd(buf, base, cur - base);
1578 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1579 cur++;
1580 base = cur;
1581 } else if (*cur == '>') {
1582 if (base != cur)
1583 xmlBufferAdd(buf, base, cur - base);
1584 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1585 cur++;
1586 base = cur;
1587 } else if (*cur == '&') {
1588 if (base != cur)
1589 xmlBufferAdd(buf, base, cur - base);
1590 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1591 cur++;
1592 base = cur;
1593 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1594 (doc->encoding == NULL))) {
1595 /*
1596 * We assume we have UTF-8 content.
1597 */
1598 unsigned char tmp[10];
1599 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001600
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001601 if (base != cur)
1602 xmlBufferAdd(buf, base, cur - base);
1603 if (*cur < 0xC0) {
1604 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1605 if (doc != NULL)
1606 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1607 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001608 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001609 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001610 base = cur;
1611 continue;
1612 } else if (*cur < 0xE0) {
1613 val = (cur[0]) & 0x1F;
1614 val <<= 6;
1615 val |= (cur[1]) & 0x3F;
1616 l = 2;
1617 } else if (*cur < 0xF0) {
1618 val = (cur[0]) & 0x0F;
1619 val <<= 6;
1620 val |= (cur[1]) & 0x3F;
1621 val <<= 6;
1622 val |= (cur[2]) & 0x3F;
1623 l = 3;
1624 } else if (*cur < 0xF8) {
1625 val = (cur[0]) & 0x07;
1626 val <<= 6;
1627 val |= (cur[1]) & 0x3F;
1628 val <<= 6;
1629 val |= (cur[2]) & 0x3F;
1630 val <<= 6;
1631 val |= (cur[3]) & 0x3F;
1632 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001633 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001634 if ((l == 1) || (!IS_CHAR(val))) {
1635 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1636 if (doc != NULL)
1637 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1638
1639 xmlSerializeHexCharRef(tmp, *cur);
1640 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1641 cur++;
1642 base = cur;
1643 continue;
1644 }
1645 /*
1646 * We could do multiple things here. Just save
1647 * as a char ref
1648 */
1649 xmlSerializeHexCharRef(tmp, val);
1650 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1651 cur += l;
1652 base = cur;
1653 } else {
1654 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001655 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001656 }
1657 if (base != cur)
1658 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001659}
1660
1661/**
1662 * xmlNodeDump:
1663 * @buf: the XML buffer output
1664 * @doc: the document
1665 * @cur: the current node
1666 * @level: the imbrication level for indenting
1667 * @format: is formatting allowed
1668 *
1669 * Dump an XML node, recursive behaviour,children are printed too.
1670 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1671 * or xmlKeepBlanksDefault(0) was called
1672 *
1673 * Returns the number of bytes written to the buffer or -1 in case of error
1674 */
1675int
1676xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1677 int format)
1678{
1679 unsigned int use;
1680 int ret;
1681 xmlOutputBufferPtr outbuf;
1682
1683 xmlInitParser();
1684
1685 if (cur == NULL) {
1686#ifdef DEBUG_TREE
1687 xmlGenericError(xmlGenericErrorContext,
1688 "xmlNodeDump : node == NULL\n");
1689#endif
1690 return (-1);
1691 }
1692 if (buf == NULL) {
1693#ifdef DEBUG_TREE
1694 xmlGenericError(xmlGenericErrorContext,
1695 "xmlNodeDump : buf == NULL\n");
1696#endif
1697 return (-1);
1698 }
1699 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1700 if (outbuf == NULL) {
1701 xmlSaveErrMemory("creating buffer");
1702 return (-1);
1703 }
1704 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1705 outbuf->buffer = buf;
1706 outbuf->encoder = NULL;
1707 outbuf->writecallback = NULL;
1708 outbuf->closecallback = NULL;
1709 outbuf->context = NULL;
1710 outbuf->written = 0;
1711
1712 use = buf->use;
1713 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1714 xmlFree(outbuf);
1715 ret = buf->use - use;
1716 return (ret);
1717}
1718
1719/**
1720 * xmlElemDump:
1721 * @f: the FILE * for the output
1722 * @doc: the document
1723 * @cur: the current node
1724 *
1725 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1726 */
1727void
1728xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1729{
1730 xmlOutputBufferPtr outbuf;
1731
1732 xmlInitParser();
1733
1734 if (cur == NULL) {
1735#ifdef DEBUG_TREE
1736 xmlGenericError(xmlGenericErrorContext,
1737 "xmlElemDump : cur == NULL\n");
1738#endif
1739 return;
1740 }
1741#ifdef DEBUG_TREE
1742 if (doc == NULL) {
1743 xmlGenericError(xmlGenericErrorContext,
1744 "xmlElemDump : doc == NULL\n");
1745 }
1746#endif
1747
1748 outbuf = xmlOutputBufferCreateFile(f, NULL);
1749 if (outbuf == NULL)
1750 return;
1751 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1752#ifdef LIBXML_HTML_ENABLED
1753 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1754#else
1755 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1756#endif /* LIBXML_HTML_ENABLED */
1757 } else
1758 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1759 xmlOutputBufferClose(outbuf);
1760}
1761
1762/************************************************************************
1763 * *
1764 * Saving functions front-ends *
1765 * *
1766 ************************************************************************/
1767
1768/**
1769 * xmlNodeDumpOutput:
1770 * @buf: the XML buffer output
1771 * @doc: the document
1772 * @cur: the current node
1773 * @level: the imbrication level for indenting
1774 * @format: is formatting allowed
1775 * @encoding: an optional encoding string
1776 *
1777 * Dump an XML node, recursive behaviour, children are printed too.
1778 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1779 * or xmlKeepBlanksDefault(0) was called
1780 */
1781void
1782xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1783 int level, int format, const char *encoding)
1784{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001785 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001786#ifdef LIBXML_HTML_ENABLED
1787 xmlDtdPtr dtd;
1788 int is_xhtml = 0;
1789#endif
1790
1791 xmlInitParser();
1792
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001793 memset(&ctxt, 0, sizeof(ctxt));
1794 ctxt.doc = doc;
1795 ctxt.buf = buf;
1796 ctxt.level = level;
1797 ctxt.format = format;
1798 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001799 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001800
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001801#ifdef LIBXML_HTML_ENABLED
1802 dtd = xmlGetIntSubset(doc);
1803 if (dtd != NULL) {
1804 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1805 if (is_xhtml < 0)
1806 is_xhtml = 0;
1807 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1808 (cur->type == XML_ELEMENT_NODE) &&
1809 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1810 if (encoding != NULL)
1811 htmlSetMetaEncoding((htmlDocPtr) doc,
1812 (const xmlChar *) encoding);
1813 else
1814 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1815 }
1816 }
1817
1818 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001819 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001820 else
1821#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001822 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001823}
1824
1825/**
1826 * xmlDocDumpFormatMemoryEnc:
1827 * @out_doc: Document to generate XML text from
1828 * @doc_txt_ptr: Memory pointer for allocated XML text
1829 * @doc_txt_len: Length of the generated XML text
1830 * @txt_encoding: Character encoding to use when generating XML text
1831 * @format: should formatting spaces been added
1832 *
1833 * Dump the current DOM tree into memory using the character encoding specified
1834 * by the caller. Note it is up to the caller of this function to free the
1835 * allocated memory with xmlFree().
1836 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1837 * or xmlKeepBlanksDefault(0) was called
1838 */
1839
1840void
1841xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1842 int * doc_txt_len, const char * txt_encoding,
1843 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001844 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001845 int dummy = 0;
1846 xmlOutputBufferPtr out_buff = NULL;
1847 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1848
1849 if (doc_txt_len == NULL) {
1850 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1851 }
1852
1853 if (doc_txt_ptr == NULL) {
1854 *doc_txt_len = 0;
1855 return;
1856 }
1857
1858 *doc_txt_ptr = NULL;
1859 *doc_txt_len = 0;
1860
1861 if (out_doc == NULL) {
1862 /* No document, no output */
1863 return;
1864 }
1865
1866 /*
1867 * Validate the encoding value, if provided.
1868 * This logic is copied from xmlSaveFileEnc.
1869 */
1870
1871 if (txt_encoding == NULL)
1872 txt_encoding = (const char *) out_doc->encoding;
1873 if (txt_encoding != NULL) {
1874 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1875 if ( conv_hdlr == NULL ) {
1876 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1877 txt_encoding);
1878 return;
1879 }
1880 }
1881
1882 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1883 xmlSaveErrMemory("creating buffer");
1884 return;
1885 }
1886
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001887 memset(&ctxt, 0, sizeof(ctxt));
1888 ctxt.doc = out_doc;
1889 ctxt.buf = out_buff;
1890 ctxt.level = 0;
1891 ctxt.format = format;
1892 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001893 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001894 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001895 xmlOutputBufferFlush(out_buff);
1896 if (out_buff->conv != NULL) {
1897 *doc_txt_len = out_buff->conv->use;
1898 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1899 } else {
1900 *doc_txt_len = out_buff->buffer->use;
1901 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1902 }
1903 (void)xmlOutputBufferClose(out_buff);
1904
1905 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1906 *doc_txt_len = 0;
1907 xmlSaveErrMemory("creating output");
1908 }
1909
1910 return;
1911}
1912
1913/**
1914 * xmlDocDumpMemory:
1915 * @cur: the document
1916 * @mem: OUT: the memory pointer
1917 * @size: OUT: the memory length
1918 *
1919 * Dump an XML document in memory and return the #xmlChar * and it's size
1920 * in bytes. It's up to the caller to free the memory with xmlFree().
1921 * The resulting byte array is zero terminated, though the last 0 is not
1922 * included in the returned size.
1923 */
1924void
1925xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1926 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1927}
1928
1929/**
1930 * xmlDocDumpFormatMemory:
1931 * @cur: the document
1932 * @mem: OUT: the memory pointer
1933 * @size: OUT: the memory length
1934 * @format: should formatting spaces been added
1935 *
1936 *
1937 * Dump an XML document in memory and return the #xmlChar * and it's size.
1938 * It's up to the caller to free the memory with xmlFree().
1939 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1940 * or xmlKeepBlanksDefault(0) was called
1941 */
1942void
1943xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1944 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1945}
1946
1947/**
1948 * xmlDocDumpMemoryEnc:
1949 * @out_doc: Document to generate XML text from
1950 * @doc_txt_ptr: Memory pointer for allocated XML text
1951 * @doc_txt_len: Length of the generated XML text
1952 * @txt_encoding: Character encoding to use when generating XML text
1953 *
1954 * Dump the current DOM tree into memory using the character encoding specified
1955 * by the caller. Note it is up to the caller of this function to free the
1956 * allocated memory with xmlFree().
1957 */
1958
1959void
1960xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1961 int * doc_txt_len, const char * txt_encoding) {
1962 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1963 txt_encoding, 0);
1964}
1965
1966/**
1967 * xmlDocFormatDump:
1968 * @f: the FILE*
1969 * @cur: the document
1970 * @format: should formatting spaces been added
1971 *
1972 * Dump an XML document to an open FILE.
1973 *
1974 * returns: the number of bytes written or -1 in case of failure.
1975 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1976 * or xmlKeepBlanksDefault(0) was called
1977 */
1978int
1979xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001980 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001981 xmlOutputBufferPtr buf;
1982 const char * encoding;
1983 xmlCharEncodingHandlerPtr handler = NULL;
1984 int ret;
1985
1986 if (cur == NULL) {
1987#ifdef DEBUG_TREE
1988 xmlGenericError(xmlGenericErrorContext,
1989 "xmlDocDump : document == NULL\n");
1990#endif
1991 return(-1);
1992 }
1993 encoding = (const char *) cur->encoding;
1994
1995 if (encoding != NULL) {
1996 handler = xmlFindCharEncodingHandler(encoding);
1997 if (handler == NULL) {
1998 xmlFree((char *) cur->encoding);
1999 cur->encoding = NULL;
2000 }
2001 }
2002 buf = xmlOutputBufferCreateFile(f, handler);
2003 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002004 memset(&ctxt, 0, sizeof(ctxt));
2005 ctxt.doc = cur;
2006 ctxt.buf = buf;
2007 ctxt.level = 0;
2008 ctxt.format = format;
2009 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002010 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002011 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002012
2013 ret = xmlOutputBufferClose(buf);
2014 return(ret);
2015}
2016
2017/**
2018 * xmlDocDump:
2019 * @f: the FILE*
2020 * @cur: the document
2021 *
2022 * Dump an XML document to an open FILE.
2023 *
2024 * returns: the number of bytes written or -1 in case of failure.
2025 */
2026int
2027xmlDocDump(FILE *f, xmlDocPtr cur) {
2028 return(xmlDocFormatDump (f, cur, 0));
2029}
2030
2031/**
2032 * xmlSaveFileTo:
2033 * @buf: an output I/O buffer
2034 * @cur: the document
2035 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2036 *
2037 * Dump an XML document to an I/O buffer.
2038 *
2039 * returns: the number of bytes written or -1 in case of failure.
2040 */
2041int
2042xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002043 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002044 int ret;
2045
2046 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002047 memset(&ctxt, 0, sizeof(ctxt));
2048 ctxt.doc = cur;
2049 ctxt.buf = buf;
2050 ctxt.level = 0;
2051 ctxt.format = 0;
2052 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002053 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002054 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002055 ret = xmlOutputBufferClose(buf);
2056 return(ret);
2057}
2058
2059/**
2060 * xmlSaveFormatFileTo:
2061 * @buf: an output I/O buffer
2062 * @cur: the document
2063 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2064 * @format: should formatting spaces been added
2065 *
2066 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002067 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002068 *
2069 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002070 */
2071int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002072xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2073 const char *encoding, int format)
2074{
2075 xmlSaveCtxt ctxt;
2076 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002077
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002078 if (buf == NULL)
2079 return (0);
2080 memset(&ctxt, 0, sizeof(ctxt));
2081 ctxt.doc = cur;
2082 ctxt.buf = buf;
2083 ctxt.level = 0;
2084 ctxt.format = format;
2085 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002086 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002087 xmlDocContentDumpOutput(&ctxt, cur);
2088 ret = xmlOutputBufferClose(buf);
2089 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002090}
2091
2092/**
2093 * xmlSaveFormatFileEnc:
2094 * @filename: the filename or URL to output
2095 * @cur: the document being saved
2096 * @encoding: the name of the encoding to use or NULL.
2097 * @format: should formatting spaces be added.
2098 *
2099 * Dump an XML document to a file or an URL.
2100 *
2101 * Returns the number of bytes written or -1 in case of error.
2102 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2103 * or xmlKeepBlanksDefault(0) was called
2104 */
2105int
2106xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2107 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002108 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002109 xmlOutputBufferPtr buf;
2110 xmlCharEncodingHandlerPtr handler = NULL;
2111 int ret;
2112
2113 if (cur == NULL)
2114 return(-1);
2115
2116 if (encoding == NULL)
2117 encoding = (const char *) cur->encoding;
2118
2119 if (encoding != NULL) {
2120
2121 handler = xmlFindCharEncodingHandler(encoding);
2122 if (handler == NULL)
2123 return(-1);
2124 }
2125
2126#ifdef HAVE_ZLIB_H
2127 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2128#endif
2129 /*
2130 * save the content to a temp buffer.
2131 */
2132 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2133 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002134 memset(&ctxt, 0, sizeof(ctxt));
2135 ctxt.doc = cur;
2136 ctxt.buf = buf;
2137 ctxt.level = 0;
2138 ctxt.format = format;
2139 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002140 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002141
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002142 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002143
2144 ret = xmlOutputBufferClose(buf);
2145 return(ret);
2146}
2147
2148
2149/**
2150 * xmlSaveFileEnc:
2151 * @filename: the filename (or URL)
2152 * @cur: the document
2153 * @encoding: the name of an encoding (or NULL)
2154 *
2155 * Dump an XML document, converting it to the given encoding
2156 *
2157 * returns: the number of bytes written or -1 in case of failure.
2158 */
2159int
2160xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2161 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2162}
2163
2164/**
2165 * xmlSaveFormatFile:
2166 * @filename: the filename (or URL)
2167 * @cur: the document
2168 * @format: should formatting spaces been added
2169 *
2170 * Dump an XML document to a file. Will use compression if
2171 * compiled in and enabled. If @filename is "-" the stdout file is
2172 * used. If @format is set then the document will be indented on output.
2173 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2174 * or xmlKeepBlanksDefault(0) was called
2175 *
2176 * returns: the number of bytes written or -1 in case of failure.
2177 */
2178int
2179xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2180 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2181}
2182
2183/**
2184 * xmlSaveFile:
2185 * @filename: the filename (or URL)
2186 * @cur: the document
2187 *
2188 * Dump an XML document to a file. Will use compression if
2189 * compiled in and enabled. If @filename is "-" the stdout file is
2190 * used.
2191 * returns: the number of bytes written or -1 in case of failure.
2192 */
2193int
2194xmlSaveFile(const char *filename, xmlDocPtr cur) {
2195 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2196}
2197
2198#endif /* LIBXML_OUTPUT_ENABLED */
2199