blob: 49268e6cfe43c4e571c03665f5ee57dd80c8c9a7 [file] [log] [blame]
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001/*
2 * xmlsave.c: Implemetation of the document serializer
3 *
4 * See Copyright for the status of this software.
5 *
6 * daniel@veillard.com
7 */
8
9#define IN_LIBXML
10#include "libxml.h"
11
12#include <string.h>
13#include <libxml/xmlmemory.h>
14#include <libxml/parserInternals.h>
15#include <libxml/tree.h>
16#include <libxml/xmlsave.h>
Daniel Veillard1a8741c2004-03-04 13:40:59 +000017
Daniel Veillard753086a2004-03-28 16:12:44 +000018#define MAX_INDENT 60
19
Daniel Veillard656ce942004-04-30 23:11:45 +000020#include <libxml/HTMLtree.h>
21
Daniel Veillard1a8741c2004-03-04 13:40:59 +000022/************************************************************************
23 * *
24 * XHTML detection *
25 * *
26 ************************************************************************/
27#define XHTML_STRICT_PUBLIC_ID BAD_CAST \
28 "-//W3C//DTD XHTML 1.0 Strict//EN"
29#define XHTML_STRICT_SYSTEM_ID BAD_CAST \
30 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
31#define XHTML_FRAME_PUBLIC_ID BAD_CAST \
32 "-//W3C//DTD XHTML 1.0 Frameset//EN"
33#define XHTML_FRAME_SYSTEM_ID BAD_CAST \
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
35#define XHTML_TRANS_PUBLIC_ID BAD_CAST \
36 "-//W3C//DTD XHTML 1.0 Transitional//EN"
37#define XHTML_TRANS_SYSTEM_ID BAD_CAST \
38 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
39
40#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
41/**
42 * xmlIsXHTML:
43 * @systemID: the system identifier
44 * @publicID: the public identifier
45 *
46 * Try to find if the document correspond to an XHTML DTD
47 *
48 * Returns 1 if true, 0 if not and -1 in case of error
49 */
50int
51xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
52 if ((systemID == NULL) && (publicID == NULL))
53 return(-1);
54 if (publicID != NULL) {
55 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
56 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
57 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
58 }
59 if (systemID != NULL) {
60 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
61 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
62 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
63 }
64 return(0);
65}
Daniel Veillard1a8741c2004-03-04 13:40:59 +000066
67#ifdef LIBXML_OUTPUT_ENABLED
68
69#define TODO \
70 xmlGenericError(xmlGenericErrorContext, \
71 "Unimplemented block at %s:%d\n", \
72 __FILE__, __LINE__);
73
74struct _xmlSaveCtxt {
75 void *_private;
76 int type;
77 int fd;
78 const xmlChar *filename;
79 const xmlChar *encoding;
80 xmlCharEncodingHandlerPtr handler;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000081 xmlOutputBufferPtr buf;
82 xmlDocPtr doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000083 int options;
84 int level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000085 int format;
Daniel Veillard753086a2004-03-28 16:12:44 +000086 char indent[MAX_INDENT + 1];
87 int indent_nr;
88 int indent_size;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000089};
90
91/************************************************************************
92 * *
93 * Output error handlers *
94 * *
95 ************************************************************************/
96/**
97 * xmlSaveErrMemory:
98 * @extra: extra informations
99 *
100 * Handle an out of memory condition
101 */
102static void
103xmlSaveErrMemory(const char *extra)
104{
105 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
106}
107
108/**
109 * xmlSaveErr:
110 * @code: the error number
111 * @node: the location of the error.
112 * @extra: extra informations
113 *
114 * Handle an out of memory condition
115 */
116static void
117xmlSaveErr(int code, xmlNodePtr node, const char *extra)
118{
119 const char *msg = NULL;
120
121 switch(code) {
122 case XML_SAVE_NOT_UTF8:
123 msg = "string is not in UTF-8";
124 break;
125 case XML_SAVE_CHAR_INVALID:
126 msg = "invalid character value";
127 break;
128 case XML_SAVE_UNKNOWN_ENCODING:
129 msg = "unknown encoding %s";
130 break;
131 case XML_SAVE_NO_DOCTYPE:
132 msg = "document has no DOCTYPE";
133 break;
134 default:
135 msg = "unexpected error number";
136 }
137 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
138}
139
140/************************************************************************
141 * *
Daniel Veillard83a75e02004-05-14 21:50:42 +0000142 * Special escaping routines *
143 * *
144 ************************************************************************/
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000145static unsigned char *
146xmlSerializeHexCharRef(unsigned char *out, int val) {
147 unsigned char *ptr;
148
149 *out++ = '&';
150 *out++ = '#';
151 *out++ = 'x';
152 if (val < 0x10) ptr = out;
153 else if (val < 0x100) ptr = out + 1;
154 else if (val < 0x1000) ptr = out + 2;
155 else if (val < 0x10000) ptr = out + 3;
156 else if (val < 0x100000) ptr = out + 4;
157 else ptr = out + 5;
158 out = ptr + 1;
159 while (val > 0) {
160 switch (val & 0xF) {
161 case 0: *ptr-- = '0'; break;
162 case 1: *ptr-- = '1'; break;
163 case 2: *ptr-- = '2'; break;
164 case 3: *ptr-- = '3'; break;
165 case 4: *ptr-- = '4'; break;
166 case 5: *ptr-- = '5'; break;
167 case 6: *ptr-- = '6'; break;
168 case 7: *ptr-- = '7'; break;
169 case 8: *ptr-- = '8'; break;
170 case 9: *ptr-- = '9'; break;
171 case 0xA: *ptr-- = 'A'; break;
172 case 0xB: *ptr-- = 'B'; break;
173 case 0xC: *ptr-- = 'C'; break;
174 case 0xD: *ptr-- = 'D'; break;
175 case 0xE: *ptr-- = 'E'; break;
176 case 0xF: *ptr-- = 'F'; break;
177 default: *ptr-- = '0'; break;
178 }
179 val >>= 4;
180 }
181 *out++ = ';';
182 *out = 0;
183 return(out);
184}
185
Daniel Veillard83a75e02004-05-14 21:50:42 +0000186/**
187 * xmlEscapeEntities:
188 * @out: a pointer to an array of bytes to store the result
189 * @outlen: the length of @out
190 * @in: a pointer to an array of unescaped UTF-8 bytes
191 * @inlen: the length of @in
192 *
193 * Take a block of UTF-8 chars in and escape them. Used when there is no
194 * encoding specified.
195 *
196 * Returns 0 if success, or -1 otherwise
197 * The value of @inlen after return is the number of octets consumed
198 * if the return value is positive, else unpredictable.
199 * The value of @outlen after return is the number of octets consumed.
200 */
201static int
202xmlEscapeEntities(unsigned char* out, int *outlen,
203 const xmlChar* in, int *inlen) {
204 unsigned char* outstart = out;
205 const unsigned char* base = in;
206 unsigned char* outend = out + *outlen;
207 const unsigned char* inend;
208 int val;
209
210 inend = in + (*inlen);
211
212 while ((in < inend) && (out < outend)) {
213 if (*in == '<') {
214 if (outend - out < 4) break;
215 *out++ = '&';
216 *out++ = 'l';
217 *out++ = 't';
218 *out++ = ';';
219 in++;
220 continue;
221 } else if (*in == '>') {
222 if (outend - out < 4) break;
223 *out++ = '&';
224 *out++ = 'g';
225 *out++ = 't';
226 *out++ = ';';
227 in++;
228 continue;
229 } else if (*in == '&') {
230 if (outend - out < 5) break;
231 *out++ = '&';
232 *out++ = 'a';
233 *out++ = 'm';
234 *out++ = 'p';
235 *out++ = ';';
236 in++;
237 continue;
238 } else if (((*in >= 0x20) && (*in < 0x80)) ||
239 (*in == '\n') || (*in == '\t')) {
240 /*
241 * default case, just copy !
242 */
243 *out++ = *in++;
244 continue;
245 } else if (*in >= 0x80) {
246 /*
247 * We assume we have UTF-8 input.
248 */
Daniel Veillard83a75e02004-05-14 21:50:42 +0000249 if (outend - out < 10) break;
250
251 if (*in < 0xC0) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000252 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000253 in++;
254 goto error;
255 } else if (*in < 0xE0) {
256 if (inend - in < 2) break;
257 val = (in[0]) & 0x1F;
258 val <<= 6;
259 val |= (in[1]) & 0x3F;
260 in += 2;
261 } else if (*in < 0xF0) {
262 if (inend - in < 3) break;
263 val = (in[0]) & 0x0F;
264 val <<= 6;
265 val |= (in[1]) & 0x3F;
266 val <<= 6;
267 val |= (in[2]) & 0x3F;
268 in += 3;
269 } else if (*in < 0xF8) {
270 if (inend - in < 4) break;
271 val = (in[0]) & 0x07;
272 val <<= 6;
273 val |= (in[1]) & 0x3F;
274 val <<= 6;
275 val |= (in[2]) & 0x3F;
276 val <<= 6;
277 val |= (in[3]) & 0x3F;
278 in += 4;
279 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000280 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000281 in++;
282 goto error;
283 }
284 if (!IS_CHAR(val)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000285 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000286 in++;
287 goto error;
288 }
289
290 /*
291 * We could do multiple things here. Just save as a char ref
292 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000293 out = xmlSerializeHexCharRef(out, val);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000294 } else if (IS_BYTE_CHAR(*in)) {
295 if (outend - out < 6) break;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000296 out = xmlSerializeHexCharRef(out, *in++);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000297 } else {
298 xmlGenericError(xmlGenericErrorContext,
299 "xmlEscapeEntities : char out of range\n");
300 in++;
301 goto error;
302 }
303 }
304 *outlen = out - outstart;
305 *inlen = in - base;
306 return(0);
307error:
308 *outlen = out - outstart;
309 *inlen = in - base;
310 return(-1);
311}
312
313/************************************************************************
314 * *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000315 * Allocation and deallocation *
316 * *
317 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000318/**
319 * xmlSaveCtxtInit:
320 * @ctxt: the saving context
321 *
322 * Initialize a saving context
323 */
324static void
325xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
326{
327 int i;
328
329 if (ctxt == NULL) return;
330 if (xmlTreeIndentString == NULL) {
331 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
332 } else {
333 ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
334 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
335 for (i = 0;i < ctxt->indent_nr;i++)
336 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
337 ctxt->indent_size);
338 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
339 }
340}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000341
342/**
343 * xmlFreeSaveCtxt:
344 *
345 * Free a saving context, destroying the ouptut in any remaining buffer
346 */
347static void
348xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
349{
350 if (ctxt == NULL) return;
351 if (ctxt->encoding != NULL)
352 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000353 if (ctxt->buf != NULL)
354 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000355 xmlFree(ctxt);
356}
357
358/**
359 * xmlNewSaveCtxt:
360 *
361 * Create a new saving context
362 *
363 * Returns the new structure or NULL in case of error
364 */
365static xmlSaveCtxtPtr
366xmlNewSaveCtxt(const char *encoding, int options)
367{
368 xmlSaveCtxtPtr ret;
369
370 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
371 if (ret == NULL) {
372 xmlSaveErrMemory("creating saving context");
373 return ( NULL );
374 }
375 memset(ret, 0, sizeof(xmlSaveCtxt));
376 ret->options = options;
377 if (encoding != NULL) {
378 ret->handler = xmlFindCharEncodingHandler(encoding);
379 if (ret->handler == NULL) {
380 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
381 xmlFreeSaveCtxt(ret);
382 return(NULL);
383 }
384 ret->encoding = xmlStrdup((const xmlChar *)encoding);
385 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000386 xmlSaveCtxtInit(ret);
387
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000388 return(ret);
389}
390
391/************************************************************************
392 * *
393 * Dumping XML tree content to a simple buffer *
394 * *
395 ************************************************************************/
396/**
397 * xmlAttrSerializeContent:
398 * @buf: the XML buffer output
399 * @doc: the document
400 * @attr: the attribute pointer
401 *
402 * Serialize the attribute in the buffer
403 */
404static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000405xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000406{
407 xmlNodePtr children;
408
409 children = attr->children;
410 while (children != NULL) {
411 switch (children->type) {
412 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000413 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
414 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000415 break;
416 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000417 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
418 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000419 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000420 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000421 break;
422 default:
423 /* should not happen unless we have a badly built tree */
424 break;
425 }
426 children = children->next;
427 }
428}
429
430/************************************************************************
431 * *
432 * Dumping XML tree content to an I/O output buffer *
433 * *
434 ************************************************************************/
435
436#ifdef LIBXML_HTML_ENABLED
437static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000438xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000439#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000440static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
441static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000442void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
443
444/**
445 * xmlNsDumpOutput:
446 * @buf: the XML buffer output
447 * @cur: a namespace
448 *
449 * Dump a local Namespace definition.
450 * Should be called in the context of attributes dumps.
451 */
452static void
453xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000454 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000455 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
456 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
457 return;
458
459 /* Within the context of an element attributes */
460 if (cur->prefix != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000461 xmlOutputBufferWrite(buf, 7, " xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000462 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
463 } else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000464 xmlOutputBufferWrite(buf, 6, " xmlns");
465 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000466 xmlBufferWriteQuotedString(buf->buffer, cur->href);
467 }
468}
469
470/**
471 * xmlNsListDumpOutput:
472 * @buf: the XML buffer output
473 * @cur: the first namespace
474 *
475 * Dump a list of local Namespace definitions.
476 * Should be called in the context of attributes dumps.
477 */
478void
479xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
480 while (cur != NULL) {
481 xmlNsDumpOutput(buf, cur);
482 cur = cur->next;
483 }
484}
485
486/**
487 * xmlDtdDumpOutput:
488 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000489 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000490 *
491 * Dump the XML document DTD, if any.
492 */
493static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000494xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
495 xmlOutputBufferPtr buf;
496 int format, level;
497 xmlDocPtr doc;
498
499 if (dtd == NULL) return;
500 if ((ctxt == NULL) || (ctxt->buf == NULL))
501 return;
502 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000503 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000504 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
505 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000506 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000507 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000508 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000509 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
510 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000511 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000512 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
513 }
514 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
515 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
516 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000517 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000518 return;
519 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000520 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000521 format = ctxt->format;
522 level = ctxt->level;
523 doc = ctxt->doc;
524 ctxt->format = 0;
525 ctxt->level = -1;
526 ctxt->doc = dtd->doc;
527 xmlNodeListDumpOutput(ctxt, dtd->children);
528 ctxt->format = format;
529 ctxt->level = level;
530 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000531 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000532}
533
534/**
535 * xmlAttrDumpOutput:
536 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000537 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000538 *
539 * Dump an XML attribute
540 */
541static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000542xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
543 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000544
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000545 if (cur == NULL) return;
546 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000547 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000548 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
549 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000550 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000551 }
552 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000553 xmlOutputBufferWrite(buf, 2, "=\"");
554 xmlAttrSerializeContent(buf, cur);
555 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000556}
557
558/**
559 * xmlAttrListDumpOutput:
560 * @buf: the XML buffer output
561 * @doc: the document
562 * @cur: the first attribute pointer
563 * @encoding: an optional encoding string
564 *
565 * Dump a list of XML attributes
566 */
567static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000568xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
569 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000570 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000571 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000572 cur = cur->next;
573 }
574}
575
576
577
578/**
579 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000580 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000581 *
582 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000583 */
584static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000585xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000586 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000587
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000588 if (cur == NULL) return;
589 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000590 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000591 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000592 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000593 xmlOutputBufferWrite(buf, ctxt->indent_size *
594 (ctxt->level > ctxt->indent_nr ?
595 ctxt->indent_nr : ctxt->level),
596 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000597 xmlNodeDumpOutputInternal(ctxt, cur);
598 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000599 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000600 }
601 cur = cur->next;
602 }
603}
604
605/**
606 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000607 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000608 *
609 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000610 */
611static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000612xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000613 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000614 xmlNodePtr tmp;
615 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000616 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000617
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000618 if (cur == NULL) return;
619 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000620 if (cur->type == XML_XINCLUDE_START)
621 return;
622 if (cur->type == XML_XINCLUDE_END)
623 return;
624 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000625 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000626 return;
627 }
628 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000629 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000630 return;
631 }
632 if (cur->type == XML_ELEMENT_DECL) {
633 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
634 return;
635 }
636 if (cur->type == XML_ATTRIBUTE_DECL) {
637 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
638 return;
639 }
640 if (cur->type == XML_ENTITY_DECL) {
641 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
642 return;
643 }
644 if (cur->type == XML_TEXT_NODE) {
645 if (cur->content != NULL) {
646 if ((cur->name == xmlStringText) ||
647 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000648
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000649 if (ctxt->encoding == NULL) {
Daniel Veillard83a75e02004-05-14 21:50:42 +0000650 xmlOutputBufferWriteEscape(buf, cur->content,
651 xmlEscapeEntities);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000652 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +0000653 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000654 }
655 } else {
656 /*
657 * Disable escaping, needed for XSLT
658 */
659 xmlOutputBufferWriteString(buf, (const char *) cur->content);
660 }
661 }
662
663 return;
664 }
665 if (cur->type == XML_PI_NODE) {
666 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000667 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000668 xmlOutputBufferWriteString(buf, (const char *)cur->name);
669 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000670 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000671 xmlOutputBufferWriteString(buf, (const char *)cur->content);
672 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000673 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000674 } else {
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);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000677 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000678 }
679 return;
680 }
681 if (cur->type == XML_COMMENT_NODE) {
682 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000683 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000684 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000685 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000686 }
687 return;
688 }
689 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000690 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000691 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000692 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000693 return;
694 }
695 if (cur->type == XML_CDATA_SECTION_NODE) {
696 start = end = cur->content;
697 while (*end != '\0') {
698 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
699 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000700 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000701 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000702 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000703 start = end;
704 }
705 end++;
706 }
707 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000708 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000709 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000710 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000711 }
712 return;
713 }
714 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000715 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000716 return;
717 }
718 if (cur->type == XML_NAMESPACE_DECL) {
719 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
720 return;
721 }
722
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000723 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000724 if (format == 1) {
725 tmp = cur->children;
726 while (tmp != NULL) {
727 if ((tmp->type == XML_TEXT_NODE) ||
728 (tmp->type == XML_CDATA_SECTION_NODE) ||
729 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000730 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000731 break;
732 }
733 tmp = tmp->next;
734 }
735 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000736 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000737 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
738 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000739 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000740 }
741
742 xmlOutputBufferWriteString(buf, (const char *)cur->name);
743 if (cur->nsDef)
744 xmlNsListDumpOutput(buf, cur->nsDef);
745 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000746 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000747
748 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
749 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000750 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000751 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000752 return;
753 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000754 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000755 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000756 if (ctxt->encoding == NULL) {
Daniel Veillard83a75e02004-05-14 21:50:42 +0000757 xmlOutputBufferWriteEscape(buf, cur->content, xmlEscapeEntities);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000758 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +0000759 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000760 }
761 }
762 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000763 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000764 if (ctxt->level >= 0) ctxt->level++;
765 xmlNodeListDumpOutput(ctxt, cur->children);
766 if (ctxt->level > 0) ctxt->level--;
767 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000768 xmlOutputBufferWrite(buf, ctxt->indent_size *
769 (ctxt->level > ctxt->indent_nr ?
770 ctxt->indent_nr : ctxt->level),
771 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000772 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000773 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000774 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
775 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000776 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000777 }
778
779 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000780 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000781 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000782}
783
784/**
785 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000786 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000787 *
788 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000789 */
790static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000791xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000792#ifdef LIBXML_HTML_ENABLED
793 xmlDtdPtr dtd;
794 int is_xhtml = 0;
795#endif
796 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000797 const xmlChar *encoding = ctxt->encoding;
798 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000799
800 xmlInitParser();
801
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000802 if (ctxt->encoding != NULL)
803 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000804
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000805 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000806 xmlOutputBufferWrite(buf, 14, "<?xml version=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000807 if (cur->version != NULL)
808 xmlBufferWriteQuotedString(buf->buffer, cur->version);
809 else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000810 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000811 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000812 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000813 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000814 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000815 encoding = (const xmlChar *)
816 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000817 }
818 if (encoding != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000819 xmlOutputBufferWrite(buf, 10, " encoding=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000820 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
821 }
822 switch (cur->standalone) {
823 case 0:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000824 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000825 break;
826 case 1:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000827 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000828 break;
829 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000830 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000831
832#ifdef LIBXML_HTML_ENABLED
833 dtd = xmlGetIntSubset(cur);
834 if (dtd != NULL) {
835 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
836 if (is_xhtml < 0) is_xhtml = 0;
837 }
838 if (is_xhtml) {
839 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000840 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000841 else
842 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
843 }
844#endif
845 if (cur->children != NULL) {
846 xmlNodePtr child = cur->children;
847
848 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000849 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000850#ifdef LIBXML_HTML_ENABLED
851 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000852 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000853 else
854#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000855 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000856 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000857 child = child->next;
858 }
859 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000860 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000861 cur->encoding = oldenc;
862}
863
864#ifdef LIBXML_HTML_ENABLED
865/************************************************************************
866 * *
867 * Functions specific to XHTML serialization *
868 * *
869 ************************************************************************/
870
871/**
872 * xhtmlIsEmpty:
873 * @node: the node
874 *
875 * Check if a node is an empty xhtml node
876 *
877 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
878 */
879static int
880xhtmlIsEmpty(xmlNodePtr node) {
881 if (node == NULL)
882 return(-1);
883 if (node->type != XML_ELEMENT_NODE)
884 return(0);
885 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
886 return(0);
887 if (node->children != NULL)
888 return(0);
889 switch (node->name[0]) {
890 case 'a':
891 if (xmlStrEqual(node->name, BAD_CAST "area"))
892 return(1);
893 return(0);
894 case 'b':
895 if (xmlStrEqual(node->name, BAD_CAST "br"))
896 return(1);
897 if (xmlStrEqual(node->name, BAD_CAST "base"))
898 return(1);
899 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
900 return(1);
901 return(0);
902 case 'c':
903 if (xmlStrEqual(node->name, BAD_CAST "col"))
904 return(1);
905 return(0);
906 case 'f':
907 if (xmlStrEqual(node->name, BAD_CAST "frame"))
908 return(1);
909 return(0);
910 case 'h':
911 if (xmlStrEqual(node->name, BAD_CAST "hr"))
912 return(1);
913 return(0);
914 case 'i':
915 if (xmlStrEqual(node->name, BAD_CAST "img"))
916 return(1);
917 if (xmlStrEqual(node->name, BAD_CAST "input"))
918 return(1);
919 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
920 return(1);
921 return(0);
922 case 'l':
923 if (xmlStrEqual(node->name, BAD_CAST "link"))
924 return(1);
925 return(0);
926 case 'm':
927 if (xmlStrEqual(node->name, BAD_CAST "meta"))
928 return(1);
929 return(0);
930 case 'p':
931 if (xmlStrEqual(node->name, BAD_CAST "param"))
932 return(1);
933 return(0);
934 }
935 return(0);
936}
937
938/**
939 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000940 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000941 *
942 * Dump a list of XML attributes
943 */
944static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000945xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000946 xmlAttrPtr xml_lang = NULL;
947 xmlAttrPtr lang = NULL;
948 xmlAttrPtr name = NULL;
949 xmlAttrPtr id = NULL;
950 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000951 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000952
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000953 if (cur == NULL) return;
954 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000955 parent = cur->parent;
956 while (cur != NULL) {
957 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
958 id = cur;
959 else
960 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
961 name = cur;
962 else
963 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
964 lang = cur;
965 else
966 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
967 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
968 xml_lang = cur;
969 else if ((cur->ns == NULL) &&
970 ((cur->children == NULL) ||
971 (cur->children->content == NULL) ||
972 (cur->children->content[0] == 0)) &&
973 (htmlIsBooleanAttr(cur->name))) {
974 if (cur->children != NULL)
975 xmlFreeNode(cur->children);
976 cur->children = xmlNewText(cur->name);
977 if (cur->children != NULL)
978 cur->children->parent = (xmlNodePtr) cur;
979 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000980 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000981 cur = cur->next;
982 }
983 /*
984 * C.8
985 */
986 if ((name != NULL) && (id == NULL)) {
987 if ((parent != NULL) && (parent->name != NULL) &&
988 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
989 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
990 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
991 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
992 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
993 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
994 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
995 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
996 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000997 xmlOutputBufferWrite(buf, 5, " id=\"");
998 xmlAttrSerializeContent(buf, name);
999 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001000 }
1001 }
1002 /*
1003 * C.7.
1004 */
1005 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001006 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1007 xmlAttrSerializeContent(buf, lang);
1008 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001009 } else
1010 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001011 xmlOutputBufferWrite(buf, 7, " lang=\"");
1012 xmlAttrSerializeContent(buf, xml_lang);
1013 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001014 }
1015}
1016
1017/**
1018 * xhtmlNodeListDumpOutput:
1019 * @buf: the XML buffer output
1020 * @doc: the XHTML document
1021 * @cur: the first node
1022 * @level: the imbrication level for indenting
1023 * @format: is formatting allowed
1024 * @encoding: an optional encoding string
1025 *
1026 * Dump an XML node list, recursive behaviour, children are printed too.
1027 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1028 * or xmlKeepBlanksDefault(0) was called
1029 */
1030static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001031xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001032 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001033
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001034 if (cur == NULL) return;
1035 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001036 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001037 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001038 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001039 xmlOutputBufferWrite(buf, ctxt->indent_size *
1040 (ctxt->level > ctxt->indent_nr ?
1041 ctxt->indent_nr : ctxt->level),
1042 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001043 xhtmlNodeDumpOutput(ctxt, cur);
1044 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001045 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001046 }
1047 cur = cur->next;
1048 }
1049}
1050
1051/**
1052 * xhtmlNodeDumpOutput:
1053 * @buf: the XML buffer output
1054 * @doc: the XHTML document
1055 * @cur: the current node
1056 * @level: the imbrication level for indenting
1057 * @format: is formatting allowed
1058 * @encoding: an optional encoding string
1059 *
1060 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001061 */
1062static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001063xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +00001064 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001065 xmlNodePtr tmp;
1066 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001067 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001068
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001069 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001070 if (cur->type == XML_XINCLUDE_START)
1071 return;
1072 if (cur->type == XML_XINCLUDE_END)
1073 return;
1074 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001075 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001076 return;
1077 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001078 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001079 if (cur->type == XML_ELEMENT_DECL) {
1080 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1081 return;
1082 }
1083 if (cur->type == XML_ATTRIBUTE_DECL) {
1084 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1085 return;
1086 }
1087 if (cur->type == XML_ENTITY_DECL) {
1088 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1089 return;
1090 }
1091 if (cur->type == XML_TEXT_NODE) {
1092 if (cur->content != NULL) {
1093 if ((cur->name == xmlStringText) ||
1094 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001095
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001096 if (ctxt->encoding == NULL) {
Daniel Veillard83a75e02004-05-14 21:50:42 +00001097 xmlOutputBufferWriteEscape(buf, cur->content,
1098 xmlEscapeEntities);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001099 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +00001100 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001101 }
1102 } else {
1103 /*
1104 * Disable escaping, needed for XSLT
1105 */
1106 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1107 }
1108 }
1109
1110 return;
1111 }
1112 if (cur->type == XML_PI_NODE) {
1113 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001114 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001115 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1116 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001117 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001118 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1119 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001120 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001121 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001122 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001123 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001124 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001125 }
1126 return;
1127 }
1128 if (cur->type == XML_COMMENT_NODE) {
1129 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001130 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001131 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001132 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001133 }
1134 return;
1135 }
1136 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001137 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001138 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001139 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001140 return;
1141 }
1142 if (cur->type == XML_CDATA_SECTION_NODE) {
1143 start = end = cur->content;
1144 while (*end != '\0') {
1145 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1146 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001147 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001148 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001149 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001150 start = end;
1151 }
1152 end++;
1153 }
1154 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001155 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001156 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001157 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001158 }
1159 return;
1160 }
1161
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001162 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001163 if (format == 1) {
1164 tmp = cur->children;
1165 while (tmp != NULL) {
1166 if ((tmp->type == XML_TEXT_NODE) ||
1167 (tmp->type == XML_ENTITY_REF_NODE)) {
1168 format = 0;
1169 break;
1170 }
1171 tmp = tmp->next;
1172 }
1173 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001174 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001175 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1176 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001177 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001178 }
1179
1180 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1181 if (cur->nsDef)
1182 xmlNsListDumpOutput(buf, cur->nsDef);
1183 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1184 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1185 /*
1186 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1187 */
1188 xmlOutputBufferWriteString(buf,
1189 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1190 }
1191 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001192 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001193
1194 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1195 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1196 (xhtmlIsEmpty(cur) == 1)) {
1197 /*
1198 * C.2. Empty Elements
1199 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001200 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001201 } else {
1202 /*
1203 * C.3. Element Minimization and Empty Element Content
1204 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001205 xmlOutputBufferWrite(buf, 3, "></");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001206 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1207 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001208 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001209 }
1210 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001211 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001212 }
1213 return;
1214 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001215 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001216 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001217 if (ctxt->encoding == NULL) {
Daniel Veillard83a75e02004-05-14 21:50:42 +00001218 xmlOutputBufferWriteEscape(buf, cur->content, xmlEscapeEntities);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001219 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +00001220 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001221 }
1222 }
1223
1224 /*
1225 * 4.8. Script and Style elements
1226 */
1227 if ((cur->type == XML_ELEMENT_NODE) &&
1228 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1229 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1230 ((cur->ns == NULL) ||
1231 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1232 xmlNodePtr child = cur->children;
1233
1234 while (child != NULL) {
1235 if ((child->type == XML_TEXT_NODE) ||
1236 (child->type == XML_CDATA_SECTION_NODE)) {
1237 /*
1238 * Apparently CDATA escaping for style just break on IE,
1239 * mozilla and galeon, so ...
1240 */
1241 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1242 (xmlStrchr(child->content, '<') == NULL) &&
1243 (xmlStrchr(child->content, '>') == NULL) &&
1244 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001245 int level = ctxt->level;
1246 int indent = ctxt->format;
1247
1248 ctxt->level = 0;
1249 ctxt->format = 0;
1250 xhtmlNodeDumpOutput(ctxt, child);
1251 ctxt->level = level;
1252 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001253 } else {
1254 start = end = child->content;
1255 while (*end != '\0') {
1256 if (*end == ']' &&
1257 *(end + 1) == ']' &&
1258 *(end + 2) == '>') {
1259 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001260 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001261 xmlOutputBufferWrite(buf, end - start,
1262 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001263 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001264 start = end;
1265 }
1266 end++;
1267 }
1268 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001269 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1270 xmlOutputBufferWrite(buf, end - start,
1271 (const char *)start);
1272 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001273 }
1274 }
1275 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001276 int level = ctxt->level;
1277 int indent = ctxt->format;
1278
1279 ctxt->level = 0;
1280 ctxt->format = 0;
1281 xhtmlNodeDumpOutput(ctxt, child);
1282 ctxt->level = level;
1283 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001284 }
1285 child = child->next;
1286 }
1287 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001288 int indent = ctxt->format;
1289
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001290 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001291 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001292 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001293 xhtmlNodeListDumpOutput(ctxt, cur->children);
1294 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001295 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001296 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001297 xmlOutputBufferWrite(buf, ctxt->indent_size *
1298 (ctxt->level > ctxt->indent_nr ?
1299 ctxt->indent_nr : ctxt->level),
1300 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001301 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001302 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001303 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1304 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001305 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001306 }
1307
1308 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001309 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001310}
1311#endif
1312
1313/************************************************************************
1314 * *
1315 * Public entry points *
1316 * *
1317 ************************************************************************/
1318
1319/**
1320 * xmlSaveToFd:
1321 * @fd: a file descriptor number
1322 * @encoding: the encoding name to use or NULL
1323 * @options: a set of xmlSaveOptions
1324 *
1325 * Create a document saving context serializing to a file descriptor
1326 * with the encoding and the options given.
1327 *
1328 * Returns a new serialization context or NULL in case of error.
1329 */
1330xmlSaveCtxtPtr
1331xmlSaveToFd(int fd, const char *encoding, int options)
1332{
1333 xmlSaveCtxtPtr ret;
1334
1335 ret = xmlNewSaveCtxt(encoding, options);
1336 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001337 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1338 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001339 xmlFreeSaveCtxt(ret);
1340 return(NULL);
1341 }
1342 return(ret);
1343}
1344
1345/**
1346 * xmlSaveToFilename:
1347 * @filename: a file name or an URL
1348 * @encoding: the encoding name to use or NULL
1349 * @options: a set of xmlSaveOptions
1350 *
1351 * Create a document saving context serializing to a filename or possibly
1352 * to an URL (but this is less reliable) with the encoding and the options
1353 * given.
1354 *
1355 * Returns a new serialization context or NULL in case of error.
1356 */
1357xmlSaveCtxtPtr
1358xmlSaveToFilename(const char *filename, const char *encoding, int options)
1359{
1360 xmlSaveCtxtPtr ret;
1361 int compression = 0; /* TODO handle compression option */
1362
1363 ret = xmlNewSaveCtxt(encoding, options);
1364 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001365 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001366 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001367 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001368 xmlFreeSaveCtxt(ret);
1369 return(NULL);
1370 }
1371 return(ret);
1372}
1373
1374#if 0
1375/**
1376 * xmlSaveToBuffer:
1377 * @buffer: a buffer
1378 * @encoding: the encoding name to use or NULL
1379 * @options: a set of xmlSaveOptions
1380 *
1381 * Create a document saving context serializing to a buffer
1382 * with the encoding and the options given
1383 *
1384 * Returns a new serialization context or NULL in case of error.
1385 */
1386xmlSaveCtxtPtr
1387xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1388{
1389 TODO
1390 return(NULL);
1391}
1392#endif
1393
1394/**
1395 * xmlSaveToIO:
1396 * @iowrite: an I/O write function
1397 * @ioclose: an I/O close function
1398 * @ioctx: an I/O handler
1399 * @encoding: the encoding name to use or NULL
1400 * @options: a set of xmlSaveOptions
1401 *
1402 * Create a document saving context serializing to a file descriptor
1403 * with the encoding and the options given
1404 *
1405 * Returns a new serialization context or NULL in case of error.
1406 */
1407xmlSaveCtxtPtr
1408xmlSaveToIO(xmlOutputWriteCallback iowrite,
1409 xmlOutputCloseCallback ioclose,
1410 void *ioctx, const char *encoding, int options)
1411{
1412 xmlSaveCtxtPtr ret;
1413
1414 ret = xmlNewSaveCtxt(encoding, options);
1415 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001416 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1417 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001418 xmlFreeSaveCtxt(ret);
1419 return(NULL);
1420 }
1421 return(ret);
1422}
1423
1424/**
1425 * xmlSaveDoc:
1426 * @ctxt: a document saving context
1427 * @doc: a document
1428 *
1429 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001430 * TODO: The function is not fully implemented yet as it does not return the
1431 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001432 *
1433 * Returns the number of byte written or -1 in case of error
1434 */
1435long
1436xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1437{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001438 long ret = 0;
1439
1440 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001441 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001442}
1443
1444/**
1445 * xmlSaveTree:
1446 * @ctxt: a document saving context
1447 * @node: a document
1448 *
1449 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001450 * TODO: The function is not fully implemented yet as it does not return the
1451 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001452 *
1453 * Returns the number of byte written or -1 in case of error
1454 */
1455long
1456xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1457{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001458 long ret = 0;
1459
1460 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001461 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001462}
1463
1464/**
1465 * xmlSaveFlush:
1466 * @ctxt: a document saving context
1467 *
1468 * Flush a document saving context, i.e. make sure that all bytes have
1469 * been output.
1470 *
1471 * Returns the number of byte written or -1 in case of error.
1472 */
1473int
1474xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1475{
1476 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001477 if (ctxt->buf == NULL) return(-1);
1478 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001479}
1480
1481/**
1482 * xmlSaveClose:
1483 * @ctxt: a document saving context
1484 *
1485 * Close a document saving context, i.e. make sure that all bytes have
1486 * been output and free the associated data.
1487 *
1488 * Returns the number of byte written or -1 in case of error.
1489 */
1490int
1491xmlSaveClose(xmlSaveCtxtPtr ctxt)
1492{
1493 int ret;
1494
1495 if (ctxt == NULL) return(-1);
1496 ret = xmlSaveFlush(ctxt);
1497 xmlFreeSaveCtxt(ctxt);
1498 return(ret);
1499}
1500
1501/************************************************************************
1502 * *
1503 * Public entry points based on buffers *
1504 * *
1505 ************************************************************************/
1506/**
1507 * xmlAttrSerializeTxtContent:
1508 * @buf: the XML buffer output
1509 * @doc: the document
1510 * @attr: the attribute node
1511 * @string: the text content
1512 *
1513 * Serialize text attribute values to an xml simple buffer
1514 */
1515void
1516xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001517 xmlAttrPtr attr, const xmlChar * string)
1518{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001519 xmlChar *base, *cur;
1520
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001521 if (string == NULL)
1522 return;
1523 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001524 while (*cur != 0) {
1525 if (*cur == '\n') {
1526 if (base != cur)
1527 xmlBufferAdd(buf, base, cur - base);
1528 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1529 cur++;
1530 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001531 } else if (*cur == '\r') {
1532 if (base != cur)
1533 xmlBufferAdd(buf, base, cur - base);
1534 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1535 cur++;
1536 base = cur;
1537 } else if (*cur == '\t') {
1538 if (base != cur)
1539 xmlBufferAdd(buf, base, cur - base);
1540 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1541 cur++;
1542 base = cur;
1543 } else if (*cur == '"') {
1544 if (base != cur)
1545 xmlBufferAdd(buf, base, cur - base);
1546 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1547 cur++;
1548 base = cur;
1549 } else if (*cur == '<') {
1550 if (base != cur)
1551 xmlBufferAdd(buf, base, cur - base);
1552 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1553 cur++;
1554 base = cur;
1555 } else if (*cur == '>') {
1556 if (base != cur)
1557 xmlBufferAdd(buf, base, cur - base);
1558 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1559 cur++;
1560 base = cur;
1561 } else if (*cur == '&') {
1562 if (base != cur)
1563 xmlBufferAdd(buf, base, cur - base);
1564 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1565 cur++;
1566 base = cur;
1567 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1568 (doc->encoding == NULL))) {
1569 /*
1570 * We assume we have UTF-8 content.
1571 */
1572 unsigned char tmp[10];
1573 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001574
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001575 if (base != cur)
1576 xmlBufferAdd(buf, base, cur - base);
1577 if (*cur < 0xC0) {
1578 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1579 if (doc != NULL)
1580 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1581 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001582 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001583 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001584 base = cur;
1585 continue;
1586 } else if (*cur < 0xE0) {
1587 val = (cur[0]) & 0x1F;
1588 val <<= 6;
1589 val |= (cur[1]) & 0x3F;
1590 l = 2;
1591 } else if (*cur < 0xF0) {
1592 val = (cur[0]) & 0x0F;
1593 val <<= 6;
1594 val |= (cur[1]) & 0x3F;
1595 val <<= 6;
1596 val |= (cur[2]) & 0x3F;
1597 l = 3;
1598 } else if (*cur < 0xF8) {
1599 val = (cur[0]) & 0x07;
1600 val <<= 6;
1601 val |= (cur[1]) & 0x3F;
1602 val <<= 6;
1603 val |= (cur[2]) & 0x3F;
1604 val <<= 6;
1605 val |= (cur[3]) & 0x3F;
1606 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001607 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001608 if ((l == 1) || (!IS_CHAR(val))) {
1609 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1610 if (doc != NULL)
1611 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1612
1613 xmlSerializeHexCharRef(tmp, *cur);
1614 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1615 cur++;
1616 base = cur;
1617 continue;
1618 }
1619 /*
1620 * We could do multiple things here. Just save
1621 * as a char ref
1622 */
1623 xmlSerializeHexCharRef(tmp, val);
1624 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1625 cur += l;
1626 base = cur;
1627 } else {
1628 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001629 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001630 }
1631 if (base != cur)
1632 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001633}
1634
1635/**
1636 * xmlNodeDump:
1637 * @buf: the XML buffer output
1638 * @doc: the document
1639 * @cur: the current node
1640 * @level: the imbrication level for indenting
1641 * @format: is formatting allowed
1642 *
1643 * Dump an XML node, recursive behaviour,children are printed too.
1644 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1645 * or xmlKeepBlanksDefault(0) was called
1646 *
1647 * Returns the number of bytes written to the buffer or -1 in case of error
1648 */
1649int
1650xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1651 int format)
1652{
1653 unsigned int use;
1654 int ret;
1655 xmlOutputBufferPtr outbuf;
1656
1657 xmlInitParser();
1658
1659 if (cur == NULL) {
1660#ifdef DEBUG_TREE
1661 xmlGenericError(xmlGenericErrorContext,
1662 "xmlNodeDump : node == NULL\n");
1663#endif
1664 return (-1);
1665 }
1666 if (buf == NULL) {
1667#ifdef DEBUG_TREE
1668 xmlGenericError(xmlGenericErrorContext,
1669 "xmlNodeDump : buf == NULL\n");
1670#endif
1671 return (-1);
1672 }
1673 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1674 if (outbuf == NULL) {
1675 xmlSaveErrMemory("creating buffer");
1676 return (-1);
1677 }
1678 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1679 outbuf->buffer = buf;
1680 outbuf->encoder = NULL;
1681 outbuf->writecallback = NULL;
1682 outbuf->closecallback = NULL;
1683 outbuf->context = NULL;
1684 outbuf->written = 0;
1685
1686 use = buf->use;
1687 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1688 xmlFree(outbuf);
1689 ret = buf->use - use;
1690 return (ret);
1691}
1692
1693/**
1694 * xmlElemDump:
1695 * @f: the FILE * for the output
1696 * @doc: the document
1697 * @cur: the current node
1698 *
1699 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1700 */
1701void
1702xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1703{
1704 xmlOutputBufferPtr outbuf;
1705
1706 xmlInitParser();
1707
1708 if (cur == NULL) {
1709#ifdef DEBUG_TREE
1710 xmlGenericError(xmlGenericErrorContext,
1711 "xmlElemDump : cur == NULL\n");
1712#endif
1713 return;
1714 }
1715#ifdef DEBUG_TREE
1716 if (doc == NULL) {
1717 xmlGenericError(xmlGenericErrorContext,
1718 "xmlElemDump : doc == NULL\n");
1719 }
1720#endif
1721
1722 outbuf = xmlOutputBufferCreateFile(f, NULL);
1723 if (outbuf == NULL)
1724 return;
1725 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1726#ifdef LIBXML_HTML_ENABLED
1727 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1728#else
1729 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1730#endif /* LIBXML_HTML_ENABLED */
1731 } else
1732 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1733 xmlOutputBufferClose(outbuf);
1734}
1735
1736/************************************************************************
1737 * *
1738 * Saving functions front-ends *
1739 * *
1740 ************************************************************************/
1741
1742/**
1743 * xmlNodeDumpOutput:
1744 * @buf: the XML buffer output
1745 * @doc: the document
1746 * @cur: the current node
1747 * @level: the imbrication level for indenting
1748 * @format: is formatting allowed
1749 * @encoding: an optional encoding string
1750 *
1751 * Dump an XML node, recursive behaviour, children are printed too.
1752 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1753 * or xmlKeepBlanksDefault(0) was called
1754 */
1755void
1756xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1757 int level, int format, const char *encoding)
1758{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001759 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001760#ifdef LIBXML_HTML_ENABLED
1761 xmlDtdPtr dtd;
1762 int is_xhtml = 0;
1763#endif
1764
1765 xmlInitParser();
1766
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001767 memset(&ctxt, 0, sizeof(ctxt));
1768 ctxt.doc = doc;
1769 ctxt.buf = buf;
1770 ctxt.level = level;
1771 ctxt.format = format;
1772 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001773 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001774
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001775#ifdef LIBXML_HTML_ENABLED
1776 dtd = xmlGetIntSubset(doc);
1777 if (dtd != NULL) {
1778 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1779 if (is_xhtml < 0)
1780 is_xhtml = 0;
1781 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1782 (cur->type == XML_ELEMENT_NODE) &&
1783 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1784 if (encoding != NULL)
1785 htmlSetMetaEncoding((htmlDocPtr) doc,
1786 (const xmlChar *) encoding);
1787 else
1788 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1789 }
1790 }
1791
1792 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001793 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001794 else
1795#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001796 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001797}
1798
1799/**
1800 * xmlDocDumpFormatMemoryEnc:
1801 * @out_doc: Document to generate XML text from
1802 * @doc_txt_ptr: Memory pointer for allocated XML text
1803 * @doc_txt_len: Length of the generated XML text
1804 * @txt_encoding: Character encoding to use when generating XML text
1805 * @format: should formatting spaces been added
1806 *
1807 * Dump the current DOM tree into memory using the character encoding specified
1808 * by the caller. Note it is up to the caller of this function to free the
1809 * allocated memory with xmlFree().
1810 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1811 * or xmlKeepBlanksDefault(0) was called
1812 */
1813
1814void
1815xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1816 int * doc_txt_len, const char * txt_encoding,
1817 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001818 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001819 int dummy = 0;
1820 xmlOutputBufferPtr out_buff = NULL;
1821 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1822
1823 if (doc_txt_len == NULL) {
1824 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1825 }
1826
1827 if (doc_txt_ptr == NULL) {
1828 *doc_txt_len = 0;
1829 return;
1830 }
1831
1832 *doc_txt_ptr = NULL;
1833 *doc_txt_len = 0;
1834
1835 if (out_doc == NULL) {
1836 /* No document, no output */
1837 return;
1838 }
1839
1840 /*
1841 * Validate the encoding value, if provided.
1842 * This logic is copied from xmlSaveFileEnc.
1843 */
1844
1845 if (txt_encoding == NULL)
1846 txt_encoding = (const char *) out_doc->encoding;
1847 if (txt_encoding != NULL) {
1848 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1849 if ( conv_hdlr == NULL ) {
1850 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1851 txt_encoding);
1852 return;
1853 }
1854 }
1855
1856 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1857 xmlSaveErrMemory("creating buffer");
1858 return;
1859 }
1860
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001861 memset(&ctxt, 0, sizeof(ctxt));
1862 ctxt.doc = out_doc;
1863 ctxt.buf = out_buff;
1864 ctxt.level = 0;
1865 ctxt.format = format;
1866 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001867 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001868 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001869 xmlOutputBufferFlush(out_buff);
1870 if (out_buff->conv != NULL) {
1871 *doc_txt_len = out_buff->conv->use;
1872 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1873 } else {
1874 *doc_txt_len = out_buff->buffer->use;
1875 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1876 }
1877 (void)xmlOutputBufferClose(out_buff);
1878
1879 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1880 *doc_txt_len = 0;
1881 xmlSaveErrMemory("creating output");
1882 }
1883
1884 return;
1885}
1886
1887/**
1888 * xmlDocDumpMemory:
1889 * @cur: the document
1890 * @mem: OUT: the memory pointer
1891 * @size: OUT: the memory length
1892 *
1893 * Dump an XML document in memory and return the #xmlChar * and it's size
1894 * in bytes. It's up to the caller to free the memory with xmlFree().
1895 * The resulting byte array is zero terminated, though the last 0 is not
1896 * included in the returned size.
1897 */
1898void
1899xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1900 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1901}
1902
1903/**
1904 * xmlDocDumpFormatMemory:
1905 * @cur: the document
1906 * @mem: OUT: the memory pointer
1907 * @size: OUT: the memory length
1908 * @format: should formatting spaces been added
1909 *
1910 *
1911 * Dump an XML document in memory and return the #xmlChar * and it's size.
1912 * It's up to the caller to free the memory with xmlFree().
1913 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1914 * or xmlKeepBlanksDefault(0) was called
1915 */
1916void
1917xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1918 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1919}
1920
1921/**
1922 * xmlDocDumpMemoryEnc:
1923 * @out_doc: Document to generate XML text from
1924 * @doc_txt_ptr: Memory pointer for allocated XML text
1925 * @doc_txt_len: Length of the generated XML text
1926 * @txt_encoding: Character encoding to use when generating XML text
1927 *
1928 * Dump the current DOM tree into memory using the character encoding specified
1929 * by the caller. Note it is up to the caller of this function to free the
1930 * allocated memory with xmlFree().
1931 */
1932
1933void
1934xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1935 int * doc_txt_len, const char * txt_encoding) {
1936 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1937 txt_encoding, 0);
1938}
1939
1940/**
1941 * xmlDocFormatDump:
1942 * @f: the FILE*
1943 * @cur: the document
1944 * @format: should formatting spaces been added
1945 *
1946 * Dump an XML document to an open FILE.
1947 *
1948 * returns: the number of bytes written or -1 in case of failure.
1949 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1950 * or xmlKeepBlanksDefault(0) was called
1951 */
1952int
1953xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001954 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001955 xmlOutputBufferPtr buf;
1956 const char * encoding;
1957 xmlCharEncodingHandlerPtr handler = NULL;
1958 int ret;
1959
1960 if (cur == NULL) {
1961#ifdef DEBUG_TREE
1962 xmlGenericError(xmlGenericErrorContext,
1963 "xmlDocDump : document == NULL\n");
1964#endif
1965 return(-1);
1966 }
1967 encoding = (const char *) cur->encoding;
1968
1969 if (encoding != NULL) {
1970 handler = xmlFindCharEncodingHandler(encoding);
1971 if (handler == NULL) {
1972 xmlFree((char *) cur->encoding);
1973 cur->encoding = NULL;
1974 }
1975 }
1976 buf = xmlOutputBufferCreateFile(f, handler);
1977 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001978 memset(&ctxt, 0, sizeof(ctxt));
1979 ctxt.doc = cur;
1980 ctxt.buf = buf;
1981 ctxt.level = 0;
1982 ctxt.format = format;
1983 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001984 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001985 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001986
1987 ret = xmlOutputBufferClose(buf);
1988 return(ret);
1989}
1990
1991/**
1992 * xmlDocDump:
1993 * @f: the FILE*
1994 * @cur: the document
1995 *
1996 * Dump an XML document to an open FILE.
1997 *
1998 * returns: the number of bytes written or -1 in case of failure.
1999 */
2000int
2001xmlDocDump(FILE *f, xmlDocPtr cur) {
2002 return(xmlDocFormatDump (f, cur, 0));
2003}
2004
2005/**
2006 * xmlSaveFileTo:
2007 * @buf: an output I/O buffer
2008 * @cur: the document
2009 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2010 *
2011 * Dump an XML document to an I/O buffer.
2012 *
2013 * returns: the number of bytes written or -1 in case of failure.
2014 */
2015int
2016xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002017 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002018 int ret;
2019
2020 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002021 memset(&ctxt, 0, sizeof(ctxt));
2022 ctxt.doc = cur;
2023 ctxt.buf = buf;
2024 ctxt.level = 0;
2025 ctxt.format = 0;
2026 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002027 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002028 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002029 ret = xmlOutputBufferClose(buf);
2030 return(ret);
2031}
2032
2033/**
2034 * xmlSaveFormatFileTo:
2035 * @buf: an output I/O buffer
2036 * @cur: the document
2037 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2038 * @format: should formatting spaces been added
2039 *
2040 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002041 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002042 *
2043 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002044 */
2045int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002046xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2047 const char *encoding, int format)
2048{
2049 xmlSaveCtxt ctxt;
2050 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002051
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002052 if (buf == NULL)
2053 return (0);
2054 memset(&ctxt, 0, sizeof(ctxt));
2055 ctxt.doc = cur;
2056 ctxt.buf = buf;
2057 ctxt.level = 0;
2058 ctxt.format = format;
2059 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002060 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002061 xmlDocContentDumpOutput(&ctxt, cur);
2062 ret = xmlOutputBufferClose(buf);
2063 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002064}
2065
2066/**
2067 * xmlSaveFormatFileEnc:
2068 * @filename: the filename or URL to output
2069 * @cur: the document being saved
2070 * @encoding: the name of the encoding to use or NULL.
2071 * @format: should formatting spaces be added.
2072 *
2073 * Dump an XML document to a file or an URL.
2074 *
2075 * Returns the number of bytes written or -1 in case of error.
2076 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2077 * or xmlKeepBlanksDefault(0) was called
2078 */
2079int
2080xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2081 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002082 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002083 xmlOutputBufferPtr buf;
2084 xmlCharEncodingHandlerPtr handler = NULL;
2085 int ret;
2086
2087 if (cur == NULL)
2088 return(-1);
2089
2090 if (encoding == NULL)
2091 encoding = (const char *) cur->encoding;
2092
2093 if (encoding != NULL) {
2094
2095 handler = xmlFindCharEncodingHandler(encoding);
2096 if (handler == NULL)
2097 return(-1);
2098 }
2099
2100#ifdef HAVE_ZLIB_H
2101 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2102#endif
2103 /*
2104 * save the content to a temp buffer.
2105 */
2106 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2107 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002108 memset(&ctxt, 0, sizeof(ctxt));
2109 ctxt.doc = cur;
2110 ctxt.buf = buf;
2111 ctxt.level = 0;
2112 ctxt.format = format;
2113 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002114 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002115
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002116 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002117
2118 ret = xmlOutputBufferClose(buf);
2119 return(ret);
2120}
2121
2122
2123/**
2124 * xmlSaveFileEnc:
2125 * @filename: the filename (or URL)
2126 * @cur: the document
2127 * @encoding: the name of an encoding (or NULL)
2128 *
2129 * Dump an XML document, converting it to the given encoding
2130 *
2131 * returns: the number of bytes written or -1 in case of failure.
2132 */
2133int
2134xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2135 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2136}
2137
2138/**
2139 * xmlSaveFormatFile:
2140 * @filename: the filename (or URL)
2141 * @cur: the document
2142 * @format: should formatting spaces been added
2143 *
2144 * Dump an XML document to a file. Will use compression if
2145 * compiled in and enabled. If @filename is "-" the stdout file is
2146 * used. If @format is set then the document will be indented on output.
2147 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2148 * or xmlKeepBlanksDefault(0) was called
2149 *
2150 * returns: the number of bytes written or -1 in case of failure.
2151 */
2152int
2153xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2154 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2155}
2156
2157/**
2158 * xmlSaveFile:
2159 * @filename: the filename (or URL)
2160 * @cur: the document
2161 *
2162 * Dump an XML document to a file. Will use compression if
2163 * compiled in and enabled. If @filename is "-" the stdout file is
2164 * used.
2165 * returns: the number of bytes written or -1 in case of failure.
2166 */
2167int
2168xmlSaveFile(const char *filename, xmlDocPtr cur) {
2169 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2170}
2171
2172#endif /* LIBXML_OUTPUT_ENABLED */
2173