blob: 89df7fba62fed7ea1706d163c67bf2ca4b5b8bee [file] [log] [blame]
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001/*
2 * xmlsave.c: Implemetation of the document serializer
3 *
4 * See Copyright for the status of this software.
5 *
6 * daniel@veillard.com
7 */
8
9#define IN_LIBXML
10#include "libxml.h"
11
12#include <string.h>
13#include <libxml/xmlmemory.h>
14#include <libxml/parserInternals.h>
15#include <libxml/tree.h>
16#include <libxml/xmlsave.h>
Daniel Veillard1a8741c2004-03-04 13:40:59 +000017
Daniel Veillard753086a2004-03-28 16:12:44 +000018#define MAX_INDENT 60
19
Daniel Veillard656ce942004-04-30 23:11:45 +000020#include <libxml/HTMLtree.h>
21
Daniel Veillard1a8741c2004-03-04 13:40:59 +000022/************************************************************************
23 * *
24 * XHTML detection *
25 * *
26 ************************************************************************/
27#define XHTML_STRICT_PUBLIC_ID BAD_CAST \
28 "-//W3C//DTD XHTML 1.0 Strict//EN"
29#define XHTML_STRICT_SYSTEM_ID BAD_CAST \
30 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
31#define XHTML_FRAME_PUBLIC_ID BAD_CAST \
32 "-//W3C//DTD XHTML 1.0 Frameset//EN"
33#define XHTML_FRAME_SYSTEM_ID BAD_CAST \
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
35#define XHTML_TRANS_PUBLIC_ID BAD_CAST \
36 "-//W3C//DTD XHTML 1.0 Transitional//EN"
37#define XHTML_TRANS_SYSTEM_ID BAD_CAST \
38 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
39
40#define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
41/**
42 * xmlIsXHTML:
43 * @systemID: the system identifier
44 * @publicID: the public identifier
45 *
46 * Try to find if the document correspond to an XHTML DTD
47 *
48 * Returns 1 if true, 0 if not and -1 in case of error
49 */
50int
51xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
52 if ((systemID == NULL) && (publicID == NULL))
53 return(-1);
54 if (publicID != NULL) {
55 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
56 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
57 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
58 }
59 if (systemID != NULL) {
60 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
61 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
62 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
63 }
64 return(0);
65}
Daniel Veillard1a8741c2004-03-04 13:40:59 +000066
67#ifdef LIBXML_OUTPUT_ENABLED
68
69#define TODO \
70 xmlGenericError(xmlGenericErrorContext, \
71 "Unimplemented block at %s:%d\n", \
72 __FILE__, __LINE__);
73
74struct _xmlSaveCtxt {
75 void *_private;
76 int type;
77 int fd;
78 const xmlChar *filename;
79 const xmlChar *encoding;
80 xmlCharEncodingHandlerPtr handler;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000081 xmlOutputBufferPtr buf;
82 xmlDocPtr doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000083 int options;
84 int level;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +000085 int format;
Daniel Veillard753086a2004-03-28 16:12:44 +000086 char indent[MAX_INDENT + 1];
87 int indent_nr;
88 int indent_size;
Daniel Veillard1a8741c2004-03-04 13:40:59 +000089};
90
91/************************************************************************
92 * *
93 * Output error handlers *
94 * *
95 ************************************************************************/
96/**
97 * xmlSaveErrMemory:
98 * @extra: extra informations
99 *
100 * Handle an out of memory condition
101 */
102static void
103xmlSaveErrMemory(const char *extra)
104{
105 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
106}
107
108/**
109 * xmlSaveErr:
110 * @code: the error number
111 * @node: the location of the error.
112 * @extra: extra informations
113 *
114 * Handle an out of memory condition
115 */
116static void
117xmlSaveErr(int code, xmlNodePtr node, const char *extra)
118{
119 const char *msg = NULL;
120
121 switch(code) {
122 case XML_SAVE_NOT_UTF8:
123 msg = "string is not in UTF-8";
124 break;
125 case XML_SAVE_CHAR_INVALID:
126 msg = "invalid character value";
127 break;
128 case XML_SAVE_UNKNOWN_ENCODING:
129 msg = "unknown encoding %s";
130 break;
131 case XML_SAVE_NO_DOCTYPE:
132 msg = "document has no DOCTYPE";
133 break;
134 default:
135 msg = "unexpected error number";
136 }
137 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
138}
139
140/************************************************************************
141 * *
Daniel Veillard83a75e02004-05-14 21:50:42 +0000142 * Special escaping routines *
143 * *
144 ************************************************************************/
145/**
146 * xmlEscapeEntities:
147 * @out: a pointer to an array of bytes to store the result
148 * @outlen: the length of @out
149 * @in: a pointer to an array of unescaped UTF-8 bytes
150 * @inlen: the length of @in
151 *
152 * Take a block of UTF-8 chars in and escape them. Used when there is no
153 * encoding specified.
154 *
155 * Returns 0 if success, or -1 otherwise
156 * The value of @inlen after return is the number of octets consumed
157 * if the return value is positive, else unpredictable.
158 * The value of @outlen after return is the number of octets consumed.
159 */
160static int
161xmlEscapeEntities(unsigned char* out, int *outlen,
162 const xmlChar* in, int *inlen) {
163 unsigned char* outstart = out;
164 const unsigned char* base = in;
165 unsigned char* outend = out + *outlen;
166 const unsigned char* inend;
167 int val;
168
169 inend = in + (*inlen);
170
171 while ((in < inend) && (out < outend)) {
172 if (*in == '<') {
173 if (outend - out < 4) break;
174 *out++ = '&';
175 *out++ = 'l';
176 *out++ = 't';
177 *out++ = ';';
178 in++;
179 continue;
180 } else if (*in == '>') {
181 if (outend - out < 4) break;
182 *out++ = '&';
183 *out++ = 'g';
184 *out++ = 't';
185 *out++ = ';';
186 in++;
187 continue;
188 } else if (*in == '&') {
189 if (outend - out < 5) break;
190 *out++ = '&';
191 *out++ = 'a';
192 *out++ = 'm';
193 *out++ = 'p';
194 *out++ = ';';
195 in++;
196 continue;
197 } else if (((*in >= 0x20) && (*in < 0x80)) ||
198 (*in == '\n') || (*in == '\t')) {
199 /*
200 * default case, just copy !
201 */
202 *out++ = *in++;
203 continue;
204 } else if (*in >= 0x80) {
205 /*
206 * We assume we have UTF-8 input.
207 */
208 unsigned char* ptr;
209
210 if (outend - out < 10) break;
211
212 if (*in < 0xC0) {
213 xmlGenericError(xmlGenericErrorContext,
214 "xmlEscapeEntities : input not UTF-8\n");
215 in++;
216 goto error;
217 } else if (*in < 0xE0) {
218 if (inend - in < 2) break;
219 val = (in[0]) & 0x1F;
220 val <<= 6;
221 val |= (in[1]) & 0x3F;
222 in += 2;
223 } else if (*in < 0xF0) {
224 if (inend - in < 3) break;
225 val = (in[0]) & 0x0F;
226 val <<= 6;
227 val |= (in[1]) & 0x3F;
228 val <<= 6;
229 val |= (in[2]) & 0x3F;
230 in += 3;
231 } else if (*in < 0xF8) {
232 if (inend - in < 4) break;
233 val = (in[0]) & 0x07;
234 val <<= 6;
235 val |= (in[1]) & 0x3F;
236 val <<= 6;
237 val |= (in[2]) & 0x3F;
238 val <<= 6;
239 val |= (in[3]) & 0x3F;
240 in += 4;
241 } else {
242 xmlGenericError(xmlGenericErrorContext,
243 "xmlEscapeEntities : char out of range\n");
244 in++;
245 goto error;
246 }
247 if (!IS_CHAR(val)) {
248 xmlGenericError(xmlGenericErrorContext,
249 "xmlEscapeEntities : char out of range\n");
250 in++;
251 goto error;
252 }
253
254 /*
255 * We could do multiple things here. Just save as a char ref
256 */
257serialize_hex_charref:
258 *out++ = '&';
259 *out++ = '#';
260 *out++ = 'x';
261 if (val < 0x10) ptr = out;
262 else if (val < 0x100) ptr = out + 1;
263 else if (val < 0x1000) ptr = out + 2;
264 else if (val < 0x10000) ptr = out + 3;
265 else if (val < 0x100000) ptr = out + 4;
266 else ptr = out + 5;
267 out = ptr + 1;
268 while (val > 0) {
269 switch (val & 0xF) {
270 case 0: *ptr-- = '0'; break;
271 case 1: *ptr-- = '1'; break;
272 case 2: *ptr-- = '2'; break;
273 case 3: *ptr-- = '3'; break;
274 case 4: *ptr-- = '4'; break;
275 case 5: *ptr-- = '5'; break;
276 case 6: *ptr-- = '6'; break;
277 case 7: *ptr-- = '7'; break;
278 case 8: *ptr-- = '8'; break;
279 case 9: *ptr-- = '9'; break;
280 case 0xA: *ptr-- = 'A'; break;
281 case 0xB: *ptr-- = 'B'; break;
282 case 0xC: *ptr-- = 'C'; break;
283 case 0xD: *ptr-- = 'D'; break;
284 case 0xE: *ptr-- = 'E'; break;
285 case 0xF: *ptr-- = 'F'; break;
286 default: *ptr-- = '0'; break;
287 }
288 val >>= 4;
289 }
290 *out++ = ';';
291 continue;
292 } else if (IS_BYTE_CHAR(*in)) {
293 if (outend - out < 6) break;
294 val = *in++;
295 goto serialize_hex_charref;
296 } else {
297 xmlGenericError(xmlGenericErrorContext,
298 "xmlEscapeEntities : char out of range\n");
299 in++;
300 goto error;
301 }
302 }
303 *outlen = out - outstart;
304 *inlen = in - base;
305 return(0);
306error:
307 *outlen = out - outstart;
308 *inlen = in - base;
309 return(-1);
310}
311
312/************************************************************************
313 * *
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000314 * Allocation and deallocation *
315 * *
316 ************************************************************************/
Daniel Veillard753086a2004-03-28 16:12:44 +0000317/**
318 * xmlSaveCtxtInit:
319 * @ctxt: the saving context
320 *
321 * Initialize a saving context
322 */
323static void
324xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
325{
326 int i;
327
328 if (ctxt == NULL) return;
329 if (xmlTreeIndentString == NULL) {
330 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
331 } else {
332 ctxt->indent_size = xmlStrlen((const xmlChar *) xmlTreeIndentString);
333 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
334 for (i = 0;i < ctxt->indent_nr;i++)
335 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
336 ctxt->indent_size);
337 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
338 }
339}
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000340
341/**
342 * xmlFreeSaveCtxt:
343 *
344 * Free a saving context, destroying the ouptut in any remaining buffer
345 */
346static void
347xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
348{
349 if (ctxt == NULL) return;
350 if (ctxt->encoding != NULL)
351 xmlFree((char *) ctxt->encoding);
Daniel Veillarde2161a62004-04-29 17:14:25 +0000352 if (ctxt->buf != NULL)
353 xmlOutputBufferClose(ctxt->buf);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000354 xmlFree(ctxt);
355}
356
357/**
358 * xmlNewSaveCtxt:
359 *
360 * Create a new saving context
361 *
362 * Returns the new structure or NULL in case of error
363 */
364static xmlSaveCtxtPtr
365xmlNewSaveCtxt(const char *encoding, int options)
366{
367 xmlSaveCtxtPtr ret;
368
369 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
370 if (ret == NULL) {
371 xmlSaveErrMemory("creating saving context");
372 return ( NULL );
373 }
374 memset(ret, 0, sizeof(xmlSaveCtxt));
375 ret->options = options;
376 if (encoding != NULL) {
377 ret->handler = xmlFindCharEncodingHandler(encoding);
378 if (ret->handler == NULL) {
379 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
380 xmlFreeSaveCtxt(ret);
381 return(NULL);
382 }
383 ret->encoding = xmlStrdup((const xmlChar *)encoding);
384 }
Daniel Veillard753086a2004-03-28 16:12:44 +0000385 xmlSaveCtxtInit(ret);
386
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000387 return(ret);
388}
389
390/************************************************************************
391 * *
392 * Dumping XML tree content to a simple buffer *
393 * *
394 ************************************************************************/
395/**
396 * xmlAttrSerializeContent:
397 * @buf: the XML buffer output
398 * @doc: the document
399 * @attr: the attribute pointer
400 *
401 * Serialize the attribute in the buffer
402 */
403static void
404xmlAttrSerializeContent(xmlBufferPtr buf, xmlDocPtr doc, xmlAttrPtr attr)
405{
406 xmlNodePtr children;
407
408 children = attr->children;
409 while (children != NULL) {
410 switch (children->type) {
411 case XML_TEXT_NODE:
412 xmlAttrSerializeTxtContent(buf, doc, attr, children->content);
413 break;
414 case XML_ENTITY_REF_NODE:
415 xmlBufferAdd(buf, BAD_CAST "&", 1);
416 xmlBufferAdd(buf, children->name,
417 xmlStrlen(children->name));
418 xmlBufferAdd(buf, BAD_CAST ";", 1);
419 break;
420 default:
421 /* should not happen unless we have a badly built tree */
422 break;
423 }
424 children = children->next;
425 }
426}
427
428/************************************************************************
429 * *
430 * Dumping XML tree content to an I/O output buffer *
431 * *
432 ************************************************************************/
433
434#ifdef LIBXML_HTML_ENABLED
435static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000436xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000437#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000438static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
439static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000440void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
441
442/**
443 * xmlNsDumpOutput:
444 * @buf: the XML buffer output
445 * @cur: a namespace
446 *
447 * Dump a local Namespace definition.
448 * Should be called in the context of attributes dumps.
449 */
450static void
451xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000452 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000453 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
454 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
455 return;
456
457 /* Within the context of an element attributes */
458 if (cur->prefix != NULL) {
459 xmlOutputBufferWriteString(buf, " xmlns:");
460 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
461 } else
462 xmlOutputBufferWriteString(buf, " xmlns");
463 xmlOutputBufferWriteString(buf, "=");
464 xmlBufferWriteQuotedString(buf->buffer, cur->href);
465 }
466}
467
468/**
469 * xmlNsListDumpOutput:
470 * @buf: the XML buffer output
471 * @cur: the first namespace
472 *
473 * Dump a list of local Namespace definitions.
474 * Should be called in the context of attributes dumps.
475 */
476void
477xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
478 while (cur != NULL) {
479 xmlNsDumpOutput(buf, cur);
480 cur = cur->next;
481 }
482}
483
484/**
485 * xmlDtdDumpOutput:
486 * @buf: the XML buffer output
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000487 * @dtd: the pointer to the DTD
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000488 *
489 * Dump the XML document DTD, if any.
490 */
491static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000492xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
493 xmlOutputBufferPtr buf;
494 int format, level;
495 xmlDocPtr doc;
496
497 if (dtd == NULL) return;
498 if ((ctxt == NULL) || (ctxt->buf == NULL))
499 return;
500 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000501 xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
502 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
503 if (dtd->ExternalID != NULL) {
504 xmlOutputBufferWriteString(buf, " PUBLIC ");
505 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
506 xmlOutputBufferWriteString(buf, " ");
507 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
508 } else if (dtd->SystemID != NULL) {
509 xmlOutputBufferWriteString(buf, " SYSTEM ");
510 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
511 }
512 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
513 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
514 (dtd->pentities == NULL)) {
515 xmlOutputBufferWriteString(buf, ">");
516 return;
517 }
518 xmlOutputBufferWriteString(buf, " [\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000519 format = ctxt->format;
520 level = ctxt->level;
521 doc = ctxt->doc;
522 ctxt->format = 0;
523 ctxt->level = -1;
524 ctxt->doc = dtd->doc;
525 xmlNodeListDumpOutput(ctxt, dtd->children);
526 ctxt->format = format;
527 ctxt->level = level;
528 ctxt->doc = doc;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000529 xmlOutputBufferWriteString(buf, "]>");
530}
531
532/**
533 * xmlAttrDumpOutput:
534 * @buf: the XML buffer output
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000535 * @cur: the attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000536 *
537 * Dump an XML attribute
538 */
539static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000540xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
541 xmlOutputBufferPtr buf;
542 if (cur == NULL) return;
543 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000544 xmlOutputBufferWriteString(buf, " ");
545 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
546 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
547 xmlOutputBufferWriteString(buf, ":");
548 }
549 xmlOutputBufferWriteString(buf, (const char *)cur->name);
550 xmlOutputBufferWriteString(buf, "=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000551 xmlAttrSerializeContent(buf->buffer, ctxt->doc, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000552 xmlOutputBufferWriteString(buf, "\"");
553}
554
555/**
556 * xmlAttrListDumpOutput:
557 * @buf: the XML buffer output
558 * @doc: the document
559 * @cur: the first attribute pointer
560 * @encoding: an optional encoding string
561 *
562 * Dump a list of XML attributes
563 */
564static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000565xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
566 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000567 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000568 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000569 cur = cur->next;
570 }
571}
572
573
574
575/**
576 * xmlNodeListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000577 * @cur: the first node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000578 *
579 * Dump an XML node list, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000580 */
581static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000582xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000583 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000584
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000585 if (cur == NULL) return;
586 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000587 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000588 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000589 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +0000590 xmlOutputBufferWrite(buf, ctxt->indent_size *
591 (ctxt->level > ctxt->indent_nr ?
592 ctxt->indent_nr : ctxt->level),
593 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000594 xmlNodeDumpOutputInternal(ctxt, cur);
595 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000596 xmlOutputBufferWriteString(buf, "\n");
597 }
598 cur = cur->next;
599 }
600}
601
602/**
603 * xmlNodeDumpOutputInternal:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000604 * @cur: the current node
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000605 *
606 * Dump an XML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000607 */
608static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000609xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +0000610 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000611 xmlNodePtr tmp;
612 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000613 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000614
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000615 if (cur == NULL) return;
616 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000617 if (cur->type == XML_XINCLUDE_START)
618 return;
619 if (cur->type == XML_XINCLUDE_END)
620 return;
621 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000622 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000623 return;
624 }
625 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000626 xmlNodeListDumpOutput(ctxt, cur->children);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000627 return;
628 }
629 if (cur->type == XML_ELEMENT_DECL) {
630 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
631 return;
632 }
633 if (cur->type == XML_ATTRIBUTE_DECL) {
634 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
635 return;
636 }
637 if (cur->type == XML_ENTITY_DECL) {
638 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
639 return;
640 }
641 if (cur->type == XML_TEXT_NODE) {
642 if (cur->content != NULL) {
643 if ((cur->name == xmlStringText) ||
644 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000645
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000646 if (ctxt->encoding == NULL) {
Daniel Veillard83a75e02004-05-14 21:50:42 +0000647 xmlOutputBufferWriteEscape(buf, cur->content,
648 xmlEscapeEntities);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000649 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +0000650 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000651 }
652 } else {
653 /*
654 * Disable escaping, needed for XSLT
655 */
656 xmlOutputBufferWriteString(buf, (const char *) cur->content);
657 }
658 }
659
660 return;
661 }
662 if (cur->type == XML_PI_NODE) {
663 if (cur->content != NULL) {
664 xmlOutputBufferWriteString(buf, "<?");
665 xmlOutputBufferWriteString(buf, (const char *)cur->name);
666 if (cur->content != NULL) {
667 xmlOutputBufferWriteString(buf, " ");
668 xmlOutputBufferWriteString(buf, (const char *)cur->content);
669 }
670 xmlOutputBufferWriteString(buf, "?>");
671 } else {
672 xmlOutputBufferWriteString(buf, "<?");
673 xmlOutputBufferWriteString(buf, (const char *)cur->name);
674 xmlOutputBufferWriteString(buf, "?>");
675 }
676 return;
677 }
678 if (cur->type == XML_COMMENT_NODE) {
679 if (cur->content != NULL) {
680 xmlOutputBufferWriteString(buf, "<!--");
681 xmlOutputBufferWriteString(buf, (const char *)cur->content);
682 xmlOutputBufferWriteString(buf, "-->");
683 }
684 return;
685 }
686 if (cur->type == XML_ENTITY_REF_NODE) {
687 xmlOutputBufferWriteString(buf, "&");
688 xmlOutputBufferWriteString(buf, (const char *)cur->name);
689 xmlOutputBufferWriteString(buf, ";");
690 return;
691 }
692 if (cur->type == XML_CDATA_SECTION_NODE) {
693 start = end = cur->content;
694 while (*end != '\0') {
695 if ((*end == ']') && (*(end + 1) == ']') && (*(end + 2) == '>')) {
696 end = end + 2;
697 xmlOutputBufferWriteString(buf, "<![CDATA[");
698 xmlOutputBufferWrite(buf, end - start, (const char *)start);
699 xmlOutputBufferWriteString(buf, "]]>");
700 start = end;
701 }
702 end++;
703 }
704 if (start != end) {
705 xmlOutputBufferWriteString(buf, "<![CDATA[");
706 xmlOutputBufferWriteString(buf, (const char *)start);
707 xmlOutputBufferWriteString(buf, "]]>");
708 }
709 return;
710 }
711 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000712 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000713 return;
714 }
715 if (cur->type == XML_NAMESPACE_DECL) {
716 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
717 return;
718 }
719
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000720 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000721 if (format == 1) {
722 tmp = cur->children;
723 while (tmp != NULL) {
724 if ((tmp->type == XML_TEXT_NODE) ||
725 (tmp->type == XML_CDATA_SECTION_NODE) ||
726 (tmp->type == XML_ENTITY_REF_NODE)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000727 ctxt->format = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000728 break;
729 }
730 tmp = tmp->next;
731 }
732 }
733 xmlOutputBufferWriteString(buf, "<");
734 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
735 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
736 xmlOutputBufferWriteString(buf, ":");
737 }
738
739 xmlOutputBufferWriteString(buf, (const char *)cur->name);
740 if (cur->nsDef)
741 xmlNsListDumpOutput(buf, cur->nsDef);
742 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000743 xmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000744
745 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
746 (cur->children == NULL) && (!xmlSaveNoEmptyTags)) {
747 xmlOutputBufferWriteString(buf, "/>");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000748 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000749 return;
750 }
751 xmlOutputBufferWriteString(buf, ">");
752 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000753 if (ctxt->encoding == NULL) {
Daniel Veillard83a75e02004-05-14 21:50:42 +0000754 xmlOutputBufferWriteEscape(buf, cur->content, xmlEscapeEntities);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +0000755 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +0000756 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000757 }
758 }
759 if (cur->children != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000760 if (ctxt->format) xmlOutputBufferWriteString(buf, "\n");
761 if (ctxt->level >= 0) ctxt->level++;
762 xmlNodeListDumpOutput(ctxt, cur->children);
763 if (ctxt->level > 0) ctxt->level--;
764 if ((xmlIndentTreeOutput) && (ctxt->format))
Daniel Veillard753086a2004-03-28 16:12:44 +0000765 xmlOutputBufferWrite(buf, ctxt->indent_size *
766 (ctxt->level > ctxt->indent_nr ?
767 ctxt->indent_nr : ctxt->level),
768 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000769 }
770 xmlOutputBufferWriteString(buf, "</");
771 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
772 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
773 xmlOutputBufferWriteString(buf, ":");
774 }
775
776 xmlOutputBufferWriteString(buf, (const char *)cur->name);
777 xmlOutputBufferWriteString(buf, ">");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000778 ctxt->format = format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000779}
780
781/**
782 * xmlDocContentDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000783 * @cur: the document
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000784 *
785 * Dump an XML document.
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000786 */
787static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000788xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000789#ifdef LIBXML_HTML_ENABLED
790 xmlDtdPtr dtd;
791 int is_xhtml = 0;
792#endif
793 const xmlChar *oldenc = cur->encoding;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000794 const xmlChar *encoding = ctxt->encoding;
795 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000796
797 xmlInitParser();
798
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000799 if (ctxt->encoding != NULL)
800 cur->encoding = BAD_CAST ctxt->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000801
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000802 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000803 xmlOutputBufferWriteString(buf, "<?xml version=");
804 if (cur->version != NULL)
805 xmlBufferWriteQuotedString(buf->buffer, cur->version);
806 else
807 xmlOutputBufferWriteString(buf, "\"1.0\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000808 if (ctxt->encoding == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000809 if (cur->encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000810 encoding = cur->encoding;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000811 else if (cur->charset != XML_CHAR_ENCODING_UTF8)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000812 encoding = (const xmlChar *)
813 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000814 }
815 if (encoding != NULL) {
816 xmlOutputBufferWriteString(buf, " encoding=");
817 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
818 }
819 switch (cur->standalone) {
820 case 0:
821 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
822 break;
823 case 1:
824 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
825 break;
826 }
827 xmlOutputBufferWriteString(buf, "?>\n");
828
829#ifdef LIBXML_HTML_ENABLED
830 dtd = xmlGetIntSubset(cur);
831 if (dtd != NULL) {
832 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
833 if (is_xhtml < 0) is_xhtml = 0;
834 }
835 if (is_xhtml) {
836 if (encoding != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000837 htmlSetMetaEncoding(cur, (const xmlChar *) ctxt->encoding);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000838 else
839 htmlSetMetaEncoding(cur, BAD_CAST "UTF-8");
840 }
841#endif
842 if (cur->children != NULL) {
843 xmlNodePtr child = cur->children;
844
845 while (child != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000846 ctxt->level = 0;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000847#ifdef LIBXML_HTML_ENABLED
848 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000849 xhtmlNodeDumpOutput(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000850 else
851#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000852 xmlNodeDumpOutputInternal(ctxt, child);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000853 xmlOutputBufferWriteString(buf, "\n");
854 child = child->next;
855 }
856 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000857 if (ctxt->encoding != NULL)
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000858 cur->encoding = oldenc;
859}
860
861#ifdef LIBXML_HTML_ENABLED
862/************************************************************************
863 * *
864 * Functions specific to XHTML serialization *
865 * *
866 ************************************************************************/
867
868/**
869 * xhtmlIsEmpty:
870 * @node: the node
871 *
872 * Check if a node is an empty xhtml node
873 *
874 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
875 */
876static int
877xhtmlIsEmpty(xmlNodePtr node) {
878 if (node == NULL)
879 return(-1);
880 if (node->type != XML_ELEMENT_NODE)
881 return(0);
882 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
883 return(0);
884 if (node->children != NULL)
885 return(0);
886 switch (node->name[0]) {
887 case 'a':
888 if (xmlStrEqual(node->name, BAD_CAST "area"))
889 return(1);
890 return(0);
891 case 'b':
892 if (xmlStrEqual(node->name, BAD_CAST "br"))
893 return(1);
894 if (xmlStrEqual(node->name, BAD_CAST "base"))
895 return(1);
896 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
897 return(1);
898 return(0);
899 case 'c':
900 if (xmlStrEqual(node->name, BAD_CAST "col"))
901 return(1);
902 return(0);
903 case 'f':
904 if (xmlStrEqual(node->name, BAD_CAST "frame"))
905 return(1);
906 return(0);
907 case 'h':
908 if (xmlStrEqual(node->name, BAD_CAST "hr"))
909 return(1);
910 return(0);
911 case 'i':
912 if (xmlStrEqual(node->name, BAD_CAST "img"))
913 return(1);
914 if (xmlStrEqual(node->name, BAD_CAST "input"))
915 return(1);
916 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
917 return(1);
918 return(0);
919 case 'l':
920 if (xmlStrEqual(node->name, BAD_CAST "link"))
921 return(1);
922 return(0);
923 case 'm':
924 if (xmlStrEqual(node->name, BAD_CAST "meta"))
925 return(1);
926 return(0);
927 case 'p':
928 if (xmlStrEqual(node->name, BAD_CAST "param"))
929 return(1);
930 return(0);
931 }
932 return(0);
933}
934
935/**
936 * xhtmlAttrListDumpOutput:
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000937 * @cur: the first attribute pointer
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000938 *
939 * Dump a list of XML attributes
940 */
941static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000942xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000943 xmlAttrPtr xml_lang = NULL;
944 xmlAttrPtr lang = NULL;
945 xmlAttrPtr name = NULL;
946 xmlAttrPtr id = NULL;
947 xmlNodePtr parent;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000948 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000949
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000950 if (cur == NULL) return;
951 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000952 parent = cur->parent;
953 while (cur != NULL) {
954 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
955 id = cur;
956 else
957 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
958 name = cur;
959 else
960 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
961 lang = cur;
962 else
963 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
964 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
965 xml_lang = cur;
966 else if ((cur->ns == NULL) &&
967 ((cur->children == NULL) ||
968 (cur->children->content == NULL) ||
969 (cur->children->content[0] == 0)) &&
970 (htmlIsBooleanAttr(cur->name))) {
971 if (cur->children != NULL)
972 xmlFreeNode(cur->children);
973 cur->children = xmlNewText(cur->name);
974 if (cur->children != NULL)
975 cur->children->parent = (xmlNodePtr) cur;
976 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000977 xmlAttrDumpOutput(ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000978 cur = cur->next;
979 }
980 /*
981 * C.8
982 */
983 if ((name != NULL) && (id == NULL)) {
984 if ((parent != NULL) && (parent->name != NULL) &&
985 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
986 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
987 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
988 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
989 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
990 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
991 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
992 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
993 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
994 xmlOutputBufferWriteString(buf, " id=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +0000995 xmlAttrSerializeContent(buf->buffer, ctxt->doc, name);
Daniel Veillard1a8741c2004-03-04 13:40:59 +0000996 xmlOutputBufferWriteString(buf, "\"");
997 }
998 }
999 /*
1000 * C.7.
1001 */
1002 if ((lang != NULL) && (xml_lang == NULL)) {
1003 xmlOutputBufferWriteString(buf, " xml:lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001004 xmlAttrSerializeContent(buf->buffer, ctxt->doc, lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001005 xmlOutputBufferWriteString(buf, "\"");
1006 } else
1007 if ((xml_lang != NULL) && (lang == NULL)) {
1008 xmlOutputBufferWriteString(buf, " lang=\"");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001009 xmlAttrSerializeContent(buf->buffer, ctxt->doc, xml_lang);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001010 xmlOutputBufferWriteString(buf, "\"");
1011 }
1012}
1013
1014/**
1015 * xhtmlNodeListDumpOutput:
1016 * @buf: the XML buffer output
1017 * @doc: the XHTML document
1018 * @cur: the first node
1019 * @level: the imbrication level for indenting
1020 * @format: is formatting allowed
1021 * @encoding: an optional encoding string
1022 *
1023 * Dump an XML node list, recursive behaviour, children are printed too.
1024 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1025 * or xmlKeepBlanksDefault(0) was called
1026 */
1027static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001028xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001029 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001030
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001031 if (cur == NULL) return;
1032 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001033 while (cur != NULL) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001034 if ((ctxt->format) && (xmlIndentTreeOutput) &&
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001035 (cur->type == XML_ELEMENT_NODE))
Daniel Veillard753086a2004-03-28 16:12:44 +00001036 xmlOutputBufferWrite(buf, ctxt->indent_size *
1037 (ctxt->level > ctxt->indent_nr ?
1038 ctxt->indent_nr : ctxt->level),
1039 ctxt->indent);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001040 xhtmlNodeDumpOutput(ctxt, cur);
1041 if (ctxt->format) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001042 xmlOutputBufferWriteString(buf, "\n");
1043 }
1044 cur = cur->next;
1045 }
1046}
1047
1048/**
1049 * xhtmlNodeDumpOutput:
1050 * @buf: the XML buffer output
1051 * @doc: the XHTML document
1052 * @cur: the current node
1053 * @level: the imbrication level for indenting
1054 * @format: is formatting allowed
1055 * @encoding: an optional encoding string
1056 *
1057 * Dump an XHTML node, recursive behaviour, children are printed too.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001058 */
1059static void
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001060xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
Daniel Veillard753086a2004-03-28 16:12:44 +00001061 int format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001062 xmlNodePtr tmp;
1063 xmlChar *start, *end;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001064 xmlOutputBufferPtr buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001065
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001066 if (cur == NULL) return;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001067 if (cur->type == XML_XINCLUDE_START)
1068 return;
1069 if (cur->type == XML_XINCLUDE_END)
1070 return;
1071 if (cur->type == XML_DTD_NODE) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001072 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001073 return;
1074 }
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001075 buf = ctxt->buf;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001076 if (cur->type == XML_ELEMENT_DECL) {
1077 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1078 return;
1079 }
1080 if (cur->type == XML_ATTRIBUTE_DECL) {
1081 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1082 return;
1083 }
1084 if (cur->type == XML_ENTITY_DECL) {
1085 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1086 return;
1087 }
1088 if (cur->type == XML_TEXT_NODE) {
1089 if (cur->content != NULL) {
1090 if ((cur->name == xmlStringText) ||
1091 (cur->name != xmlStringTextNoenc)) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001092
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001093 if (ctxt->encoding == NULL) {
Daniel Veillard83a75e02004-05-14 21:50:42 +00001094 xmlOutputBufferWriteEscape(buf, cur->content,
1095 xmlEscapeEntities);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001096 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +00001097 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001098 }
1099 } else {
1100 /*
1101 * Disable escaping, needed for XSLT
1102 */
1103 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1104 }
1105 }
1106
1107 return;
1108 }
1109 if (cur->type == XML_PI_NODE) {
1110 if (cur->content != NULL) {
1111 xmlOutputBufferWriteString(buf, "<?");
1112 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1113 if (cur->content != NULL) {
1114 xmlOutputBufferWriteString(buf, " ");
1115 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1116 }
1117 xmlOutputBufferWriteString(buf, "?>");
1118 } else {
1119 xmlOutputBufferWriteString(buf, "<?");
1120 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1121 xmlOutputBufferWriteString(buf, "?>");
1122 }
1123 return;
1124 }
1125 if (cur->type == XML_COMMENT_NODE) {
1126 if (cur->content != NULL) {
1127 xmlOutputBufferWriteString(buf, "<!--");
1128 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1129 xmlOutputBufferWriteString(buf, "-->");
1130 }
1131 return;
1132 }
1133 if (cur->type == XML_ENTITY_REF_NODE) {
1134 xmlOutputBufferWriteString(buf, "&");
1135 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1136 xmlOutputBufferWriteString(buf, ";");
1137 return;
1138 }
1139 if (cur->type == XML_CDATA_SECTION_NODE) {
1140 start = end = cur->content;
1141 while (*end != '\0') {
1142 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1143 end = end + 2;
1144 xmlOutputBufferWriteString(buf, "<![CDATA[");
1145 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1146 xmlOutputBufferWriteString(buf, "]]>");
1147 start = end;
1148 }
1149 end++;
1150 }
1151 if (start != end) {
1152 xmlOutputBufferWriteString(buf, "<![CDATA[");
1153 xmlOutputBufferWriteString(buf, (const char *)start);
1154 xmlOutputBufferWriteString(buf, "]]>");
1155 }
1156 return;
1157 }
1158
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001159 format = ctxt->format;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001160 if (format == 1) {
1161 tmp = cur->children;
1162 while (tmp != NULL) {
1163 if ((tmp->type == XML_TEXT_NODE) ||
1164 (tmp->type == XML_ENTITY_REF_NODE)) {
1165 format = 0;
1166 break;
1167 }
1168 tmp = tmp->next;
1169 }
1170 }
1171 xmlOutputBufferWriteString(buf, "<");
1172 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1173 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1174 xmlOutputBufferWriteString(buf, ":");
1175 }
1176
1177 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1178 if (cur->nsDef)
1179 xmlNsListDumpOutput(buf, cur->nsDef);
1180 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1181 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1182 /*
1183 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1184 */
1185 xmlOutputBufferWriteString(buf,
1186 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1187 }
1188 if (cur->properties != NULL)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001189 xhtmlAttrListDumpOutput(ctxt, cur->properties);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001190
1191 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1192 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1193 (xhtmlIsEmpty(cur) == 1)) {
1194 /*
1195 * C.2. Empty Elements
1196 */
1197 xmlOutputBufferWriteString(buf, " />");
1198 } else {
1199 /*
1200 * C.3. Element Minimization and Empty Element Content
1201 */
1202 xmlOutputBufferWriteString(buf, "></");
1203 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1204 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1205 xmlOutputBufferWriteString(buf, ":");
1206 }
1207 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1208 xmlOutputBufferWriteString(buf, ">");
1209 }
1210 return;
1211 }
1212 xmlOutputBufferWriteString(buf, ">");
1213 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001214 if (ctxt->encoding == NULL) {
Daniel Veillard83a75e02004-05-14 21:50:42 +00001215 xmlOutputBufferWriteEscape(buf, cur->content, xmlEscapeEntities);
Daniel Veillard5d1a4d82004-05-13 14:31:25 +00001216 } else {
Daniel Veillardee8960b2004-05-14 03:25:14 +00001217 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001218 }
1219 }
1220
1221 /*
1222 * 4.8. Script and Style elements
1223 */
1224 if ((cur->type == XML_ELEMENT_NODE) &&
1225 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1226 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1227 ((cur->ns == NULL) ||
1228 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1229 xmlNodePtr child = cur->children;
1230
1231 while (child != NULL) {
1232 if ((child->type == XML_TEXT_NODE) ||
1233 (child->type == XML_CDATA_SECTION_NODE)) {
1234 /*
1235 * Apparently CDATA escaping for style just break on IE,
1236 * mozilla and galeon, so ...
1237 */
1238 if (xmlStrEqual(cur->name, BAD_CAST "style") &&
1239 (xmlStrchr(child->content, '<') == NULL) &&
1240 (xmlStrchr(child->content, '>') == NULL) &&
1241 (xmlStrchr(child->content, '&') == NULL)) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001242 int level = ctxt->level;
1243 int indent = ctxt->format;
1244
1245 ctxt->level = 0;
1246 ctxt->format = 0;
1247 xhtmlNodeDumpOutput(ctxt, child);
1248 ctxt->level = level;
1249 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001250 } else {
1251 start = end = child->content;
1252 while (*end != '\0') {
1253 if (*end == ']' &&
1254 *(end + 1) == ']' &&
1255 *(end + 2) == '>') {
1256 end = end + 2;
1257 xmlOutputBufferWriteString(buf, "<![CDATA[");
1258 xmlOutputBufferWrite(buf, end - start,
1259 (const char *)start);
1260 xmlOutputBufferWriteString(buf, "]]>");
1261 start = end;
1262 }
1263 end++;
1264 }
1265 if (start != end) {
1266 xmlOutputBufferWriteString(buf, "<![CDATA[");
1267 xmlOutputBufferWriteString(buf, (const char *)start);
1268 xmlOutputBufferWriteString(buf, "]]>");
1269 }
1270 }
1271 } else {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001272 int level = ctxt->level;
1273 int indent = ctxt->format;
1274
1275 ctxt->level = 0;
1276 ctxt->format = 0;
1277 xhtmlNodeDumpOutput(ctxt, child);
1278 ctxt->level = level;
1279 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001280 }
1281 child = child->next;
1282 }
1283 } else if (cur->children != NULL) {
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001284 int indent = ctxt->format;
1285
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001286 if (format) xmlOutputBufferWriteString(buf, "\n");
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001287 if (ctxt->level >= 0) ctxt->level++;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001288 ctxt->format = format;
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001289 xhtmlNodeListDumpOutput(ctxt, cur->children);
1290 if (ctxt->level > 0) ctxt->level--;
Daniel Veillardf0244ce2004-05-09 23:48:39 +00001291 ctxt->format = indent;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001292 if ((xmlIndentTreeOutput) && (format))
Daniel Veillard753086a2004-03-28 16:12:44 +00001293 xmlOutputBufferWrite(buf, ctxt->indent_size *
1294 (ctxt->level > ctxt->indent_nr ?
1295 ctxt->indent_nr : ctxt->level),
1296 ctxt->indent);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001297 }
1298 xmlOutputBufferWriteString(buf, "</");
1299 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1300 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1301 xmlOutputBufferWriteString(buf, ":");
1302 }
1303
1304 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1305 xmlOutputBufferWriteString(buf, ">");
1306}
1307#endif
1308
1309/************************************************************************
1310 * *
1311 * Public entry points *
1312 * *
1313 ************************************************************************/
1314
1315/**
1316 * xmlSaveToFd:
1317 * @fd: a file descriptor number
1318 * @encoding: the encoding name to use or NULL
1319 * @options: a set of xmlSaveOptions
1320 *
1321 * Create a document saving context serializing to a file descriptor
1322 * with the encoding and the options given.
1323 *
1324 * Returns a new serialization context or NULL in case of error.
1325 */
1326xmlSaveCtxtPtr
1327xmlSaveToFd(int fd, const char *encoding, int options)
1328{
1329 xmlSaveCtxtPtr ret;
1330
1331 ret = xmlNewSaveCtxt(encoding, options);
1332 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001333 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1334 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001335 xmlFreeSaveCtxt(ret);
1336 return(NULL);
1337 }
1338 return(ret);
1339}
1340
1341/**
1342 * xmlSaveToFilename:
1343 * @filename: a file name or an URL
1344 * @encoding: the encoding name to use or NULL
1345 * @options: a set of xmlSaveOptions
1346 *
1347 * Create a document saving context serializing to a filename or possibly
1348 * to an URL (but this is less reliable) with the encoding and the options
1349 * given.
1350 *
1351 * Returns a new serialization context or NULL in case of error.
1352 */
1353xmlSaveCtxtPtr
1354xmlSaveToFilename(const char *filename, const char *encoding, int options)
1355{
1356 xmlSaveCtxtPtr ret;
1357 int compression = 0; /* TODO handle compression option */
1358
1359 ret = xmlNewSaveCtxt(encoding, options);
1360 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001361 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001362 compression);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001363 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001364 xmlFreeSaveCtxt(ret);
1365 return(NULL);
1366 }
1367 return(ret);
1368}
1369
1370#if 0
1371/**
1372 * xmlSaveToBuffer:
1373 * @buffer: a buffer
1374 * @encoding: the encoding name to use or NULL
1375 * @options: a set of xmlSaveOptions
1376 *
1377 * Create a document saving context serializing to a buffer
1378 * with the encoding and the options given
1379 *
1380 * Returns a new serialization context or NULL in case of error.
1381 */
1382xmlSaveCtxtPtr
1383xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1384{
1385 TODO
1386 return(NULL);
1387}
1388#endif
1389
1390/**
1391 * xmlSaveToIO:
1392 * @iowrite: an I/O write function
1393 * @ioclose: an I/O close function
1394 * @ioctx: an I/O handler
1395 * @encoding: the encoding name to use or NULL
1396 * @options: a set of xmlSaveOptions
1397 *
1398 * Create a document saving context serializing to a file descriptor
1399 * with the encoding and the options given
1400 *
1401 * Returns a new serialization context or NULL in case of error.
1402 */
1403xmlSaveCtxtPtr
1404xmlSaveToIO(xmlOutputWriteCallback iowrite,
1405 xmlOutputCloseCallback ioclose,
1406 void *ioctx, const char *encoding, int options)
1407{
1408 xmlSaveCtxtPtr ret;
1409
1410 ret = xmlNewSaveCtxt(encoding, options);
1411 if (ret == NULL) return(NULL);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001412 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1413 if (ret->buf == NULL) {
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001414 xmlFreeSaveCtxt(ret);
1415 return(NULL);
1416 }
1417 return(ret);
1418}
1419
1420/**
1421 * xmlSaveDoc:
1422 * @ctxt: a document saving context
1423 * @doc: a document
1424 *
1425 * Save a full document to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001426 * TODO: The function is not fully implemented yet as it does not return the
1427 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001428 *
1429 * Returns the number of byte written or -1 in case of error
1430 */
1431long
1432xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1433{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001434 long ret = 0;
1435
1436 xmlDocContentDumpOutput(ctxt, doc);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001437 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001438}
1439
1440/**
1441 * xmlSaveTree:
1442 * @ctxt: a document saving context
1443 * @node: a document
1444 *
1445 * Save a subtree starting at the node parameter to a saving context
Daniel Veillard377e1a92004-04-16 16:30:05 +00001446 * TODO: The function is not fully implemented yet as it does not return the
1447 * byte count but 0 instead
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001448 *
1449 * Returns the number of byte written or -1 in case of error
1450 */
1451long
1452xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1453{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001454 long ret = 0;
1455
1456 xmlNodeDumpOutputInternal(ctxt, node);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001457 return(ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001458}
1459
1460/**
1461 * xmlSaveFlush:
1462 * @ctxt: a document saving context
1463 *
1464 * Flush a document saving context, i.e. make sure that all bytes have
1465 * been output.
1466 *
1467 * Returns the number of byte written or -1 in case of error.
1468 */
1469int
1470xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1471{
1472 if (ctxt == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001473 if (ctxt->buf == NULL) return(-1);
1474 return(xmlOutputBufferFlush(ctxt->buf));
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001475}
1476
1477/**
1478 * xmlSaveClose:
1479 * @ctxt: a document saving context
1480 *
1481 * Close a document saving context, i.e. make sure that all bytes have
1482 * been output and free the associated data.
1483 *
1484 * Returns the number of byte written or -1 in case of error.
1485 */
1486int
1487xmlSaveClose(xmlSaveCtxtPtr ctxt)
1488{
1489 int ret;
1490
1491 if (ctxt == NULL) return(-1);
1492 ret = xmlSaveFlush(ctxt);
1493 xmlFreeSaveCtxt(ctxt);
1494 return(ret);
1495}
1496
1497/************************************************************************
1498 * *
1499 * Public entry points based on buffers *
1500 * *
1501 ************************************************************************/
1502/**
1503 * xmlAttrSerializeTxtContent:
1504 * @buf: the XML buffer output
1505 * @doc: the document
1506 * @attr: the attribute node
1507 * @string: the text content
1508 *
1509 * Serialize text attribute values to an xml simple buffer
1510 */
1511void
1512xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1513 xmlAttrPtr attr, const xmlChar *string) {
1514 xmlChar *base, *cur;
1515
1516 if (string == NULL) return;
1517 base = cur = (xmlChar *)string;
1518 while (*cur != 0) {
1519 if (*cur == '\n') {
1520 if (base != cur)
1521 xmlBufferAdd(buf, base, cur - base);
1522 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1523 cur++;
1524 base = cur;
1525 } else if (*cur == '\r') {
1526 if (base != cur)
1527 xmlBufferAdd(buf, base, cur - base);
1528 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1529 cur++;
1530 base = cur;
1531 } else if (*cur == '\t') {
1532 if (base != cur)
1533 xmlBufferAdd(buf, base, cur - base);
1534 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1535 cur++;
1536 base = cur;
1537 } else if (*cur == '"') {
1538 if (base != cur)
1539 xmlBufferAdd(buf, base, cur - base);
1540 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1541 cur++;
1542 base = cur;
1543 } else if (*cur == '<') {
1544 if (base != cur)
1545 xmlBufferAdd(buf, base, cur - base);
1546 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1547 cur++;
1548 base = cur;
1549 } else if (*cur == '>') {
1550 if (base != cur)
1551 xmlBufferAdd(buf, base, cur - base);
1552 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1553 cur++;
1554 base = cur;
1555 } else if (*cur == '&') {
1556 if (base != cur)
1557 xmlBufferAdd(buf, base, cur - base);
1558 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1559 cur++;
1560 base = cur;
1561 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1562 (doc->encoding == NULL))) {
1563 /*
1564 * We assume we have UTF-8 content.
1565 */
1566 char tmp[10];
1567 int val = 0, l = 1;
1568
1569 if (base != cur)
1570 xmlBufferAdd(buf, base, cur - base);
1571 if (*cur < 0xC0) {
1572 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1573 if (doc != NULL)
1574 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1575 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1576 tmp[sizeof(tmp) - 1] = 0;
1577 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1578 cur++;
1579 base = cur;
1580 continue;
1581 } else if (*cur < 0xE0) {
1582 val = (cur[0]) & 0x1F;
1583 val <<= 6;
1584 val |= (cur[1]) & 0x3F;
1585 l = 2;
1586 } else if (*cur < 0xF0) {
1587 val = (cur[0]) & 0x0F;
1588 val <<= 6;
1589 val |= (cur[1]) & 0x3F;
1590 val <<= 6;
1591 val |= (cur[2]) & 0x3F;
1592 l = 3;
1593 } else if (*cur < 0xF8) {
1594 val = (cur[0]) & 0x07;
1595 val <<= 6;
1596 val |= (cur[1]) & 0x3F;
1597 val <<= 6;
1598 val |= (cur[2]) & 0x3F;
1599 val <<= 6;
1600 val |= (cur[3]) & 0x3F;
1601 l = 4;
1602 }
1603 if ((l == 1) || (!IS_CHAR(val))) {
1604 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1605 if (doc != NULL)
1606 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1607 snprintf(tmp, sizeof(tmp), "&#%d;", *cur);
1608 tmp[sizeof(tmp) - 1] = 0;
1609 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1610 cur++;
1611 base = cur;
1612 continue;
1613 }
1614 /*
1615 * We could do multiple things here. Just save
1616 * as a char ref
1617 */
1618 snprintf(tmp, sizeof(tmp), "&#x%X;", val);
1619 tmp[sizeof(tmp) - 1] = 0;
1620 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1621 cur += l;
1622 base = cur;
1623 } else {
1624 cur++;
1625 }
1626 }
1627 if (base != cur)
1628 xmlBufferAdd(buf, base, cur - base);
1629}
1630
1631/**
1632 * xmlNodeDump:
1633 * @buf: the XML buffer output
1634 * @doc: the document
1635 * @cur: the current node
1636 * @level: the imbrication level for indenting
1637 * @format: is formatting allowed
1638 *
1639 * Dump an XML node, recursive behaviour,children are printed too.
1640 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1641 * or xmlKeepBlanksDefault(0) was called
1642 *
1643 * Returns the number of bytes written to the buffer or -1 in case of error
1644 */
1645int
1646xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
1647 int format)
1648{
1649 unsigned int use;
1650 int ret;
1651 xmlOutputBufferPtr outbuf;
1652
1653 xmlInitParser();
1654
1655 if (cur == NULL) {
1656#ifdef DEBUG_TREE
1657 xmlGenericError(xmlGenericErrorContext,
1658 "xmlNodeDump : node == NULL\n");
1659#endif
1660 return (-1);
1661 }
1662 if (buf == NULL) {
1663#ifdef DEBUG_TREE
1664 xmlGenericError(xmlGenericErrorContext,
1665 "xmlNodeDump : buf == NULL\n");
1666#endif
1667 return (-1);
1668 }
1669 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1670 if (outbuf == NULL) {
1671 xmlSaveErrMemory("creating buffer");
1672 return (-1);
1673 }
1674 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
1675 outbuf->buffer = buf;
1676 outbuf->encoder = NULL;
1677 outbuf->writecallback = NULL;
1678 outbuf->closecallback = NULL;
1679 outbuf->context = NULL;
1680 outbuf->written = 0;
1681
1682 use = buf->use;
1683 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
1684 xmlFree(outbuf);
1685 ret = buf->use - use;
1686 return (ret);
1687}
1688
1689/**
1690 * xmlElemDump:
1691 * @f: the FILE * for the output
1692 * @doc: the document
1693 * @cur: the current node
1694 *
1695 * Dump an XML/HTML node, recursive behaviour, children are printed too.
1696 */
1697void
1698xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
1699{
1700 xmlOutputBufferPtr outbuf;
1701
1702 xmlInitParser();
1703
1704 if (cur == NULL) {
1705#ifdef DEBUG_TREE
1706 xmlGenericError(xmlGenericErrorContext,
1707 "xmlElemDump : cur == NULL\n");
1708#endif
1709 return;
1710 }
1711#ifdef DEBUG_TREE
1712 if (doc == NULL) {
1713 xmlGenericError(xmlGenericErrorContext,
1714 "xmlElemDump : doc == NULL\n");
1715 }
1716#endif
1717
1718 outbuf = xmlOutputBufferCreateFile(f, NULL);
1719 if (outbuf == NULL)
1720 return;
1721 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
1722#ifdef LIBXML_HTML_ENABLED
1723 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
1724#else
1725 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
1726#endif /* LIBXML_HTML_ENABLED */
1727 } else
1728 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
1729 xmlOutputBufferClose(outbuf);
1730}
1731
1732/************************************************************************
1733 * *
1734 * Saving functions front-ends *
1735 * *
1736 ************************************************************************/
1737
1738/**
1739 * xmlNodeDumpOutput:
1740 * @buf: the XML buffer output
1741 * @doc: the document
1742 * @cur: the current node
1743 * @level: the imbrication level for indenting
1744 * @format: is formatting allowed
1745 * @encoding: an optional encoding string
1746 *
1747 * Dump an XML node, recursive behaviour, children are printed too.
1748 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1749 * or xmlKeepBlanksDefault(0) was called
1750 */
1751void
1752xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
1753 int level, int format, const char *encoding)
1754{
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001755 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001756#ifdef LIBXML_HTML_ENABLED
1757 xmlDtdPtr dtd;
1758 int is_xhtml = 0;
1759#endif
1760
1761 xmlInitParser();
1762
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001763 memset(&ctxt, 0, sizeof(ctxt));
1764 ctxt.doc = doc;
1765 ctxt.buf = buf;
1766 ctxt.level = level;
1767 ctxt.format = format;
1768 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001769 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001770
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001771#ifdef LIBXML_HTML_ENABLED
1772 dtd = xmlGetIntSubset(doc);
1773 if (dtd != NULL) {
1774 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1775 if (is_xhtml < 0)
1776 is_xhtml = 0;
1777 if ((is_xhtml) && (cur->parent == (xmlNodePtr) doc) &&
1778 (cur->type == XML_ELEMENT_NODE) &&
1779 (xmlStrEqual(cur->name, BAD_CAST "html"))) {
1780 if (encoding != NULL)
1781 htmlSetMetaEncoding((htmlDocPtr) doc,
1782 (const xmlChar *) encoding);
1783 else
1784 htmlSetMetaEncoding((htmlDocPtr) doc, BAD_CAST "UTF-8");
1785 }
1786 }
1787
1788 if (is_xhtml)
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001789 xhtmlNodeDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001790 else
1791#endif
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001792 xmlNodeDumpOutputInternal(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001793}
1794
1795/**
1796 * xmlDocDumpFormatMemoryEnc:
1797 * @out_doc: Document to generate XML text from
1798 * @doc_txt_ptr: Memory pointer for allocated XML text
1799 * @doc_txt_len: Length of the generated XML text
1800 * @txt_encoding: Character encoding to use when generating XML text
1801 * @format: should formatting spaces been added
1802 *
1803 * Dump the current DOM tree into memory using the character encoding specified
1804 * by the caller. Note it is up to the caller of this function to free the
1805 * allocated memory with xmlFree().
1806 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1807 * or xmlKeepBlanksDefault(0) was called
1808 */
1809
1810void
1811xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1812 int * doc_txt_len, const char * txt_encoding,
1813 int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001814 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001815 int dummy = 0;
1816 xmlOutputBufferPtr out_buff = NULL;
1817 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
1818
1819 if (doc_txt_len == NULL) {
1820 doc_txt_len = &dummy; /* Continue, caller just won't get length */
1821 }
1822
1823 if (doc_txt_ptr == NULL) {
1824 *doc_txt_len = 0;
1825 return;
1826 }
1827
1828 *doc_txt_ptr = NULL;
1829 *doc_txt_len = 0;
1830
1831 if (out_doc == NULL) {
1832 /* No document, no output */
1833 return;
1834 }
1835
1836 /*
1837 * Validate the encoding value, if provided.
1838 * This logic is copied from xmlSaveFileEnc.
1839 */
1840
1841 if (txt_encoding == NULL)
1842 txt_encoding = (const char *) out_doc->encoding;
1843 if (txt_encoding != NULL) {
1844 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
1845 if ( conv_hdlr == NULL ) {
1846 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
1847 txt_encoding);
1848 return;
1849 }
1850 }
1851
1852 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
1853 xmlSaveErrMemory("creating buffer");
1854 return;
1855 }
1856
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001857 memset(&ctxt, 0, sizeof(ctxt));
1858 ctxt.doc = out_doc;
1859 ctxt.buf = out_buff;
1860 ctxt.level = 0;
1861 ctxt.format = format;
1862 ctxt.encoding = (const xmlChar *) txt_encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001863 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001864 xmlDocContentDumpOutput(&ctxt, out_doc);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001865 xmlOutputBufferFlush(out_buff);
1866 if (out_buff->conv != NULL) {
1867 *doc_txt_len = out_buff->conv->use;
1868 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
1869 } else {
1870 *doc_txt_len = out_buff->buffer->use;
1871 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
1872 }
1873 (void)xmlOutputBufferClose(out_buff);
1874
1875 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
1876 *doc_txt_len = 0;
1877 xmlSaveErrMemory("creating output");
1878 }
1879
1880 return;
1881}
1882
1883/**
1884 * xmlDocDumpMemory:
1885 * @cur: the document
1886 * @mem: OUT: the memory pointer
1887 * @size: OUT: the memory length
1888 *
1889 * Dump an XML document in memory and return the #xmlChar * and it's size
1890 * in bytes. It's up to the caller to free the memory with xmlFree().
1891 * The resulting byte array is zero terminated, though the last 0 is not
1892 * included in the returned size.
1893 */
1894void
1895xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
1896 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
1897}
1898
1899/**
1900 * xmlDocDumpFormatMemory:
1901 * @cur: the document
1902 * @mem: OUT: the memory pointer
1903 * @size: OUT: the memory length
1904 * @format: should formatting spaces been added
1905 *
1906 *
1907 * Dump an XML document in memory and return the #xmlChar * and it's size.
1908 * It's up to the caller to free the memory with xmlFree().
1909 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1910 * or xmlKeepBlanksDefault(0) was called
1911 */
1912void
1913xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
1914 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
1915}
1916
1917/**
1918 * xmlDocDumpMemoryEnc:
1919 * @out_doc: Document to generate XML text from
1920 * @doc_txt_ptr: Memory pointer for allocated XML text
1921 * @doc_txt_len: Length of the generated XML text
1922 * @txt_encoding: Character encoding to use when generating XML text
1923 *
1924 * Dump the current DOM tree into memory using the character encoding specified
1925 * by the caller. Note it is up to the caller of this function to free the
1926 * allocated memory with xmlFree().
1927 */
1928
1929void
1930xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
1931 int * doc_txt_len, const char * txt_encoding) {
1932 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
1933 txt_encoding, 0);
1934}
1935
1936/**
1937 * xmlDocFormatDump:
1938 * @f: the FILE*
1939 * @cur: the document
1940 * @format: should formatting spaces been added
1941 *
1942 * Dump an XML document to an open FILE.
1943 *
1944 * returns: the number of bytes written or -1 in case of failure.
1945 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1946 * or xmlKeepBlanksDefault(0) was called
1947 */
1948int
1949xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001950 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001951 xmlOutputBufferPtr buf;
1952 const char * encoding;
1953 xmlCharEncodingHandlerPtr handler = NULL;
1954 int ret;
1955
1956 if (cur == NULL) {
1957#ifdef DEBUG_TREE
1958 xmlGenericError(xmlGenericErrorContext,
1959 "xmlDocDump : document == NULL\n");
1960#endif
1961 return(-1);
1962 }
1963 encoding = (const char *) cur->encoding;
1964
1965 if (encoding != NULL) {
1966 handler = xmlFindCharEncodingHandler(encoding);
1967 if (handler == NULL) {
1968 xmlFree((char *) cur->encoding);
1969 cur->encoding = NULL;
1970 }
1971 }
1972 buf = xmlOutputBufferCreateFile(f, handler);
1973 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001974 memset(&ctxt, 0, sizeof(ctxt));
1975 ctxt.doc = cur;
1976 ctxt.buf = buf;
1977 ctxt.level = 0;
1978 ctxt.format = format;
1979 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00001980 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00001981 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00001982
1983 ret = xmlOutputBufferClose(buf);
1984 return(ret);
1985}
1986
1987/**
1988 * xmlDocDump:
1989 * @f: the FILE*
1990 * @cur: the document
1991 *
1992 * Dump an XML document to an open FILE.
1993 *
1994 * returns: the number of bytes written or -1 in case of failure.
1995 */
1996int
1997xmlDocDump(FILE *f, xmlDocPtr cur) {
1998 return(xmlDocFormatDump (f, cur, 0));
1999}
2000
2001/**
2002 * xmlSaveFileTo:
2003 * @buf: an output I/O buffer
2004 * @cur: the document
2005 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2006 *
2007 * Dump an XML document to an I/O buffer.
2008 *
2009 * returns: the number of bytes written or -1 in case of failure.
2010 */
2011int
2012xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002013 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002014 int ret;
2015
2016 if (buf == NULL) return(0);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002017 memset(&ctxt, 0, sizeof(ctxt));
2018 ctxt.doc = cur;
2019 ctxt.buf = buf;
2020 ctxt.level = 0;
2021 ctxt.format = 0;
2022 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002023 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002024 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002025 ret = xmlOutputBufferClose(buf);
2026 return(ret);
2027}
2028
2029/**
2030 * xmlSaveFormatFileTo:
2031 * @buf: an output I/O buffer
2032 * @cur: the document
2033 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2034 * @format: should formatting spaces been added
2035 *
2036 * Dump an XML document to an I/O buffer.
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002037 * NOTE: the I/O buffer is closed as part of the call.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002038 *
2039 * returns: the number of bytes written or -1 in case of failure.
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002040 */
2041int
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002042xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2043 const char *encoding, int format)
2044{
2045 xmlSaveCtxt ctxt;
2046 int ret;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002047
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002048 if (buf == NULL)
2049 return (0);
2050 memset(&ctxt, 0, sizeof(ctxt));
2051 ctxt.doc = cur;
2052 ctxt.buf = buf;
2053 ctxt.level = 0;
2054 ctxt.format = format;
2055 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002056 xmlSaveCtxtInit(&ctxt);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002057 xmlDocContentDumpOutput(&ctxt, cur);
2058 ret = xmlOutputBufferClose(buf);
2059 return (ret);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002060}
2061
2062/**
2063 * xmlSaveFormatFileEnc:
2064 * @filename: the filename or URL to output
2065 * @cur: the document being saved
2066 * @encoding: the name of the encoding to use or NULL.
2067 * @format: should formatting spaces be added.
2068 *
2069 * Dump an XML document to a file or an URL.
2070 *
2071 * Returns the number of bytes written or -1 in case of error.
2072 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2073 * or xmlKeepBlanksDefault(0) was called
2074 */
2075int
2076xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2077 const char * encoding, int format ) {
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002078 xmlSaveCtxt ctxt;
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002079 xmlOutputBufferPtr buf;
2080 xmlCharEncodingHandlerPtr handler = NULL;
2081 int ret;
2082
2083 if (cur == NULL)
2084 return(-1);
2085
2086 if (encoding == NULL)
2087 encoding = (const char *) cur->encoding;
2088
2089 if (encoding != NULL) {
2090
2091 handler = xmlFindCharEncodingHandler(encoding);
2092 if (handler == NULL)
2093 return(-1);
2094 }
2095
2096#ifdef HAVE_ZLIB_H
2097 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2098#endif
2099 /*
2100 * save the content to a temp buffer.
2101 */
2102 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2103 if (buf == NULL) return(-1);
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002104 memset(&ctxt, 0, sizeof(ctxt));
2105 ctxt.doc = cur;
2106 ctxt.buf = buf;
2107 ctxt.level = 0;
2108 ctxt.format = format;
2109 ctxt.encoding = (const xmlChar *) encoding;
Daniel Veillard753086a2004-03-28 16:12:44 +00002110 xmlSaveCtxtInit(&ctxt);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002111
Daniel Veillard32b7cdb2004-03-15 13:46:37 +00002112 xmlDocContentDumpOutput(&ctxt, cur);
Daniel Veillard1a8741c2004-03-04 13:40:59 +00002113
2114 ret = xmlOutputBufferClose(buf);
2115 return(ret);
2116}
2117
2118
2119/**
2120 * xmlSaveFileEnc:
2121 * @filename: the filename (or URL)
2122 * @cur: the document
2123 * @encoding: the name of an encoding (or NULL)
2124 *
2125 * Dump an XML document, converting it to the given encoding
2126 *
2127 * returns: the number of bytes written or -1 in case of failure.
2128 */
2129int
2130xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2131 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2132}
2133
2134/**
2135 * xmlSaveFormatFile:
2136 * @filename: the filename (or URL)
2137 * @cur: the document
2138 * @format: should formatting spaces been added
2139 *
2140 * Dump an XML document to a file. Will use compression if
2141 * compiled in and enabled. If @filename is "-" the stdout file is
2142 * used. If @format is set then the document will be indented on output.
2143 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2144 * or xmlKeepBlanksDefault(0) was called
2145 *
2146 * returns: the number of bytes written or -1 in case of failure.
2147 */
2148int
2149xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2150 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2151}
2152
2153/**
2154 * xmlSaveFile:
2155 * @filename: the filename (or URL)
2156 * @cur: the document
2157 *
2158 * Dump an XML document to a file. Will use compression if
2159 * compiled in and enabled. If @filename is "-" the stdout file is
2160 * used.
2161 * returns: the number of bytes written or -1 in case of failure.
2162 */
2163int
2164xmlSaveFile(const char *filename, xmlDocPtr cur) {
2165 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2166}
2167
2168#endif /* LIBXML_OUTPUT_ENABLED */
2169