blob: aecbdfd65a844389285a1c270efe561d679d7728 [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));
Daniel Veillard6fc5db02005-01-16 00:05:58 +0000380
381 /*
382 * Use the options
383 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000384 ret->options = options;
Daniel Veillard6fc5db02005-01-16 00:05:58 +0000385 if (options & XML_SAVE_FORMAT)
386 ret->format = 1;
387
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000388 if (encoding != NULL) {
389 ret->handler = xmlFindCharEncodingHandler(encoding);
390 if (ret->handler == NULL) {
391 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
392 xmlFreeSaveCtxt(ret);
393 return(NULL);
394 }
395 ret->encoding = xmlStrdup((const xmlChar *)encoding);
Daniel Veillard3995bc32004-05-15 18:57:31 +0000396 ret->escape = xmlEscapeEntities;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000397 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000398 xmlSaveCtxtInit(ret);
399
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000400 return(ret);
401}
402
403/************************************************************************
404 * *
405 * Dumping XML tree content to a simple buffer *
406 * *
407 ************************************************************************/
408/**
409 * xmlAttrSerializeContent:
410 * @buf: the XML buffer output
411 * @doc: the document
412 * @attr: the attribute pointer
413 *
414 * Serialize the attribute in the buffer
415 */
416static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000417xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000418{
419 xmlNodePtr children;
420
421 children = attr->children;
422 while (children != NULL) {
423 switch (children->type) {
424 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000425 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
426 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000427 break;
428 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000429 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
430 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000431 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000432 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000433 break;
434 default:
435 /* should not happen unless we have a badly built tree */
436 break;
437 }
438 children = children->next;
439 }
440}
441
442/************************************************************************
443 * *
444 * Dumping XML tree content to an I/O output buffer *
445 * *
446 ************************************************************************/
447
448#ifdef LIBXML_HTML_ENABLED
449static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000450xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000451#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000452static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
453static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000454void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
Daniel Veillardce244ad2004-11-05 10:03:46 +0000455static void xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000456
457/**
458 * xmlNsDumpOutput:
459 * @buf: the XML buffer output
460 * @cur: a namespace
461 *
462 * Dump a local Namespace definition.
463 * Should be called in the context of attributes dumps.
464 */
465static void
466xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillardce244ad2004-11-05 10:03:46 +0000467 if ((cur == NULL) || (buf == NULL)) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000468 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
469 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
470 return;
471
472 /* Within the context of an element attributes */
473 if (cur->prefix != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000474 xmlOutputBufferWrite(buf, 7, " xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000475 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
476 } else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000477 xmlOutputBufferWrite(buf, 6, " xmlns");
478 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000479 xmlBufferWriteQuotedString(buf->buffer, cur->href);
480 }
481}
482
483/**
484 * xmlNsListDumpOutput:
485 * @buf: the XML buffer output
486 * @cur: the first namespace
487 *
488 * Dump a list of local Namespace definitions.
489 * Should be called in the context of attributes dumps.
490 */
491void
492xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
493 while (cur != NULL) {
494 xmlNsDumpOutput(buf, cur);
495 cur = cur->next;
496 }
497}
498
499/**
500 * xmlDtdDumpOutput:
501 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000502 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000503 *
504 * Dump the XML document DTD, if any.
505 */
506static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000507xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
508 xmlOutputBufferPtr buf;
509 int format, level;
510 xmlDocPtr doc;
511
512 if (dtd == NULL) return;
513 if ((ctxt == NULL) || (ctxt->buf == NULL))
514 return;
515 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000516 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000517 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
518 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000519 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000520 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000521 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000522 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
523 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000524 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000525 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
526 }
527 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
Daniel Veillard41c4a752004-09-08 20:55:38 +0000528 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
529 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000530 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000531 return;
532 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000533 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard41c4a752004-09-08 20:55:38 +0000534 /*
535 * Dump the notations first they are not in the DTD children list
536 * Do this only on a standalone DTD or on the internal subset though.
537 */
538 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
539 (dtd->doc->intSubset == dtd))) {
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000540 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
541 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000542 format = ctxt->format;
543 level = ctxt->level;
544 doc = ctxt->doc;
545 ctxt->format = 0;
546 ctxt->level = -1;
547 ctxt->doc = dtd->doc;
548 xmlNodeListDumpOutput(ctxt, dtd->children);
549 ctxt->format = format;
550 ctxt->level = level;
551 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000552 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000553}
554
555/**
556 * xmlAttrDumpOutput:
557 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000558 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000559 *
560 * Dump an XML attribute
561 */
562static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000563xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
564 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000565
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000566 if (cur == NULL) return;
567 buf = ctxt->buf;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000568 if (buf == NULL) return;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000569 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000570 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
571 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000572 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000573 }
574 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000575 xmlOutputBufferWrite(buf, 2, "=\"");
576 xmlAttrSerializeContent(buf, cur);
577 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000578}
579
580/**
581 * xmlAttrListDumpOutput:
582 * @buf: the XML buffer output
583 * @doc: the document
584 * @cur: the first attribute pointer
585 * @encoding: an optional encoding string
586 *
587 * Dump a list of XML attributes
588 */
589static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000590xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
591 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000592 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000593 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000594 cur = cur->next;
595 }
596}
597
598
599
600/**
601 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000602 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000603 *
604 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000605 */
606static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000607xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000608 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000609
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000610 if (cur == NULL) return;
611 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000612 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000613 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000614 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000615 xmlOutputBufferWrite(buf, ctxt->indent_size *
616 (ctxt->level > ctxt->indent_nr ?
617 ctxt->indent_nr : ctxt->level),
618 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000619 xmlNodeDumpOutputInternal(ctxt, cur);
620 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000621 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000622 }
623 cur = cur->next;
624 }
625}
626
627/**
628 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000629 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000630 *
631 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000632 */
633static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000634xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000635 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000636 xmlNodePtr tmp;
637 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000638 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000639
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000640 if (cur == NULL) return;
641 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000642 if (cur->type == XML_XINCLUDE_START)
643 return;
644 if (cur->type == XML_XINCLUDE_END)
645 return;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000646 if ((cur->type == XML_DOCUMENT_NODE) ||
647 (cur->type == XML_HTML_DOCUMENT_NODE)) {
648 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
649 return;
650 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000651 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000652 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000653 return;
654 }
655 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000656 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000657 return;
658 }
659 if (cur->type == XML_ELEMENT_DECL) {
660 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
661 return;
662 }
663 if (cur->type == XML_ATTRIBUTE_DECL) {
664 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
665 return;
666 }
667 if (cur->type == XML_ENTITY_DECL) {
668 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
669 return;
670 }
671 if (cur->type == XML_TEXT_NODE) {
672 if (cur->content != NULL) {
William M. Brack4e1c2db2005-02-11 10:58:55 +0000673 if (cur->name != xmlStringTextNoenc) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000674 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000675 } else {
676 /*
677 * Disable escaping, needed for XSLT
678 */
679 xmlOutputBufferWriteString(buf, (const char *) cur->content);
680 }
681 }
682
683 return;
684 }
685 if (cur->type == XML_PI_NODE) {
686 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000687 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000688 xmlOutputBufferWriteString(buf, (const char *)cur->name);
689 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000690 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000691 xmlOutputBufferWriteString(buf, (const char *)cur->content);
692 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000693 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000694 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000695 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000696 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000697 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000698 }
699 return;
700 }
701 if (cur->type == XML_COMMENT_NODE) {
702 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000703 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000704 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000705 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000706 }
707 return;
708 }
709 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000710 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000711 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000712 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000713 return;
714 }
715 if (cur->type == XML_CDATA_SECTION_NODE) {
716 start = end = cur->content;
717 while (*end != '\0') {
718 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
719 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000720 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000721 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000722 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000723 start = end;
724 }
725 end++;
726 }
727 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000728 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000729 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000730 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000731 }
732 return;
733 }
734 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000735 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000736 return;
737 }
738 if (cur->type == XML_NAMESPACE_DECL) {
739 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
740 return;
741 }
742
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000743 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000744 if (format == 1) {
745 tmp = cur->children;
746 while (tmp != NULL) {
747 if ((tmp->type == XML_TEXT_NODE) ||
748 (tmp->type == XML_CDATA_SECTION_NODE) ||
749 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000750 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000751 break;
752 }
753 tmp = tmp->next;
754 }
755 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000756 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000757 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
758 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000759 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000760 }
761
762 xmlOutputBufferWriteString(buf, (const char *)cur->name);
763 if (cur->nsDef)
764 xmlNsListDumpOutput(buf, cur->nsDef);
765 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000766 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000767
768 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
769 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000770 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000771 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000772 return;
773 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000774 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000775 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000776 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000777 }
778 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000779 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000780 if (ctxt->level >= 0) ctxt->level++;
781 xmlNodeListDumpOutput(ctxt, cur->children);
782 if (ctxt->level > 0) ctxt->level--;
783 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000784 xmlOutputBufferWrite(buf, ctxt->indent_size *
785 (ctxt->level > ctxt->indent_nr ?
786 ctxt->indent_nr : ctxt->level),
787 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000788 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000789 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000790 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
791 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000792 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000793 }
794
795 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000796 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000797 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000798}
799
800/**
801 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000802 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000803 *
804 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000805 */
806static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000807xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000808#ifdef LIBXML_HTML_ENABLED
809 xmlDtdPtr dtd;
810 int is_xhtml = 0;
811#endif
812 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000813 const xmlChar *encoding = ctxt->encoding;
814 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000815
816 xmlInitParser();
817
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000818 if (ctxt->encoding != NULL)
819 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000820
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000821 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000822 xmlOutputBufferWrite(buf, 14, "<?xml version=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000823 if (cur->version != NULL)
824 xmlBufferWriteQuotedString(buf->buffer, cur->version);
825 else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000826 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000827 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000828 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000829 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000830 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000831 encoding = (const xmlChar *)
832 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000833 }
834 if (encoding != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000835 xmlOutputBufferWrite(buf, 10, " encoding=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000836 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
837 }
838 switch (cur->standalone) {
839 case 0:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000840 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000841 break;
842 case 1:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000843 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000844 break;
845 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000846 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000847
848#ifdef LIBXML_HTML_ENABLED
849 dtd = xmlGetIntSubset(cur);
850 if (dtd != NULL) {
851 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
852 if (is_xhtml < 0) is_xhtml = 0;
853 }
854 if (is_xhtml) {
855 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000856 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000857 else
858 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
859 }
860#endif
861 if (cur->children != NULL) {
862 xmlNodePtr child = cur->children;
863
864 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000865 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000866#ifdef LIBXML_HTML_ENABLED
867 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000868 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000869 else
870#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000871 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000872 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000873 child = child->next;
874 }
875 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000876 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000877 cur->encoding = oldenc;
878}
879
880#ifdef LIBXML_HTML_ENABLED
881/************************************************************************
882 * *
883 * Functions specific to XHTML serialization *
884 * *
885 ************************************************************************/
886
887/**
888 * xhtmlIsEmpty:
889 * @node: the node
890 *
891 * Check if a node is an empty xhtml node
892 *
893 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
894 */
895static int
896xhtmlIsEmpty(xmlNodePtr node) {
897 if (node == NULL)
898 return(-1);
899 if (node->type != XML_ELEMENT_NODE)
900 return(0);
901 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
902 return(0);
903 if (node->children != NULL)
904 return(0);
905 switch (node->name[0]) {
906 case 'a':
907 if (xmlStrEqual(node->name, BAD_CAST "area"))
908 return(1);
909 return(0);
910 case 'b':
911 if (xmlStrEqual(node->name, BAD_CAST "br"))
912 return(1);
913 if (xmlStrEqual(node->name, BAD_CAST "base"))
914 return(1);
915 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
916 return(1);
917 return(0);
918 case 'c':
919 if (xmlStrEqual(node->name, BAD_CAST "col"))
920 return(1);
921 return(0);
922 case 'f':
923 if (xmlStrEqual(node->name, BAD_CAST "frame"))
924 return(1);
925 return(0);
926 case 'h':
927 if (xmlStrEqual(node->name, BAD_CAST "hr"))
928 return(1);
929 return(0);
930 case 'i':
931 if (xmlStrEqual(node->name, BAD_CAST "img"))
932 return(1);
933 if (xmlStrEqual(node->name, BAD_CAST "input"))
934 return(1);
935 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
936 return(1);
937 return(0);
938 case 'l':
939 if (xmlStrEqual(node->name, BAD_CAST "link"))
940 return(1);
941 return(0);
942 case 'm':
943 if (xmlStrEqual(node->name, BAD_CAST "meta"))
944 return(1);
945 return(0);
946 case 'p':
947 if (xmlStrEqual(node->name, BAD_CAST "param"))
948 return(1);
949 return(0);
950 }
951 return(0);
952}
953
954/**
955 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000956 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000957 *
958 * Dump a list of XML attributes
959 */
960static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000961xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000962 xmlAttrPtr xml_lang = NULL;
963 xmlAttrPtr lang = NULL;
964 xmlAttrPtr name = NULL;
965 xmlAttrPtr id = NULL;
966 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000967 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000968
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000969 if (cur == NULL) return;
970 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000971 parent = cur->parent;
972 while (cur != NULL) {
973 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
974 id = cur;
975 else
976 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
977 name = cur;
978 else
979 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
980 lang = cur;
981 else
982 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
983 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
984 xml_lang = cur;
985 else if ((cur->ns == NULL) &&
986 ((cur->children == NULL) ||
987 (cur->children->content == NULL) ||
988 (cur->children->content[0] == 0)) &&
989 (htmlIsBooleanAttr(cur->name))) {
990 if (cur->children != NULL)
991 xmlFreeNode(cur->children);
992 cur->children = xmlNewText(cur->name);
993 if (cur->children != NULL)
994 cur->children->parent = (xmlNodePtr) cur;
995 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000996 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000997 cur = cur->next;
998 }
999 /*
1000 * C.8
1001 */
1002 if ((name != NULL) && (id == NULL)) {
1003 if ((parent != NULL) && (parent->name != NULL) &&
1004 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1005 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1006 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1007 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1008 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1009 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1010 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1011 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1012 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001013 xmlOutputBufferWrite(buf, 5, " id=\"");
1014 xmlAttrSerializeContent(buf, name);
1015 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001016 }
1017 }
1018 /*
1019 * C.7.
1020 */
1021 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001022 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1023 xmlAttrSerializeContent(buf, lang);
1024 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001025 } else
1026 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001027 xmlOutputBufferWrite(buf, 7, " lang=\"");
1028 xmlAttrSerializeContent(buf, xml_lang);
1029 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001030 }
1031}
1032
1033/**
1034 * xhtmlNodeListDumpOutput:
1035 * @buf: the XML buffer output
1036 * @doc: the XHTML document
1037 * @cur: the first node
1038 * @level: the imbrication level for indenting
1039 * @format: is formatting allowed
1040 * @encoding: an optional encoding string
1041 *
1042 * Dump an XML node list, recursive behaviour, children are printed too.
1043 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1044 * or xmlKeepBlanksDefault(0) was called
1045 */
1046static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001047xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001048 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001049
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001050 if (cur == NULL) return;
1051 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001052 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001053 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001054 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001055 xmlOutputBufferWrite(buf, ctxt->indent_size *
1056 (ctxt->level > ctxt->indent_nr ?
1057 ctxt->indent_nr : ctxt->level),
1058 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001059 xhtmlNodeDumpOutput(ctxt, cur);
1060 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001061 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001062 }
1063 cur = cur->next;
1064 }
1065}
1066
1067/**
1068 * xhtmlNodeDumpOutput:
1069 * @buf: the XML buffer output
1070 * @doc: the XHTML document
1071 * @cur: the current node
1072 * @level: the imbrication level for indenting
1073 * @format: is formatting allowed
1074 * @encoding: an optional encoding string
1075 *
1076 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001077 */
1078static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001079xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +00001080 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001081 xmlNodePtr tmp;
1082 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001083 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001084
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001085 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001086 if (cur->type == XML_XINCLUDE_START)
1087 return;
1088 if (cur->type == XML_XINCLUDE_END)
1089 return;
1090 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001091 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001092 return;
1093 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001094 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001095 if (cur->type == XML_ELEMENT_DECL) {
1096 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1097 return;
1098 }
1099 if (cur->type == XML_ATTRIBUTE_DECL) {
1100 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1101 return;
1102 }
1103 if (cur->type == XML_ENTITY_DECL) {
1104 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1105 return;
1106 }
1107 if (cur->type == XML_TEXT_NODE) {
1108 if (cur->content != NULL) {
1109 if ((cur->name == xmlStringText) ||
1110 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001111 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001112 } else {
1113 /*
1114 * Disable escaping, needed for XSLT
1115 */
1116 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1117 }
1118 }
1119
1120 return;
1121 }
1122 if (cur->type == XML_PI_NODE) {
1123 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001124 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001125 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1126 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001127 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001128 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1129 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001130 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001131 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001132 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001133 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001134 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001135 }
1136 return;
1137 }
1138 if (cur->type == XML_COMMENT_NODE) {
1139 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001140 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001141 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001142 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001143 }
1144 return;
1145 }
1146 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001147 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001148 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001149 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001150 return;
1151 }
1152 if (cur->type == XML_CDATA_SECTION_NODE) {
1153 start = end = cur->content;
1154 while (*end != '\0') {
1155 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1156 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001157 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001158 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001159 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001160 start = end;
1161 }
1162 end++;
1163 }
1164 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001165 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001166 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001167 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001168 }
1169 return;
1170 }
1171
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001172 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001173 if (format == 1) {
1174 tmp = cur->children;
1175 while (tmp != NULL) {
1176 if ((tmp->type == XML_TEXT_NODE) ||
1177 (tmp->type == XML_ENTITY_REF_NODE)) {
1178 format = 0;
1179 break;
1180 }
1181 tmp = tmp->next;
1182 }
1183 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001184 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001185 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1186 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001187 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001188 }
1189
1190 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1191 if (cur->nsDef)
1192 xmlNsListDumpOutput(buf, cur->nsDef);
1193 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1194 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1195 /*
1196 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1197 */
1198 xmlOutputBufferWriteString(buf,
1199 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1200 }
1201 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001202 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001203
1204 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1205 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1206 (xhtmlIsEmpty(cur) == 1)) {
1207 /*
1208 * C.2. Empty Elements
1209 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001210 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001211 } else {
1212 /*
1213 * C.3. Element Minimization and Empty Element Content
1214 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001215 xmlOutputBufferWrite(buf, 3, "></");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001216 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1217 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001218 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001219 }
1220 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001221 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001222 }
1223 return;
1224 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001225 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001226 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001227 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001228 }
1229
1230 /*
1231 * 4.8. Script and Style elements
1232 */
1233 if ((cur->type == XML_ELEMENT_NODE) &&
1234 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1235 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1236 ((cur->ns == NULL) ||
1237 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1238 xmlNodePtr child = cur->children;
1239
1240 while (child != NULL) {
1241 if ((child->type == XML_TEXT_NODE) ||
1242 (child->type == XML_CDATA_SECTION_NODE)) {
1243 /*
1244 * Apparently CDATA escaping for style just break on IE,
1245 * mozilla and galeon, so ...
1246 */
1247 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1248 (xmlStrchr(child->content, '<') == NULL) &&
1249 (xmlStrchr(child->content, '>') == NULL) &&
1250 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001251 int level = ctxt->level;
1252 int indent = ctxt->format;
1253
1254 ctxt->level = 0;
1255 ctxt->format = 0;
1256 xhtmlNodeDumpOutput(ctxt, child);
1257 ctxt->level = level;
1258 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001259 } else {
1260 start = end = child->content;
1261 while (*end != '\0') {
1262 if (*end == ']' &&
1263 *(end + 1) == ']' &&
1264 *(end + 2) == '>') {
1265 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001266 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001267 xmlOutputBufferWrite(buf, end - start,
1268 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001269 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001270 start = end;
1271 }
1272 end++;
1273 }
1274 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001275 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1276 xmlOutputBufferWrite(buf, end - start,
1277 (const char *)start);
1278 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001279 }
1280 }
1281 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001282 int level = ctxt->level;
1283 int indent = ctxt->format;
1284
1285 ctxt->level = 0;
1286 ctxt->format = 0;
1287 xhtmlNodeDumpOutput(ctxt, child);
1288 ctxt->level = level;
1289 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001290 }
1291 child = child->next;
1292 }
1293 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001294 int indent = ctxt->format;
1295
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001296 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001297 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001298 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001299 xhtmlNodeListDumpOutput(ctxt, cur->children);
1300 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001301 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001302 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001303 xmlOutputBufferWrite(buf, ctxt->indent_size *
1304 (ctxt->level > ctxt->indent_nr ?
1305 ctxt->indent_nr : ctxt->level),
1306 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001307 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001308 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001309 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1310 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001311 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001312 }
1313
1314 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001315 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001316}
1317#endif
1318
1319/************************************************************************
1320 * *
1321 * Public entry points *
1322 * *
1323 ************************************************************************/
1324
1325/**
1326 * xmlSaveToFd:
1327 * @fd: a file descriptor number
1328 * @encoding: the encoding name to use or NULL
1329 * @options: a set of xmlSaveOptions
1330 *
1331 * Create a document saving context serializing to a file descriptor
1332 * with the encoding and the options given.
1333 *
1334 * Returns a new serialization context or NULL in case of error.
1335 */
1336xmlSaveCtxtPtr
1337xmlSaveToFd(int fd, const char *encoding, int options)
1338{
1339 xmlSaveCtxtPtr ret;
1340
1341 ret = xmlNewSaveCtxt(encoding, options);
1342 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001343 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1344 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001345 xmlFreeSaveCtxt(ret);
1346 return(NULL);
1347 }
1348 return(ret);
1349}
1350
1351/**
1352 * xmlSaveToFilename:
1353 * @filename: a file name or an URL
1354 * @encoding: the encoding name to use or NULL
1355 * @options: a set of xmlSaveOptions
1356 *
1357 * Create a document saving context serializing to a filename or possibly
1358 * to an URL (but this is less reliable) with the encoding and the options
1359 * given.
1360 *
1361 * Returns a new serialization context or NULL in case of error.
1362 */
1363xmlSaveCtxtPtr
1364xmlSaveToFilename(const char *filename, const char *encoding, int options)
1365{
1366 xmlSaveCtxtPtr ret;
1367 int compression = 0; /* TODO handle compression option */
1368
1369 ret = xmlNewSaveCtxt(encoding, options);
1370 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001371 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001372 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001373 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001374 xmlFreeSaveCtxt(ret);
1375 return(NULL);
1376 }
1377 return(ret);
1378}
1379
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001380/**
1381 * xmlSaveToBuffer:
1382 * @buffer: a buffer
1383 * @encoding: the encoding name to use or NULL
1384 * @options: a set of xmlSaveOptions
1385 *
1386 * Create a document saving context serializing to a buffer
1387 * with the encoding and the options given
1388 *
1389 * Returns a new serialization context or NULL in case of error.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001390xmlSaveCtxtPtr
1391xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1392{
1393 TODO
1394 return(NULL);
1395}
Daniel Veillarda2351322004-06-27 12:08:10 +00001396 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001397
1398/**
1399 * xmlSaveToIO:
1400 * @iowrite: an I/O write function
1401 * @ioclose: an I/O close function
1402 * @ioctx: an I/O handler
1403 * @encoding: the encoding name to use or NULL
1404 * @options: a set of xmlSaveOptions
1405 *
1406 * Create a document saving context serializing to a file descriptor
1407 * with the encoding and the options given
1408 *
1409 * Returns a new serialization context or NULL in case of error.
1410 */
1411xmlSaveCtxtPtr
1412xmlSaveToIO(xmlOutputWriteCallback iowrite,
1413 xmlOutputCloseCallback ioclose,
1414 void *ioctx, const char *encoding, int options)
1415{
1416 xmlSaveCtxtPtr ret;
1417
1418 ret = xmlNewSaveCtxt(encoding, options);
1419 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001420 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1421 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001422 xmlFreeSaveCtxt(ret);
1423 return(NULL);
1424 }
1425 return(ret);
1426}
1427
1428/**
1429 * xmlSaveDoc:
1430 * @ctxt: a document saving context
1431 * @doc: a document
1432 *
1433 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001434 * TODO: The function is not fully implemented yet as it does not return the
1435 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001436 *
1437 * Returns the number of byte written or -1 in case of error
1438 */
1439long
1440xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1441{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001442 long ret = 0;
1443
Daniel Veillardce682bc2004-11-05 17:22:25 +00001444 if ((ctxt == NULL) || (doc == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001445 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001446 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001447}
1448
1449/**
1450 * xmlSaveTree:
1451 * @ctxt: a document saving context
1452 * @node: a document
1453 *
1454 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001455 * TODO: The function is not fully implemented yet as it does not return the
1456 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001457 *
1458 * Returns the number of byte written or -1 in case of error
1459 */
1460long
1461xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1462{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001463 long ret = 0;
1464
Daniel Veillardce682bc2004-11-05 17:22:25 +00001465 if ((ctxt == NULL) || (node == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001466 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001467 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001468}
1469
1470/**
1471 * xmlSaveFlush:
1472 * @ctxt: a document saving context
1473 *
1474 * Flush a document saving context, i.e. make sure that all bytes have
1475 * been output.
1476 *
1477 * Returns the number of byte written or -1 in case of error.
1478 */
1479int
1480xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1481{
1482 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001483 if (ctxt->buf == NULL) return(-1);
1484 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001485}
1486
1487/**
1488 * xmlSaveClose:
1489 * @ctxt: a document saving context
1490 *
1491 * Close a document saving context, i.e. make sure that all bytes have
1492 * been output and free the associated data.
1493 *
1494 * Returns the number of byte written or -1 in case of error.
1495 */
1496int
1497xmlSaveClose(xmlSaveCtxtPtr ctxt)
1498{
1499 int ret;
1500
1501 if (ctxt == NULL) return(-1);
1502 ret = xmlSaveFlush(ctxt);
1503 xmlFreeSaveCtxt(ctxt);
1504 return(ret);
1505}
1506
Daniel Veillard3995bc32004-05-15 18:57:31 +00001507/**
1508 * xmlSaveSetEscape:
1509 * @ctxt: a document saving context
1510 * @escape: the escaping function
1511 *
1512 * Set a custom escaping function to be used for text in element content
1513 *
1514 * Returns 0 if successful or -1 in case of error.
1515 */
1516int
1517xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1518{
1519 if (ctxt == NULL) return(-1);
1520 ctxt->escape = escape;
1521 return(0);
1522}
1523
1524/**
1525 * xmlSaveSetAttrEscape:
1526 * @ctxt: a document saving context
1527 * @escape: the escaping function
1528 *
1529 * Set a custom escaping function to be used for text in attribute content
1530 *
1531 * Returns 0 if successful or -1 in case of error.
1532 */
1533int
1534xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1535{
1536 if (ctxt == NULL) return(-1);
1537 ctxt->escapeAttr = escape;
1538 return(0);
1539}
1540
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001541/************************************************************************
1542 * *
1543 * Public entry points based on buffers *
1544 * *
1545 ************************************************************************/
1546/**
1547 * xmlAttrSerializeTxtContent:
1548 * @buf: the XML buffer output
1549 * @doc: the document
1550 * @attr: the attribute node
1551 * @string: the text content
1552 *
1553 * Serialize text attribute values to an xml simple buffer
1554 */
1555void
1556xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001557 xmlAttrPtr attr, const xmlChar * string)
1558{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001559 xmlChar *base, *cur;
1560
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001561 if (string == NULL)
1562 return;
1563 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001564 while (*cur != 0) {
1565 if (*cur == '\n') {
1566 if (base != cur)
1567 xmlBufferAdd(buf, base, cur - base);
1568 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1569 cur++;
1570 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001571 } else if (*cur == '\r') {
1572 if (base != cur)
1573 xmlBufferAdd(buf, base, cur - base);
1574 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1575 cur++;
1576 base = cur;
1577 } else if (*cur == '\t') {
1578 if (base != cur)
1579 xmlBufferAdd(buf, base, cur - base);
1580 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1581 cur++;
1582 base = cur;
1583 } else if (*cur == '"') {
1584 if (base != cur)
1585 xmlBufferAdd(buf, base, cur - base);
1586 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1587 cur++;
1588 base = cur;
1589 } else if (*cur == '<') {
1590 if (base != cur)
1591 xmlBufferAdd(buf, base, cur - base);
1592 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1593 cur++;
1594 base = cur;
1595 } else if (*cur == '>') {
1596 if (base != cur)
1597 xmlBufferAdd(buf, base, cur - base);
1598 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1599 cur++;
1600 base = cur;
1601 } else if (*cur == '&') {
1602 if (base != cur)
1603 xmlBufferAdd(buf, base, cur - base);
1604 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1605 cur++;
1606 base = cur;
1607 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1608 (doc->encoding == NULL))) {
1609 /*
1610 * We assume we have UTF-8 content.
1611 */
1612 unsigned char tmp[10];
1613 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001614
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001615 if (base != cur)
1616 xmlBufferAdd(buf, base, cur - base);
1617 if (*cur < 0xC0) {
1618 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1619 if (doc != NULL)
1620 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1621 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001622 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001623 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001624 base = cur;
1625 continue;
1626 } else if (*cur < 0xE0) {
1627 val = (cur[0]) & 0x1F;
1628 val <<= 6;
1629 val |= (cur[1]) & 0x3F;
1630 l = 2;
1631 } else if (*cur < 0xF0) {
1632 val = (cur[0]) & 0x0F;
1633 val <<= 6;
1634 val |= (cur[1]) & 0x3F;
1635 val <<= 6;
1636 val |= (cur[2]) & 0x3F;
1637 l = 3;
1638 } else if (*cur < 0xF8) {
1639 val = (cur[0]) & 0x07;
1640 val <<= 6;
1641 val |= (cur[1]) & 0x3F;
1642 val <<= 6;
1643 val |= (cur[2]) & 0x3F;
1644 val <<= 6;
1645 val |= (cur[3]) & 0x3F;
1646 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001647 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001648 if ((l == 1) || (!IS_CHAR(val))) {
1649 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1650 if (doc != NULL)
1651 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1652
1653 xmlSerializeHexCharRef(tmp, *cur);
1654 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1655 cur++;
1656 base = cur;
1657 continue;
1658 }
1659 /*
1660 * We could do multiple things here. Just save
1661 * as a char ref
1662 */
1663 xmlSerializeHexCharRef(tmp, val);
1664 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1665 cur += l;
1666 base = cur;
1667 } else {
1668 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001669 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001670 }
1671 if (base != cur)
1672 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001673}
1674
1675/**
1676 * xmlNodeDump:
1677 * @buf: the XML buffer output
1678 * @doc: the document
1679 * @cur: the current node
1680 * @level: the imbrication level for indenting
1681 * @format: is formatting allowed
1682 *
1683 * Dump an XML node, recursive behaviour,children are printed too.
1684 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1685 * or xmlKeepBlanksDefault(0) was called
1686 *
1687 * Returns the number of bytes written to the buffer or -1 in case of error
1688 */
1689int
1690xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1691 int format)
1692{
1693 unsigned int use;
1694 int ret;
1695 xmlOutputBufferPtr outbuf;
1696
1697 xmlInitParser();
1698
1699 if (cur == NULL) {
1700#ifdef DEBUG_TREE
1701 xmlGenericError(xmlGenericErrorContext,
1702 "xmlNodeDump : node == NULL\n");
1703#endif
1704 return (-1);
1705 }
1706 if (buf == NULL) {
1707#ifdef DEBUG_TREE
1708 xmlGenericError(xmlGenericErrorContext,
1709 "xmlNodeDump : buf == NULL\n");
1710#endif
1711 return (-1);
1712 }
1713 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1714 if (outbuf == NULL) {
1715 xmlSaveErrMemory("creating buffer");
1716 return (-1);
1717 }
1718 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1719 outbuf->buffer = buf;
1720 outbuf->encoder = NULL;
1721 outbuf->writecallback = NULL;
1722 outbuf->closecallback = NULL;
1723 outbuf->context = NULL;
1724 outbuf->written = 0;
1725
1726 use = buf->use;
1727 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1728 xmlFree(outbuf);
1729 ret = buf->use - use;
1730 return (ret);
1731}
1732
1733/**
1734 * xmlElemDump:
1735 * @f: the FILE * for the output
1736 * @doc: the document
1737 * @cur: the current node
1738 *
1739 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1740 */
1741void
1742xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1743{
1744 xmlOutputBufferPtr outbuf;
1745
1746 xmlInitParser();
1747
1748 if (cur == NULL) {
1749#ifdef DEBUG_TREE
1750 xmlGenericError(xmlGenericErrorContext,
1751 "xmlElemDump : cur == NULL\n");
1752#endif
1753 return;
1754 }
1755#ifdef DEBUG_TREE
1756 if (doc == NULL) {
1757 xmlGenericError(xmlGenericErrorContext,
1758 "xmlElemDump : doc == NULL\n");
1759 }
1760#endif
1761
1762 outbuf = xmlOutputBufferCreateFile(f, NULL);
1763 if (outbuf == NULL)
1764 return;
1765 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1766#ifdef LIBXML_HTML_ENABLED
1767 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1768#else
1769 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1770#endif /* LIBXML_HTML_ENABLED */
1771 } else
1772 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1773 xmlOutputBufferClose(outbuf);
1774}
1775
1776/************************************************************************
1777 * *
1778 * Saving functions front-ends *
1779 * *
1780 ************************************************************************/
1781
1782/**
1783 * xmlNodeDumpOutput:
1784 * @buf: the XML buffer output
1785 * @doc: the document
1786 * @cur: the current node
1787 * @level: the imbrication level for indenting
1788 * @format: is formatting allowed
1789 * @encoding: an optional encoding string
1790 *
1791 * Dump an XML node, recursive behaviour, children are printed too.
1792 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1793 * or xmlKeepBlanksDefault(0) was called
1794 */
1795void
1796xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1797 int level, int format, const char *encoding)
1798{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001799 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001800#ifdef LIBXML_HTML_ENABLED
1801 xmlDtdPtr dtd;
1802 int is_xhtml = 0;
1803#endif
1804
1805 xmlInitParser();
1806
Daniel Veillardce244ad2004-11-05 10:03:46 +00001807 if ((buf == NULL) || (cur == NULL)) return;
1808
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001809 memset(&ctxt, 0, sizeof(ctxt));
1810 ctxt.doc = doc;
1811 ctxt.buf = buf;
1812 ctxt.level = level;
1813 ctxt.format = format;
1814 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001815 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001816
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001817#ifdef LIBXML_HTML_ENABLED
1818 dtd = xmlGetIntSubset(doc);
1819 if (dtd != NULL) {
1820 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1821 if (is_xhtml < 0)
1822 is_xhtml = 0;
1823 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1824 (cur->type == XML_ELEMENT_NODE) &&
1825 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1826 if (encoding != NULL)
1827 htmlSetMetaEncoding((htmlDocPtr) doc,
1828 (const xmlChar *) encoding);
1829 else
1830 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1831 }
1832 }
1833
1834 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001835 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001836 else
1837#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001838 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001839}
1840
1841/**
1842 * xmlDocDumpFormatMemoryEnc:
1843 * @out_doc: Document to generate XML text from
1844 * @doc_txt_ptr: Memory pointer for allocated XML text
1845 * @doc_txt_len: Length of the generated XML text
1846 * @txt_encoding: Character encoding to use when generating XML text
1847 * @format: should formatting spaces been added
1848 *
1849 * Dump the current DOM tree into memory using the character encoding specified
1850 * by the caller. Note it is up to the caller of this function to free the
1851 * allocated memory with xmlFree().
1852 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1853 * or xmlKeepBlanksDefault(0) was called
1854 */
1855
1856void
1857xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1858 int * doc_txt_len, const char * txt_encoding,
1859 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001860 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001861 int dummy = 0;
1862 xmlOutputBufferPtr out_buff = NULL;
1863 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1864
1865 if (doc_txt_len == NULL) {
1866 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1867 }
1868
1869 if (doc_txt_ptr == NULL) {
1870 *doc_txt_len = 0;
1871 return;
1872 }
1873
1874 *doc_txt_ptr = NULL;
1875 *doc_txt_len = 0;
1876
1877 if (out_doc == NULL) {
1878 /* No document, no output */
1879 return;
1880 }
1881
1882 /*
1883 * Validate the encoding value, if provided.
1884 * This logic is copied from xmlSaveFileEnc.
1885 */
1886
1887 if (txt_encoding == NULL)
1888 txt_encoding = (const char *) out_doc->encoding;
1889 if (txt_encoding != NULL) {
1890 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1891 if ( conv_hdlr == NULL ) {
1892 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1893 txt_encoding);
1894 return;
1895 }
1896 }
1897
1898 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1899 xmlSaveErrMemory("creating buffer");
1900 return;
1901 }
1902
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001903 memset(&ctxt, 0, sizeof(ctxt));
1904 ctxt.doc = out_doc;
1905 ctxt.buf = out_buff;
1906 ctxt.level = 0;
1907 ctxt.format = format;
1908 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001909 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001910 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001911 xmlOutputBufferFlush(out_buff);
1912 if (out_buff->conv != NULL) {
1913 *doc_txt_len = out_buff->conv->use;
1914 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1915 } else {
1916 *doc_txt_len = out_buff->buffer->use;
1917 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1918 }
1919 (void)xmlOutputBufferClose(out_buff);
1920
1921 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1922 *doc_txt_len = 0;
1923 xmlSaveErrMemory("creating output");
1924 }
1925
1926 return;
1927}
1928
1929/**
1930 * xmlDocDumpMemory:
1931 * @cur: the document
1932 * @mem: OUT: the memory pointer
1933 * @size: OUT: the memory length
1934 *
1935 * Dump an XML document in memory and return the #xmlChar * and it's size
1936 * in bytes. It's up to the caller to free the memory with xmlFree().
1937 * The resulting byte array is zero terminated, though the last 0 is not
1938 * included in the returned size.
1939 */
1940void
1941xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1942 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1943}
1944
1945/**
1946 * xmlDocDumpFormatMemory:
1947 * @cur: the document
1948 * @mem: OUT: the memory pointer
1949 * @size: OUT: the memory length
1950 * @format: should formatting spaces been added
1951 *
1952 *
1953 * Dump an XML document in memory and return the #xmlChar * and it's size.
1954 * It's up to the caller to free the memory with xmlFree().
1955 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1956 * or xmlKeepBlanksDefault(0) was called
1957 */
1958void
1959xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1960 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1961}
1962
1963/**
1964 * xmlDocDumpMemoryEnc:
1965 * @out_doc: Document to generate XML text from
1966 * @doc_txt_ptr: Memory pointer for allocated XML text
1967 * @doc_txt_len: Length of the generated XML text
1968 * @txt_encoding: Character encoding to use when generating XML text
1969 *
1970 * Dump the current DOM tree into memory using the character encoding specified
1971 * by the caller. Note it is up to the caller of this function to free the
1972 * allocated memory with xmlFree().
1973 */
1974
1975void
1976xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1977 int * doc_txt_len, const char * txt_encoding) {
1978 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1979 txt_encoding, 0);
1980}
1981
1982/**
1983 * xmlDocFormatDump:
1984 * @f: the FILE*
1985 * @cur: the document
1986 * @format: should formatting spaces been added
1987 *
1988 * Dump an XML document to an open FILE.
1989 *
1990 * returns: the number of bytes written or -1 in case of failure.
1991 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1992 * or xmlKeepBlanksDefault(0) was called
1993 */
1994int
1995xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001996 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001997 xmlOutputBufferPtr buf;
1998 const char * encoding;
1999 xmlCharEncodingHandlerPtr handler = NULL;
2000 int ret;
2001
2002 if (cur == NULL) {
2003#ifdef DEBUG_TREE
2004 xmlGenericError(xmlGenericErrorContext,
2005 "xmlDocDump : document == NULL\n");
2006#endif
2007 return(-1);
2008 }
2009 encoding = (const char *) cur->encoding;
2010
2011 if (encoding != NULL) {
2012 handler = xmlFindCharEncodingHandler(encoding);
2013 if (handler == NULL) {
2014 xmlFree((char *) cur->encoding);
2015 cur->encoding = NULL;
2016 }
2017 }
2018 buf = xmlOutputBufferCreateFile(f, handler);
2019 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002020 memset(&ctxt, 0, sizeof(ctxt));
2021 ctxt.doc = cur;
2022 ctxt.buf = buf;
2023 ctxt.level = 0;
2024 ctxt.format = format;
2025 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002026 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002027 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002028
2029 ret = xmlOutputBufferClose(buf);
2030 return(ret);
2031}
2032
2033/**
2034 * xmlDocDump:
2035 * @f: the FILE*
2036 * @cur: the document
2037 *
2038 * Dump an XML document to an open FILE.
2039 *
2040 * returns: the number of bytes written or -1 in case of failure.
2041 */
2042int
2043xmlDocDump(FILE *f, xmlDocPtr cur) {
2044 return(xmlDocFormatDump (f, cur, 0));
2045}
2046
2047/**
2048 * xmlSaveFileTo:
2049 * @buf: an output I/O buffer
2050 * @cur: the document
2051 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2052 *
2053 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002054 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2055 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002056 *
2057 * returns: the number of bytes written or -1 in case of failure.
2058 */
2059int
2060xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002061 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002062 int ret;
2063
Daniel Veillard3d97e662004-11-04 10:49:00 +00002064 if (buf == NULL) return(-1);
2065 if (cur == NULL) {
2066 xmlOutputBufferClose(buf);
2067 return(-1);
2068 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002069 memset(&ctxt, 0, sizeof(ctxt));
2070 ctxt.doc = cur;
2071 ctxt.buf = buf;
2072 ctxt.level = 0;
2073 ctxt.format = 0;
2074 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002075 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002076 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002077 ret = xmlOutputBufferClose(buf);
2078 return(ret);
2079}
2080
2081/**
2082 * xmlSaveFormatFileTo:
2083 * @buf: an output I/O buffer
2084 * @cur: the document
2085 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2086 * @format: should formatting spaces been added
2087 *
2088 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002089 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2090 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002091 *
2092 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002093 */
2094int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002095xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2096 const char *encoding, int format)
2097{
2098 xmlSaveCtxt ctxt;
2099 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002100
Daniel Veillard3d97e662004-11-04 10:49:00 +00002101 if (buf == NULL) return(-1);
Daniel Veillardce244ad2004-11-05 10:03:46 +00002102 if ((cur == NULL) ||
2103 ((cur->type != XML_DOCUMENT_NODE) &&
2104 (cur->type != XML_HTML_DOCUMENT_NODE))) {
Daniel Veillard3d97e662004-11-04 10:49:00 +00002105 xmlOutputBufferClose(buf);
2106 return(-1);
2107 }
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 Veillard32b7cdb2004-03-15 13:46:37 +00002115 xmlDocContentDumpOutput(&ctxt, cur);
2116 ret = xmlOutputBufferClose(buf);
2117 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002118}
2119
2120/**
2121 * xmlSaveFormatFileEnc:
2122 * @filename: the filename or URL to output
2123 * @cur: the document being saved
2124 * @encoding: the name of the encoding to use or NULL.
2125 * @format: should formatting spaces be added.
2126 *
2127 * Dump an XML document to a file or an URL.
2128 *
2129 * Returns the number of bytes written or -1 in case of error.
2130 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2131 * or xmlKeepBlanksDefault(0) was called
2132 */
2133int
2134xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2135 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002136 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002137 xmlOutputBufferPtr buf;
2138 xmlCharEncodingHandlerPtr handler = NULL;
2139 int ret;
2140
2141 if (cur == NULL)
2142 return(-1);
2143
2144 if (encoding == NULL)
2145 encoding = (const char *) cur->encoding;
2146
2147 if (encoding != NULL) {
2148
2149 handler = xmlFindCharEncodingHandler(encoding);
2150 if (handler == NULL)
2151 return(-1);
2152 }
2153
2154#ifdef HAVE_ZLIB_H
2155 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2156#endif
2157 /*
2158 * save the content to a temp buffer.
2159 */
2160 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2161 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002162 memset(&ctxt, 0, sizeof(ctxt));
2163 ctxt.doc = cur;
2164 ctxt.buf = buf;
2165 ctxt.level = 0;
2166 ctxt.format = format;
2167 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002168 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002169
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002170 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002171
2172 ret = xmlOutputBufferClose(buf);
2173 return(ret);
2174}
2175
2176
2177/**
2178 * xmlSaveFileEnc:
2179 * @filename: the filename (or URL)
2180 * @cur: the document
2181 * @encoding: the name of an encoding (or NULL)
2182 *
2183 * Dump an XML document, converting it to the given encoding
2184 *
2185 * returns: the number of bytes written or -1 in case of failure.
2186 */
2187int
2188xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2189 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2190}
2191
2192/**
2193 * xmlSaveFormatFile:
2194 * @filename: the filename (or URL)
2195 * @cur: the document
2196 * @format: should formatting spaces been added
2197 *
2198 * Dump an XML document to a file. Will use compression if
2199 * compiled in and enabled. If @filename is "-" the stdout file is
2200 * used. If @format is set then the document will be indented on output.
2201 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2202 * or xmlKeepBlanksDefault(0) was called
2203 *
2204 * returns: the number of bytes written or -1 in case of failure.
2205 */
2206int
2207xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2208 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2209}
2210
2211/**
2212 * xmlSaveFile:
2213 * @filename: the filename (or URL)
2214 * @cur: the document
2215 *
2216 * Dump an XML document to a file. Will use compression if
2217 * compiled in and enabled. If @filename is "-" the stdout file is
2218 * used.
2219 * returns: the number of bytes written or -1 in case of failure.
2220 */
2221int
2222xmlSaveFile(const char *filename, xmlDocPtr cur) {
2223 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2224}
2225
2226#endif /* LIBXML_OUTPUT_ENABLED */
2227