blob: 72ad699476f67bdf4f8d31a2d359c8e939048e80 [file] [log] [blame]
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001/*
2 * xmlsave.c: Implemetation of the document serializer
3 *
4 * See Copyright for the status of this software.
5 *
6 * daniel@veillard.com
7 */
8
9#define IN_LIBXML
10#include "libxml.h"
11
12#include <string.h>
13#include <libxml/xmlmemory.h>
14#include <libxml/parserInternals.h>
15#include <libxml/tree.h>
16#include <libxml/xmlsave.h>
Daniel Veillard1a8741c2004-03-04 13:40:59 +000017
Daniel Veillard753086a2004-03-28 16:12:44 +000018#define MAX_INDENT 60
19
Daniel Veillard656ce942004-04-30 23:11:45 +000020#include <libxml/HTMLtree.h>
21
Daniel Veillard1a8741c2004-03-04 13:40:59 +000022/************************************************************************
23 * *
24 * XHTML detection *
25 * *
26 ************************************************************************/
27#define XHTML_STRICT_PUBLIC_ID BAD_CAST \
28 "-//W3C//DTD XHTML 1.0 Strict//EN"
29#define XHTML_STRICT_SYSTEM_ID BAD_CAST \
30 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
31#define XHTML_FRAME_PUBLIC_ID BAD_CAST \
32 "-//W3C//DTD XHTML 1.0 Frameset//EN"
33#define XHTML_FRAME_SYSTEM_ID BAD_CAST \
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
35#define XHTML_TRANS_PUBLIC_ID BAD_CAST \
36 "-//W3C//DTD XHTML 1.0 Transitional//EN"
37#define XHTML_TRANS_SYSTEM_ID BAD_CAST \
38 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
39
40#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
41/**
42 * xmlIsXHTML:
43 * @systemID: the system identifier
44 * @publicID: the public identifier
45 *
46 * Try to find if the document correspond to an XHTML DTD
47 *
48 * Returns 1 if true, 0 if not and -1 in case of error
49 */
50int
51xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
52 if ((systemID == NULL) && (publicID == NULL))
53 return(-1);
54 if (publicID != NULL) {
55 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
56 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
57 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
58 }
59 if (systemID != NULL) {
60 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
61 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
62 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
63 }
64 return(0);
65}
Daniel Veillard1a8741c2004-03-04 13:40:59 +000066
67#ifdef LIBXML_OUTPUT_ENABLED
68
69#define TODO \
70 xmlGenericError(xmlGenericErrorContext, \
71 "Unimplemented block at %s:%d\n", \
72 __FILE__, __LINE__);
73
74struct _xmlSaveCtxt {
75 void *_private;
76 int type;
77 int fd;
78 const xmlChar *filename;
79 const xmlChar *encoding;
80 xmlCharEncodingHandlerPtr handler;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000081 xmlOutputBufferPtr buf;
82 xmlDocPtr doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000083 int options;
84 int level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000085 int format;
Daniel Veillard3995bc32004-05-15 18:57:31 +000086 char indent[MAX_INDENT + 1]; /* array for indenting output */
Daniel Veillard753086a2004-03-28 16:12:44 +000087 int indent_nr;
88 int indent_size;
Daniel Veillard3995bc32004-05-15 18:57:31 +000089 xmlCharEncodingOutputFunc escape; /* used for element content */
90 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
Daniel Veillard1a8741c2004-03-04 13:40:59 +000091};
92
93/************************************************************************
94 * *
95 * Output error handlers *
96 * *
97 ************************************************************************/
98/**
99 * xmlSaveErrMemory:
100 * @extra: extra informations
101 *
102 * Handle an out of memory condition
103 */
104static void
105xmlSaveErrMemory(const char *extra)
106{
107 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
108}
109
110/**
111 * xmlSaveErr:
112 * @code: the error number
113 * @node: the location of the error.
114 * @extra: extra informations
115 *
116 * Handle an out of memory condition
117 */
118static void
119xmlSaveErr(int code, xmlNodePtr node, const char *extra)
120{
121 const char *msg = NULL;
122
123 switch(code) {
124 case XML_SAVE_NOT_UTF8:
125 msg = "string is not in UTF-8";
126 break;
127 case XML_SAVE_CHAR_INVALID:
128 msg = "invalid character value";
129 break;
130 case XML_SAVE_UNKNOWN_ENCODING:
131 msg = "unknown encoding %s";
132 break;
133 case XML_SAVE_NO_DOCTYPE:
134 msg = "document has no DOCTYPE";
135 break;
136 default:
137 msg = "unexpected error number";
138 }
139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
140}
141
142/************************************************************************
143 * *
Daniel Veillard83a75e02004-05-14 21:50:42 +0000144 * Special escaping routines *
145 * *
146 ************************************************************************/
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000147static unsigned char *
148xmlSerializeHexCharRef(unsigned char *out, int val) {
149 unsigned char *ptr;
150
151 *out++ = '&';
152 *out++ = '#';
153 *out++ = 'x';
154 if (val < 0x10) ptr = out;
155 else if (val < 0x100) ptr = out + 1;
156 else if (val < 0x1000) ptr = out + 2;
157 else if (val < 0x10000) ptr = out + 3;
158 else if (val < 0x100000) ptr = out + 4;
159 else ptr = out + 5;
160 out = ptr + 1;
161 while (val > 0) {
162 switch (val & 0xF) {
163 case 0: *ptr-- = '0'; break;
164 case 1: *ptr-- = '1'; break;
165 case 2: *ptr-- = '2'; break;
166 case 3: *ptr-- = '3'; break;
167 case 4: *ptr-- = '4'; break;
168 case 5: *ptr-- = '5'; break;
169 case 6: *ptr-- = '6'; break;
170 case 7: *ptr-- = '7'; break;
171 case 8: *ptr-- = '8'; break;
172 case 9: *ptr-- = '9'; break;
173 case 0xA: *ptr-- = 'A'; break;
174 case 0xB: *ptr-- = 'B'; break;
175 case 0xC: *ptr-- = 'C'; break;
176 case 0xD: *ptr-- = 'D'; break;
177 case 0xE: *ptr-- = 'E'; break;
178 case 0xF: *ptr-- = 'F'; break;
179 default: *ptr-- = '0'; break;
180 }
181 val >>= 4;
182 }
183 *out++ = ';';
184 *out = 0;
185 return(out);
186}
187
Daniel Veillard83a75e02004-05-14 21:50:42 +0000188/**
189 * xmlEscapeEntities:
190 * @out: a pointer to an array of bytes to store the result
191 * @outlen: the length of @out
192 * @in: a pointer to an array of unescaped UTF-8 bytes
193 * @inlen: the length of @in
194 *
195 * Take a block of UTF-8 chars in and escape them. Used when there is no
196 * encoding specified.
197 *
198 * Returns 0 if success, or -1 otherwise
199 * The value of @inlen after return is the number of octets consumed
200 * if the return value is positive, else unpredictable.
201 * The value of @outlen after return is the number of octets consumed.
202 */
203static int
204xmlEscapeEntities(unsigned char* out, int *outlen,
205 const xmlChar* in, int *inlen) {
206 unsigned char* outstart = out;
207 const unsigned char* base = in;
208 unsigned char* outend = out + *outlen;
209 const unsigned char* inend;
210 int val;
211
212 inend = in + (*inlen);
213
214 while ((in < inend) && (out < outend)) {
215 if (*in == '<') {
216 if (outend - out < 4) break;
217 *out++ = '&';
218 *out++ = 'l';
219 *out++ = 't';
220 *out++ = ';';
221 in++;
222 continue;
223 } else if (*in == '>') {
224 if (outend - out < 4) break;
225 *out++ = '&';
226 *out++ = 'g';
227 *out++ = 't';
228 *out++ = ';';
229 in++;
230 continue;
231 } else if (*in == '&') {
232 if (outend - out < 5) break;
233 *out++ = '&';
234 *out++ = 'a';
235 *out++ = 'm';
236 *out++ = 'p';
237 *out++ = ';';
238 in++;
239 continue;
240 } else if (((*in >= 0x20) && (*in < 0x80)) ||
241 (*in == '\n') || (*in == '\t')) {
242 /*
243 * default case, just copy !
244 */
245 *out++ = *in++;
246 continue;
247 } else if (*in >= 0x80) {
248 /*
249 * We assume we have UTF-8 input.
250 */
Daniel Veillard83a75e02004-05-14 21:50:42 +0000251 if (outend - out < 10) break;
252
253 if (*in < 0xC0) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000254 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000255 in++;
256 goto error;
257 } else if (*in < 0xE0) {
258 if (inend - in < 2) break;
259 val = (in[0]) & 0x1F;
260 val <<= 6;
261 val |= (in[1]) & 0x3F;
262 in += 2;
263 } else if (*in < 0xF0) {
264 if (inend - in < 3) break;
265 val = (in[0]) & 0x0F;
266 val <<= 6;
267 val |= (in[1]) & 0x3F;
268 val <<= 6;
269 val |= (in[2]) & 0x3F;
270 in += 3;
271 } else if (*in < 0xF8) {
272 if (inend - in < 4) break;
273 val = (in[0]) & 0x07;
274 val <<= 6;
275 val |= (in[1]) & 0x3F;
276 val <<= 6;
277 val |= (in[2]) & 0x3F;
278 val <<= 6;
279 val |= (in[3]) & 0x3F;
280 in += 4;
281 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000282 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000283 in++;
284 goto error;
285 }
286 if (!IS_CHAR(val)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000287 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000288 in++;
289 goto error;
290 }
291
292 /*
293 * We could do multiple things here. Just save as a char ref
294 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000295 out = xmlSerializeHexCharRef(out, val);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000296 } else if (IS_BYTE_CHAR(*in)) {
297 if (outend - out < 6) break;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000298 out = xmlSerializeHexCharRef(out, *in++);
Daniel Veillard83a75e02004-05-14 21:50:42 +0000299 } else {
300 xmlGenericError(xmlGenericErrorContext,
301 "xmlEscapeEntities : char out of range\n");
302 in++;
303 goto error;
304 }
305 }
306 *outlen = out - outstart;
307 *inlen = in - base;
308 return(0);
309error:
310 *outlen = out - outstart;
311 *inlen = in - base;
312 return(-1);
313}
314
315/************************************************************************
316 * *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000317 * Allocation and deallocation *
318 * *
319 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000320/**
321 * xmlSaveCtxtInit:
322 * @ctxt: the saving context
323 *
324 * Initialize a saving context
325 */
326static void
327xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
328{
329 int i;
330
331 if (ctxt == NULL) return;
Daniel Veillard3995bc32004-05-15 18:57:31 +0000332 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
333 ctxt->escape = xmlEscapeEntities;
Daniel Veillard753086a2004-03-28 16:12:44 +0000334 if (xmlTreeIndentString == NULL) {
335 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
336 } else {
337 ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
338 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
339 for (i = 0;i < ctxt->indent_nr;i++)
340 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
341 ctxt->indent_size);
342 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
343 }
344}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000345
346/**
347 * xmlFreeSaveCtxt:
348 *
349 * Free a saving context, destroying the ouptut in any remaining buffer
350 */
351static void
352xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
353{
354 if (ctxt == NULL) return;
355 if (ctxt->encoding != NULL)
356 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000357 if (ctxt->buf != NULL)
358 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000359 xmlFree(ctxt);
360}
361
362/**
363 * xmlNewSaveCtxt:
364 *
365 * Create a new saving context
366 *
367 * Returns the new structure or NULL in case of error
368 */
369static xmlSaveCtxtPtr
370xmlNewSaveCtxt(const char *encoding, int options)
371{
372 xmlSaveCtxtPtr ret;
373
374 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
375 if (ret == NULL) {
376 xmlSaveErrMemory("creating saving context");
377 return ( NULL );
378 }
379 memset(ret, 0, sizeof(xmlSaveCtxt));
380 ret->options = options;
381 if (encoding != NULL) {
382 ret->handler = xmlFindCharEncodingHandler(encoding);
383 if (ret->handler == NULL) {
384 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
385 xmlFreeSaveCtxt(ret);
386 return(NULL);
387 }
388 ret->encoding = xmlStrdup((const xmlChar *)encoding);
Daniel Veillard3995bc32004-05-15 18:57:31 +0000389 ret->escape = xmlEscapeEntities;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000390 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000391 xmlSaveCtxtInit(ret);
392
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000393 return(ret);
394}
395
396/************************************************************************
397 * *
398 * Dumping XML tree content to a simple buffer *
399 * *
400 ************************************************************************/
401/**
402 * xmlAttrSerializeContent:
403 * @buf: the XML buffer output
404 * @doc: the document
405 * @attr: the attribute pointer
406 *
407 * Serialize the attribute in the buffer
408 */
409static void
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000410xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000411{
412 xmlNodePtr children;
413
414 children = attr->children;
415 while (children != NULL) {
416 switch (children->type) {
417 case XML_TEXT_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000418 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
419 attr, children->content);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000420 break;
421 case XML_ENTITY_REF_NODE:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000422 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
423 xmlBufferAdd(buf->buffer, children->name,
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000424 xmlStrlen(children->name));
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000425 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000426 break;
427 default:
428 /* should not happen unless we have a badly built tree */
429 break;
430 }
431 children = children->next;
432 }
433}
434
435/************************************************************************
436 * *
437 * Dumping XML tree content to an I/O output buffer *
438 * *
439 ************************************************************************/
440
441#ifdef LIBXML_HTML_ENABLED
442static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000443xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000444#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000445static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
446static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000447void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
Daniel Veillardce244ad2004-11-05 10:03:46 +0000448static void xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000449
450/**
451 * xmlNsDumpOutput:
452 * @buf: the XML buffer output
453 * @cur: a namespace
454 *
455 * Dump a local Namespace definition.
456 * Should be called in the context of attributes dumps.
457 */
458static void
459xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillardce244ad2004-11-05 10:03:46 +0000460 if ((cur == NULL) || (buf == NULL)) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000461 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
462 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
463 return;
464
465 /* Within the context of an element attributes */
466 if (cur->prefix != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000467 xmlOutputBufferWrite(buf, 7, " xmlns:");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000468 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
469 } else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000470 xmlOutputBufferWrite(buf, 6, " xmlns");
471 xmlOutputBufferWrite(buf, 1, "=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000472 xmlBufferWriteQuotedString(buf->buffer, cur->href);
473 }
474}
475
476/**
477 * xmlNsListDumpOutput:
478 * @buf: the XML buffer output
479 * @cur: the first namespace
480 *
481 * Dump a list of local Namespace definitions.
482 * Should be called in the context of attributes dumps.
483 */
484void
485xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
486 while (cur != NULL) {
487 xmlNsDumpOutput(buf, cur);
488 cur = cur->next;
489 }
490}
491
492/**
493 * xmlDtdDumpOutput:
494 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000495 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000496 *
497 * Dump the XML document DTD, if any.
498 */
499static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000500xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
501 xmlOutputBufferPtr buf;
502 int format, level;
503 xmlDocPtr doc;
504
505 if (dtd == NULL) return;
506 if ((ctxt == NULL) || (ctxt->buf == NULL))
507 return;
508 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000509 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000510 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
511 if (dtd->ExternalID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000512 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000513 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000514 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000515 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
516 } else if (dtd->SystemID != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000517 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000518 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
519 }
520 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
Daniel Veillard41c4a752004-09-08 20:55:38 +0000521 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
522 (dtd->pentities == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000523 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000524 return;
525 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000526 xmlOutputBufferWrite(buf, 3, " [\n");
Daniel Veillard41c4a752004-09-08 20:55:38 +0000527 /*
528 * Dump the notations first they are not in the DTD children list
529 * Do this only on a standalone DTD or on the internal subset though.
530 */
531 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
532 (dtd->doc->intSubset == dtd))) {
Daniel Veillardda3b29a2004-08-14 11:15:13 +0000533 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
534 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000535 format = ctxt->format;
536 level = ctxt->level;
537 doc = ctxt->doc;
538 ctxt->format = 0;
539 ctxt->level = -1;
540 ctxt->doc = dtd->doc;
541 xmlNodeListDumpOutput(ctxt, dtd->children);
542 ctxt->format = format;
543 ctxt->level = level;
544 ctxt->doc = doc;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000545 xmlOutputBufferWrite(buf, 2, "]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000546}
547
548/**
549 * xmlAttrDumpOutput:
550 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000551 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000552 *
553 * Dump an XML attribute
554 */
555static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000556xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
557 xmlOutputBufferPtr buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000558
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000559 if (cur == NULL) return;
560 buf = ctxt->buf;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000561 if (buf == NULL) return;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000562 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000563 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
564 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000565 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000566 }
567 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000568 xmlOutputBufferWrite(buf, 2, "=\"");
569 xmlAttrSerializeContent(buf, cur);
570 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000571}
572
573/**
574 * xmlAttrListDumpOutput:
575 * @buf: the XML buffer output
576 * @doc: the document
577 * @cur: the first attribute pointer
578 * @encoding: an optional encoding string
579 *
580 * Dump a list of XML attributes
581 */
582static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000583xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
584 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000585 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000586 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000587 cur = cur->next;
588 }
589}
590
591
592
593/**
594 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000595 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000596 *
597 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000598 */
599static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000600xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000601 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000602
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000603 if (cur == NULL) return;
604 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000605 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000606 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000607 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000608 xmlOutputBufferWrite(buf, ctxt->indent_size *
609 (ctxt->level > ctxt->indent_nr ?
610 ctxt->indent_nr : ctxt->level),
611 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000612 xmlNodeDumpOutputInternal(ctxt, cur);
613 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000614 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000615 }
616 cur = cur->next;
617 }
618}
619
620/**
621 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000622 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000623 *
624 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000625 */
626static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000627xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000628 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000629 xmlNodePtr tmp;
630 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000631 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000632
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000633 if (cur == NULL) return;
634 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000635 if (cur->type == XML_XINCLUDE_START)
636 return;
637 if (cur->type == XML_XINCLUDE_END)
638 return;
Daniel Veillardce244ad2004-11-05 10:03:46 +0000639 if ((cur->type == XML_DOCUMENT_NODE) ||
640 (cur->type == XML_HTML_DOCUMENT_NODE)) {
641 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
642 return;
643 }
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000644 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000645 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000646 return;
647 }
648 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000649 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000650 return;
651 }
652 if (cur->type == XML_ELEMENT_DECL) {
653 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
654 return;
655 }
656 if (cur->type == XML_ATTRIBUTE_DECL) {
657 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
658 return;
659 }
660 if (cur->type == XML_ENTITY_DECL) {
661 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
662 return;
663 }
664 if (cur->type == XML_TEXT_NODE) {
665 if (cur->content != NULL) {
666 if ((cur->name == xmlStringText) ||
667 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000668
Daniel Veillard3995bc32004-05-15 18:57:31 +0000669 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000670 } else {
671 /*
672 * Disable escaping, needed for XSLT
673 */
674 xmlOutputBufferWriteString(buf, (const char *) cur->content);
675 }
676 }
677
678 return;
679 }
680 if (cur->type == XML_PI_NODE) {
681 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000682 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000683 xmlOutputBufferWriteString(buf, (const char *)cur->name);
684 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000685 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000686 xmlOutputBufferWriteString(buf, (const char *)cur->content);
687 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000688 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000689 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000690 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000691 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000692 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000693 }
694 return;
695 }
696 if (cur->type == XML_COMMENT_NODE) {
697 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000698 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000699 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000700 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000701 }
702 return;
703 }
704 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000705 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000706 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000707 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000708 return;
709 }
710 if (cur->type == XML_CDATA_SECTION_NODE) {
711 start = end = cur->content;
712 while (*end != '\0') {
713 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
714 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000715 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000716 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000717 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000718 start = end;
719 }
720 end++;
721 }
722 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000723 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000724 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000725 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000726 }
727 return;
728 }
729 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000730 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000731 return;
732 }
733 if (cur->type == XML_NAMESPACE_DECL) {
734 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
735 return;
736 }
737
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000738 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000739 if (format == 1) {
740 tmp = cur->children;
741 while (tmp != NULL) {
742 if ((tmp->type == XML_TEXT_NODE) ||
743 (tmp->type == XML_CDATA_SECTION_NODE) ||
744 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000745 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000746 break;
747 }
748 tmp = tmp->next;
749 }
750 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000751 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000752 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
753 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000754 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000755 }
756
757 xmlOutputBufferWriteString(buf, (const char *)cur->name);
758 if (cur->nsDef)
759 xmlNsListDumpOutput(buf, cur->nsDef);
760 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000761 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000762
763 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
764 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000765 xmlOutputBufferWrite(buf, 2, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000766 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000767 return;
768 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000769 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000770 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +0000771 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000772 }
773 if (cur->children != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000774 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000775 if (ctxt->level >= 0) ctxt->level++;
776 xmlNodeListDumpOutput(ctxt, cur->children);
777 if (ctxt->level > 0) ctxt->level--;
778 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000779 xmlOutputBufferWrite(buf, ctxt->indent_size *
780 (ctxt->level > ctxt->indent_nr ?
781 ctxt->indent_nr : ctxt->level),
782 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000783 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000784 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000785 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
786 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000787 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000788 }
789
790 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000791 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000792 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000793}
794
795/**
796 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000797 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000798 *
799 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000800 */
801static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000802xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000803#ifdef LIBXML_HTML_ENABLED
804 xmlDtdPtr dtd;
805 int is_xhtml = 0;
806#endif
807 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000808 const xmlChar *encoding = ctxt->encoding;
809 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000810
811 xmlInitParser();
812
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000813 if (ctxt->encoding != NULL)
814 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000815
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000816 buf = ctxt->buf;
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000817 xmlOutputBufferWrite(buf, 14, "<?xml version=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000818 if (cur->version != NULL)
819 xmlBufferWriteQuotedString(buf->buffer, cur->version);
820 else
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000821 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000822 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000823 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000824 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000825 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000826 encoding = (const xmlChar *)
827 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000828 }
829 if (encoding != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000830 xmlOutputBufferWrite(buf, 10, " encoding=");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000831 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
832 }
833 switch (cur->standalone) {
834 case 0:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000835 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000836 break;
837 case 1:
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000838 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000839 break;
840 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000841 xmlOutputBufferWrite(buf, 3, "?>\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000842
843#ifdef LIBXML_HTML_ENABLED
844 dtd = xmlGetIntSubset(cur);
845 if (dtd != NULL) {
846 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
847 if (is_xhtml < 0) is_xhtml = 0;
848 }
849 if (is_xhtml) {
850 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000851 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000852 else
853 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
854 }
855#endif
856 if (cur->children != NULL) {
857 xmlNodePtr child = cur->children;
858
859 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000860 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000861#ifdef LIBXML_HTML_ENABLED
862 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000863 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000864 else
865#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000866 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard7a6361f2004-05-15 16:37:50 +0000867 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000868 child = child->next;
869 }
870 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000871 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000872 cur->encoding = oldenc;
873}
874
875#ifdef LIBXML_HTML_ENABLED
876/************************************************************************
877 * *
878 * Functions specific to XHTML serialization *
879 * *
880 ************************************************************************/
881
882/**
883 * xhtmlIsEmpty:
884 * @node: the node
885 *
886 * Check if a node is an empty xhtml node
887 *
888 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
889 */
890static int
891xhtmlIsEmpty(xmlNodePtr node) {
892 if (node == NULL)
893 return(-1);
894 if (node->type != XML_ELEMENT_NODE)
895 return(0);
896 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
897 return(0);
898 if (node->children != NULL)
899 return(0);
900 switch (node->name[0]) {
901 case 'a':
902 if (xmlStrEqual(node->name, BAD_CAST "area"))
903 return(1);
904 return(0);
905 case 'b':
906 if (xmlStrEqual(node->name, BAD_CAST "br"))
907 return(1);
908 if (xmlStrEqual(node->name, BAD_CAST "base"))
909 return(1);
910 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
911 return(1);
912 return(0);
913 case 'c':
914 if (xmlStrEqual(node->name, BAD_CAST "col"))
915 return(1);
916 return(0);
917 case 'f':
918 if (xmlStrEqual(node->name, BAD_CAST "frame"))
919 return(1);
920 return(0);
921 case 'h':
922 if (xmlStrEqual(node->name, BAD_CAST "hr"))
923 return(1);
924 return(0);
925 case 'i':
926 if (xmlStrEqual(node->name, BAD_CAST "img"))
927 return(1);
928 if (xmlStrEqual(node->name, BAD_CAST "input"))
929 return(1);
930 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
931 return(1);
932 return(0);
933 case 'l':
934 if (xmlStrEqual(node->name, BAD_CAST "link"))
935 return(1);
936 return(0);
937 case 'm':
938 if (xmlStrEqual(node->name, BAD_CAST "meta"))
939 return(1);
940 return(0);
941 case 'p':
942 if (xmlStrEqual(node->name, BAD_CAST "param"))
943 return(1);
944 return(0);
945 }
946 return(0);
947}
948
949/**
950 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000951 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000952 *
953 * Dump a list of XML attributes
954 */
955static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000956xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000957 xmlAttrPtr xml_lang = NULL;
958 xmlAttrPtr lang = NULL;
959 xmlAttrPtr name = NULL;
960 xmlAttrPtr id = NULL;
961 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000962 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000963
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000964 if (cur == NULL) return;
965 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000966 parent = cur->parent;
967 while (cur != NULL) {
968 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
969 id = cur;
970 else
971 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
972 name = cur;
973 else
974 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
975 lang = cur;
976 else
977 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
978 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
979 xml_lang = cur;
980 else if ((cur->ns == NULL) &&
981 ((cur->children == NULL) ||
982 (cur->children->content == NULL) ||
983 (cur->children->content[0] == 0)) &&
984 (htmlIsBooleanAttr(cur->name))) {
985 if (cur->children != NULL)
986 xmlFreeNode(cur->children);
987 cur->children = xmlNewText(cur->name);
988 if (cur->children != NULL)
989 cur->children->parent = (xmlNodePtr) cur;
990 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000991 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000992 cur = cur->next;
993 }
994 /*
995 * C.8
996 */
997 if ((name != NULL) && (id == NULL)) {
998 if ((parent != NULL) && (parent->name != NULL) &&
999 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1000 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1001 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1002 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1003 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1004 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1005 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1006 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1007 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001008 xmlOutputBufferWrite(buf, 5, " id=\"");
1009 xmlAttrSerializeContent(buf, name);
1010 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001011 }
1012 }
1013 /*
1014 * C.7.
1015 */
1016 if ((lang != NULL) && (xml_lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001017 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1018 xmlAttrSerializeContent(buf, lang);
1019 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001020 } else
1021 if ((xml_lang != NULL) && (lang == NULL)) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001022 xmlOutputBufferWrite(buf, 7, " lang=\"");
1023 xmlAttrSerializeContent(buf, xml_lang);
1024 xmlOutputBufferWrite(buf, 1, "\"");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001025 }
1026}
1027
1028/**
1029 * xhtmlNodeListDumpOutput:
1030 * @buf: the XML buffer output
1031 * @doc: the XHTML document
1032 * @cur: the first node
1033 * @level: the imbrication level for indenting
1034 * @format: is formatting allowed
1035 * @encoding: an optional encoding string
1036 *
1037 * Dump an XML node list, recursive behaviour, children are printed too.
1038 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1039 * or xmlKeepBlanksDefault(0) was called
1040 */
1041static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001042xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001043 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001044
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001045 if (cur == NULL) return;
1046 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001047 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001048 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001049 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001050 xmlOutputBufferWrite(buf, ctxt->indent_size *
1051 (ctxt->level > ctxt->indent_nr ?
1052 ctxt->indent_nr : ctxt->level),
1053 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001054 xhtmlNodeDumpOutput(ctxt, cur);
1055 if (ctxt->format) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001056 xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001057 }
1058 cur = cur->next;
1059 }
1060}
1061
1062/**
1063 * xhtmlNodeDumpOutput:
1064 * @buf: the XML buffer output
1065 * @doc: the XHTML document
1066 * @cur: the current node
1067 * @level: the imbrication level for indenting
1068 * @format: is formatting allowed
1069 * @encoding: an optional encoding string
1070 *
1071 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001072 */
1073static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001074xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +00001075 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001076 xmlNodePtr tmp;
1077 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001078 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001079
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001080 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001081 if (cur->type == XML_XINCLUDE_START)
1082 return;
1083 if (cur->type == XML_XINCLUDE_END)
1084 return;
1085 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001086 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001087 return;
1088 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001089 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001090 if (cur->type == XML_ELEMENT_DECL) {
1091 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1092 return;
1093 }
1094 if (cur->type == XML_ATTRIBUTE_DECL) {
1095 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1096 return;
1097 }
1098 if (cur->type == XML_ENTITY_DECL) {
1099 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1100 return;
1101 }
1102 if (cur->type == XML_TEXT_NODE) {
1103 if (cur->content != NULL) {
1104 if ((cur->name == xmlStringText) ||
1105 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001106 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001107 } else {
1108 /*
1109 * Disable escaping, needed for XSLT
1110 */
1111 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1112 }
1113 }
1114
1115 return;
1116 }
1117 if (cur->type == XML_PI_NODE) {
1118 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001119 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001120 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1121 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001122 xmlOutputBufferWrite(buf, 1, " ");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001123 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1124 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001125 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001126 } else {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001127 xmlOutputBufferWrite(buf, 2, "<?");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001128 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001129 xmlOutputBufferWrite(buf, 2, "?>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001130 }
1131 return;
1132 }
1133 if (cur->type == XML_COMMENT_NODE) {
1134 if (cur->content != NULL) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001135 xmlOutputBufferWrite(buf, 4, "<!--");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001136 xmlOutputBufferWriteString(buf, (const char *)cur->content);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001137 xmlOutputBufferWrite(buf, 3, "-->");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001138 }
1139 return;
1140 }
1141 if (cur->type == XML_ENTITY_REF_NODE) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001142 xmlOutputBufferWrite(buf, 1, "&");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001143 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001144 xmlOutputBufferWrite(buf, 1, ";");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001145 return;
1146 }
1147 if (cur->type == XML_CDATA_SECTION_NODE) {
1148 start = end = cur->content;
1149 while (*end != '\0') {
1150 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1151 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001152 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001153 xmlOutputBufferWrite(buf, end - start, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001154 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001155 start = end;
1156 }
1157 end++;
1158 }
1159 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001160 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001161 xmlOutputBufferWriteString(buf, (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001162 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001163 }
1164 return;
1165 }
1166
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001167 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001168 if (format == 1) {
1169 tmp = cur->children;
1170 while (tmp != NULL) {
1171 if ((tmp->type == XML_TEXT_NODE) ||
1172 (tmp->type == XML_ENTITY_REF_NODE)) {
1173 format = 0;
1174 break;
1175 }
1176 tmp = tmp->next;
1177 }
1178 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001179 xmlOutputBufferWrite(buf, 1, "<");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001180 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1181 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001182 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001183 }
1184
1185 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1186 if (cur->nsDef)
1187 xmlNsListDumpOutput(buf, cur->nsDef);
1188 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1189 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1190 /*
1191 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1192 */
1193 xmlOutputBufferWriteString(buf,
1194 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1195 }
1196 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001197 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001198
1199 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1200 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1201 (xhtmlIsEmpty(cur) == 1)) {
1202 /*
1203 * C.2. Empty Elements
1204 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001205 xmlOutputBufferWrite(buf, 3, " />");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001206 } else {
1207 /*
1208 * C.3. Element Minimization and Empty Element Content
1209 */
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001210 xmlOutputBufferWrite(buf, 3, "></");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001211 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1212 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001213 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001214 }
1215 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001216 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001217 }
1218 return;
1219 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001220 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001221 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard3995bc32004-05-15 18:57:31 +00001222 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001223 }
1224
1225 /*
1226 * 4.8. Script and Style elements
1227 */
1228 if ((cur->type == XML_ELEMENT_NODE) &&
1229 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1230 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1231 ((cur->ns == NULL) ||
1232 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1233 xmlNodePtr child = cur->children;
1234
1235 while (child != NULL) {
1236 if ((child->type == XML_TEXT_NODE) ||
1237 (child->type == XML_CDATA_SECTION_NODE)) {
1238 /*
1239 * Apparently CDATA escaping for style just break on IE,
1240 * mozilla and galeon, so ...
1241 */
1242 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1243 (xmlStrchr(child->content, '<') == NULL) &&
1244 (xmlStrchr(child->content, '>') == NULL) &&
1245 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001246 int level = ctxt->level;
1247 int indent = ctxt->format;
1248
1249 ctxt->level = 0;
1250 ctxt->format = 0;
1251 xhtmlNodeDumpOutput(ctxt, child);
1252 ctxt->level = level;
1253 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001254 } else {
1255 start = end = child->content;
1256 while (*end != '\0') {
1257 if (*end == ']' &&
1258 *(end + 1) == ']' &&
1259 *(end + 2) == '>') {
1260 end = end + 2;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001261 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001262 xmlOutputBufferWrite(buf, end - start,
1263 (const char *)start);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001264 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001265 start = end;
1266 }
1267 end++;
1268 }
1269 if (start != end) {
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001270 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1271 xmlOutputBufferWrite(buf, end - start,
1272 (const char *)start);
1273 xmlOutputBufferWrite(buf, 3, "]]>");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001274 }
1275 }
1276 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001277 int level = ctxt->level;
1278 int indent = ctxt->format;
1279
1280 ctxt->level = 0;
1281 ctxt->format = 0;
1282 xhtmlNodeDumpOutput(ctxt, child);
1283 ctxt->level = level;
1284 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001285 }
1286 child = child->next;
1287 }
1288 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001289 int indent = ctxt->format;
1290
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001291 if (format) xmlOutputBufferWrite(buf, 1, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001292 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001293 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001294 xhtmlNodeListDumpOutput(ctxt, cur->children);
1295 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001296 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001297 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001298 xmlOutputBufferWrite(buf, ctxt->indent_size *
1299 (ctxt->level > ctxt->indent_nr ?
1300 ctxt->indent_nr : ctxt->level),
1301 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001302 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001303 xmlOutputBufferWrite(buf, 2, "</");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001304 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1305 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001306 xmlOutputBufferWrite(buf, 1, ":");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001307 }
1308
1309 xmlOutputBufferWriteString(buf, (const char *)cur->name);
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001310 xmlOutputBufferWrite(buf, 1, ">");
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001311}
1312#endif
1313
1314/************************************************************************
1315 * *
1316 * Public entry points *
1317 * *
1318 ************************************************************************/
1319
1320/**
1321 * xmlSaveToFd:
1322 * @fd: a file descriptor number
1323 * @encoding: the encoding name to use or NULL
1324 * @options: a set of xmlSaveOptions
1325 *
1326 * Create a document saving context serializing to a file descriptor
1327 * with the encoding and the options given.
1328 *
1329 * Returns a new serialization context or NULL in case of error.
1330 */
1331xmlSaveCtxtPtr
1332xmlSaveToFd(int fd, const char *encoding, int options)
1333{
1334 xmlSaveCtxtPtr ret;
1335
1336 ret = xmlNewSaveCtxt(encoding, options);
1337 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001338 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1339 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001340 xmlFreeSaveCtxt(ret);
1341 return(NULL);
1342 }
1343 return(ret);
1344}
1345
1346/**
1347 * xmlSaveToFilename:
1348 * @filename: a file name or an URL
1349 * @encoding: the encoding name to use or NULL
1350 * @options: a set of xmlSaveOptions
1351 *
1352 * Create a document saving context serializing to a filename or possibly
1353 * to an URL (but this is less reliable) with the encoding and the options
1354 * given.
1355 *
1356 * Returns a new serialization context or NULL in case of error.
1357 */
1358xmlSaveCtxtPtr
1359xmlSaveToFilename(const char *filename, const char *encoding, int options)
1360{
1361 xmlSaveCtxtPtr ret;
1362 int compression = 0; /* TODO handle compression option */
1363
1364 ret = xmlNewSaveCtxt(encoding, options);
1365 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001366 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001367 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001368 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001369 xmlFreeSaveCtxt(ret);
1370 return(NULL);
1371 }
1372 return(ret);
1373}
1374
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001375/**
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.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001385xmlSaveCtxtPtr
1386xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1387{
1388 TODO
1389 return(NULL);
1390}
Daniel Veillarda2351322004-06-27 12:08:10 +00001391 */
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001392
1393/**
1394 * xmlSaveToIO:
1395 * @iowrite: an I/O write function
1396 * @ioclose: an I/O close function
1397 * @ioctx: an I/O handler
1398 * @encoding: the encoding name to use or NULL
1399 * @options: a set of xmlSaveOptions
1400 *
1401 * Create a document saving context serializing to a file descriptor
1402 * with the encoding and the options given
1403 *
1404 * Returns a new serialization context or NULL in case of error.
1405 */
1406xmlSaveCtxtPtr
1407xmlSaveToIO(xmlOutputWriteCallback iowrite,
1408 xmlOutputCloseCallback ioclose,
1409 void *ioctx, const char *encoding, int options)
1410{
1411 xmlSaveCtxtPtr ret;
1412
1413 ret = xmlNewSaveCtxt(encoding, options);
1414 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001415 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1416 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001417 xmlFreeSaveCtxt(ret);
1418 return(NULL);
1419 }
1420 return(ret);
1421}
1422
1423/**
1424 * xmlSaveDoc:
1425 * @ctxt: a document saving context
1426 * @doc: a document
1427 *
1428 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001429 * TODO: The function is not fully implemented yet as it does not return the
1430 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001431 *
1432 * Returns the number of byte written or -1 in case of error
1433 */
1434long
1435xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1436{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001437 long ret = 0;
1438
Daniel Veillardce682bc2004-11-05 17:22:25 +00001439 if ((ctxt == NULL) || (doc == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001440 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
Daniel Veillardce682bc2004-11-05 17:22:25 +00001460 if ((ctxt == NULL) || (node == NULL)) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001461 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001462 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001463}
1464
1465/**
1466 * xmlSaveFlush:
1467 * @ctxt: a document saving context
1468 *
1469 * Flush a document saving context, i.e. make sure that all bytes have
1470 * been output.
1471 *
1472 * Returns the number of byte written or -1 in case of error.
1473 */
1474int
1475xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1476{
1477 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001478 if (ctxt->buf == NULL) return(-1);
1479 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001480}
1481
1482/**
1483 * xmlSaveClose:
1484 * @ctxt: a document saving context
1485 *
1486 * Close a document saving context, i.e. make sure that all bytes have
1487 * been output and free the associated data.
1488 *
1489 * Returns the number of byte written or -1 in case of error.
1490 */
1491int
1492xmlSaveClose(xmlSaveCtxtPtr ctxt)
1493{
1494 int ret;
1495
1496 if (ctxt == NULL) return(-1);
1497 ret = xmlSaveFlush(ctxt);
1498 xmlFreeSaveCtxt(ctxt);
1499 return(ret);
1500}
1501
Daniel Veillard3995bc32004-05-15 18:57:31 +00001502/**
1503 * xmlSaveSetEscape:
1504 * @ctxt: a document saving context
1505 * @escape: the escaping function
1506 *
1507 * Set a custom escaping function to be used for text in element content
1508 *
1509 * Returns 0 if successful or -1 in case of error.
1510 */
1511int
1512xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1513{
1514 if (ctxt == NULL) return(-1);
1515 ctxt->escape = escape;
1516 return(0);
1517}
1518
1519/**
1520 * xmlSaveSetAttrEscape:
1521 * @ctxt: a document saving context
1522 * @escape: the escaping function
1523 *
1524 * Set a custom escaping function to be used for text in attribute content
1525 *
1526 * Returns 0 if successful or -1 in case of error.
1527 */
1528int
1529xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1530{
1531 if (ctxt == NULL) return(-1);
1532 ctxt->escapeAttr = escape;
1533 return(0);
1534}
1535
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001536/************************************************************************
1537 * *
1538 * Public entry points based on buffers *
1539 * *
1540 ************************************************************************/
1541/**
1542 * xmlAttrSerializeTxtContent:
1543 * @buf: the XML buffer output
1544 * @doc: the document
1545 * @attr: the attribute node
1546 * @string: the text content
1547 *
1548 * Serialize text attribute values to an xml simple buffer
1549 */
1550void
1551xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001552 xmlAttrPtr attr, const xmlChar * string)
1553{
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001554 xmlChar *base, *cur;
1555
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001556 if (string == NULL)
1557 return;
1558 base = cur = (xmlChar *) string;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001559 while (*cur != 0) {
1560 if (*cur == '\n') {
1561 if (base != cur)
1562 xmlBufferAdd(buf, base, cur - base);
1563 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1564 cur++;
1565 base = cur;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001566 } else if (*cur == '\r') {
1567 if (base != cur)
1568 xmlBufferAdd(buf, base, cur - base);
1569 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1570 cur++;
1571 base = cur;
1572 } else if (*cur == '\t') {
1573 if (base != cur)
1574 xmlBufferAdd(buf, base, cur - base);
1575 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1576 cur++;
1577 base = cur;
1578 } else if (*cur == '"') {
1579 if (base != cur)
1580 xmlBufferAdd(buf, base, cur - base);
1581 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1582 cur++;
1583 base = cur;
1584 } else if (*cur == '<') {
1585 if (base != cur)
1586 xmlBufferAdd(buf, base, cur - base);
1587 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1588 cur++;
1589 base = cur;
1590 } else if (*cur == '>') {
1591 if (base != cur)
1592 xmlBufferAdd(buf, base, cur - base);
1593 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1594 cur++;
1595 base = cur;
1596 } else if (*cur == '&') {
1597 if (base != cur)
1598 xmlBufferAdd(buf, base, cur - base);
1599 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1600 cur++;
1601 base = cur;
1602 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1603 (doc->encoding == NULL))) {
1604 /*
1605 * We assume we have UTF-8 content.
1606 */
1607 unsigned char tmp[10];
1608 int val = 0, l = 1;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001609
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001610 if (base != cur)
1611 xmlBufferAdd(buf, base, cur - base);
1612 if (*cur < 0xC0) {
1613 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1614 if (doc != NULL)
1615 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1616 xmlSerializeHexCharRef(tmp, *cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001617 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001618 cur++;
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001619 base = cur;
1620 continue;
1621 } else if (*cur < 0xE0) {
1622 val = (cur[0]) & 0x1F;
1623 val <<= 6;
1624 val |= (cur[1]) & 0x3F;
1625 l = 2;
1626 } else if (*cur < 0xF0) {
1627 val = (cur[0]) & 0x0F;
1628 val <<= 6;
1629 val |= (cur[1]) & 0x3F;
1630 val <<= 6;
1631 val |= (cur[2]) & 0x3F;
1632 l = 3;
1633 } else if (*cur < 0xF8) {
1634 val = (cur[0]) & 0x07;
1635 val <<= 6;
1636 val |= (cur[1]) & 0x3F;
1637 val <<= 6;
1638 val |= (cur[2]) & 0x3F;
1639 val <<= 6;
1640 val |= (cur[3]) & 0x3F;
1641 l = 4;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001642 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001643 if ((l == 1) || (!IS_CHAR(val))) {
1644 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1645 if (doc != NULL)
1646 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1647
1648 xmlSerializeHexCharRef(tmp, *cur);
1649 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1650 cur++;
1651 base = cur;
1652 continue;
1653 }
1654 /*
1655 * We could do multiple things here. Just save
1656 * as a char ref
1657 */
1658 xmlSerializeHexCharRef(tmp, val);
1659 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1660 cur += l;
1661 base = cur;
1662 } else {
1663 cur++;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001664 }
Daniel Veillard7a6361f2004-05-15 16:37:50 +00001665 }
1666 if (base != cur)
1667 xmlBufferAdd(buf, base, cur - base);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001668}
1669
1670/**
1671 * xmlNodeDump:
1672 * @buf: the XML buffer output
1673 * @doc: the document
1674 * @cur: the current node
1675 * @level: the imbrication level for indenting
1676 * @format: is formatting allowed
1677 *
1678 * Dump an XML node, recursive behaviour,children are printed too.
1679 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1680 * or xmlKeepBlanksDefault(0) was called
1681 *
1682 * Returns the number of bytes written to the buffer or -1 in case of error
1683 */
1684int
1685xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1686 int format)
1687{
1688 unsigned int use;
1689 int ret;
1690 xmlOutputBufferPtr outbuf;
1691
1692 xmlInitParser();
1693
1694 if (cur == NULL) {
1695#ifdef DEBUG_TREE
1696 xmlGenericError(xmlGenericErrorContext,
1697 "xmlNodeDump : node == NULL\n");
1698#endif
1699 return (-1);
1700 }
1701 if (buf == NULL) {
1702#ifdef DEBUG_TREE
1703 xmlGenericError(xmlGenericErrorContext,
1704 "xmlNodeDump : buf == NULL\n");
1705#endif
1706 return (-1);
1707 }
1708 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1709 if (outbuf == NULL) {
1710 xmlSaveErrMemory("creating buffer");
1711 return (-1);
1712 }
1713 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1714 outbuf->buffer = buf;
1715 outbuf->encoder = NULL;
1716 outbuf->writecallback = NULL;
1717 outbuf->closecallback = NULL;
1718 outbuf->context = NULL;
1719 outbuf->written = 0;
1720
1721 use = buf->use;
1722 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1723 xmlFree(outbuf);
1724 ret = buf->use - use;
1725 return (ret);
1726}
1727
1728/**
1729 * xmlElemDump:
1730 * @f: the FILE * for the output
1731 * @doc: the document
1732 * @cur: the current node
1733 *
1734 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1735 */
1736void
1737xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1738{
1739 xmlOutputBufferPtr outbuf;
1740
1741 xmlInitParser();
1742
1743 if (cur == NULL) {
1744#ifdef DEBUG_TREE
1745 xmlGenericError(xmlGenericErrorContext,
1746 "xmlElemDump : cur == NULL\n");
1747#endif
1748 return;
1749 }
1750#ifdef DEBUG_TREE
1751 if (doc == NULL) {
1752 xmlGenericError(xmlGenericErrorContext,
1753 "xmlElemDump : doc == NULL\n");
1754 }
1755#endif
1756
1757 outbuf = xmlOutputBufferCreateFile(f, NULL);
1758 if (outbuf == NULL)
1759 return;
1760 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1761#ifdef LIBXML_HTML_ENABLED
1762 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1763#else
1764 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1765#endif /* LIBXML_HTML_ENABLED */
1766 } else
1767 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1768 xmlOutputBufferClose(outbuf);
1769}
1770
1771/************************************************************************
1772 * *
1773 * Saving functions front-ends *
1774 * *
1775 ************************************************************************/
1776
1777/**
1778 * xmlNodeDumpOutput:
1779 * @buf: the XML buffer output
1780 * @doc: the document
1781 * @cur: the current node
1782 * @level: the imbrication level for indenting
1783 * @format: is formatting allowed
1784 * @encoding: an optional encoding string
1785 *
1786 * Dump an XML node, recursive behaviour, children are printed too.
1787 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1788 * or xmlKeepBlanksDefault(0) was called
1789 */
1790void
1791xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1792 int level, int format, const char *encoding)
1793{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001794 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001795#ifdef LIBXML_HTML_ENABLED
1796 xmlDtdPtr dtd;
1797 int is_xhtml = 0;
1798#endif
1799
1800 xmlInitParser();
1801
Daniel Veillardce244ad2004-11-05 10:03:46 +00001802 if ((buf == NULL) || (cur == NULL)) return;
1803
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001804 memset(&ctxt, 0, sizeof(ctxt));
1805 ctxt.doc = doc;
1806 ctxt.buf = buf;
1807 ctxt.level = level;
1808 ctxt.format = format;
1809 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001810 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001811
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001812#ifdef LIBXML_HTML_ENABLED
1813 dtd = xmlGetIntSubset(doc);
1814 if (dtd != NULL) {
1815 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1816 if (is_xhtml < 0)
1817 is_xhtml = 0;
1818 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1819 (cur->type == XML_ELEMENT_NODE) &&
1820 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1821 if (encoding != NULL)
1822 htmlSetMetaEncoding((htmlDocPtr) doc,
1823 (const xmlChar *) encoding);
1824 else
1825 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1826 }
1827 }
1828
1829 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001830 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001831 else
1832#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001833 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001834}
1835
1836/**
1837 * xmlDocDumpFormatMemoryEnc:
1838 * @out_doc: Document to generate XML text from
1839 * @doc_txt_ptr: Memory pointer for allocated XML text
1840 * @doc_txt_len: Length of the generated XML text
1841 * @txt_encoding: Character encoding to use when generating XML text
1842 * @format: should formatting spaces been added
1843 *
1844 * Dump the current DOM tree into memory using the character encoding specified
1845 * by the caller. Note it is up to the caller of this function to free the
1846 * allocated memory with xmlFree().
1847 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1848 * or xmlKeepBlanksDefault(0) was called
1849 */
1850
1851void
1852xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1853 int * doc_txt_len, const char * txt_encoding,
1854 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001855 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001856 int dummy = 0;
1857 xmlOutputBufferPtr out_buff = NULL;
1858 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1859
1860 if (doc_txt_len == NULL) {
1861 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1862 }
1863
1864 if (doc_txt_ptr == NULL) {
1865 *doc_txt_len = 0;
1866 return;
1867 }
1868
1869 *doc_txt_ptr = NULL;
1870 *doc_txt_len = 0;
1871
1872 if (out_doc == NULL) {
1873 /* No document, no output */
1874 return;
1875 }
1876
1877 /*
1878 * Validate the encoding value, if provided.
1879 * This logic is copied from xmlSaveFileEnc.
1880 */
1881
1882 if (txt_encoding == NULL)
1883 txt_encoding = (const char *) out_doc->encoding;
1884 if (txt_encoding != NULL) {
1885 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1886 if ( conv_hdlr == NULL ) {
1887 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1888 txt_encoding);
1889 return;
1890 }
1891 }
1892
1893 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1894 xmlSaveErrMemory("creating buffer");
1895 return;
1896 }
1897
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001898 memset(&ctxt, 0, sizeof(ctxt));
1899 ctxt.doc = out_doc;
1900 ctxt.buf = out_buff;
1901 ctxt.level = 0;
1902 ctxt.format = format;
1903 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001904 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001905 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001906 xmlOutputBufferFlush(out_buff);
1907 if (out_buff->conv != NULL) {
1908 *doc_txt_len = out_buff->conv->use;
1909 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1910 } else {
1911 *doc_txt_len = out_buff->buffer->use;
1912 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1913 }
1914 (void)xmlOutputBufferClose(out_buff);
1915
1916 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1917 *doc_txt_len = 0;
1918 xmlSaveErrMemory("creating output");
1919 }
1920
1921 return;
1922}
1923
1924/**
1925 * xmlDocDumpMemory:
1926 * @cur: the document
1927 * @mem: OUT: the memory pointer
1928 * @size: OUT: the memory length
1929 *
1930 * Dump an XML document in memory and return the #xmlChar * and it's size
1931 * in bytes. It's up to the caller to free the memory with xmlFree().
1932 * The resulting byte array is zero terminated, though the last 0 is not
1933 * included in the returned size.
1934 */
1935void
1936xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1937 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1938}
1939
1940/**
1941 * xmlDocDumpFormatMemory:
1942 * @cur: the document
1943 * @mem: OUT: the memory pointer
1944 * @size: OUT: the memory length
1945 * @format: should formatting spaces been added
1946 *
1947 *
1948 * Dump an XML document in memory and return the #xmlChar * and it's size.
1949 * It's up to the caller to free the memory with xmlFree().
1950 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1951 * or xmlKeepBlanksDefault(0) was called
1952 */
1953void
1954xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1955 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1956}
1957
1958/**
1959 * xmlDocDumpMemoryEnc:
1960 * @out_doc: Document to generate XML text from
1961 * @doc_txt_ptr: Memory pointer for allocated XML text
1962 * @doc_txt_len: Length of the generated XML text
1963 * @txt_encoding: Character encoding to use when generating XML text
1964 *
1965 * Dump the current DOM tree into memory using the character encoding specified
1966 * by the caller. Note it is up to the caller of this function to free the
1967 * allocated memory with xmlFree().
1968 */
1969
1970void
1971xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1972 int * doc_txt_len, const char * txt_encoding) {
1973 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1974 txt_encoding, 0);
1975}
1976
1977/**
1978 * xmlDocFormatDump:
1979 * @f: the FILE*
1980 * @cur: the document
1981 * @format: should formatting spaces been added
1982 *
1983 * Dump an XML document to an open FILE.
1984 *
1985 * returns: the number of bytes written or -1 in case of failure.
1986 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1987 * or xmlKeepBlanksDefault(0) was called
1988 */
1989int
1990xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001991 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001992 xmlOutputBufferPtr buf;
1993 const char * encoding;
1994 xmlCharEncodingHandlerPtr handler = NULL;
1995 int ret;
1996
1997 if (cur == NULL) {
1998#ifdef DEBUG_TREE
1999 xmlGenericError(xmlGenericErrorContext,
2000 "xmlDocDump : document == NULL\n");
2001#endif
2002 return(-1);
2003 }
2004 encoding = (const char *) cur->encoding;
2005
2006 if (encoding != NULL) {
2007 handler = xmlFindCharEncodingHandler(encoding);
2008 if (handler == NULL) {
2009 xmlFree((char *) cur->encoding);
2010 cur->encoding = NULL;
2011 }
2012 }
2013 buf = xmlOutputBufferCreateFile(f, handler);
2014 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002015 memset(&ctxt, 0, sizeof(ctxt));
2016 ctxt.doc = cur;
2017 ctxt.buf = buf;
2018 ctxt.level = 0;
2019 ctxt.format = format;
2020 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002021 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002022 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002023
2024 ret = xmlOutputBufferClose(buf);
2025 return(ret);
2026}
2027
2028/**
2029 * xmlDocDump:
2030 * @f: the FILE*
2031 * @cur: the document
2032 *
2033 * Dump an XML document to an open FILE.
2034 *
2035 * returns: the number of bytes written or -1 in case of failure.
2036 */
2037int
2038xmlDocDump(FILE *f, xmlDocPtr cur) {
2039 return(xmlDocFormatDump (f, cur, 0));
2040}
2041
2042/**
2043 * xmlSaveFileTo:
2044 * @buf: an output I/O buffer
2045 * @cur: the document
2046 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2047 *
2048 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002049 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2050 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002051 *
2052 * returns: the number of bytes written or -1 in case of failure.
2053 */
2054int
2055xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002056 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002057 int ret;
2058
Daniel Veillard3d97e662004-11-04 10:49:00 +00002059 if (buf == NULL) return(-1);
2060 if (cur == NULL) {
2061 xmlOutputBufferClose(buf);
2062 return(-1);
2063 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002064 memset(&ctxt, 0, sizeof(ctxt));
2065 ctxt.doc = cur;
2066 ctxt.buf = buf;
2067 ctxt.level = 0;
2068 ctxt.format = 0;
2069 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002070 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002071 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002072 ret = xmlOutputBufferClose(buf);
2073 return(ret);
2074}
2075
2076/**
2077 * xmlSaveFormatFileTo:
2078 * @buf: an output I/O buffer
2079 * @cur: the document
2080 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2081 * @format: should formatting spaces been added
2082 *
2083 * Dump an XML document to an I/O buffer.
Daniel Veillard3d97e662004-11-04 10:49:00 +00002084 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2085 * after this call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002086 *
2087 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002088 */
2089int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002090xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2091 const char *encoding, int format)
2092{
2093 xmlSaveCtxt ctxt;
2094 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002095
Daniel Veillard3d97e662004-11-04 10:49:00 +00002096 if (buf == NULL) return(-1);
Daniel Veillardce244ad2004-11-05 10:03:46 +00002097 if ((cur == NULL) ||
2098 ((cur->type != XML_DOCUMENT_NODE) &&
2099 (cur->type != XML_HTML_DOCUMENT_NODE))) {
Daniel Veillard3d97e662004-11-04 10:49:00 +00002100 xmlOutputBufferClose(buf);
2101 return(-1);
2102 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002103 memset(&ctxt, 0, sizeof(ctxt));
2104 ctxt.doc = cur;
2105 ctxt.buf = buf;
2106 ctxt.level = 0;
2107 ctxt.format = format;
2108 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002109 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002110 xmlDocContentDumpOutput(&ctxt, cur);
2111 ret = xmlOutputBufferClose(buf);
2112 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002113}
2114
2115/**
2116 * xmlSaveFormatFileEnc:
2117 * @filename: the filename or URL to output
2118 * @cur: the document being saved
2119 * @encoding: the name of the encoding to use or NULL.
2120 * @format: should formatting spaces be added.
2121 *
2122 * Dump an XML document to a file or an URL.
2123 *
2124 * Returns the number of bytes written or -1 in case of error.
2125 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2126 * or xmlKeepBlanksDefault(0) was called
2127 */
2128int
2129xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2130 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002131 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002132 xmlOutputBufferPtr buf;
2133 xmlCharEncodingHandlerPtr handler = NULL;
2134 int ret;
2135
2136 if (cur == NULL)
2137 return(-1);
2138
2139 if (encoding == NULL)
2140 encoding = (const char *) cur->encoding;
2141
2142 if (encoding != NULL) {
2143
2144 handler = xmlFindCharEncodingHandler(encoding);
2145 if (handler == NULL)
2146 return(-1);
2147 }
2148
2149#ifdef HAVE_ZLIB_H
2150 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2151#endif
2152 /*
2153 * save the content to a temp buffer.
2154 */
2155 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2156 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002157 memset(&ctxt, 0, sizeof(ctxt));
2158 ctxt.doc = cur;
2159 ctxt.buf = buf;
2160 ctxt.level = 0;
2161 ctxt.format = format;
2162 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002163 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002164
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002165 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002166
2167 ret = xmlOutputBufferClose(buf);
2168 return(ret);
2169}
2170
2171
2172/**
2173 * xmlSaveFileEnc:
2174 * @filename: the filename (or URL)
2175 * @cur: the document
2176 * @encoding: the name of an encoding (or NULL)
2177 *
2178 * Dump an XML document, converting it to the given encoding
2179 *
2180 * returns: the number of bytes written or -1 in case of failure.
2181 */
2182int
2183xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2184 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2185}
2186
2187/**
2188 * xmlSaveFormatFile:
2189 * @filename: the filename (or URL)
2190 * @cur: the document
2191 * @format: should formatting spaces been added
2192 *
2193 * Dump an XML document to a file. Will use compression if
2194 * compiled in and enabled. If @filename is "-" the stdout file is
2195 * used. If @format is set then the document will be indented on output.
2196 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2197 * or xmlKeepBlanksDefault(0) was called
2198 *
2199 * returns: the number of bytes written or -1 in case of failure.
2200 */
2201int
2202xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2203 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2204}
2205
2206/**
2207 * xmlSaveFile:
2208 * @filename: the filename (or URL)
2209 * @cur: the document
2210 *
2211 * Dump an XML document to a file. Will use compression if
2212 * compiled in and enabled. If @filename is "-" the stdout file is
2213 * used.
2214 * returns: the number of bytes written or -1 in case of failure.
2215 */
2216int
2217xmlSaveFile(const char *filename, xmlDocPtr cur) {
2218 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2219}
2220
2221#endif /* LIBXML_OUTPUT_ENABLED */
2222