Merge trunk head (r43756)
[reactos.git] / reactos / lib / 3rdparty / libxml2 / xmlsave.c
1 /*
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>
17
18 #define MAX_INDENT 60
19
20 #include <libxml/HTMLtree.h>
21
22 /************************************************************************
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 */
50 int
51 xmlIsXHTML(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 }
66
67 #ifdef LIBXML_OUTPUT_ENABLED
68
69 #define TODO \
70 xmlGenericError(xmlGenericErrorContext, \
71 "Unimplemented block at %s:%d\n", \
72 __FILE__, __LINE__);
73
74 struct _xmlSaveCtxt {
75 void *_private;
76 int type;
77 int fd;
78 const xmlChar *filename;
79 const xmlChar *encoding;
80 xmlCharEncodingHandlerPtr handler;
81 xmlOutputBufferPtr buf;
82 xmlDocPtr doc;
83 int options;
84 int level;
85 int format;
86 char indent[MAX_INDENT + 1]; /* array for indenting output */
87 int indent_nr;
88 int indent_size;
89 xmlCharEncodingOutputFunc escape; /* used for element content */
90 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
91 };
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 */
104 static void
105 xmlSaveErrMemory(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 */
118 static void
119 xmlSaveErr(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\n";
126 break;
127 case XML_SAVE_CHAR_INVALID:
128 msg = "invalid character value\n";
129 break;
130 case XML_SAVE_UNKNOWN_ENCODING:
131 msg = "unknown encoding %s\n";
132 break;
133 case XML_SAVE_NO_DOCTYPE:
134 msg = "document has no DOCTYPE\n";
135 break;
136 default:
137 msg = "unexpected error number\n";
138 }
139 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
140 }
141
142 /************************************************************************
143 * *
144 * Special escaping routines *
145 * *
146 ************************************************************************/
147 static unsigned char *
148 xmlSerializeHexCharRef(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
188 /**
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 */
203 static int
204 xmlEscapeEntities(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 */
251 if (outend - out < 10) break;
252
253 if (*in < 0xC0) {
254 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
255 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 {
282 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
283 in++;
284 goto error;
285 }
286 if (!IS_CHAR(val)) {
287 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
288 in++;
289 goto error;
290 }
291
292 /*
293 * We could do multiple things here. Just save as a char ref
294 */
295 out = xmlSerializeHexCharRef(out, val);
296 } else if (IS_BYTE_CHAR(*in)) {
297 if (outend - out < 6) break;
298 out = xmlSerializeHexCharRef(out, *in++);
299 } 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);
309 error:
310 *outlen = out - outstart;
311 *inlen = in - base;
312 return(-1);
313 }
314
315 /************************************************************************
316 * *
317 * Allocation and deallocation *
318 * *
319 ************************************************************************/
320 /**
321 * xmlSaveCtxtInit:
322 * @ctxt: the saving context
323 *
324 * Initialize a saving context
325 */
326 static void
327 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
328 {
329 int i;
330 int len;
331
332 if (ctxt == NULL) return;
333 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
334 ctxt->escape = xmlEscapeEntities;
335 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
336 if ((xmlTreeIndentString == NULL) || (len == 0)) {
337 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
338 } else {
339 ctxt->indent_size = len;
340 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
341 for (i = 0;i < ctxt->indent_nr;i++)
342 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
343 ctxt->indent_size);
344 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
345 }
346
347 if (xmlSaveNoEmptyTags) {
348 ctxt->options |= XML_SAVE_NO_EMPTY;
349 }
350 }
351
352 /**
353 * xmlFreeSaveCtxt:
354 *
355 * Free a saving context, destroying the ouptut in any remaining buffer
356 */
357 static void
358 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
359 {
360 if (ctxt == NULL) return;
361 if (ctxt->encoding != NULL)
362 xmlFree((char *) ctxt->encoding);
363 if (ctxt->buf != NULL)
364 xmlOutputBufferClose(ctxt->buf);
365 xmlFree(ctxt);
366 }
367
368 /**
369 * xmlNewSaveCtxt:
370 *
371 * Create a new saving context
372 *
373 * Returns the new structure or NULL in case of error
374 */
375 static xmlSaveCtxtPtr
376 xmlNewSaveCtxt(const char *encoding, int options)
377 {
378 xmlSaveCtxtPtr ret;
379
380 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
381 if (ret == NULL) {
382 xmlSaveErrMemory("creating saving context");
383 return ( NULL );
384 }
385 memset(ret, 0, sizeof(xmlSaveCtxt));
386
387 if (encoding != NULL) {
388 ret->handler = xmlFindCharEncodingHandler(encoding);
389 if (ret->handler == NULL) {
390 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
391 xmlFreeSaveCtxt(ret);
392 return(NULL);
393 }
394 ret->encoding = xmlStrdup((const xmlChar *)encoding);
395 ret->escape = NULL;
396 }
397 xmlSaveCtxtInit(ret);
398
399 /*
400 * Use the options
401 */
402
403 /* Re-check this option as it may already have been set */
404 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
405 options |= XML_SAVE_NO_EMPTY;
406 }
407
408 ret->options = options;
409 if (options & XML_SAVE_FORMAT)
410 ret->format = 1;
411
412 return(ret);
413 }
414
415 /************************************************************************
416 * *
417 * Dumping XML tree content to a simple buffer *
418 * *
419 ************************************************************************/
420 /**
421 * xmlAttrSerializeContent:
422 * @buf: the XML buffer output
423 * @doc: the document
424 * @attr: the attribute pointer
425 *
426 * Serialize the attribute in the buffer
427 */
428 static void
429 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
430 {
431 xmlNodePtr children;
432
433 children = attr->children;
434 while (children != NULL) {
435 switch (children->type) {
436 case XML_TEXT_NODE:
437 xmlAttrSerializeTxtContent(buf->buffer, attr->doc,
438 attr, children->content);
439 break;
440 case XML_ENTITY_REF_NODE:
441 xmlBufferAdd(buf->buffer, BAD_CAST "&", 1);
442 xmlBufferAdd(buf->buffer, children->name,
443 xmlStrlen(children->name));
444 xmlBufferAdd(buf->buffer, BAD_CAST ";", 1);
445 break;
446 default:
447 /* should not happen unless we have a badly built tree */
448 break;
449 }
450 children = children->next;
451 }
452 }
453
454 /************************************************************************
455 * *
456 * Dumping XML tree content to an I/O output buffer *
457 * *
458 ************************************************************************/
459
460 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
461 xmlOutputBufferPtr buf = ctxt->buf;
462
463 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
464 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
465 if (buf->encoder == NULL) {
466 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
467 (const char *)encoding);
468 return(-1);
469 }
470 buf->conv = xmlBufferCreate();
471 if (buf->conv == NULL) {
472 xmlCharEncCloseFunc(buf->encoder);
473 xmlSaveErrMemory("creating encoding buffer");
474 return(-1);
475 }
476 /*
477 * initialize the state, e.g. if outputting a BOM
478 */
479 xmlCharEncOutFunc(buf->encoder, buf->conv, NULL);
480 }
481 return(0);
482 }
483
484 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
485 xmlOutputBufferPtr buf = ctxt->buf;
486 xmlOutputBufferFlush(buf);
487 xmlCharEncCloseFunc(buf->encoder);
488 xmlBufferFree(buf->conv);
489 buf->encoder = NULL;
490 buf->conv = NULL;
491 return(0);
492 }
493
494 #ifdef LIBXML_HTML_ENABLED
495 static void
496 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
497 #endif
498 static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
499 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
500 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
501 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
502
503 /**
504 * xmlNsDumpOutput:
505 * @buf: the XML buffer output
506 * @cur: a namespace
507 *
508 * Dump a local Namespace definition.
509 * Should be called in the context of attributes dumps.
510 */
511 static void
512 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
513 if ((cur == NULL) || (buf == NULL)) return;
514 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
515 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
516 return;
517
518 /* Within the context of an element attributes */
519 if (cur->prefix != NULL) {
520 xmlOutputBufferWrite(buf, 7, " xmlns:");
521 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
522 } else
523 xmlOutputBufferWrite(buf, 6, " xmlns");
524 xmlOutputBufferWrite(buf, 1, "=");
525 xmlBufferWriteQuotedString(buf->buffer, cur->href);
526 }
527 }
528
529 /**
530 * xmlNsListDumpOutput:
531 * @buf: the XML buffer output
532 * @cur: the first namespace
533 *
534 * Dump a list of local Namespace definitions.
535 * Should be called in the context of attributes dumps.
536 */
537 void
538 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
539 while (cur != NULL) {
540 xmlNsDumpOutput(buf, cur);
541 cur = cur->next;
542 }
543 }
544
545 /**
546 * xmlDtdDumpOutput:
547 * @buf: the XML buffer output
548 * @dtd: the pointer to the DTD
549 *
550 * Dump the XML document DTD, if any.
551 */
552 static void
553 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
554 xmlOutputBufferPtr buf;
555 int format, level;
556 xmlDocPtr doc;
557
558 if (dtd == NULL) return;
559 if ((ctxt == NULL) || (ctxt->buf == NULL))
560 return;
561 buf = ctxt->buf;
562 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
563 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
564 if (dtd->ExternalID != NULL) {
565 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
566 xmlBufferWriteQuotedString(buf->buffer, dtd->ExternalID);
567 xmlOutputBufferWrite(buf, 1, " ");
568 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
569 } else if (dtd->SystemID != NULL) {
570 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
571 xmlBufferWriteQuotedString(buf->buffer, dtd->SystemID);
572 }
573 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
574 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
575 (dtd->pentities == NULL)) {
576 xmlOutputBufferWrite(buf, 1, ">");
577 return;
578 }
579 xmlOutputBufferWrite(buf, 3, " [\n");
580 /*
581 * Dump the notations first they are not in the DTD children list
582 * Do this only on a standalone DTD or on the internal subset though.
583 */
584 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
585 (dtd->doc->intSubset == dtd))) {
586 xmlDumpNotationTable(buf->buffer, (xmlNotationTablePtr) dtd->notations);
587 }
588 format = ctxt->format;
589 level = ctxt->level;
590 doc = ctxt->doc;
591 ctxt->format = 0;
592 ctxt->level = -1;
593 ctxt->doc = dtd->doc;
594 xmlNodeListDumpOutput(ctxt, dtd->children);
595 ctxt->format = format;
596 ctxt->level = level;
597 ctxt->doc = doc;
598 xmlOutputBufferWrite(buf, 2, "]>");
599 }
600
601 /**
602 * xmlAttrDumpOutput:
603 * @buf: the XML buffer output
604 * @cur: the attribute pointer
605 *
606 * Dump an XML attribute
607 */
608 static void
609 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
610 xmlOutputBufferPtr buf;
611
612 if (cur == NULL) return;
613 buf = ctxt->buf;
614 if (buf == NULL) return;
615 xmlOutputBufferWrite(buf, 1, " ");
616 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
617 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
618 xmlOutputBufferWrite(buf, 1, ":");
619 }
620 xmlOutputBufferWriteString(buf, (const char *)cur->name);
621 xmlOutputBufferWrite(buf, 2, "=\"");
622 xmlAttrSerializeContent(buf, cur);
623 xmlOutputBufferWrite(buf, 1, "\"");
624 }
625
626 /**
627 * xmlAttrListDumpOutput:
628 * @buf: the XML buffer output
629 * @doc: the document
630 * @cur: the first attribute pointer
631 * @encoding: an optional encoding string
632 *
633 * Dump a list of XML attributes
634 */
635 static void
636 xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
637 if (cur == NULL) return;
638 while (cur != NULL) {
639 xmlAttrDumpOutput(ctxt, cur);
640 cur = cur->next;
641 }
642 }
643
644
645
646 /**
647 * xmlNodeListDumpOutput:
648 * @cur: the first node
649 *
650 * Dump an XML node list, recursive behaviour, children are printed too.
651 */
652 static void
653 xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
654 xmlOutputBufferPtr buf;
655
656 if (cur == NULL) return;
657 buf = ctxt->buf;
658 while (cur != NULL) {
659 if ((ctxt->format) && (xmlIndentTreeOutput) &&
660 ((cur->type == XML_ELEMENT_NODE) ||
661 (cur->type == XML_COMMENT_NODE) ||
662 (cur->type == XML_PI_NODE)))
663 xmlOutputBufferWrite(buf, ctxt->indent_size *
664 (ctxt->level > ctxt->indent_nr ?
665 ctxt->indent_nr : ctxt->level),
666 ctxt->indent);
667 xmlNodeDumpOutputInternal(ctxt, cur);
668 if (ctxt->format) {
669 xmlOutputBufferWrite(buf, 1, "\n");
670 }
671 cur = cur->next;
672 }
673 }
674
675 #ifdef LIBXML_HTML_ENABLED
676 /**
677 * xmlNodeDumpOutputInternal:
678 * @cur: the current node
679 *
680 * Dump an HTML node, recursive behaviour, children are printed too.
681 */
682 static int
683 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
684 const xmlChar *oldenc = NULL;
685 const xmlChar *oldctxtenc = ctxt->encoding;
686 const xmlChar *encoding = ctxt->encoding;
687 xmlOutputBufferPtr buf = ctxt->buf;
688 int switched_encoding = 0;
689 xmlDocPtr doc;
690
691 xmlInitParser();
692
693 doc = cur->doc;
694 if (doc != NULL) {
695 oldenc = doc->encoding;
696 if (ctxt->encoding != NULL) {
697 doc->encoding = BAD_CAST ctxt->encoding;
698 } else if (doc->encoding != NULL) {
699 encoding = doc->encoding;
700 }
701 }
702
703 if ((encoding != NULL) && (doc != NULL))
704 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
705 if ((encoding == NULL) && (doc != NULL))
706 encoding = htmlGetMetaEncoding(doc);
707 if (encoding == NULL)
708 encoding = BAD_CAST "HTML";
709 if ((encoding != NULL) && (oldctxtenc == NULL) &&
710 (buf->encoder == NULL) && (buf->conv == NULL)) {
711 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
712 doc->encoding = oldenc;
713 return(-1);
714 }
715 switched_encoding = 1;
716 }
717 if (ctxt->options & XML_SAVE_FORMAT)
718 htmlNodeDumpFormatOutput(buf, doc, cur,
719 (const char *)encoding, 1);
720 else
721 htmlNodeDumpFormatOutput(buf, doc, cur,
722 (const char *)encoding, 0);
723 /*
724 * Restore the state of the saving context at the end of the document
725 */
726 if ((switched_encoding) && (oldctxtenc == NULL)) {
727 xmlSaveClearEncoding(ctxt);
728 }
729 if (doc != NULL)
730 doc->encoding = oldenc;
731 return(0);
732 }
733 #endif
734
735 /**
736 * xmlNodeDumpOutputInternal:
737 * @cur: the current node
738 *
739 * Dump an XML node, recursive behaviour, children are printed too.
740 */
741 static void
742 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
743 int format;
744 xmlNodePtr tmp;
745 xmlChar *start, *end;
746 xmlOutputBufferPtr buf;
747
748 if (cur == NULL) return;
749 buf = ctxt->buf;
750 if (cur->type == XML_XINCLUDE_START)
751 return;
752 if (cur->type == XML_XINCLUDE_END)
753 return;
754 if ((cur->type == XML_DOCUMENT_NODE) ||
755 (cur->type == XML_HTML_DOCUMENT_NODE)) {
756 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
757 return;
758 }
759 #ifdef LIBXML_HTML_ENABLED
760 if (ctxt->options & XML_SAVE_XHTML) {
761 xhtmlNodeDumpOutput(ctxt, cur);
762 return;
763 }
764 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
765 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
766 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
767 (ctxt->options & XML_SAVE_AS_HTML)) {
768 htmlNodeDumpOutputInternal(ctxt, cur);
769 return;
770 }
771 #endif
772 if (cur->type == XML_DTD_NODE) {
773 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
774 return;
775 }
776 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
777 xmlNodeListDumpOutput(ctxt, cur->children);
778 return;
779 }
780 if (cur->type == XML_ELEMENT_DECL) {
781 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
782 return;
783 }
784 if (cur->type == XML_ATTRIBUTE_DECL) {
785 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
786 return;
787 }
788 if (cur->type == XML_ENTITY_DECL) {
789 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
790 return;
791 }
792 if (cur->type == XML_TEXT_NODE) {
793 if (cur->content != NULL) {
794 if (cur->name != xmlStringTextNoenc) {
795 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
796 } else {
797 /*
798 * Disable escaping, needed for XSLT
799 */
800 xmlOutputBufferWriteString(buf, (const char *) cur->content);
801 }
802 }
803
804 return;
805 }
806 if (cur->type == XML_PI_NODE) {
807 if (cur->content != NULL) {
808 xmlOutputBufferWrite(buf, 2, "<?");
809 xmlOutputBufferWriteString(buf, (const char *)cur->name);
810 if (cur->content != NULL) {
811 xmlOutputBufferWrite(buf, 1, " ");
812 xmlOutputBufferWriteString(buf, (const char *)cur->content);
813 }
814 xmlOutputBufferWrite(buf, 2, "?>");
815 } else {
816 xmlOutputBufferWrite(buf, 2, "<?");
817 xmlOutputBufferWriteString(buf, (const char *)cur->name);
818 xmlOutputBufferWrite(buf, 2, "?>");
819 }
820 return;
821 }
822 if (cur->type == XML_COMMENT_NODE) {
823 if (cur->content != NULL) {
824 xmlOutputBufferWrite(buf, 4, "<!--");
825 xmlOutputBufferWriteString(buf, (const char *)cur->content);
826 xmlOutputBufferWrite(buf, 3, "-->");
827 }
828 return;
829 }
830 if (cur->type == XML_ENTITY_REF_NODE) {
831 xmlOutputBufferWrite(buf, 1, "&");
832 xmlOutputBufferWriteString(buf, (const char *)cur->name);
833 xmlOutputBufferWrite(buf, 1, ";");
834 return;
835 }
836 if (cur->type == XML_CDATA_SECTION_NODE) {
837 if (cur->content == NULL || *cur->content == '\0') {
838 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
839 } else {
840 start = end = cur->content;
841 while (*end != '\0') {
842 if ((*end == ']') && (*(end + 1) == ']') &&
843 (*(end + 2) == '>')) {
844 end = end + 2;
845 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
846 xmlOutputBufferWrite(buf, end - start, (const char *)start);
847 xmlOutputBufferWrite(buf, 3, "]]>");
848 start = end;
849 }
850 end++;
851 }
852 if (start != end) {
853 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
854 xmlOutputBufferWriteString(buf, (const char *)start);
855 xmlOutputBufferWrite(buf, 3, "]]>");
856 }
857 }
858 return;
859 }
860 if (cur->type == XML_ATTRIBUTE_NODE) {
861 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
862 return;
863 }
864 if (cur->type == XML_NAMESPACE_DECL) {
865 xmlNsDumpOutput(buf, (xmlNsPtr) cur);
866 return;
867 }
868
869 format = ctxt->format;
870 if (format == 1) {
871 tmp = cur->children;
872 while (tmp != NULL) {
873 if ((tmp->type == XML_TEXT_NODE) ||
874 (tmp->type == XML_CDATA_SECTION_NODE) ||
875 (tmp->type == XML_ENTITY_REF_NODE)) {
876 ctxt->format = 0;
877 break;
878 }
879 tmp = tmp->next;
880 }
881 }
882 xmlOutputBufferWrite(buf, 1, "<");
883 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
884 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
885 xmlOutputBufferWrite(buf, 1, ":");
886 }
887
888 xmlOutputBufferWriteString(buf, (const char *)cur->name);
889 if (cur->nsDef)
890 xmlNsListDumpOutput(buf, cur->nsDef);
891 if (cur->properties != NULL)
892 xmlAttrListDumpOutput(ctxt, cur->properties);
893
894 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
895 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
896 xmlOutputBufferWrite(buf, 2, "/>");
897 ctxt->format = format;
898 return;
899 }
900 xmlOutputBufferWrite(buf, 1, ">");
901 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
902 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
903 }
904 if (cur->children != NULL) {
905 if (ctxt->format) xmlOutputBufferWrite(buf, 1, "\n");
906 if (ctxt->level >= 0) ctxt->level++;
907 xmlNodeListDumpOutput(ctxt, cur->children);
908 if (ctxt->level > 0) ctxt->level--;
909 if ((xmlIndentTreeOutput) && (ctxt->format))
910 xmlOutputBufferWrite(buf, ctxt->indent_size *
911 (ctxt->level > ctxt->indent_nr ?
912 ctxt->indent_nr : ctxt->level),
913 ctxt->indent);
914 }
915 xmlOutputBufferWrite(buf, 2, "</");
916 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
917 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
918 xmlOutputBufferWrite(buf, 1, ":");
919 }
920
921 xmlOutputBufferWriteString(buf, (const char *)cur->name);
922 xmlOutputBufferWrite(buf, 1, ">");
923 ctxt->format = format;
924 }
925
926 /**
927 * xmlDocContentDumpOutput:
928 * @cur: the document
929 *
930 * Dump an XML document.
931 */
932 static int
933 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
934 #ifdef LIBXML_HTML_ENABLED
935 xmlDtdPtr dtd;
936 int is_xhtml = 0;
937 #endif
938 const xmlChar *oldenc = cur->encoding;
939 const xmlChar *oldctxtenc = ctxt->encoding;
940 const xmlChar *encoding = ctxt->encoding;
941 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
942 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
943 xmlOutputBufferPtr buf = ctxt->buf;
944 xmlCharEncoding enc;
945 int switched_encoding = 0;
946
947 xmlInitParser();
948
949 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
950 (cur->type != XML_DOCUMENT_NODE))
951 return(-1);
952
953 if (ctxt->encoding != NULL) {
954 cur->encoding = BAD_CAST ctxt->encoding;
955 } else if (cur->encoding != NULL) {
956 encoding = cur->encoding;
957 } else if (cur->charset != XML_CHAR_ENCODING_UTF8) {
958 encoding = (const xmlChar *)
959 xmlGetCharEncodingName((xmlCharEncoding) cur->charset);
960 }
961
962 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
963 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
964 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
965 (ctxt->options & XML_SAVE_AS_HTML)) {
966 #ifdef LIBXML_HTML_ENABLED
967 if (encoding != NULL)
968 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
969 if (encoding == NULL)
970 encoding = htmlGetMetaEncoding(cur);
971 if (encoding == NULL)
972 encoding = BAD_CAST "HTML";
973 if ((encoding != NULL) && (oldctxtenc == NULL) &&
974 (buf->encoder == NULL) && (buf->conv == NULL)) {
975 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
976 cur->encoding = oldenc;
977 return(-1);
978 }
979 }
980 if (ctxt->options & XML_SAVE_FORMAT)
981 htmlDocContentDumpFormatOutput(buf, cur,
982 (const char *)encoding, 1);
983 else
984 htmlDocContentDumpFormatOutput(buf, cur,
985 (const char *)encoding, 0);
986 if (ctxt->encoding != NULL)
987 cur->encoding = oldenc;
988 return(0);
989 #else
990 return(-1);
991 #endif
992 } else if ((cur->type == XML_DOCUMENT_NODE) ||
993 (ctxt->options & XML_SAVE_AS_XML) ||
994 (ctxt->options & XML_SAVE_XHTML)) {
995 enc = xmlParseCharEncoding((const char*) encoding);
996 if ((encoding != NULL) && (oldctxtenc == NULL) &&
997 (buf->encoder == NULL) && (buf->conv == NULL) &&
998 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
999 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1000 (enc != XML_CHAR_ENCODING_NONE) &&
1001 (enc != XML_CHAR_ENCODING_ASCII)) {
1002 /*
1003 * we need to switch to this encoding but just for this
1004 * document since we output the XMLDecl the conversion
1005 * must be done to not generate not well formed documents.
1006 */
1007 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1008 cur->encoding = oldenc;
1009 return(-1);
1010 }
1011 switched_encoding = 1;
1012 }
1013 if (ctxt->escape == xmlEscapeEntities)
1014 ctxt->escape = NULL;
1015 if (ctxt->escapeAttr == xmlEscapeEntities)
1016 ctxt->escapeAttr = NULL;
1017 }
1018
1019
1020 /*
1021 * Save the XML declaration
1022 */
1023 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1024 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1025 if (cur->version != NULL)
1026 xmlBufferWriteQuotedString(buf->buffer, cur->version);
1027 else
1028 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1029 if (encoding != NULL) {
1030 xmlOutputBufferWrite(buf, 10, " encoding=");
1031 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1032 }
1033 switch (cur->standalone) {
1034 case 0:
1035 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1036 break;
1037 case 1:
1038 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1039 break;
1040 }
1041 xmlOutputBufferWrite(buf, 3, "?>\n");
1042 }
1043
1044 #ifdef LIBXML_HTML_ENABLED
1045 if (ctxt->options & XML_SAVE_XHTML)
1046 is_xhtml = 1;
1047 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1048 dtd = xmlGetIntSubset(cur);
1049 if (dtd != NULL) {
1050 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1051 if (is_xhtml < 0) is_xhtml = 0;
1052 }
1053 }
1054 #endif
1055 if (cur->children != NULL) {
1056 xmlNodePtr child = cur->children;
1057
1058 while (child != NULL) {
1059 ctxt->level = 0;
1060 #ifdef LIBXML_HTML_ENABLED
1061 if (is_xhtml)
1062 xhtmlNodeDumpOutput(ctxt, child);
1063 else
1064 #endif
1065 xmlNodeDumpOutputInternal(ctxt, child);
1066 xmlOutputBufferWrite(buf, 1, "\n");
1067 child = child->next;
1068 }
1069 }
1070 }
1071
1072 /*
1073 * Restore the state of the saving context at the end of the document
1074 */
1075 if ((switched_encoding) && (oldctxtenc == NULL)) {
1076 xmlSaveClearEncoding(ctxt);
1077 ctxt->escape = oldescape;
1078 ctxt->escapeAttr = oldescapeAttr;
1079 }
1080 cur->encoding = oldenc;
1081 return(0);
1082 }
1083
1084 #ifdef LIBXML_HTML_ENABLED
1085 /************************************************************************
1086 * *
1087 * Functions specific to XHTML serialization *
1088 * *
1089 ************************************************************************/
1090
1091 /**
1092 * xhtmlIsEmpty:
1093 * @node: the node
1094 *
1095 * Check if a node is an empty xhtml node
1096 *
1097 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1098 */
1099 static int
1100 xhtmlIsEmpty(xmlNodePtr node) {
1101 if (node == NULL)
1102 return(-1);
1103 if (node->type != XML_ELEMENT_NODE)
1104 return(0);
1105 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1106 return(0);
1107 if (node->children != NULL)
1108 return(0);
1109 switch (node->name[0]) {
1110 case 'a':
1111 if (xmlStrEqual(node->name, BAD_CAST "area"))
1112 return(1);
1113 return(0);
1114 case 'b':
1115 if (xmlStrEqual(node->name, BAD_CAST "br"))
1116 return(1);
1117 if (xmlStrEqual(node->name, BAD_CAST "base"))
1118 return(1);
1119 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1120 return(1);
1121 return(0);
1122 case 'c':
1123 if (xmlStrEqual(node->name, BAD_CAST "col"))
1124 return(1);
1125 return(0);
1126 case 'f':
1127 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1128 return(1);
1129 return(0);
1130 case 'h':
1131 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1132 return(1);
1133 return(0);
1134 case 'i':
1135 if (xmlStrEqual(node->name, BAD_CAST "img"))
1136 return(1);
1137 if (xmlStrEqual(node->name, BAD_CAST "input"))
1138 return(1);
1139 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1140 return(1);
1141 return(0);
1142 case 'l':
1143 if (xmlStrEqual(node->name, BAD_CAST "link"))
1144 return(1);
1145 return(0);
1146 case 'm':
1147 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1148 return(1);
1149 return(0);
1150 case 'p':
1151 if (xmlStrEqual(node->name, BAD_CAST "param"))
1152 return(1);
1153 return(0);
1154 }
1155 return(0);
1156 }
1157
1158 /**
1159 * xhtmlAttrListDumpOutput:
1160 * @cur: the first attribute pointer
1161 *
1162 * Dump a list of XML attributes
1163 */
1164 static void
1165 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1166 xmlAttrPtr xml_lang = NULL;
1167 xmlAttrPtr lang = NULL;
1168 xmlAttrPtr name = NULL;
1169 xmlAttrPtr id = NULL;
1170 xmlNodePtr parent;
1171 xmlOutputBufferPtr buf;
1172
1173 if (cur == NULL) return;
1174 buf = ctxt->buf;
1175 parent = cur->parent;
1176 while (cur != NULL) {
1177 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1178 id = cur;
1179 else
1180 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1181 name = cur;
1182 else
1183 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1184 lang = cur;
1185 else
1186 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1187 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1188 xml_lang = cur;
1189 else if ((cur->ns == NULL) &&
1190 ((cur->children == NULL) ||
1191 (cur->children->content == NULL) ||
1192 (cur->children->content[0] == 0)) &&
1193 (htmlIsBooleanAttr(cur->name))) {
1194 if (cur->children != NULL)
1195 xmlFreeNode(cur->children);
1196 cur->children = xmlNewText(cur->name);
1197 if (cur->children != NULL)
1198 cur->children->parent = (xmlNodePtr) cur;
1199 }
1200 xmlAttrDumpOutput(ctxt, cur);
1201 cur = cur->next;
1202 }
1203 /*
1204 * C.8
1205 */
1206 if ((name != NULL) && (id == NULL)) {
1207 if ((parent != NULL) && (parent->name != NULL) &&
1208 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1209 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1210 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1211 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1212 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1213 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1214 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1215 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1216 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1217 xmlOutputBufferWrite(buf, 5, " id=\"");
1218 xmlAttrSerializeContent(buf, name);
1219 xmlOutputBufferWrite(buf, 1, "\"");
1220 }
1221 }
1222 /*
1223 * C.7.
1224 */
1225 if ((lang != NULL) && (xml_lang == NULL)) {
1226 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1227 xmlAttrSerializeContent(buf, lang);
1228 xmlOutputBufferWrite(buf, 1, "\"");
1229 } else
1230 if ((xml_lang != NULL) && (lang == NULL)) {
1231 xmlOutputBufferWrite(buf, 7, " lang=\"");
1232 xmlAttrSerializeContent(buf, xml_lang);
1233 xmlOutputBufferWrite(buf, 1, "\"");
1234 }
1235 }
1236
1237 /**
1238 * xhtmlNodeListDumpOutput:
1239 * @buf: the XML buffer output
1240 * @doc: the XHTML document
1241 * @cur: the first node
1242 * @level: the imbrication level for indenting
1243 * @format: is formatting allowed
1244 * @encoding: an optional encoding string
1245 *
1246 * Dump an XML node list, recursive behaviour, children are printed too.
1247 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1248 * or xmlKeepBlanksDefault(0) was called
1249 */
1250 static void
1251 xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1252 xmlOutputBufferPtr buf;
1253
1254 if (cur == NULL) return;
1255 buf = ctxt->buf;
1256 while (cur != NULL) {
1257 if ((ctxt->format) && (xmlIndentTreeOutput) &&
1258 (cur->type == XML_ELEMENT_NODE))
1259 xmlOutputBufferWrite(buf, ctxt->indent_size *
1260 (ctxt->level > ctxt->indent_nr ?
1261 ctxt->indent_nr : ctxt->level),
1262 ctxt->indent);
1263 xhtmlNodeDumpOutput(ctxt, cur);
1264 if (ctxt->format) {
1265 xmlOutputBufferWrite(buf, 1, "\n");
1266 }
1267 cur = cur->next;
1268 }
1269 }
1270
1271 /**
1272 * xhtmlNodeDumpOutput:
1273 * @buf: the XML buffer output
1274 * @doc: the XHTML document
1275 * @cur: the current node
1276 * @level: the imbrication level for indenting
1277 * @format: is formatting allowed
1278 * @encoding: an optional encoding string
1279 *
1280 * Dump an XHTML node, recursive behaviour, children are printed too.
1281 */
1282 static void
1283 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1284 int format, addmeta = 0;
1285 xmlNodePtr tmp;
1286 xmlChar *start, *end;
1287 xmlOutputBufferPtr buf;
1288
1289 if (cur == NULL) return;
1290 if ((cur->type == XML_DOCUMENT_NODE) ||
1291 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1292 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1293 return;
1294 }
1295 if (cur->type == XML_XINCLUDE_START)
1296 return;
1297 if (cur->type == XML_XINCLUDE_END)
1298 return;
1299 if (cur->type == XML_DTD_NODE) {
1300 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1301 return;
1302 }
1303 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
1304 xhtmlNodeListDumpOutput(ctxt, cur->children);
1305 return;
1306 }
1307 buf = ctxt->buf;
1308 if (cur->type == XML_ELEMENT_DECL) {
1309 xmlDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1310 return;
1311 }
1312 if (cur->type == XML_ATTRIBUTE_DECL) {
1313 xmlDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1314 return;
1315 }
1316 if (cur->type == XML_ENTITY_DECL) {
1317 xmlDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1318 return;
1319 }
1320 if (cur->type == XML_TEXT_NODE) {
1321 if (cur->content != NULL) {
1322 if ((cur->name == xmlStringText) ||
1323 (cur->name != xmlStringTextNoenc)) {
1324 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1325 } else {
1326 /*
1327 * Disable escaping, needed for XSLT
1328 */
1329 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1330 }
1331 }
1332
1333 return;
1334 }
1335 if (cur->type == XML_PI_NODE) {
1336 if (cur->content != NULL) {
1337 xmlOutputBufferWrite(buf, 2, "<?");
1338 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1339 if (cur->content != NULL) {
1340 xmlOutputBufferWrite(buf, 1, " ");
1341 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1342 }
1343 xmlOutputBufferWrite(buf, 2, "?>");
1344 } else {
1345 xmlOutputBufferWrite(buf, 2, "<?");
1346 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1347 xmlOutputBufferWrite(buf, 2, "?>");
1348 }
1349 return;
1350 }
1351 if (cur->type == XML_COMMENT_NODE) {
1352 if (cur->content != NULL) {
1353 xmlOutputBufferWrite(buf, 4, "<!--");
1354 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1355 xmlOutputBufferWrite(buf, 3, "-->");
1356 }
1357 return;
1358 }
1359 if (cur->type == XML_ENTITY_REF_NODE) {
1360 xmlOutputBufferWrite(buf, 1, "&");
1361 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1362 xmlOutputBufferWrite(buf, 1, ";");
1363 return;
1364 }
1365 if (cur->type == XML_CDATA_SECTION_NODE) {
1366 if (cur->content == NULL || *cur->content == '\0') {
1367 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1368 } else {
1369 start = end = cur->content;
1370 while (*end != '\0') {
1371 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1372 end = end + 2;
1373 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1374 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1375 xmlOutputBufferWrite(buf, 3, "]]>");
1376 start = end;
1377 }
1378 end++;
1379 }
1380 if (start != end) {
1381 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1382 xmlOutputBufferWriteString(buf, (const char *)start);
1383 xmlOutputBufferWrite(buf, 3, "]]>");
1384 }
1385 }
1386 return;
1387 }
1388 if (cur->type == XML_ATTRIBUTE_NODE) {
1389 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1390 return;
1391 }
1392
1393 format = ctxt->format;
1394 if (format == 1) {
1395 tmp = cur->children;
1396 while (tmp != NULL) {
1397 if ((tmp->type == XML_TEXT_NODE) ||
1398 (tmp->type == XML_ENTITY_REF_NODE)) {
1399 format = 0;
1400 break;
1401 }
1402 tmp = tmp->next;
1403 }
1404 }
1405 xmlOutputBufferWrite(buf, 1, "<");
1406 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1407 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1408 xmlOutputBufferWrite(buf, 1, ":");
1409 }
1410
1411 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1412 if (cur->nsDef)
1413 xmlNsListDumpOutput(buf, cur->nsDef);
1414 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1415 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1416 /*
1417 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1418 */
1419 xmlOutputBufferWriteString(buf,
1420 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1421 }
1422 if (cur->properties != NULL)
1423 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1424
1425 if ((cur->type == XML_ELEMENT_NODE) &&
1426 (cur->parent != NULL) &&
1427 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1428 xmlStrEqual(cur->name, BAD_CAST"head") &&
1429 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1430
1431 tmp = cur->children;
1432 while (tmp != NULL) {
1433 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1434 xmlChar *httpequiv;
1435
1436 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1437 if (httpequiv != NULL) {
1438 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1439 xmlFree(httpequiv);
1440 break;
1441 }
1442 xmlFree(httpequiv);
1443 }
1444 }
1445 tmp = tmp->next;
1446 }
1447 if (tmp == NULL)
1448 addmeta = 1;
1449 }
1450
1451 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1452 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1453 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1454 /*
1455 * C.2. Empty Elements
1456 */
1457 xmlOutputBufferWrite(buf, 3, " />");
1458 } else {
1459 if (addmeta == 1) {
1460 xmlOutputBufferWrite(buf, 1, ">");
1461 if (ctxt->format) {
1462 xmlOutputBufferWrite(buf, 1, "\n");
1463 if (xmlIndentTreeOutput)
1464 xmlOutputBufferWrite(buf, ctxt->indent_size *
1465 (ctxt->level + 1 > ctxt->indent_nr ?
1466 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1467 }
1468 xmlOutputBufferWriteString(buf,
1469 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1470 if (ctxt->encoding) {
1471 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1472 } else {
1473 xmlOutputBufferWrite(buf, 5, "UTF-8");
1474 }
1475 xmlOutputBufferWrite(buf, 4, "\" />");
1476 if (ctxt->format)
1477 xmlOutputBufferWrite(buf, 1, "\n");
1478 } else {
1479 xmlOutputBufferWrite(buf, 1, ">");
1480 }
1481 /*
1482 * C.3. Element Minimization and Empty Element Content
1483 */
1484 xmlOutputBufferWrite(buf, 2, "</");
1485 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1486 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1487 xmlOutputBufferWrite(buf, 1, ":");
1488 }
1489 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1490 xmlOutputBufferWrite(buf, 1, ">");
1491 }
1492 return;
1493 }
1494 xmlOutputBufferWrite(buf, 1, ">");
1495 if (addmeta == 1) {
1496 if (ctxt->format) {
1497 xmlOutputBufferWrite(buf, 1, "\n");
1498 if (xmlIndentTreeOutput)
1499 xmlOutputBufferWrite(buf, ctxt->indent_size *
1500 (ctxt->level + 1 > ctxt->indent_nr ?
1501 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1502 }
1503 xmlOutputBufferWriteString(buf,
1504 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1505 if (ctxt->encoding) {
1506 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1507 } else {
1508 xmlOutputBufferWrite(buf, 5, "UTF-8");
1509 }
1510 xmlOutputBufferWrite(buf, 4, "\" />");
1511 }
1512 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1513 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1514 }
1515
1516 #if 0
1517 /*
1518 * This was removed due to problems with HTML processors.
1519 * See bug #345147.
1520 */
1521 /*
1522 * 4.8. Script and Style elements
1523 */
1524 if ((cur->type == XML_ELEMENT_NODE) &&
1525 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1526 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1527 ((cur->ns == NULL) ||
1528 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1529 xmlNodePtr child = cur->children;
1530
1531 while (child != NULL) {
1532 if (child->type == XML_TEXT_NODE) {
1533 if ((xmlStrchr(child->content, '<') == NULL) &&
1534 (xmlStrchr(child->content, '&') == NULL) &&
1535 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
1536 /* Nothing to escape, so just output as is... */
1537 /* FIXME: Should we do something about "--" also? */
1538 int level = ctxt->level;
1539 int indent = ctxt->format;
1540
1541 ctxt->level = 0;
1542 ctxt->format = 0;
1543 xmlOutputBufferWriteString(buf, (const char *) child->content);
1544 /* (We cannot use xhtmlNodeDumpOutput() here because
1545 * we wish to leave '>' unescaped!) */
1546 ctxt->level = level;
1547 ctxt->format = indent;
1548 } else {
1549 /* We must use a CDATA section. Unfortunately,
1550 * this will break CSS and JavaScript when read by
1551 * a browser in HTML4-compliant mode. :-( */
1552 start = end = child->content;
1553 while (*end != '\0') {
1554 if (*end == ']' &&
1555 *(end + 1) == ']' &&
1556 *(end + 2) == '>') {
1557 end = end + 2;
1558 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1559 xmlOutputBufferWrite(buf, end - start,
1560 (const char *)start);
1561 xmlOutputBufferWrite(buf, 3, "]]>");
1562 start = end;
1563 }
1564 end++;
1565 }
1566 if (start != end) {
1567 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1568 xmlOutputBufferWrite(buf, end - start,
1569 (const char *)start);
1570 xmlOutputBufferWrite(buf, 3, "]]>");
1571 }
1572 }
1573 } else {
1574 int level = ctxt->level;
1575 int indent = ctxt->format;
1576
1577 ctxt->level = 0;
1578 ctxt->format = 0;
1579 xhtmlNodeDumpOutput(ctxt, child);
1580 ctxt->level = level;
1581 ctxt->format = indent;
1582 }
1583 child = child->next;
1584 }
1585 }
1586 #endif
1587
1588 if (cur->children != NULL) {
1589 int indent = ctxt->format;
1590
1591 if (format) xmlOutputBufferWrite(buf, 1, "\n");
1592 if (ctxt->level >= 0) ctxt->level++;
1593 ctxt->format = format;
1594 xhtmlNodeListDumpOutput(ctxt, cur->children);
1595 if (ctxt->level > 0) ctxt->level--;
1596 ctxt->format = indent;
1597 if ((xmlIndentTreeOutput) && (format))
1598 xmlOutputBufferWrite(buf, ctxt->indent_size *
1599 (ctxt->level > ctxt->indent_nr ?
1600 ctxt->indent_nr : ctxt->level),
1601 ctxt->indent);
1602 }
1603 xmlOutputBufferWrite(buf, 2, "</");
1604 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1605 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1606 xmlOutputBufferWrite(buf, 1, ":");
1607 }
1608
1609 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1610 xmlOutputBufferWrite(buf, 1, ">");
1611 }
1612 #endif
1613
1614 /************************************************************************
1615 * *
1616 * Public entry points *
1617 * *
1618 ************************************************************************/
1619
1620 /**
1621 * xmlSaveToFd:
1622 * @fd: a file descriptor number
1623 * @encoding: the encoding name to use or NULL
1624 * @options: a set of xmlSaveOptions
1625 *
1626 * Create a document saving context serializing to a file descriptor
1627 * with the encoding and the options given.
1628 *
1629 * Returns a new serialization context or NULL in case of error.
1630 */
1631 xmlSaveCtxtPtr
1632 xmlSaveToFd(int fd, const char *encoding, int options)
1633 {
1634 xmlSaveCtxtPtr ret;
1635
1636 ret = xmlNewSaveCtxt(encoding, options);
1637 if (ret == NULL) return(NULL);
1638 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1639 if (ret->buf == NULL) {
1640 xmlFreeSaveCtxt(ret);
1641 return(NULL);
1642 }
1643 return(ret);
1644 }
1645
1646 /**
1647 * xmlSaveToFilename:
1648 * @filename: a file name or an URL
1649 * @encoding: the encoding name to use or NULL
1650 * @options: a set of xmlSaveOptions
1651 *
1652 * Create a document saving context serializing to a filename or possibly
1653 * to an URL (but this is less reliable) with the encoding and the options
1654 * given.
1655 *
1656 * Returns a new serialization context or NULL in case of error.
1657 */
1658 xmlSaveCtxtPtr
1659 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1660 {
1661 xmlSaveCtxtPtr ret;
1662 int compression = 0; /* TODO handle compression option */
1663
1664 ret = xmlNewSaveCtxt(encoding, options);
1665 if (ret == NULL) return(NULL);
1666 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1667 compression);
1668 if (ret->buf == NULL) {
1669 xmlFreeSaveCtxt(ret);
1670 return(NULL);
1671 }
1672 return(ret);
1673 }
1674
1675 /**
1676 * xmlSaveToBuffer:
1677 * @buffer: a buffer
1678 * @encoding: the encoding name to use or NULL
1679 * @options: a set of xmlSaveOptions
1680 *
1681 * Create a document saving context serializing to a buffer
1682 * with the encoding and the options given
1683 *
1684 * Returns a new serialization context or NULL in case of error.
1685 */
1686
1687 xmlSaveCtxtPtr
1688 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1689 {
1690 xmlSaveCtxtPtr ret;
1691 xmlOutputBufferPtr out_buff;
1692 xmlCharEncodingHandlerPtr handler;
1693
1694 ret = xmlNewSaveCtxt(encoding, options);
1695 if (ret == NULL) return(NULL);
1696
1697 if (encoding != NULL) {
1698 handler = xmlFindCharEncodingHandler(encoding);
1699 if (handler == NULL) {
1700 xmlFree(ret);
1701 return(NULL);
1702 }
1703 } else
1704 handler = NULL;
1705 out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
1706 if (out_buff == NULL) {
1707 xmlFree(ret);
1708 if (handler) xmlCharEncCloseFunc(handler);
1709 return(NULL);
1710 }
1711
1712 ret->buf = out_buff;
1713 return(ret);
1714 }
1715
1716 /**
1717 * xmlSaveToIO:
1718 * @iowrite: an I/O write function
1719 * @ioclose: an I/O close function
1720 * @ioctx: an I/O handler
1721 * @encoding: the encoding name to use or NULL
1722 * @options: a set of xmlSaveOptions
1723 *
1724 * Create a document saving context serializing to a file descriptor
1725 * with the encoding and the options given
1726 *
1727 * Returns a new serialization context or NULL in case of error.
1728 */
1729 xmlSaveCtxtPtr
1730 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1731 xmlOutputCloseCallback ioclose,
1732 void *ioctx, const char *encoding, int options)
1733 {
1734 xmlSaveCtxtPtr ret;
1735
1736 ret = xmlNewSaveCtxt(encoding, options);
1737 if (ret == NULL) return(NULL);
1738 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1739 if (ret->buf == NULL) {
1740 xmlFreeSaveCtxt(ret);
1741 return(NULL);
1742 }
1743 return(ret);
1744 }
1745
1746 /**
1747 * xmlSaveDoc:
1748 * @ctxt: a document saving context
1749 * @doc: a document
1750 *
1751 * Save a full document to a saving context
1752 * TODO: The function is not fully implemented yet as it does not return the
1753 * byte count but 0 instead
1754 *
1755 * Returns the number of byte written or -1 in case of error
1756 */
1757 long
1758 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1759 {
1760 long ret = 0;
1761
1762 if ((ctxt == NULL) || (doc == NULL)) return(-1);
1763 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1764 return(-1);
1765 return(ret);
1766 }
1767
1768 /**
1769 * xmlSaveTree:
1770 * @ctxt: a document saving context
1771 * @node: the top node of the subtree to save
1772 *
1773 * Save a subtree starting at the node parameter to a saving context
1774 * TODO: The function is not fully implemented yet as it does not return the
1775 * byte count but 0 instead
1776 *
1777 * Returns the number of byte written or -1 in case of error
1778 */
1779 long
1780 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1781 {
1782 long ret = 0;
1783
1784 if ((ctxt == NULL) || (node == NULL)) return(-1);
1785 xmlNodeDumpOutputInternal(ctxt, node);
1786 return(ret);
1787 }
1788
1789 /**
1790 * xmlSaveFlush:
1791 * @ctxt: a document saving context
1792 *
1793 * Flush a document saving context, i.e. make sure that all bytes have
1794 * been output.
1795 *
1796 * Returns the number of byte written or -1 in case of error.
1797 */
1798 int
1799 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1800 {
1801 if (ctxt == NULL) return(-1);
1802 if (ctxt->buf == NULL) return(-1);
1803 return(xmlOutputBufferFlush(ctxt->buf));
1804 }
1805
1806 /**
1807 * xmlSaveClose:
1808 * @ctxt: a document saving context
1809 *
1810 * Close a document saving context, i.e. make sure that all bytes have
1811 * been output and free the associated data.
1812 *
1813 * Returns the number of byte written or -1 in case of error.
1814 */
1815 int
1816 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1817 {
1818 int ret;
1819
1820 if (ctxt == NULL) return(-1);
1821 ret = xmlSaveFlush(ctxt);
1822 xmlFreeSaveCtxt(ctxt);
1823 return(ret);
1824 }
1825
1826 /**
1827 * xmlSaveSetEscape:
1828 * @ctxt: a document saving context
1829 * @escape: the escaping function
1830 *
1831 * Set a custom escaping function to be used for text in element content
1832 *
1833 * Returns 0 if successful or -1 in case of error.
1834 */
1835 int
1836 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1837 {
1838 if (ctxt == NULL) return(-1);
1839 ctxt->escape = escape;
1840 return(0);
1841 }
1842
1843 /**
1844 * xmlSaveSetAttrEscape:
1845 * @ctxt: a document saving context
1846 * @escape: the escaping function
1847 *
1848 * Set a custom escaping function to be used for text in attribute content
1849 *
1850 * Returns 0 if successful or -1 in case of error.
1851 */
1852 int
1853 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1854 {
1855 if (ctxt == NULL) return(-1);
1856 ctxt->escapeAttr = escape;
1857 return(0);
1858 }
1859
1860 /************************************************************************
1861 * *
1862 * Public entry points based on buffers *
1863 * *
1864 ************************************************************************/
1865 /**
1866 * xmlAttrSerializeTxtContent:
1867 * @buf: the XML buffer output
1868 * @doc: the document
1869 * @attr: the attribute node
1870 * @string: the text content
1871 *
1872 * Serialize text attribute values to an xml simple buffer
1873 */
1874 void
1875 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
1876 xmlAttrPtr attr, const xmlChar * string)
1877 {
1878 xmlChar *base, *cur;
1879
1880 if (string == NULL)
1881 return;
1882 base = cur = (xmlChar *) string;
1883 while (*cur != 0) {
1884 if (*cur == '\n') {
1885 if (base != cur)
1886 xmlBufferAdd(buf, base, cur - base);
1887 xmlBufferAdd(buf, BAD_CAST "&#10;", 5);
1888 cur++;
1889 base = cur;
1890 } else if (*cur == '\r') {
1891 if (base != cur)
1892 xmlBufferAdd(buf, base, cur - base);
1893 xmlBufferAdd(buf, BAD_CAST "&#13;", 5);
1894 cur++;
1895 base = cur;
1896 } else if (*cur == '\t') {
1897 if (base != cur)
1898 xmlBufferAdd(buf, base, cur - base);
1899 xmlBufferAdd(buf, BAD_CAST "&#9;", 4);
1900 cur++;
1901 base = cur;
1902 } else if (*cur == '"') {
1903 if (base != cur)
1904 xmlBufferAdd(buf, base, cur - base);
1905 xmlBufferAdd(buf, BAD_CAST "&quot;", 6);
1906 cur++;
1907 base = cur;
1908 } else if (*cur == '<') {
1909 if (base != cur)
1910 xmlBufferAdd(buf, base, cur - base);
1911 xmlBufferAdd(buf, BAD_CAST "&lt;", 4);
1912 cur++;
1913 base = cur;
1914 } else if (*cur == '>') {
1915 if (base != cur)
1916 xmlBufferAdd(buf, base, cur - base);
1917 xmlBufferAdd(buf, BAD_CAST "&gt;", 4);
1918 cur++;
1919 base = cur;
1920 } else if (*cur == '&') {
1921 if (base != cur)
1922 xmlBufferAdd(buf, base, cur - base);
1923 xmlBufferAdd(buf, BAD_CAST "&amp;", 5);
1924 cur++;
1925 base = cur;
1926 } else if ((*cur >= 0x80) && ((doc == NULL) ||
1927 (doc->encoding == NULL))) {
1928 /*
1929 * We assume we have UTF-8 content.
1930 */
1931 unsigned char tmp[10];
1932 int val = 0, l = 1;
1933
1934 if (base != cur)
1935 xmlBufferAdd(buf, base, cur - base);
1936 if (*cur < 0xC0) {
1937 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
1938 if (doc != NULL)
1939 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1940 xmlSerializeHexCharRef(tmp, *cur);
1941 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1942 cur++;
1943 base = cur;
1944 continue;
1945 } else if (*cur < 0xE0) {
1946 val = (cur[0]) & 0x1F;
1947 val <<= 6;
1948 val |= (cur[1]) & 0x3F;
1949 l = 2;
1950 } else if (*cur < 0xF0) {
1951 val = (cur[0]) & 0x0F;
1952 val <<= 6;
1953 val |= (cur[1]) & 0x3F;
1954 val <<= 6;
1955 val |= (cur[2]) & 0x3F;
1956 l = 3;
1957 } else if (*cur < 0xF8) {
1958 val = (cur[0]) & 0x07;
1959 val <<= 6;
1960 val |= (cur[1]) & 0x3F;
1961 val <<= 6;
1962 val |= (cur[2]) & 0x3F;
1963 val <<= 6;
1964 val |= (cur[3]) & 0x3F;
1965 l = 4;
1966 }
1967 if ((l == 1) || (!IS_CHAR(val))) {
1968 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
1969 if (doc != NULL)
1970 doc->encoding = xmlStrdup(BAD_CAST "ISO-8859-1");
1971
1972 xmlSerializeHexCharRef(tmp, *cur);
1973 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1974 cur++;
1975 base = cur;
1976 continue;
1977 }
1978 /*
1979 * We could do multiple things here. Just save
1980 * as a char ref
1981 */
1982 xmlSerializeHexCharRef(tmp, val);
1983 xmlBufferAdd(buf, (xmlChar *) tmp, -1);
1984 cur += l;
1985 base = cur;
1986 } else {
1987 cur++;
1988 }
1989 }
1990 if (base != cur)
1991 xmlBufferAdd(buf, base, cur - base);
1992 }
1993
1994 /**
1995 * xmlNodeDump:
1996 * @buf: the XML buffer output
1997 * @doc: the document
1998 * @cur: the current node
1999 * @level: the imbrication level for indenting
2000 * @format: is formatting allowed
2001 *
2002 * Dump an XML node, recursive behaviour,children are printed too.
2003 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2004 * or xmlKeepBlanksDefault(0) was called
2005 *
2006 * Returns the number of bytes written to the buffer or -1 in case of error
2007 */
2008 int
2009 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2010 int format)
2011 {
2012 unsigned int use;
2013 int ret;
2014 xmlOutputBufferPtr outbuf;
2015
2016 xmlInitParser();
2017
2018 if (cur == NULL) {
2019 #ifdef DEBUG_TREE
2020 xmlGenericError(xmlGenericErrorContext,
2021 "xmlNodeDump : node == NULL\n");
2022 #endif
2023 return (-1);
2024 }
2025 if (buf == NULL) {
2026 #ifdef DEBUG_TREE
2027 xmlGenericError(xmlGenericErrorContext,
2028 "xmlNodeDump : buf == NULL\n");
2029 #endif
2030 return (-1);
2031 }
2032 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2033 if (outbuf == NULL) {
2034 xmlSaveErrMemory("creating buffer");
2035 return (-1);
2036 }
2037 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2038 outbuf->buffer = buf;
2039 outbuf->encoder = NULL;
2040 outbuf->writecallback = NULL;
2041 outbuf->closecallback = NULL;
2042 outbuf->context = NULL;
2043 outbuf->written = 0;
2044
2045 use = buf->use;
2046 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2047 xmlFree(outbuf);
2048 ret = buf->use - use;
2049 return (ret);
2050 }
2051
2052 /**
2053 * xmlElemDump:
2054 * @f: the FILE * for the output
2055 * @doc: the document
2056 * @cur: the current node
2057 *
2058 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2059 */
2060 void
2061 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2062 {
2063 xmlOutputBufferPtr outbuf;
2064
2065 xmlInitParser();
2066
2067 if (cur == NULL) {
2068 #ifdef DEBUG_TREE
2069 xmlGenericError(xmlGenericErrorContext,
2070 "xmlElemDump : cur == NULL\n");
2071 #endif
2072 return;
2073 }
2074 #ifdef DEBUG_TREE
2075 if (doc == NULL) {
2076 xmlGenericError(xmlGenericErrorContext,
2077 "xmlElemDump : doc == NULL\n");
2078 }
2079 #endif
2080
2081 outbuf = xmlOutputBufferCreateFile(f, NULL);
2082 if (outbuf == NULL)
2083 return;
2084 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2085 #ifdef LIBXML_HTML_ENABLED
2086 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2087 #else
2088 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2089 #endif /* LIBXML_HTML_ENABLED */
2090 } else
2091 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2092 xmlOutputBufferClose(outbuf);
2093 }
2094
2095 /************************************************************************
2096 * *
2097 * Saving functions front-ends *
2098 * *
2099 ************************************************************************/
2100
2101 /**
2102 * xmlNodeDumpOutput:
2103 * @buf: the XML buffer output
2104 * @doc: the document
2105 * @cur: the current node
2106 * @level: the imbrication level for indenting
2107 * @format: is formatting allowed
2108 * @encoding: an optional encoding string
2109 *
2110 * Dump an XML node, recursive behaviour, children are printed too.
2111 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2112 * or xmlKeepBlanksDefault(0) was called
2113 */
2114 void
2115 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2116 int level, int format, const char *encoding)
2117 {
2118 xmlSaveCtxt ctxt;
2119 #ifdef LIBXML_HTML_ENABLED
2120 xmlDtdPtr dtd;
2121 int is_xhtml = 0;
2122 #endif
2123
2124 xmlInitParser();
2125
2126 if ((buf == NULL) || (cur == NULL)) return;
2127
2128 if (encoding == NULL)
2129 encoding = "UTF-8";
2130
2131 memset(&ctxt, 0, sizeof(ctxt));
2132 ctxt.doc = doc;
2133 ctxt.buf = buf;
2134 ctxt.level = level;
2135 ctxt.format = format;
2136 ctxt.encoding = (const xmlChar *) encoding;
2137 xmlSaveCtxtInit(&ctxt);
2138 ctxt.options |= XML_SAVE_AS_XML;
2139
2140 #ifdef LIBXML_HTML_ENABLED
2141 dtd = xmlGetIntSubset(doc);
2142 if (dtd != NULL) {
2143 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2144 if (is_xhtml < 0)
2145 is_xhtml = 0;
2146 }
2147
2148 if (is_xhtml)
2149 xhtmlNodeDumpOutput(&ctxt, cur);
2150 else
2151 #endif
2152 xmlNodeDumpOutputInternal(&ctxt, cur);
2153 }
2154
2155 /**
2156 * xmlDocDumpFormatMemoryEnc:
2157 * @out_doc: Document to generate XML text from
2158 * @doc_txt_ptr: Memory pointer for allocated XML text
2159 * @doc_txt_len: Length of the generated XML text
2160 * @txt_encoding: Character encoding to use when generating XML text
2161 * @format: should formatting spaces been added
2162 *
2163 * Dump the current DOM tree into memory using the character encoding specified
2164 * by the caller. Note it is up to the caller of this function to free the
2165 * allocated memory with xmlFree().
2166 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2167 * or xmlKeepBlanksDefault(0) was called
2168 */
2169
2170 void
2171 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2172 int * doc_txt_len, const char * txt_encoding,
2173 int format) {
2174 xmlSaveCtxt ctxt;
2175 int dummy = 0;
2176 xmlOutputBufferPtr out_buff = NULL;
2177 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2178
2179 if (doc_txt_len == NULL) {
2180 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2181 }
2182
2183 if (doc_txt_ptr == NULL) {
2184 *doc_txt_len = 0;
2185 return;
2186 }
2187
2188 *doc_txt_ptr = NULL;
2189 *doc_txt_len = 0;
2190
2191 if (out_doc == NULL) {
2192 /* No document, no output */
2193 return;
2194 }
2195
2196 /*
2197 * Validate the encoding value, if provided.
2198 * This logic is copied from xmlSaveFileEnc.
2199 */
2200
2201 if (txt_encoding == NULL)
2202 txt_encoding = (const char *) out_doc->encoding;
2203 if (txt_encoding != NULL) {
2204 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2205 if ( conv_hdlr == NULL ) {
2206 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2207 txt_encoding);
2208 return;
2209 }
2210 }
2211
2212 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2213 xmlSaveErrMemory("creating buffer");
2214 return;
2215 }
2216
2217 memset(&ctxt, 0, sizeof(ctxt));
2218 ctxt.doc = out_doc;
2219 ctxt.buf = out_buff;
2220 ctxt.level = 0;
2221 ctxt.format = format;
2222 ctxt.encoding = (const xmlChar *) txt_encoding;
2223 xmlSaveCtxtInit(&ctxt);
2224 ctxt.options |= XML_SAVE_AS_XML;
2225 xmlDocContentDumpOutput(&ctxt, out_doc);
2226 xmlOutputBufferFlush(out_buff);
2227 if (out_buff->conv != NULL) {
2228 *doc_txt_len = out_buff->conv->use;
2229 *doc_txt_ptr = xmlStrndup(out_buff->conv->content, *doc_txt_len);
2230 } else {
2231 *doc_txt_len = out_buff->buffer->use;
2232 *doc_txt_ptr = xmlStrndup(out_buff->buffer->content, *doc_txt_len);
2233 }
2234 (void)xmlOutputBufferClose(out_buff);
2235
2236 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2237 *doc_txt_len = 0;
2238 xmlSaveErrMemory("creating output");
2239 }
2240
2241 return;
2242 }
2243
2244 /**
2245 * xmlDocDumpMemory:
2246 * @cur: the document
2247 * @mem: OUT: the memory pointer
2248 * @size: OUT: the memory length
2249 *
2250 * Dump an XML document in memory and return the #xmlChar * and it's size
2251 * in bytes. It's up to the caller to free the memory with xmlFree().
2252 * The resulting byte array is zero terminated, though the last 0 is not
2253 * included in the returned size.
2254 */
2255 void
2256 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2257 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2258 }
2259
2260 /**
2261 * xmlDocDumpFormatMemory:
2262 * @cur: the document
2263 * @mem: OUT: the memory pointer
2264 * @size: OUT: the memory length
2265 * @format: should formatting spaces been added
2266 *
2267 *
2268 * Dump an XML document in memory and return the #xmlChar * and it's size.
2269 * It's up to the caller to free the memory with xmlFree().
2270 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2271 * or xmlKeepBlanksDefault(0) was called
2272 */
2273 void
2274 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2275 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2276 }
2277
2278 /**
2279 * xmlDocDumpMemoryEnc:
2280 * @out_doc: Document to generate XML text from
2281 * @doc_txt_ptr: Memory pointer for allocated XML text
2282 * @doc_txt_len: Length of the generated XML text
2283 * @txt_encoding: Character encoding to use when generating XML text
2284 *
2285 * Dump the current DOM tree into memory using the character encoding specified
2286 * by the caller. Note it is up to the caller of this function to free the
2287 * allocated memory with xmlFree().
2288 */
2289
2290 void
2291 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2292 int * doc_txt_len, const char * txt_encoding) {
2293 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2294 txt_encoding, 0);
2295 }
2296
2297 /**
2298 * xmlDocFormatDump:
2299 * @f: the FILE*
2300 * @cur: the document
2301 * @format: should formatting spaces been added
2302 *
2303 * Dump an XML document to an open FILE.
2304 *
2305 * returns: the number of bytes written or -1 in case of failure.
2306 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2307 * or xmlKeepBlanksDefault(0) was called
2308 */
2309 int
2310 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2311 xmlSaveCtxt ctxt;
2312 xmlOutputBufferPtr buf;
2313 const char * encoding;
2314 xmlCharEncodingHandlerPtr handler = NULL;
2315 int ret;
2316
2317 if (cur == NULL) {
2318 #ifdef DEBUG_TREE
2319 xmlGenericError(xmlGenericErrorContext,
2320 "xmlDocDump : document == NULL\n");
2321 #endif
2322 return(-1);
2323 }
2324 encoding = (const char *) cur->encoding;
2325
2326 if (encoding != NULL) {
2327 handler = xmlFindCharEncodingHandler(encoding);
2328 if (handler == NULL) {
2329 xmlFree((char *) cur->encoding);
2330 cur->encoding = NULL;
2331 encoding = NULL;
2332 }
2333 }
2334 buf = xmlOutputBufferCreateFile(f, handler);
2335 if (buf == NULL) return(-1);
2336 memset(&ctxt, 0, sizeof(ctxt));
2337 ctxt.doc = cur;
2338 ctxt.buf = buf;
2339 ctxt.level = 0;
2340 ctxt.format = format;
2341 ctxt.encoding = (const xmlChar *) encoding;
2342 xmlSaveCtxtInit(&ctxt);
2343 ctxt.options |= XML_SAVE_AS_XML;
2344 xmlDocContentDumpOutput(&ctxt, cur);
2345
2346 ret = xmlOutputBufferClose(buf);
2347 return(ret);
2348 }
2349
2350 /**
2351 * xmlDocDump:
2352 * @f: the FILE*
2353 * @cur: the document
2354 *
2355 * Dump an XML document to an open FILE.
2356 *
2357 * returns: the number of bytes written or -1 in case of failure.
2358 */
2359 int
2360 xmlDocDump(FILE *f, xmlDocPtr cur) {
2361 return(xmlDocFormatDump (f, cur, 0));
2362 }
2363
2364 /**
2365 * xmlSaveFileTo:
2366 * @buf: an output I/O buffer
2367 * @cur: the document
2368 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2369 *
2370 * Dump an XML document to an I/O buffer.
2371 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2372 * after this call.
2373 *
2374 * returns: the number of bytes written or -1 in case of failure.
2375 */
2376 int
2377 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2378 xmlSaveCtxt ctxt;
2379 int ret;
2380
2381 if (buf == NULL) return(-1);
2382 if (cur == NULL) {
2383 xmlOutputBufferClose(buf);
2384 return(-1);
2385 }
2386 memset(&ctxt, 0, sizeof(ctxt));
2387 ctxt.doc = cur;
2388 ctxt.buf = buf;
2389 ctxt.level = 0;
2390 ctxt.format = 0;
2391 ctxt.encoding = (const xmlChar *) encoding;
2392 xmlSaveCtxtInit(&ctxt);
2393 ctxt.options |= XML_SAVE_AS_XML;
2394 xmlDocContentDumpOutput(&ctxt, cur);
2395 ret = xmlOutputBufferClose(buf);
2396 return(ret);
2397 }
2398
2399 /**
2400 * xmlSaveFormatFileTo:
2401 * @buf: an output I/O buffer
2402 * @cur: the document
2403 * @encoding: the encoding if any assuming the I/O layer handles the trancoding
2404 * @format: should formatting spaces been added
2405 *
2406 * Dump an XML document to an I/O buffer.
2407 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2408 * after this call.
2409 *
2410 * returns: the number of bytes written or -1 in case of failure.
2411 */
2412 int
2413 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2414 const char *encoding, int format)
2415 {
2416 xmlSaveCtxt ctxt;
2417 int ret;
2418
2419 if (buf == NULL) return(-1);
2420 if ((cur == NULL) ||
2421 ((cur->type != XML_DOCUMENT_NODE) &&
2422 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2423 xmlOutputBufferClose(buf);
2424 return(-1);
2425 }
2426 memset(&ctxt, 0, sizeof(ctxt));
2427 ctxt.doc = cur;
2428 ctxt.buf = buf;
2429 ctxt.level = 0;
2430 ctxt.format = format;
2431 ctxt.encoding = (const xmlChar *) encoding;
2432 xmlSaveCtxtInit(&ctxt);
2433 ctxt.options |= XML_SAVE_AS_XML;
2434 xmlDocContentDumpOutput(&ctxt, cur);
2435 ret = xmlOutputBufferClose(buf);
2436 return (ret);
2437 }
2438
2439 /**
2440 * xmlSaveFormatFileEnc:
2441 * @filename: the filename or URL to output
2442 * @cur: the document being saved
2443 * @encoding: the name of the encoding to use or NULL.
2444 * @format: should formatting spaces be added.
2445 *
2446 * Dump an XML document to a file or an URL.
2447 *
2448 * Returns the number of bytes written or -1 in case of error.
2449 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2450 * or xmlKeepBlanksDefault(0) was called
2451 */
2452 int
2453 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2454 const char * encoding, int format ) {
2455 xmlSaveCtxt ctxt;
2456 xmlOutputBufferPtr buf;
2457 xmlCharEncodingHandlerPtr handler = NULL;
2458 int ret;
2459
2460 if (cur == NULL)
2461 return(-1);
2462
2463 if (encoding == NULL)
2464 encoding = (const char *) cur->encoding;
2465
2466 if (encoding != NULL) {
2467
2468 handler = xmlFindCharEncodingHandler(encoding);
2469 if (handler == NULL)
2470 return(-1);
2471 }
2472
2473 #ifdef HAVE_ZLIB_H
2474 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2475 #endif
2476 /*
2477 * save the content to a temp buffer.
2478 */
2479 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2480 if (buf == NULL) return(-1);
2481 memset(&ctxt, 0, sizeof(ctxt));
2482 ctxt.doc = cur;
2483 ctxt.buf = buf;
2484 ctxt.level = 0;
2485 ctxt.format = format;
2486 ctxt.encoding = (const xmlChar *) encoding;
2487 xmlSaveCtxtInit(&ctxt);
2488 ctxt.options |= XML_SAVE_AS_XML;
2489
2490 xmlDocContentDumpOutput(&ctxt, cur);
2491
2492 ret = xmlOutputBufferClose(buf);
2493 return(ret);
2494 }
2495
2496
2497 /**
2498 * xmlSaveFileEnc:
2499 * @filename: the filename (or URL)
2500 * @cur: the document
2501 * @encoding: the name of an encoding (or NULL)
2502 *
2503 * Dump an XML document, converting it to the given encoding
2504 *
2505 * returns: the number of bytes written or -1 in case of failure.
2506 */
2507 int
2508 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2509 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2510 }
2511
2512 /**
2513 * xmlSaveFormatFile:
2514 * @filename: the filename (or URL)
2515 * @cur: the document
2516 * @format: should formatting spaces been added
2517 *
2518 * Dump an XML document to a file. Will use compression if
2519 * compiled in and enabled. If @filename is "-" the stdout file is
2520 * used. If @format is set then the document will be indented on output.
2521 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2522 * or xmlKeepBlanksDefault(0) was called
2523 *
2524 * returns: the number of bytes written or -1 in case of failure.
2525 */
2526 int
2527 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2528 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2529 }
2530
2531 /**
2532 * xmlSaveFile:
2533 * @filename: the filename (or URL)
2534 * @cur: the document
2535 *
2536 * Dump an XML document to a file. Will use compression if
2537 * compiled in and enabled. If @filename is "-" the stdout file is
2538 * used.
2539 * returns: the number of bytes written or -1 in case of failure.
2540 */
2541 int
2542 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2543 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2544 }
2545
2546 #endif /* LIBXML_OUTPUT_ENABLED */
2547
2548 #define bottom_xmlsave
2549 #include "elfgcchack.h"