1 /*
2 * xmlsave.c: Implementation of the document serializer
3 *
4 * See Copyright for the status of this software.
5 *
6 * [email protected]
7 */
8
9 #define IN_LIBXML
10 #include "libxml.h"
11
12 #include <limits.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <libxml/xmlmemory.h>
16 #include <libxml/parserInternals.h>
17 #include <libxml/tree.h>
18 #include <libxml/xmlsave.h>
19
20 #define MAX_INDENT 60
21
22 #include <libxml/HTMLtree.h>
23
24 #include "private/buf.h"
25 #include "private/enc.h"
26 #include "private/entities.h"
27 #include "private/error.h"
28 #include "private/io.h"
29 #include "private/save.h"
30
31 #ifdef LIBXML_OUTPUT_ENABLED
32
33 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
34
35 struct _xmlSaveCtxt {
36 void *_private;
37 int type;
38 int fd;
39 const xmlChar *filename;
40 const xmlChar *encoding;
41 xmlCharEncodingHandlerPtr handler;
42 xmlOutputBufferPtr buf;
43 int options;
44 int level;
45 int format;
46 char indent[MAX_INDENT + 1]; /* array for indenting output */
47 int indent_nr;
48 int indent_size;
49 xmlCharEncodingOutputFunc escape; /* used for element content */
50 };
51
52 /************************************************************************
53 * *
54 * Output error handlers *
55 * *
56 ************************************************************************/
57 /**
58 * xmlSaveErrMemory:
59 * @extra: extra information
60 *
61 * Handle an out of memory condition
62 */
63 static void
xmlSaveErrMemory(xmlOutputBufferPtr out)64 xmlSaveErrMemory(xmlOutputBufferPtr out)
65 {
66 if (out != NULL)
67 out->error = XML_ERR_NO_MEMORY;
68 xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_OUTPUT, NULL);
69 }
70
71 /**
72 * xmlSaveErr:
73 * @code: the error number
74 * @node: the location of the error.
75 * @extra: extra information
76 *
77 * Handle an out of memory condition
78 */
79 static void
xmlSaveErr(xmlOutputBufferPtr out,int code,xmlNodePtr node,const char * extra)80 xmlSaveErr(xmlOutputBufferPtr out, int code, xmlNodePtr node,
81 const char *extra)
82 {
83 const char *msg = NULL;
84 int res;
85
86 /* Don't overwrite memory errors */
87 if ((out != NULL) && (out->error == XML_ERR_NO_MEMORY))
88 return;
89
90 if (code == XML_ERR_NO_MEMORY) {
91 xmlSaveErrMemory(out);
92 return;
93 }
94
95 if (out != NULL)
96 out->error = code;
97
98 if (code == XML_ERR_UNSUPPORTED_ENCODING) {
99 msg = "Unsupported encoding: %s";
100 } else {
101 msg = xmlErrString(code);
102 extra = NULL;
103 }
104
105 res = xmlRaiseError(NULL, NULL, NULL, NULL, node,
106 XML_FROM_OUTPUT, code, XML_ERR_ERROR, NULL, 0,
107 extra, NULL, NULL, 0, 0,
108 msg, extra);
109 if (res < 0)
110 xmlSaveErrMemory(out);
111 }
112
113 /************************************************************************
114 * *
115 * Special escaping routines *
116 * *
117 ************************************************************************/
118
119 /*
120 * Tables generated with tools/genEscape.py
121 */
122
123 static const char xmlEscapeContent[] = {
124 8, '&', '#', 'x', 'F', 'F', 'F', 'D', ';', 4, '&', '#',
125 '9', ';', 5, '&', '#', '1', '0', ';', 5, '&', '#', '1',
126 '3', ';', 6, '&', 'q', 'u', 'o', 't', ';', 5, '&', 'a',
127 'm', 'p', ';', 4, '&', 'l', 't', ';', 4, '&', 'g', 't',
128 ';',
129 };
130
131 static const signed char xmlEscapeTab[128] = {
132 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 20, 0, 0,
133 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
134 -1, -1, -1, -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1,
135 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 39, -1, 44, -1,
136 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
137 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
138 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
139 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
140 };
141
142 static const signed char xmlEscapeTabAttr[128] = {
143 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 14, 0, 0, 20, 0, 0,
144 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
145 -1, -1, 26, -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1,
146 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 39, -1, 44, -1,
147 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
148 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
149 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
150 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
151 };
152
153 static void
xmlSerializeText(xmlOutputBufferPtr buf,const xmlChar * string,unsigned flags)154 xmlSerializeText(xmlOutputBufferPtr buf, const xmlChar *string,
155 unsigned flags) {
156 const char *cur;
157 const signed char *tab;
158
159 if (string == NULL)
160 return;
161
162 if (flags & XML_ESCAPE_ATTR)
163 tab = xmlEscapeTabAttr;
164 else
165 tab = xmlEscapeTab;
166
167 cur = (const char *) string;
168
169 while (*cur != 0) {
170 const char *base;
171 int c;
172 int offset;
173
174 base = cur;
175 offset = -1;
176
177 while (1) {
178 c = (unsigned char) *cur;
179
180 if (c < 0x80) {
181 offset = tab[c];
182 if (offset >= 0)
183 break;
184 } else if (flags & XML_ESCAPE_NON_ASCII) {
185 break;
186 }
187
188 cur += 1;
189 }
190
191 if (cur > base)
192 xmlOutputBufferWrite(buf, cur - base, base);
193
194 if (offset >= 0) {
195 if (c == 0)
196 break;
197
198 xmlOutputBufferWrite(buf, xmlEscapeContent[offset],
199 &xmlEscapeContent[offset+1]);
200 cur += 1;
201 } else {
202 char tempBuf[12];
203 int tempSize;
204 int val = 0, len = 4;
205
206 val = xmlGetUTF8Char((const xmlChar *) cur, &len);
207 if (val < 0) {
208 val = 0xFFFD;
209 cur += 1;
210 } else {
211 if (!IS_CHAR(val))
212 val = 0xFFFD;
213 cur += len;
214 }
215
216 tempSize = xmlSerializeHexCharRef(tempBuf, val);
217 xmlOutputBufferWrite(buf, tempSize, tempBuf);
218 }
219 }
220 }
221
222 /************************************************************************
223 * *
224 * Allocation and deallocation *
225 * *
226 ************************************************************************/
227
228 /**
229 * xmlSaveSetIndentString:
230 * @ctxt: save context
231 * @indent: indent string
232 *
233 * Sets the indent string.
234 *
235 * Available since 2.14.0.
236 *
237 * Returns 0 on success, -1 if the string is NULL, empty or too long.
238 */
239 int
xmlSaveSetIndentString(xmlSaveCtxtPtr ctxt,const char * indent)240 xmlSaveSetIndentString(xmlSaveCtxtPtr ctxt, const char *indent) {
241 size_t len;
242 int i;
243
244 if ((ctxt == NULL) || (indent == NULL))
245 return(-1);
246
247 len = strlen(indent);
248 if ((len <= 0) || (len > MAX_INDENT))
249 return(-1);
250
251 ctxt->indent_size = len;
252 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
253 for (i = 0; i < ctxt->indent_nr; i++)
254 memcpy(&ctxt->indent[i * ctxt->indent_size], indent, len);
255
256 return(0);
257 }
258
259 /**
260 * xmlSaveCtxtInit:
261 * @ctxt: the saving context
262 *
263 * Initialize a saving context
264 */
265 static void
xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt,int options)266 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt, int options)
267 {
268 if (ctxt == NULL) return;
269
270 xmlSaveSetIndentString(ctxt, xmlTreeIndentString);
271
272 if (options & XML_SAVE_FORMAT)
273 ctxt->format = 1;
274 else if (options & XML_SAVE_WSNONSIG)
275 ctxt->format = 2;
276
277 if (((options & XML_SAVE_EMPTY) == 0) &&
278 (xmlSaveNoEmptyTags))
279 options |= XML_SAVE_NO_EMPTY;
280
281 ctxt->options = options;
282 }
283
284 /**
285 * xmlFreeSaveCtxt:
286 *
287 * Free a saving context, destroying the output in any remaining buffer
288 */
289 static void
xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)290 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
291 {
292 if (ctxt == NULL) return;
293 if (ctxt->encoding != NULL)
294 xmlFree((char *) ctxt->encoding);
295 if (ctxt->buf != NULL)
296 xmlOutputBufferClose(ctxt->buf);
297 xmlFree(ctxt);
298 }
299
300 /**
301 * xmlNewSaveCtxt:
302 *
303 * Create a new saving context
304 *
305 * Returns the new structure or NULL in case of error
306 */
307 static xmlSaveCtxtPtr
xmlNewSaveCtxt(const char * encoding,int options)308 xmlNewSaveCtxt(const char *encoding, int options)
309 {
310 xmlSaveCtxtPtr ret;
311
312 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
313 if (ret == NULL) {
314 xmlSaveErrMemory(NULL);
315 return ( NULL );
316 }
317 memset(ret, 0, sizeof(xmlSaveCtxt));
318
319 if (encoding != NULL) {
320 int res;
321
322 res = xmlOpenCharEncodingHandler(encoding, /* output */ 1,
323 &ret->handler);
324 if (res != XML_ERR_OK) {
325 xmlSaveErr(NULL, res, NULL, encoding);
326 xmlFreeSaveCtxt(ret);
327 return(NULL);
328 }
329 ret->encoding = xmlStrdup((const xmlChar *)encoding);
330 }
331
332 xmlSaveCtxtInit(ret, options);
333
334 return(ret);
335 }
336
337 /************************************************************************
338 * *
339 * Dumping XML tree content to a simple buffer *
340 * *
341 ************************************************************************/
342
343 static void
xmlSaveWriteText(xmlSaveCtxt * ctxt,const xmlChar * text,unsigned flags)344 xmlSaveWriteText(xmlSaveCtxt *ctxt, const xmlChar *text, unsigned flags) {
345 if (ctxt->encoding == NULL)
346 flags |= XML_ESCAPE_NON_ASCII;
347
348 xmlSerializeText(ctxt->buf, text, flags);
349 }
350
351 /**
352 * xmlSaveWriteAttrContent:
353 * @ctxt: save context
354 * @attr: the attribute pointer
355 *
356 * Serialize the attribute in the buffer
357 */
358 static void
xmlSaveWriteAttrContent(xmlSaveCtxt * ctxt,xmlAttrPtr attr)359 xmlSaveWriteAttrContent(xmlSaveCtxt *ctxt, xmlAttrPtr attr)
360 {
361 xmlNodePtr children;
362 xmlOutputBufferPtr buf = ctxt->buf;
363
364 children = attr->children;
365 while (children != NULL) {
366 switch (children->type) {
367 case XML_TEXT_NODE:
368 xmlSaveWriteText(ctxt, children->content, XML_ESCAPE_ATTR);
369 break;
370 case XML_ENTITY_REF_NODE:
371 xmlOutputBufferWrite(buf, 1, "&");
372 xmlOutputBufferWriteString(buf, (const char *) children->name);
373 xmlOutputBufferWrite(buf, 1, ";");
374 break;
375 default:
376 /* should not happen unless we have a badly built tree */
377 break;
378 }
379 children = children->next;
380 }
381 }
382
383 /**
384 * xmlBufDumpNotationDecl:
385 * @buf: the XML buffer output
386 * @nota: A notation declaration
387 *
388 * This will dump the content the notation declaration as an XML DTD definition
389 */
390 static void
xmlBufDumpNotationDecl(xmlOutputBufferPtr buf,xmlNotationPtr nota)391 xmlBufDumpNotationDecl(xmlOutputBufferPtr buf, xmlNotationPtr nota) {
392 xmlOutputBufferWrite(buf, 11, "<!NOTATION ");
393 xmlOutputBufferWriteString(buf, (const char *) nota->name);
394
395 if (nota->PublicID != NULL) {
396 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
397 xmlOutputBufferWriteQuotedString(buf, nota->PublicID);
398 if (nota->SystemID != NULL) {
399 xmlOutputBufferWrite(buf, 1, " ");
400 xmlOutputBufferWriteQuotedString(buf, nota->SystemID);
401 }
402 } else {
403 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
404 xmlOutputBufferWriteQuotedString(buf, nota->SystemID);
405 }
406
407 xmlOutputBufferWrite(buf, 3, " >\n");
408 }
409
410 /**
411 * xmlBufDumpNotationDeclScan:
412 * @nota: A notation declaration
413 * @buf: the XML buffer output
414 *
415 * This is called with the hash scan function, and just reverses args
416 */
417 static void
xmlBufDumpNotationDeclScan(void * nota,void * buf,const xmlChar * name ATTRIBUTE_UNUSED)418 xmlBufDumpNotationDeclScan(void *nota, void *buf,
419 const xmlChar *name ATTRIBUTE_UNUSED) {
420 xmlBufDumpNotationDecl((xmlOutputBufferPtr) buf, (xmlNotationPtr) nota);
421 }
422
423 /**
424 * xmlBufDumpNotationTable:
425 * @buf: an xmlBufPtr output
426 * @table: A notation table
427 *
428 * This will dump the content of the notation table as an XML DTD definition
429 */
430 static void
xmlBufDumpNotationTable(xmlOutputBufferPtr buf,xmlNotationTablePtr table)431 xmlBufDumpNotationTable(xmlOutputBufferPtr buf, xmlNotationTablePtr table) {
432 xmlHashScan(table, xmlBufDumpNotationDeclScan, buf);
433 }
434
435 /**
436 * xmlBufDumpElementOccur:
437 * @buf: output buffer
438 * @cur: element table
439 *
440 * Dump the occurrence operator of an element.
441 */
442 static void
xmlBufDumpElementOccur(xmlOutputBufferPtr buf,xmlElementContentPtr cur)443 xmlBufDumpElementOccur(xmlOutputBufferPtr buf, xmlElementContentPtr cur) {
444 switch (cur->ocur) {
445 case XML_ELEMENT_CONTENT_ONCE:
446 break;
447 case XML_ELEMENT_CONTENT_OPT:
448 xmlOutputBufferWrite(buf, 1, "?");
449 break;
450 case XML_ELEMENT_CONTENT_MULT:
451 xmlOutputBufferWrite(buf, 1, "*");
452 break;
453 case XML_ELEMENT_CONTENT_PLUS:
454 xmlOutputBufferWrite(buf, 1, "+");
455 break;
456 }
457 }
458
459 /**
460 * xmlBufDumpElementContent:
461 * @buf: output buffer
462 * @content: element table
463 *
464 * This will dump the content of the element table as an XML DTD definition
465 */
466 static void
xmlBufDumpElementContent(xmlOutputBufferPtr buf,xmlElementContentPtr content)467 xmlBufDumpElementContent(xmlOutputBufferPtr buf,
468 xmlElementContentPtr content) {
469 xmlElementContentPtr cur;
470
471 if (content == NULL) return;
472
473 xmlOutputBufferWrite(buf, 1, "(");
474 cur = content;
475
476 do {
477 if (cur == NULL) return;
478
479 switch (cur->type) {
480 case XML_ELEMENT_CONTENT_PCDATA:
481 xmlOutputBufferWrite(buf, 7, "#PCDATA");
482 break;
483 case XML_ELEMENT_CONTENT_ELEMENT:
484 if (cur->prefix != NULL) {
485 xmlOutputBufferWriteString(buf,
486 (const char *) cur->prefix);
487 xmlOutputBufferWrite(buf, 1, ":");
488 }
489 xmlOutputBufferWriteString(buf, (const char *) cur->name);
490 break;
491 case XML_ELEMENT_CONTENT_SEQ:
492 case XML_ELEMENT_CONTENT_OR:
493 if ((cur != content) &&
494 (cur->parent != NULL) &&
495 ((cur->type != cur->parent->type) ||
496 (cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
497 xmlOutputBufferWrite(buf, 1, "(");
498 cur = cur->c1;
499 continue;
500 }
501
502 while (cur != content) {
503 xmlElementContentPtr parent = cur->parent;
504
505 if (parent == NULL) return;
506
507 if (((cur->type == XML_ELEMENT_CONTENT_OR) ||
508 (cur->type == XML_ELEMENT_CONTENT_SEQ)) &&
509 ((cur->type != parent->type) ||
510 (cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
511 xmlOutputBufferWrite(buf, 1, ")");
512 xmlBufDumpElementOccur(buf, cur);
513
514 if (cur == parent->c1) {
515 if (parent->type == XML_ELEMENT_CONTENT_SEQ)
516 xmlOutputBufferWrite(buf, 3, " , ");
517 else if (parent->type == XML_ELEMENT_CONTENT_OR)
518 xmlOutputBufferWrite(buf, 3, " | ");
519
520 cur = parent->c2;
521 break;
522 }
523
524 cur = parent;
525 }
526 } while (cur != content);
527
528 xmlOutputBufferWrite(buf, 1, ")");
529 xmlBufDumpElementOccur(buf, content);
530 }
531
532 /**
533 * xmlBufDumpElementDecl:
534 * @buf: an xmlBufPtr output
535 * @elem: An element table
536 *
537 * This will dump the content of the element declaration as an XML
538 * DTD definition
539 */
540 static void
xmlBufDumpElementDecl(xmlOutputBufferPtr buf,xmlElementPtr elem)541 xmlBufDumpElementDecl(xmlOutputBufferPtr buf, xmlElementPtr elem) {
542 xmlOutputBufferWrite(buf, 10, "<!ELEMENT ");
543 if (elem->prefix != NULL) {
544 xmlOutputBufferWriteString(buf, (const char *) elem->prefix);
545 xmlOutputBufferWrite(buf, 1, ":");
546 }
547 xmlOutputBufferWriteString(buf, (const char *) elem->name);
548 xmlOutputBufferWrite(buf, 1, " ");
549
550 switch (elem->etype) {
551 case XML_ELEMENT_TYPE_EMPTY:
552 xmlOutputBufferWrite(buf, 5, "EMPTY");
553 break;
554 case XML_ELEMENT_TYPE_ANY:
555 xmlOutputBufferWrite(buf, 3, "ANY");
556 break;
557 case XML_ELEMENT_TYPE_MIXED:
558 case XML_ELEMENT_TYPE_ELEMENT:
559 xmlBufDumpElementContent(buf, elem->content);
560 break;
561 default:
562 /* assert(0); */
563 break;
564 }
565
566 xmlOutputBufferWrite(buf, 2, ">\n");
567 }
568
569 /**
570 * xmlBufDumpEnumeration:
571 * @buf: output buffer
572 * @enum: An enumeration
573 *
574 * This will dump the content of the enumeration
575 */
576 static void
xmlBufDumpEnumeration(xmlOutputBufferPtr buf,xmlEnumerationPtr cur)577 xmlBufDumpEnumeration(xmlOutputBufferPtr buf, xmlEnumerationPtr cur) {
578 while (cur != NULL) {
579 xmlOutputBufferWriteString(buf, (const char *) cur->name);
580 if (cur->next != NULL)
581 xmlOutputBufferWrite(buf, 3, " | ");
582
583 cur = cur->next;
584 }
585
586 xmlOutputBufferWrite(buf, 1, ")");
587 }
588 /**
589 * xmlBufDumpAttributeDecl:
590 * @buf: output buffer
591 * @attr: An attribute declaration
592 *
593 * This will dump the content of the attribute declaration as an XML
594 * DTD definition
595 */
596 static void
xmlBufDumpAttributeDecl(xmlOutputBufferPtr buf,xmlAttributePtr attr)597 xmlBufDumpAttributeDecl(xmlOutputBufferPtr buf, xmlAttributePtr attr) {
598 xmlOutputBufferWrite(buf, 10, "<!ATTLIST ");
599 xmlOutputBufferWriteString(buf, (const char *) attr->elem);
600 xmlOutputBufferWrite(buf, 1, " ");
601 if (attr->prefix != NULL) {
602 xmlOutputBufferWriteString(buf, (const char *) attr->prefix);
603 xmlOutputBufferWrite(buf, 1, ":");
604 }
605 xmlOutputBufferWriteString(buf, (const char *) attr->name);
606
607 switch (attr->atype) {
608 case XML_ATTRIBUTE_CDATA:
609 xmlOutputBufferWrite(buf, 6, " CDATA");
610 break;
611 case XML_ATTRIBUTE_ID:
612 xmlOutputBufferWrite(buf, 3, " ID");
613 break;
614 case XML_ATTRIBUTE_IDREF:
615 xmlOutputBufferWrite(buf, 6, " IDREF");
616 break;
617 case XML_ATTRIBUTE_IDREFS:
618 xmlOutputBufferWrite(buf, 7, " IDREFS");
619 break;
620 case XML_ATTRIBUTE_ENTITY:
621 xmlOutputBufferWrite(buf, 7, " ENTITY");
622 break;
623 case XML_ATTRIBUTE_ENTITIES:
624 xmlOutputBufferWrite(buf, 9, " ENTITIES");
625 break;
626 case XML_ATTRIBUTE_NMTOKEN:
627 xmlOutputBufferWrite(buf, 8, " NMTOKEN");
628 break;
629 case XML_ATTRIBUTE_NMTOKENS:
630 xmlOutputBufferWrite(buf, 9, " NMTOKENS");
631 break;
632 case XML_ATTRIBUTE_ENUMERATION:
633 xmlOutputBufferWrite(buf, 2, " (");
634 xmlBufDumpEnumeration(buf, attr->tree);
635 break;
636 case XML_ATTRIBUTE_NOTATION:
637 xmlOutputBufferWrite(buf, 11, " NOTATION (");
638 xmlBufDumpEnumeration(buf, attr->tree);
639 break;
640 default:
641 /* assert(0); */
642 break;
643 }
644
645 switch (attr->def) {
646 case XML_ATTRIBUTE_NONE:
647 break;
648 case XML_ATTRIBUTE_REQUIRED:
649 xmlOutputBufferWrite(buf, 10, " #REQUIRED");
650 break;
651 case XML_ATTRIBUTE_IMPLIED:
652 xmlOutputBufferWrite(buf, 9, " #IMPLIED");
653 break;
654 case XML_ATTRIBUTE_FIXED:
655 xmlOutputBufferWrite(buf, 7, " #FIXED");
656 break;
657 default:
658 /* assert(0); */
659 break;
660 }
661
662 if (attr->defaultValue != NULL) {
663 xmlOutputBufferWrite(buf, 1, " ");
664 xmlOutputBufferWriteQuotedString(buf, attr->defaultValue);
665 }
666
667 xmlOutputBufferWrite(buf, 2, ">\n");
668 }
669
670 /**
671 * xmlBufDumpEntityContent:
672 * @buf: output buffer
673 * @content: entity content.
674 *
675 * This will dump the quoted string value, taking care of the special
676 * treatment required by %
677 */
678 static void
xmlBufDumpEntityContent(xmlOutputBufferPtr buf,const xmlChar * content)679 xmlBufDumpEntityContent(xmlOutputBufferPtr buf, const xmlChar *content) {
680 if (xmlStrchr(content, '%')) {
681 const char * base, *cur;
682
683 xmlOutputBufferWrite(buf, 1, "\"");
684 base = cur = (const char *) content;
685 while (*cur != 0) {
686 if (*cur == '"') {
687 if (base != cur)
688 xmlOutputBufferWrite(buf, cur - base, base);
689 xmlOutputBufferWrite(buf, 6, """);
690 cur++;
691 base = cur;
692 } else if (*cur == '%') {
693 if (base != cur)
694 xmlOutputBufferWrite(buf, cur - base, base);
695 xmlOutputBufferWrite(buf, 6, "%");
696 cur++;
697 base = cur;
698 } else {
699 cur++;
700 }
701 }
702 if (base != cur)
703 xmlOutputBufferWrite(buf, cur - base, base);
704 xmlOutputBufferWrite(buf, 1, "\"");
705 } else {
706 xmlOutputBufferWriteQuotedString(buf, content);
707 }
708 }
709
710 /**
711 * xmlBufDumpEntityDecl:
712 * @buf: an xmlBufPtr output
713 * @ent: An entity table
714 *
715 * This will dump the content of the entity table as an XML DTD definition
716 */
717 static void
xmlBufDumpEntityDecl(xmlOutputBufferPtr buf,xmlEntityPtr ent)718 xmlBufDumpEntityDecl(xmlOutputBufferPtr buf, xmlEntityPtr ent) {
719 if ((ent->etype == XML_INTERNAL_PARAMETER_ENTITY) ||
720 (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY))
721 xmlOutputBufferWrite(buf, 11, "<!ENTITY % ");
722 else
723 xmlOutputBufferWrite(buf, 9, "<!ENTITY ");
724 xmlOutputBufferWriteString(buf, (const char *) ent->name);
725 xmlOutputBufferWrite(buf, 1, " ");
726
727 if ((ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) ||
728 (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) ||
729 (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) {
730 if (ent->ExternalID != NULL) {
731 xmlOutputBufferWrite(buf, 7, "PUBLIC ");
732 xmlOutputBufferWriteQuotedString(buf, ent->ExternalID);
733 xmlOutputBufferWrite(buf, 1, " ");
734 } else {
735 xmlOutputBufferWrite(buf, 7, "SYSTEM ");
736 }
737 xmlOutputBufferWriteQuotedString(buf, ent->SystemID);
738 }
739
740 if (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
741 if (ent->content != NULL) { /* Should be true ! */
742 xmlOutputBufferWrite(buf, 7, " NDATA ");
743 if (ent->orig != NULL)
744 xmlOutputBufferWriteString(buf, (const char *) ent->orig);
745 else
746 xmlOutputBufferWriteString(buf, (const char *) ent->content);
747 }
748 }
749
750 if ((ent->etype == XML_INTERNAL_GENERAL_ENTITY) ||
751 (ent->etype == XML_INTERNAL_PARAMETER_ENTITY)) {
752 if (ent->orig != NULL)
753 xmlOutputBufferWriteQuotedString(buf, ent->orig);
754 else
755 xmlBufDumpEntityContent(buf, ent->content);
756 }
757
758 xmlOutputBufferWrite(buf, 2, ">\n");
759 }
760
761 /************************************************************************
762 * *
763 * Dumping XML tree content to an I/O output buffer *
764 * *
765 ************************************************************************/
766
xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt,const char * encoding)767 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
768 xmlOutputBufferPtr buf = ctxt->buf;
769
770 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
771 xmlCharEncodingHandler *handler;
772 int res;
773
774 res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
775 if (res != XML_ERR_OK) {
776 xmlSaveErr(buf, res, NULL, encoding);
777 return(-1);
778 }
779 buf->conv = xmlBufCreate(4000 /* MINLEN */);
780 if (buf->conv == NULL) {
781 xmlCharEncCloseFunc(handler);
782 xmlSaveErrMemory(buf);
783 return(-1);
784 }
785 buf->encoder = handler;
786 /*
787 * initialize the state, e.g. if outputting a BOM
788 */
789 xmlCharEncOutput(buf, 1);
790 }
791 return(0);
792 }
793
xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt)794 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
795 xmlOutputBufferPtr buf = ctxt->buf;
796 xmlOutputBufferFlush(buf);
797 xmlCharEncCloseFunc(buf->encoder);
798 xmlBufFree(buf->conv);
799 buf->encoder = NULL;
800 buf->conv = NULL;
801 return(0);
802 }
803
804 #ifdef LIBXML_HTML_ENABLED
805 static void
806 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
807 #endif
808 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
809 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
810
811 static void
xmlSaveWriteIndent(xmlSaveCtxtPtr ctxt,int extra)812 xmlSaveWriteIndent(xmlSaveCtxtPtr ctxt, int extra)
813 {
814 int level;
815
816 if ((ctxt->options & XML_SAVE_NO_INDENT) ||
817 (((ctxt->options & XML_SAVE_INDENT) == 0) &&
818 (xmlIndentTreeOutput == 0)))
819 return;
820
821 level = ctxt->level + extra;
822 if (level > ctxt->indent_nr)
823 level = ctxt->indent_nr;
824 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size * level, ctxt->indent);
825 }
826
827 /**
828 * xmlOutputBufferWriteWSNonSig:
829 * @ctxt: The save context
830 * @extra: Number of extra indents to apply to ctxt->level
831 *
832 * Write out formatting for non-significant whitespace output.
833 */
834 static void
xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt,int extra)835 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
836 {
837 int i;
838 if ((ctxt == NULL) || (ctxt->buf == NULL))
839 return;
840 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
841 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
842 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
843 ((ctxt->level + extra - i) > ctxt->indent_nr ?
844 ctxt->indent_nr : (ctxt->level + extra - i)),
845 ctxt->indent);
846 }
847 }
848
849 /**
850 * xmlNsDumpOutput:
851 * @buf: the XML buffer output
852 * @cur: a namespace
853 * @ctxt: the output save context. Optional.
854 *
855 * Dump a local Namespace definition.
856 * Should be called in the context of attributes dumps.
857 * If @ctxt is supplied, @buf should be its buffer.
858 */
859 static void
xmlNsDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur,xmlSaveCtxtPtr ctxt)860 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
861 unsigned escapeFlags = XML_ESCAPE_ATTR;
862
863 if ((cur == NULL) || (buf == NULL)) return;
864
865 if ((ctxt == NULL) || (ctxt->encoding == NULL))
866 escapeFlags |= XML_ESCAPE_NON_ASCII;
867
868 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
869 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
870 return;
871
872 if (ctxt != NULL && ctxt->format == 2)
873 xmlOutputBufferWriteWSNonSig(ctxt, 2);
874 else
875 xmlOutputBufferWrite(buf, 1, " ");
876
877 /* Within the context of an element attributes */
878 if (cur->prefix != NULL) {
879 xmlOutputBufferWrite(buf, 6, "xmlns:");
880 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
881 } else
882 xmlOutputBufferWrite(buf, 5, "xmlns");
883 xmlOutputBufferWrite(buf, 2, "=\"");
884 xmlSerializeText(buf, cur->href, escapeFlags);
885 xmlOutputBufferWrite(buf, 1, "\"");
886 }
887 }
888
889 /**
890 * xmlNsListDumpOutputCtxt
891 * @ctxt: the save context
892 * @cur: the first namespace
893 *
894 * Dump a list of local namespace definitions to a save context.
895 * Should be called in the context of attribute dumps.
896 */
897 static void
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)898 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
899 while (cur != NULL) {
900 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
901 cur = cur->next;
902 }
903 }
904
905 /**
906 * xmlNsListDumpOutput:
907 * @buf: the XML buffer output
908 * @cur: the first namespace
909 *
910 * Dump a list of local Namespace definitions.
911 * Should be called in the context of attributes dumps.
912 */
913 void
xmlNsListDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur)914 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
915 while (cur != NULL) {
916 xmlNsDumpOutput(buf, cur, NULL);
917 cur = cur->next;
918 }
919 }
920
921 /**
922 * xmlDtdDumpOutput:
923 * @buf: the XML buffer output
924 * @dtd: the pointer to the DTD
925 *
926 * Dump the XML document DTD, if any.
927 */
928 static void
xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt,xmlDtdPtr dtd)929 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
930 xmlOutputBufferPtr buf;
931 xmlNodePtr cur;
932 int format, level;
933
934 if (dtd == NULL) return;
935 if ((ctxt == NULL) || (ctxt->buf == NULL))
936 return;
937 buf = ctxt->buf;
938 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
939 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
940 if (dtd->ExternalID != NULL) {
941 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
942 xmlOutputBufferWriteQuotedString(buf, dtd->ExternalID);
943 xmlOutputBufferWrite(buf, 1, " ");
944 xmlOutputBufferWriteQuotedString(buf, dtd->SystemID);
945 } else if (dtd->SystemID != NULL) {
946 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
947 xmlOutputBufferWriteQuotedString(buf, dtd->SystemID);
948 }
949 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
950 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
951 (dtd->pentities == NULL)) {
952 xmlOutputBufferWrite(buf, 1, ">");
953 return;
954 }
955 xmlOutputBufferWrite(buf, 3, " [\n");
956 /*
957 * Dump the notations first they are not in the DTD children list
958 * Do this only on a standalone DTD or on the internal subset though.
959 */
960 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
961 (dtd->doc->intSubset == dtd))) {
962 xmlBufDumpNotationTable(buf, (xmlNotationTablePtr) dtd->notations);
963 }
964 format = ctxt->format;
965 level = ctxt->level;
966 ctxt->format = 0;
967 ctxt->level = -1;
968 for (cur = dtd->children; cur != NULL; cur = cur->next) {
969 xmlNodeDumpOutputInternal(ctxt, cur);
970 }
971 ctxt->format = format;
972 ctxt->level = level;
973 xmlOutputBufferWrite(buf, 2, "]>");
974 }
975
976 /**
977 * xmlAttrDumpOutput:
978 * @buf: the XML buffer output
979 * @cur: the attribute pointer
980 *
981 * Dump an XML attribute
982 */
983 static void
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)984 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
985 xmlOutputBufferPtr buf;
986
987 if (cur == NULL) return;
988 buf = ctxt->buf;
989 if (buf == NULL) return;
990 if (ctxt->format == 2)
991 xmlOutputBufferWriteWSNonSig(ctxt, 2);
992 else
993 xmlOutputBufferWrite(buf, 1, " ");
994 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
995 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
996 xmlOutputBufferWrite(buf, 1, ":");
997 }
998 xmlOutputBufferWriteString(buf, (const char *)cur->name);
999 xmlOutputBufferWrite(buf, 2, "=\"");
1000 #ifdef LIBXML_HTML_ENABLED
1001 if ((ctxt->options & XML_SAVE_XHTML) &&
1002 (cur->ns == NULL) &&
1003 ((cur->children == NULL) ||
1004 (cur->children->content == NULL) ||
1005 (cur->children->content[0] == 0)) &&
1006 (htmlIsBooleanAttr(cur->name))) {
1007 xmlOutputBufferWriteString(buf, (const char *) cur->name);
1008 } else
1009 #endif
1010 {
1011 xmlSaveWriteAttrContent(ctxt, cur);
1012 }
1013 xmlOutputBufferWrite(buf, 1, "\"");
1014 }
1015
1016 #ifdef LIBXML_HTML_ENABLED
1017 /**
1018 * htmlNodeDumpOutputInternal:
1019 * @cur: the current node
1020 *
1021 * Dump an HTML node, recursive behaviour, children are printed too.
1022 */
1023 static int
htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1024 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1025 const xmlChar *oldctxtenc = ctxt->encoding;
1026 const xmlChar *encoding = ctxt->encoding;
1027 xmlOutputBufferPtr buf = ctxt->buf;
1028 int switched_encoding = 0;
1029 xmlDocPtr doc;
1030
1031 xmlInitParser();
1032
1033 doc = cur->doc;
1034 if ((encoding == NULL) && (doc != NULL))
1035 encoding = doc->encoding;
1036
1037 if ((encoding != NULL) && (doc != NULL))
1038 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
1039 if ((encoding == NULL) && (doc != NULL))
1040 encoding = htmlGetMetaEncoding(doc);
1041 if (encoding == NULL)
1042 encoding = BAD_CAST "HTML";
1043 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1044 (buf->encoder == NULL) && (buf->conv == NULL)) {
1045 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0)
1046 return(-1);
1047 switched_encoding = 1;
1048 }
1049 if (ctxt->options & XML_SAVE_FORMAT)
1050 htmlNodeDumpFormatOutput(buf, doc, cur,
1051 (const char *)encoding, 1);
1052 else
1053 htmlNodeDumpFormatOutput(buf, doc, cur,
1054 (const char *)encoding, 0);
1055 /*
1056 * Restore the state of the saving context at the end of the document
1057 */
1058 if ((switched_encoding) && (oldctxtenc == NULL)) {
1059 xmlSaveClearEncoding(ctxt);
1060 }
1061 return(0);
1062 }
1063 #endif
1064
1065 /**
1066 * xmlNodeDumpOutputInternal:
1067 * @cur: the current node
1068 *
1069 * Dump an XML node, recursive behaviour, children are printed too.
1070 */
1071 static void
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1072 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1073 int format = ctxt->format;
1074 xmlNodePtr tmp, root, unformattedNode = NULL, parent;
1075 xmlAttrPtr attr;
1076 xmlChar *start, *end;
1077 xmlOutputBufferPtr buf;
1078
1079 if (cur == NULL) return;
1080 buf = ctxt->buf;
1081
1082 root = cur;
1083 parent = cur->parent;
1084 while (1) {
1085 switch (cur->type) {
1086 case XML_DOCUMENT_NODE:
1087 case XML_HTML_DOCUMENT_NODE:
1088 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1089 break;
1090
1091 case XML_DTD_NODE:
1092 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1093 break;
1094
1095 case XML_DOCUMENT_FRAG_NODE:
1096 /* Always validate cur->parent when descending. */
1097 if ((cur->parent == parent) && (cur->children != NULL)) {
1098 parent = cur;
1099 cur = cur->children;
1100 continue;
1101 }
1102 break;
1103
1104 case XML_ELEMENT_DECL:
1105 xmlBufDumpElementDecl(buf, (xmlElementPtr) cur);
1106 break;
1107
1108 case XML_ATTRIBUTE_DECL:
1109 xmlBufDumpAttributeDecl(buf, (xmlAttributePtr) cur);
1110 break;
1111
1112 case XML_ENTITY_DECL:
1113 xmlBufDumpEntityDecl(buf, (xmlEntityPtr) cur);
1114 break;
1115
1116 case XML_ELEMENT_NODE:
1117 if ((cur != root) && (ctxt->format == 1))
1118 xmlSaveWriteIndent(ctxt, 0);
1119
1120 /*
1121 * Some users like lxml are known to pass nodes with a corrupted
1122 * tree structure. Fall back to a recursive call to handle this
1123 * case.
1124 */
1125 if ((cur->parent != parent) && (cur->children != NULL)) {
1126 xmlNodeDumpOutputInternal(ctxt, cur);
1127 break;
1128 }
1129
1130 xmlOutputBufferWrite(buf, 1, "<");
1131 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1132 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1133 xmlOutputBufferWrite(buf, 1, ":");
1134 }
1135 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1136 if (cur->nsDef)
1137 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1138 for (attr = cur->properties; attr != NULL; attr = attr->next)
1139 xmlAttrDumpOutput(ctxt, attr);
1140
1141 if (cur->children == NULL) {
1142 if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
1143 if (ctxt->format == 2)
1144 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1145 xmlOutputBufferWrite(buf, 2, "/>");
1146 } else {
1147 if (ctxt->format == 2)
1148 xmlOutputBufferWriteWSNonSig(ctxt, 1);
1149 xmlOutputBufferWrite(buf, 3, "></");
1150 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1151 xmlOutputBufferWriteString(buf,
1152 (const char *)cur->ns->prefix);
1153 xmlOutputBufferWrite(buf, 1, ":");
1154 }
1155 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1156 if (ctxt->format == 2)
1157 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1158 xmlOutputBufferWrite(buf, 1, ">");
1159 }
1160 } else {
1161 if (ctxt->format == 1) {
1162 tmp = cur->children;
1163 while (tmp != NULL) {
1164 if ((tmp->type == XML_TEXT_NODE) ||
1165 (tmp->type == XML_CDATA_SECTION_NODE) ||
1166 (tmp->type == XML_ENTITY_REF_NODE)) {
1167 ctxt->format = 0;
1168 unformattedNode = cur;
1169 break;
1170 }
1171 tmp = tmp->next;
1172 }
1173 }
1174 if (ctxt->format == 2)
1175 xmlOutputBufferWriteWSNonSig(ctxt, 1);
1176 xmlOutputBufferWrite(buf, 1, ">");
1177 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1178 if (ctxt->level >= 0) ctxt->level++;
1179 parent = cur;
1180 cur = cur->children;
1181 continue;
1182 }
1183
1184 break;
1185
1186 case XML_TEXT_NODE:
1187 if (cur->content == NULL)
1188 break;
1189 if (cur->name != xmlStringTextNoenc) {
1190 if (ctxt->escape)
1191 xmlOutputBufferWriteEscape(buf, cur->content,
1192 ctxt->escape);
1193 #ifdef TEST_OUTPUT_BUFFER_WRITE_ESCAPE
1194 else if (ctxt->encoding)
1195 xmlOutputBufferWriteEscape(buf, cur->content, NULL);
1196 #endif
1197 else
1198 xmlSaveWriteText(ctxt, cur->content, /* flags */ 0);
1199 } else {
1200 /*
1201 * Disable escaping, needed for XSLT
1202 */
1203 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1204 }
1205 break;
1206
1207 case XML_PI_NODE:
1208 if ((cur != root) && (ctxt->format == 1))
1209 xmlSaveWriteIndent(ctxt, 0);
1210
1211 if (cur->content != NULL) {
1212 xmlOutputBufferWrite(buf, 2, "<?");
1213 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1214 if (cur->content != NULL) {
1215 if (ctxt->format == 2)
1216 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1217 else
1218 xmlOutputBufferWrite(buf, 1, " ");
1219 xmlOutputBufferWriteString(buf,
1220 (const char *)cur->content);
1221 }
1222 xmlOutputBufferWrite(buf, 2, "?>");
1223 } else {
1224 xmlOutputBufferWrite(buf, 2, "<?");
1225 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1226 if (ctxt->format == 2)
1227 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1228 xmlOutputBufferWrite(buf, 2, "?>");
1229 }
1230 break;
1231
1232 case XML_COMMENT_NODE:
1233 if ((cur != root) && (ctxt->format == 1))
1234 xmlSaveWriteIndent(ctxt, 0);
1235
1236 if (cur->content != NULL) {
1237 xmlOutputBufferWrite(buf, 4, "<!--");
1238 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1239 xmlOutputBufferWrite(buf, 3, "-->");
1240 }
1241 break;
1242
1243 case XML_ENTITY_REF_NODE:
1244 xmlOutputBufferWrite(buf, 1, "&");
1245 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1246 xmlOutputBufferWrite(buf, 1, ";");
1247 break;
1248
1249 case XML_CDATA_SECTION_NODE:
1250 if (cur->content == NULL || *cur->content == '\0') {
1251 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1252 } else {
1253 start = end = cur->content;
1254 while (*end != '\0') {
1255 if ((*end == ']') && (*(end + 1) == ']') &&
1256 (*(end + 2) == '>')) {
1257 end = end + 2;
1258 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1259 xmlOutputBufferWrite(buf, end - start,
1260 (const char *)start);
1261 xmlOutputBufferWrite(buf, 3, "]]>");
1262 start = end;
1263 }
1264 end++;
1265 }
1266 if (start != end) {
1267 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1268 xmlOutputBufferWriteString(buf, (const char *)start);
1269 xmlOutputBufferWrite(buf, 3, "]]>");
1270 }
1271 }
1272 break;
1273
1274 case XML_ATTRIBUTE_NODE:
1275 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1276 break;
1277
1278 case XML_NAMESPACE_DECL:
1279 xmlNsDumpOutput(buf, (xmlNsPtr) cur, ctxt);
1280 break;
1281
1282 default:
1283 break;
1284 }
1285
1286 while (1) {
1287 if (cur == root)
1288 return;
1289 if ((ctxt->format == 1) &&
1290 (cur->type != XML_XINCLUDE_START) &&
1291 (cur->type != XML_XINCLUDE_END))
1292 xmlOutputBufferWrite(buf, 1, "\n");
1293 if (cur->next != NULL) {
1294 cur = cur->next;
1295 break;
1296 }
1297
1298 cur = parent;
1299 /* cur->parent was validated when descending. */
1300 parent = cur->parent;
1301
1302 if (cur->type == XML_ELEMENT_NODE) {
1303 if (ctxt->level > 0) ctxt->level--;
1304 if (ctxt->format == 1)
1305 xmlSaveWriteIndent(ctxt, 0);
1306
1307 xmlOutputBufferWrite(buf, 2, "</");
1308 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1309 xmlOutputBufferWriteString(buf,
1310 (const char *)cur->ns->prefix);
1311 xmlOutputBufferWrite(buf, 1, ":");
1312 }
1313
1314 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1315 if (ctxt->format == 2)
1316 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1317 xmlOutputBufferWrite(buf, 1, ">");
1318
1319 if (cur == unformattedNode) {
1320 ctxt->format = format;
1321 unformattedNode = NULL;
1322 }
1323 }
1324 }
1325 }
1326 }
1327
1328 /**
1329 * xmlDocContentDumpOutput:
1330 * @cur: the document
1331 *
1332 * Dump an XML document.
1333 */
1334 static int
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt,xmlDocPtr cur)1335 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1336 #ifdef LIBXML_HTML_ENABLED
1337 xmlDtdPtr dtd;
1338 int is_xhtml = 0;
1339 #endif
1340 const xmlChar *oldctxtenc = ctxt->encoding;
1341 const xmlChar *encoding = ctxt->encoding;
1342 xmlOutputBufferPtr buf = ctxt->buf;
1343 xmlCharEncoding enc;
1344 int switched_encoding = 0;
1345
1346 xmlInitParser();
1347
1348 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1349 (cur->type != XML_DOCUMENT_NODE))
1350 return(-1);
1351
1352 if (ctxt->encoding == NULL)
1353 encoding = cur->encoding;
1354
1355 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1356 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1357 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1358 (ctxt->options & XML_SAVE_AS_HTML)) {
1359 #ifdef LIBXML_HTML_ENABLED
1360 if (encoding != NULL)
1361 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1362 if (encoding == NULL)
1363 encoding = htmlGetMetaEncoding(cur);
1364 if (encoding == NULL)
1365 encoding = BAD_CAST "HTML";
1366 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1367 (buf->encoder == NULL) && (buf->conv == NULL)) {
1368 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1369 return(-1);
1370 }
1371 }
1372 if (ctxt->options & XML_SAVE_FORMAT)
1373 htmlDocContentDumpFormatOutput(buf, cur,
1374 (const char *)encoding, 1);
1375 else
1376 htmlDocContentDumpFormatOutput(buf, cur,
1377 (const char *)encoding, 0);
1378 return(0);
1379 #else
1380 return(-1);
1381 #endif
1382 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1383 (ctxt->options & XML_SAVE_AS_XML) ||
1384 (ctxt->options & XML_SAVE_XHTML)) {
1385 enc = xmlParseCharEncoding((const char*) encoding);
1386 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1387 (buf->encoder == NULL) && (buf->conv == NULL) &&
1388 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1389 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1390 (enc != XML_CHAR_ENCODING_NONE) &&
1391 (enc != XML_CHAR_ENCODING_ASCII)) {
1392 /*
1393 * we need to switch to this encoding but just for this
1394 * document since we output the XMLDecl the conversion
1395 * must be done to not generate not well formed documents.
1396 */
1397 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0)
1398 return(-1);
1399 switched_encoding = 1;
1400 }
1401 }
1402
1403
1404 /*
1405 * Save the XML declaration
1406 */
1407 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1408 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1409 if (cur->version != NULL)
1410 xmlOutputBufferWriteQuotedString(buf, cur->version);
1411 else
1412 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1413 if (encoding != NULL) {
1414 xmlOutputBufferWrite(buf, 10, " encoding=");
1415 xmlOutputBufferWriteQuotedString(buf, (xmlChar *) encoding);
1416 }
1417 switch (cur->standalone) {
1418 case 0:
1419 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1420 break;
1421 case 1:
1422 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1423 break;
1424 }
1425 xmlOutputBufferWrite(buf, 3, "?>\n");
1426 }
1427
1428 #ifdef LIBXML_HTML_ENABLED
1429 if (ctxt->options & XML_SAVE_XHTML)
1430 is_xhtml = 1;
1431 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1432 dtd = xmlGetIntSubset(cur);
1433 if (dtd != NULL) {
1434 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1435 if (is_xhtml < 0) is_xhtml = 0;
1436 }
1437 }
1438 #endif
1439 if (cur->children != NULL) {
1440 xmlNodePtr child = cur->children;
1441
1442 while (child != NULL) {
1443 ctxt->level = 0;
1444 #ifdef LIBXML_HTML_ENABLED
1445 if (is_xhtml)
1446 xhtmlNodeDumpOutput(ctxt, child);
1447 else
1448 #endif
1449 xmlNodeDumpOutputInternal(ctxt, child);
1450 if ((child->type != XML_XINCLUDE_START) &&
1451 (child->type != XML_XINCLUDE_END))
1452 xmlOutputBufferWrite(buf, 1, "\n");
1453 child = child->next;
1454 }
1455 }
1456 }
1457
1458 /*
1459 * Restore the state of the saving context at the end of the document
1460 */
1461 if ((switched_encoding) && (oldctxtenc == NULL)) {
1462 xmlSaveClearEncoding(ctxt);
1463 }
1464 return(0);
1465 }
1466
1467 #ifdef LIBXML_HTML_ENABLED
1468 /************************************************************************
1469 * *
1470 * Functions specific to XHTML serialization *
1471 * *
1472 ************************************************************************/
1473
1474 /**
1475 * xhtmlIsEmpty:
1476 * @node: the node
1477 *
1478 * Check if a node is an empty xhtml node
1479 *
1480 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1481 */
1482 static int
xhtmlIsEmpty(xmlNodePtr node)1483 xhtmlIsEmpty(xmlNodePtr node) {
1484 if (node == NULL)
1485 return(-1);
1486 if (node->type != XML_ELEMENT_NODE)
1487 return(0);
1488 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1489 return(0);
1490 if (node->children != NULL)
1491 return(0);
1492 switch (node->name ? node->name[0] : 0) {
1493 case 'a':
1494 if (xmlStrEqual(node->name, BAD_CAST "area"))
1495 return(1);
1496 return(0);
1497 case 'b':
1498 if (xmlStrEqual(node->name, BAD_CAST "br"))
1499 return(1);
1500 if (xmlStrEqual(node->name, BAD_CAST "base"))
1501 return(1);
1502 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1503 return(1);
1504 return(0);
1505 case 'c':
1506 if (xmlStrEqual(node->name, BAD_CAST "col"))
1507 return(1);
1508 return(0);
1509 case 'f':
1510 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1511 return(1);
1512 return(0);
1513 case 'h':
1514 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1515 return(1);
1516 return(0);
1517 case 'i':
1518 if (xmlStrEqual(node->name, BAD_CAST "img"))
1519 return(1);
1520 if (xmlStrEqual(node->name, BAD_CAST "input"))
1521 return(1);
1522 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1523 return(1);
1524 return(0);
1525 case 'l':
1526 if (xmlStrEqual(node->name, BAD_CAST "link"))
1527 return(1);
1528 return(0);
1529 case 'm':
1530 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1531 return(1);
1532 return(0);
1533 case 'p':
1534 if (xmlStrEqual(node->name, BAD_CAST "param"))
1535 return(1);
1536 return(0);
1537 }
1538 return(0);
1539 }
1540
1541 /**
1542 * xhtmlAttrListDumpOutput:
1543 * @cur: the first attribute pointer
1544 *
1545 * Dump a list of XML attributes
1546 */
1547 static void
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)1548 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1549 xmlAttrPtr xml_lang = NULL;
1550 xmlAttrPtr lang = NULL;
1551 xmlAttrPtr name = NULL;
1552 xmlAttrPtr id = NULL;
1553 xmlNodePtr parent;
1554 xmlOutputBufferPtr buf;
1555
1556 if (cur == NULL) return;
1557 buf = ctxt->buf;
1558 parent = cur->parent;
1559 while (cur != NULL) {
1560 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1561 id = cur;
1562 else
1563 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1564 name = cur;
1565 else
1566 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1567 lang = cur;
1568 else
1569 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1570 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1571 xml_lang = cur;
1572 xmlAttrDumpOutput(ctxt, cur);
1573 cur = cur->next;
1574 }
1575 /*
1576 * C.8
1577 */
1578 if ((name != NULL) && (id == NULL)) {
1579 if ((parent != NULL) && (parent->name != NULL) &&
1580 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1581 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1582 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1583 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1584 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1585 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1586 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1587 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1588 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1589 xmlOutputBufferWrite(buf, 5, " id=\"");
1590 xmlSaveWriteAttrContent(ctxt, name);
1591 xmlOutputBufferWrite(buf, 1, "\"");
1592 }
1593 }
1594 /*
1595 * C.7.
1596 */
1597 if ((lang != NULL) && (xml_lang == NULL)) {
1598 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1599 xmlSaveWriteAttrContent(ctxt, lang);
1600 xmlOutputBufferWrite(buf, 1, "\"");
1601 } else
1602 if ((xml_lang != NULL) && (lang == NULL)) {
1603 xmlOutputBufferWrite(buf, 7, " lang=\"");
1604 xmlSaveWriteAttrContent(ctxt, xml_lang);
1605 xmlOutputBufferWrite(buf, 1, "\"");
1606 }
1607 }
1608
1609 /**
1610 * xhtmlNodeDumpOutput:
1611 * @buf: the XML buffer output
1612 * @doc: the XHTML document
1613 * @cur: the current node
1614 * @level: the imbrication level for indenting
1615 * @format: is formatting allowed
1616 * @encoding: an optional encoding string
1617 *
1618 * Dump an XHTML node, recursive behaviour, children are printed too.
1619 */
1620 static void
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1621 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1622 int format = ctxt->format, addmeta, oldoptions;
1623 xmlNodePtr tmp, root, unformattedNode = NULL, parent;
1624 xmlChar *start, *end;
1625 xmlOutputBufferPtr buf = ctxt->buf;
1626
1627 if (cur == NULL) return;
1628
1629 oldoptions = ctxt->options;
1630 ctxt->options |= XML_SAVE_XHTML;
1631
1632 root = cur;
1633 parent = cur->parent;
1634 while (1) {
1635 switch (cur->type) {
1636 case XML_DOCUMENT_NODE:
1637 case XML_HTML_DOCUMENT_NODE:
1638 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1639 break;
1640
1641 case XML_NAMESPACE_DECL:
1642 xmlNsDumpOutput(buf, (xmlNsPtr) cur, ctxt);
1643 break;
1644
1645 case XML_DTD_NODE:
1646 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1647 break;
1648
1649 case XML_DOCUMENT_FRAG_NODE:
1650 /* Always validate cur->parent when descending. */
1651 if ((cur->parent == parent) && (cur->children != NULL)) {
1652 parent = cur;
1653 cur = cur->children;
1654 continue;
1655 }
1656 break;
1657
1658 case XML_ELEMENT_DECL:
1659 xmlBufDumpElementDecl(buf, (xmlElementPtr) cur);
1660 break;
1661
1662 case XML_ATTRIBUTE_DECL:
1663 xmlBufDumpAttributeDecl(buf, (xmlAttributePtr) cur);
1664 break;
1665
1666 case XML_ENTITY_DECL:
1667 xmlBufDumpEntityDecl(buf, (xmlEntityPtr) cur);
1668 break;
1669
1670 case XML_ELEMENT_NODE:
1671 addmeta = 0;
1672
1673 if ((cur != root) && (ctxt->format == 1))
1674 xmlSaveWriteIndent(ctxt, 0);
1675
1676 /*
1677 * Some users like lxml are known to pass nodes with a corrupted
1678 * tree structure. Fall back to a recursive call to handle this
1679 * case.
1680 */
1681 if ((cur->parent != parent) && (cur->children != NULL)) {
1682 xhtmlNodeDumpOutput(ctxt, cur);
1683 break;
1684 }
1685
1686 xmlOutputBufferWrite(buf, 1, "<");
1687 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1688 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1689 xmlOutputBufferWrite(buf, 1, ":");
1690 }
1691
1692 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1693 if (cur->nsDef)
1694 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1695 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1696 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1697 /*
1698 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1699 */
1700 xmlOutputBufferWriteString(buf,
1701 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1702 }
1703 if (cur->properties != NULL)
1704 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1705
1706 if ((parent != NULL) &&
1707 (parent->parent == (xmlNodePtr) cur->doc) &&
1708 xmlStrEqual(cur->name, BAD_CAST"head") &&
1709 xmlStrEqual(parent->name, BAD_CAST"html")) {
1710
1711 tmp = cur->children;
1712 while (tmp != NULL) {
1713 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1714 xmlChar *httpequiv;
1715
1716 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1717 if (httpequiv != NULL) {
1718 if (xmlStrcasecmp(httpequiv,
1719 BAD_CAST"Content-Type") == 0) {
1720 xmlFree(httpequiv);
1721 break;
1722 }
1723 xmlFree(httpequiv);
1724 }
1725 }
1726 tmp = tmp->next;
1727 }
1728 if (tmp == NULL)
1729 addmeta = 1;
1730 }
1731
1732 if (cur->children == NULL) {
1733 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1734 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1735 /*
1736 * C.2. Empty Elements
1737 */
1738 xmlOutputBufferWrite(buf, 3, " />");
1739 } else {
1740 if (addmeta == 1) {
1741 xmlOutputBufferWrite(buf, 1, ">");
1742 if (ctxt->format == 1) {
1743 xmlOutputBufferWrite(buf, 1, "\n");
1744 xmlSaveWriteIndent(ctxt, 1);
1745 }
1746 xmlOutputBufferWriteString(buf,
1747 "<meta http-equiv=\"Content-Type\" "
1748 "content=\"text/html; charset=");
1749 if (ctxt->encoding) {
1750 xmlOutputBufferWriteString(buf,
1751 (const char *)ctxt->encoding);
1752 } else {
1753 xmlOutputBufferWrite(buf, 5, "UTF-8");
1754 }
1755 xmlOutputBufferWrite(buf, 4, "\" />");
1756 if (ctxt->format == 1)
1757 xmlOutputBufferWrite(buf, 1, "\n");
1758 } else {
1759 xmlOutputBufferWrite(buf, 1, ">");
1760 }
1761 /*
1762 * C.3. Element Minimization and Empty Element Content
1763 */
1764 xmlOutputBufferWrite(buf, 2, "</");
1765 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1766 xmlOutputBufferWriteString(buf,
1767 (const char *)cur->ns->prefix);
1768 xmlOutputBufferWrite(buf, 1, ":");
1769 }
1770 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1771 xmlOutputBufferWrite(buf, 1, ">");
1772 }
1773 } else {
1774 xmlOutputBufferWrite(buf, 1, ">");
1775 if (addmeta == 1) {
1776 if (ctxt->format == 1) {
1777 xmlOutputBufferWrite(buf, 1, "\n");
1778 xmlSaveWriteIndent(ctxt, 1);
1779 }
1780 xmlOutputBufferWriteString(buf,
1781 "<meta http-equiv=\"Content-Type\" "
1782 "content=\"text/html; charset=");
1783 if (ctxt->encoding) {
1784 xmlOutputBufferWriteString(buf,
1785 (const char *)ctxt->encoding);
1786 } else {
1787 xmlOutputBufferWrite(buf, 5, "UTF-8");
1788 }
1789 xmlOutputBufferWrite(buf, 4, "\" />");
1790 }
1791
1792 if (ctxt->format == 1) {
1793 tmp = cur->children;
1794 while (tmp != NULL) {
1795 if ((tmp->type == XML_TEXT_NODE) ||
1796 (tmp->type == XML_ENTITY_REF_NODE)) {
1797 unformattedNode = cur;
1798 ctxt->format = 0;
1799 break;
1800 }
1801 tmp = tmp->next;
1802 }
1803 }
1804
1805 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1806 if (ctxt->level >= 0) ctxt->level++;
1807 parent = cur;
1808 cur = cur->children;
1809 continue;
1810 }
1811
1812 break;
1813
1814 case XML_TEXT_NODE:
1815 if (cur->content == NULL)
1816 break;
1817 if ((cur->name == xmlStringText) ||
1818 (cur->name != xmlStringTextNoenc)) {
1819 if (ctxt->escape)
1820 xmlOutputBufferWriteEscape(buf, cur->content,
1821 ctxt->escape);
1822 else
1823 xmlSaveWriteText(ctxt, cur->content, /* flags */ 0);
1824 } else {
1825 /*
1826 * Disable escaping, needed for XSLT
1827 */
1828 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1829 }
1830 break;
1831
1832 case XML_PI_NODE:
1833 if (cur->content != NULL) {
1834 xmlOutputBufferWrite(buf, 2, "<?");
1835 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1836 if (cur->content != NULL) {
1837 xmlOutputBufferWrite(buf, 1, " ");
1838 xmlOutputBufferWriteString(buf,
1839 (const char *)cur->content);
1840 }
1841 xmlOutputBufferWrite(buf, 2, "?>");
1842 } else {
1843 xmlOutputBufferWrite(buf, 2, "<?");
1844 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1845 xmlOutputBufferWrite(buf, 2, "?>");
1846 }
1847 break;
1848
1849 case XML_COMMENT_NODE:
1850 if (cur->content != NULL) {
1851 xmlOutputBufferWrite(buf, 4, "<!--");
1852 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1853 xmlOutputBufferWrite(buf, 3, "-->");
1854 }
1855 break;
1856
1857 case XML_ENTITY_REF_NODE:
1858 xmlOutputBufferWrite(buf, 1, "&");
1859 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1860 xmlOutputBufferWrite(buf, 1, ";");
1861 break;
1862
1863 case XML_CDATA_SECTION_NODE:
1864 if (cur->content == NULL || *cur->content == '\0') {
1865 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1866 } else {
1867 start = end = cur->content;
1868 while (*end != '\0') {
1869 if (*end == ']' && *(end + 1) == ']' &&
1870 *(end + 2) == '>') {
1871 end = end + 2;
1872 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1873 xmlOutputBufferWrite(buf, end - start,
1874 (const char *)start);
1875 xmlOutputBufferWrite(buf, 3, "]]>");
1876 start = end;
1877 }
1878 end++;
1879 }
1880 if (start != end) {
1881 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1882 xmlOutputBufferWriteString(buf, (const char *)start);
1883 xmlOutputBufferWrite(buf, 3, "]]>");
1884 }
1885 }
1886 break;
1887
1888 case XML_ATTRIBUTE_NODE:
1889 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1890 break;
1891
1892 default:
1893 break;
1894 }
1895
1896 while (1) {
1897 if (cur == root)
1898 return;
1899 if (ctxt->format == 1)
1900 xmlOutputBufferWrite(buf, 1, "\n");
1901 if (cur->next != NULL) {
1902 cur = cur->next;
1903 break;
1904 }
1905
1906 cur = parent;
1907 /* cur->parent was validated when descending. */
1908 parent = cur->parent;
1909
1910 if (cur->type == XML_ELEMENT_NODE) {
1911 if (ctxt->level > 0) ctxt->level--;
1912 if (ctxt->format == 1)
1913 xmlSaveWriteIndent(ctxt, 0);
1914
1915 xmlOutputBufferWrite(buf, 2, "</");
1916 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1917 xmlOutputBufferWriteString(buf,
1918 (const char *)cur->ns->prefix);
1919 xmlOutputBufferWrite(buf, 1, ":");
1920 }
1921
1922 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1923 xmlOutputBufferWrite(buf, 1, ">");
1924
1925 if (cur == unformattedNode) {
1926 ctxt->format = format;
1927 unformattedNode = NULL;
1928 }
1929 }
1930 }
1931 }
1932
1933 ctxt->options = oldoptions;
1934 }
1935 #endif
1936
1937 /************************************************************************
1938 * *
1939 * Public entry points *
1940 * *
1941 ************************************************************************/
1942
1943 /**
1944 * xmlSaveToFd:
1945 * @fd: a file descriptor number
1946 * @encoding: the encoding name to use or NULL
1947 * @options: a set of xmlSaveOptions
1948 *
1949 * Create a document saving context serializing to a file descriptor
1950 * with the encoding and the options given.
1951 *
1952 * Returns a new serialization context or NULL in case of error.
1953 */
1954 xmlSaveCtxtPtr
xmlSaveToFd(int fd,const char * encoding,int options)1955 xmlSaveToFd(int fd, const char *encoding, int options)
1956 {
1957 xmlSaveCtxtPtr ret;
1958
1959 ret = xmlNewSaveCtxt(encoding, options);
1960 if (ret == NULL) return(NULL);
1961 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1962 if (ret->buf == NULL) {
1963 xmlFreeSaveCtxt(ret);
1964 return(NULL);
1965 }
1966 return(ret);
1967 }
1968
1969 /**
1970 * xmlSaveToFilename:
1971 * @filename: a file name or an URL
1972 * @encoding: the encoding name to use or NULL
1973 * @options: a set of xmlSaveOptions
1974 *
1975 * Create a document saving context serializing to a filename or possibly
1976 * to an URL (but this is less reliable) with the encoding and the options
1977 * given.
1978 *
1979 * Returns a new serialization context or NULL in case of error.
1980 */
1981 xmlSaveCtxtPtr
xmlSaveToFilename(const char * filename,const char * encoding,int options)1982 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1983 {
1984 xmlSaveCtxtPtr ret;
1985 int compression = 0; /* TODO handle compression option */
1986
1987 ret = xmlNewSaveCtxt(encoding, options);
1988 if (ret == NULL) return(NULL);
1989 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1990 compression);
1991 if (ret->buf == NULL) {
1992 xmlFreeSaveCtxt(ret);
1993 return(NULL);
1994 }
1995 return(ret);
1996 }
1997
1998 /**
1999 * xmlSaveToBuffer:
2000 * @buffer: a buffer
2001 * @encoding: the encoding name to use or NULL
2002 * @options: a set of xmlSaveOptions
2003 *
2004 * Create a document saving context serializing to a buffer
2005 * with the encoding and the options given
2006 *
2007 * Returns a new serialization context or NULL in case of error.
2008 */
2009
2010 xmlSaveCtxtPtr
xmlSaveToBuffer(xmlBufferPtr buffer,const char * encoding,int options)2011 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
2012 {
2013 xmlSaveCtxtPtr ret;
2014
2015 ret = xmlNewSaveCtxt(encoding, options);
2016 if (ret == NULL) return(NULL);
2017 ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
2018 if (ret->buf == NULL) {
2019 xmlFreeSaveCtxt(ret);
2020 return(NULL);
2021 }
2022 return(ret);
2023 }
2024
2025 /**
2026 * xmlSaveToIO:
2027 * @iowrite: an I/O write function
2028 * @ioclose: an I/O close function
2029 * @ioctx: an I/O handler
2030 * @encoding: the encoding name to use or NULL
2031 * @options: a set of xmlSaveOptions
2032 *
2033 * Create a document saving context serializing to a file descriptor
2034 * with the encoding and the options given
2035 *
2036 * Returns a new serialization context or NULL in case of error.
2037 */
2038 xmlSaveCtxtPtr
xmlSaveToIO(xmlOutputWriteCallback iowrite,xmlOutputCloseCallback ioclose,void * ioctx,const char * encoding,int options)2039 xmlSaveToIO(xmlOutputWriteCallback iowrite,
2040 xmlOutputCloseCallback ioclose,
2041 void *ioctx, const char *encoding, int options)
2042 {
2043 xmlSaveCtxtPtr ret;
2044
2045 ret = xmlNewSaveCtxt(encoding, options);
2046 if (ret == NULL) return(NULL);
2047 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
2048 if (ret->buf == NULL) {
2049 xmlFreeSaveCtxt(ret);
2050 return(NULL);
2051 }
2052 return(ret);
2053 }
2054
2055 /**
2056 * xmlSaveDoc:
2057 * @ctxt: a document saving context
2058 * @doc: a document
2059 *
2060 * Save a full document to a saving context
2061 * TODO: The function is not fully implemented yet as it does not return the
2062 * byte count but 0 instead
2063 *
2064 * Returns the number of byte written or -1 in case of error
2065 */
2066 long
xmlSaveDoc(xmlSaveCtxtPtr ctxt,xmlDocPtr doc)2067 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
2068 {
2069 long ret = 0;
2070
2071 if ((ctxt == NULL) || (doc == NULL)) return(-1);
2072 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
2073 return(-1);
2074 return(ret);
2075 }
2076
2077 /**
2078 * xmlSaveTree:
2079 * @ctxt: a document saving context
2080 * @cur: the top node of the subtree to save
2081 *
2082 * Save a subtree starting at the node parameter to a saving context
2083 * TODO: The function is not fully implemented yet as it does not return the
2084 * byte count but 0 instead
2085 *
2086 * Returns the number of byte written or -1 in case of error
2087 */
2088 long
xmlSaveTree(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)2089 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
2090 {
2091 long ret = 0;
2092
2093 if ((ctxt == NULL) || (cur == NULL)) return(-1);
2094 #ifdef LIBXML_HTML_ENABLED
2095 if (ctxt->options & XML_SAVE_XHTML) {
2096 xhtmlNodeDumpOutput(ctxt, cur);
2097 return(ret);
2098 }
2099 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
2100 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
2101 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
2102 (ctxt->options & XML_SAVE_AS_HTML)) {
2103 htmlNodeDumpOutputInternal(ctxt, cur);
2104 return(ret);
2105 }
2106 #endif
2107 xmlNodeDumpOutputInternal(ctxt, cur);
2108 return(ret);
2109 }
2110
2111 /**
2112 * xmlSaveNotationDecl:
2113 * @ctxt: save context
2114 * @cur: notation
2115 *
2116 * Serialize a notation declaration.
2117 *
2118 * Return 0 on succes, -1 on error.
2119 */
2120 int
xmlSaveNotationDecl(xmlSaveCtxtPtr ctxt,xmlNotationPtr cur)2121 xmlSaveNotationDecl(xmlSaveCtxtPtr ctxt, xmlNotationPtr cur) {
2122 if (ctxt == NULL)
2123 return(-1);
2124 xmlBufDumpNotationDecl(ctxt->buf, cur);
2125 return(0);
2126 }
2127
2128 /**
2129 * xmlSaveNotationTable:
2130 * @ctxt: save context
2131 * @cur: notation table
2132 *
2133 * Serialize notation declarations of a document.
2134 *
2135 * Return 0 on succes, -1 on error.
2136 */
2137 int
xmlSaveNotationTable(xmlSaveCtxtPtr ctxt,xmlNotationTablePtr cur)2138 xmlSaveNotationTable(xmlSaveCtxtPtr ctxt, xmlNotationTablePtr cur) {
2139 if (ctxt == NULL)
2140 return(-1);
2141 xmlBufDumpNotationTable(ctxt->buf, cur);
2142 return(0);
2143 }
2144
2145 /**
2146 * xmlSaveFlush:
2147 * @ctxt: a document saving context
2148 *
2149 * Flush a document saving context, i.e. make sure that all bytes have
2150 * been output.
2151 *
2152 * Returns the number of byte written or -1 in case of error.
2153 */
2154 int
xmlSaveFlush(xmlSaveCtxtPtr ctxt)2155 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
2156 {
2157 if (ctxt == NULL) return(-1);
2158 if (ctxt->buf == NULL) return(-1);
2159 return(xmlOutputBufferFlush(ctxt->buf));
2160 }
2161
2162 /**
2163 * xmlSaveClose:
2164 * @ctxt: a document saving context
2165 *
2166 * Close a document saving context, i.e. make sure that all bytes have
2167 * been output and free the associated data.
2168 *
2169 * Returns the number of byte written or -1 in case of error.
2170 */
2171 int
xmlSaveClose(xmlSaveCtxtPtr ctxt)2172 xmlSaveClose(xmlSaveCtxtPtr ctxt)
2173 {
2174 int ret;
2175
2176 if (ctxt == NULL) return(-1);
2177 ret = xmlSaveFlush(ctxt);
2178 xmlFreeSaveCtxt(ctxt);
2179 return(ret);
2180 }
2181
2182 /**
2183 * xmlSaveFinish:
2184 * @ctxt: a document saving context
2185 *
2186 * Close a document saving context, i.e. make sure that all bytes have
2187 * been output and free the associated data.
2188 *
2189 * Available since 2.13.0.
2190 *
2191 * Returns an xmlParserErrors code.
2192 */
2193 int
xmlSaveFinish(xmlSaveCtxtPtr ctxt)2194 xmlSaveFinish(xmlSaveCtxtPtr ctxt)
2195 {
2196 int ret;
2197
2198 if (ctxt == NULL)
2199 return(XML_ERR_INTERNAL_ERROR);
2200 xmlSaveFlush(ctxt);
2201 ret = ctxt->buf->error;
2202 xmlFreeSaveCtxt(ctxt);
2203 return(ret);
2204 }
2205
2206 /**
2207 * xmlSaveSetEscape:
2208 * @ctxt: a document saving context
2209 * @escape: the escaping function
2210 *
2211 * DEPRECATED: Don't use.
2212 *
2213 * Set a custom escaping function to be used for text in element content
2214 *
2215 * Returns 0 if successful or -1 in case of error.
2216 */
2217 int
xmlSaveSetEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)2218 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
2219 {
2220 if (ctxt == NULL) return(-1);
2221 ctxt->escape = escape;
2222 return(0);
2223 }
2224
2225 /**
2226 * xmlSaveSetAttrEscape:
2227 * @ctxt: a document saving context
2228 * @escape: the escaping function
2229 *
2230 * DEPRECATED: Don't use.
2231 *
2232 * Has no effect.
2233 *
2234 * Returns 0 if successful or -1 in case of error.
2235 */
2236 int
xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape ATTRIBUTE_UNUSED)2237 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt,
2238 xmlCharEncodingOutputFunc escape ATTRIBUTE_UNUSED)
2239 {
2240 if (ctxt == NULL) return(-1);
2241 return(0);
2242 }
2243
2244 /************************************************************************
2245 * *
2246 * Public entry points based on buffers *
2247 * *
2248 ************************************************************************/
2249
2250 /**
2251 * xmlBufAttrSerializeTxtContent:
2252 * @buf: output buffer
2253 * @doc: the document
2254 * @string: the text content
2255 *
2256 * Serialize text attribute values to an xmlBufPtr
2257 */
2258 void
xmlBufAttrSerializeTxtContent(xmlOutputBufferPtr buf,xmlDocPtr doc,const xmlChar * string)2259 xmlBufAttrSerializeTxtContent(xmlOutputBufferPtr buf, xmlDocPtr doc,
2260 const xmlChar *string)
2261 {
2262 int flags = XML_ESCAPE_ATTR;
2263
2264 if ((doc == NULL) || (doc->encoding == NULL))
2265 flags |= XML_ESCAPE_NON_ASCII;
2266 xmlSerializeText(buf, string, flags);
2267 }
2268
2269 /**
2270 * xmlAttrSerializeTxtContent:
2271 * @buf: the XML buffer output
2272 * @doc: the document
2273 * @attr: the attribute node
2274 * @string: the text content
2275 *
2276 * Serialize text attribute values to an xml simple buffer
2277 */
2278 void
xmlAttrSerializeTxtContent(xmlBufferPtr buf,xmlDocPtr doc,xmlAttrPtr attr ATTRIBUTE_UNUSED,const xmlChar * string)2279 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2280 xmlAttrPtr attr ATTRIBUTE_UNUSED,
2281 const xmlChar *string)
2282 {
2283 xmlOutputBufferPtr out;
2284
2285 if ((buf == NULL) || (string == NULL))
2286 return;
2287 out = xmlOutputBufferCreateBuffer(buf, NULL);
2288 xmlBufAttrSerializeTxtContent(out, doc, string);
2289 xmlOutputBufferFlush(out);
2290 if ((out == NULL) || (out->error))
2291 xmlFree(xmlBufferDetach(buf));
2292 xmlOutputBufferClose(out);
2293 }
2294
2295 /**
2296 * xmlNodeDump:
2297 * @buf: the XML buffer output
2298 * @doc: the document
2299 * @cur: the current node
2300 * @level: the imbrication level for indenting
2301 * @format: is formatting allowed
2302 *
2303 * Dump an XML node, recursive behaviour,children are printed too.
2304 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2305 * or xmlKeepBlanksDefault(0) was called.
2306 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2307 * deprecated, use xmlNodeDumpOutput() instead.
2308 *
2309 * Returns the number of bytes written to the buffer or -1 in case of error
2310 */
2311 int
xmlNodeDump(xmlBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2312 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2313 int format)
2314 {
2315 xmlBufPtr buffer;
2316 size_t ret1;
2317 int ret2;
2318
2319 if ((buf == NULL) || (cur == NULL))
2320 return(-1);
2321 if (level < 0)
2322 level = 0;
2323 else if (level > 100)
2324 level = 100;
2325 buffer = xmlBufFromBuffer(buf);
2326 if (buffer == NULL)
2327 return(-1);
2328 ret1 = xmlBufNodeDump(buffer, doc, cur, level, format);
2329 ret2 = xmlBufBackToBuffer(buffer, buf);
2330 if ((ret1 == (size_t) -1) || (ret2 < 0))
2331 return(-1);
2332 return(ret1 > INT_MAX ? INT_MAX : ret1);
2333 }
2334
2335 /**
2336 * xmlBufNodeDump:
2337 * @buf: the XML buffer output
2338 * @doc: the document
2339 * @cur: the current node
2340 * @level: the imbrication level for indenting
2341 * @format: is formatting allowed
2342 *
2343 * Dump an XML node, recursive behaviour,children are printed too.
2344 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2345 * or xmlKeepBlanksDefault(0) was called
2346 *
2347 * Returns the number of bytes written to the buffer, in case of error 0
2348 * is returned or @buf stores the error
2349 */
2350
2351 size_t
xmlBufNodeDump(xmlBufPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2352 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2353 int format)
2354 {
2355 size_t use;
2356 size_t ret;
2357 xmlOutputBufferPtr outbuf;
2358
2359 xmlInitParser();
2360
2361 if (cur == NULL) {
2362 return ((size_t) -1);
2363 }
2364 if (buf == NULL) {
2365 return ((size_t) -1);
2366 }
2367 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2368 if (outbuf == NULL) {
2369 xmlSaveErrMemory(NULL);
2370 return ((size_t) -1);
2371 }
2372 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2373 outbuf->buffer = buf;
2374 outbuf->encoder = NULL;
2375 outbuf->writecallback = NULL;
2376 outbuf->closecallback = NULL;
2377 outbuf->context = NULL;
2378 outbuf->written = 0;
2379
2380 use = xmlBufUse(buf);
2381 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2382 if (outbuf->error)
2383 ret = (size_t) -1;
2384 else
2385 ret = xmlBufUse(buf) - use;
2386 xmlFree(outbuf);
2387 return (ret);
2388 }
2389
2390 /**
2391 * xmlElemDump:
2392 * @f: the FILE * for the output
2393 * @doc: the document
2394 * @cur: the current node
2395 *
2396 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2397 */
2398 void
xmlElemDump(FILE * f,xmlDocPtr doc,xmlNodePtr cur)2399 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2400 {
2401 xmlOutputBufferPtr outbuf;
2402
2403 xmlInitParser();
2404
2405 if (cur == NULL) {
2406 return;
2407 }
2408
2409 outbuf = xmlOutputBufferCreateFile(f, NULL);
2410 if (outbuf == NULL)
2411 return;
2412 #ifdef LIBXML_HTML_ENABLED
2413 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE))
2414 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2415 else
2416 #endif /* LIBXML_HTML_ENABLED */
2417 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2418 xmlOutputBufferClose(outbuf);
2419 }
2420
2421 /************************************************************************
2422 * *
2423 * Saving functions front-ends *
2424 * *
2425 ************************************************************************/
2426
2427 /**
2428 * xmlNodeDumpOutput:
2429 * @buf: the XML buffer output
2430 * @doc: the document
2431 * @cur: the current node
2432 * @level: the imbrication level for indenting
2433 * @format: is formatting allowed
2434 * @encoding: an optional encoding string
2435 *
2436 * Dump an XML node, recursive behaviour, children are printed too.
2437 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2438 * or xmlKeepBlanksDefault(0) was called
2439 */
2440 void
xmlNodeDumpOutput(xmlOutputBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format,const char * encoding)2441 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2442 int level, int format, const char *encoding)
2443 {
2444 xmlSaveCtxt ctxt;
2445 int options;
2446 #ifdef LIBXML_HTML_ENABLED
2447 xmlDtdPtr dtd;
2448 int is_xhtml = 0;
2449 #endif
2450
2451 (void) doc;
2452
2453 xmlInitParser();
2454
2455 if ((buf == NULL) || (cur == NULL)) return;
2456
2457 if (level < 0)
2458 level = 0;
2459 else if (level > 100)
2460 level = 100;
2461
2462 if (encoding == NULL)
2463 encoding = "UTF-8";
2464
2465 memset(&ctxt, 0, sizeof(ctxt));
2466 ctxt.buf = buf;
2467 ctxt.level = level;
2468 ctxt.encoding = (const xmlChar *) encoding;
2469
2470 options = XML_SAVE_AS_XML;
2471 if (format)
2472 options |= XML_SAVE_FORMAT;
2473 xmlSaveCtxtInit(&ctxt, options);
2474
2475 #ifdef LIBXML_HTML_ENABLED
2476 dtd = xmlGetIntSubset(doc);
2477 if (dtd != NULL) {
2478 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2479 if (is_xhtml < 0)
2480 is_xhtml = 0;
2481 }
2482
2483 if (is_xhtml)
2484 xhtmlNodeDumpOutput(&ctxt, cur);
2485 else
2486 #endif
2487 xmlNodeDumpOutputInternal(&ctxt, cur);
2488 }
2489
2490 /**
2491 * xmlDocDumpFormatMemoryEnc:
2492 * @out_doc: Document to generate XML text from
2493 * @doc_txt_ptr: Memory pointer for allocated XML text
2494 * @doc_txt_len: Length of the generated XML text
2495 * @txt_encoding: Character encoding to use when generating XML text
2496 * @format: should formatting spaces been added
2497 *
2498 * Dump the current DOM tree into memory using the character encoding specified
2499 * by the caller. Note it is up to the caller of this function to free the
2500 * allocated memory with xmlFree().
2501 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2502 * or xmlKeepBlanksDefault(0) was called
2503 */
2504
2505 void
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding,int format)2506 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2507 int * doc_txt_len, const char * txt_encoding,
2508 int format) {
2509 xmlSaveCtxt ctxt;
2510 int options;
2511 int dummy = 0;
2512 xmlOutputBufferPtr out_buff = NULL;
2513 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2514
2515 if (doc_txt_len == NULL) {
2516 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2517 }
2518
2519 if (doc_txt_ptr == NULL) {
2520 *doc_txt_len = 0;
2521 return;
2522 }
2523
2524 *doc_txt_ptr = NULL;
2525 *doc_txt_len = 0;
2526
2527 if (out_doc == NULL) {
2528 /* No document, no output */
2529 return;
2530 }
2531
2532 /*
2533 * Validate the encoding value, if provided.
2534 * This logic is copied from xmlSaveFileEnc.
2535 */
2536
2537 if (txt_encoding == NULL)
2538 txt_encoding = (const char *) out_doc->encoding;
2539 if (txt_encoding != NULL) {
2540 int res;
2541
2542 res = xmlOpenCharEncodingHandler(txt_encoding, /* output */ 1,
2543 &conv_hdlr);
2544 if (res != XML_ERR_OK) {
2545 xmlSaveErr(NULL, res, NULL, txt_encoding);
2546 return;
2547 }
2548 }
2549
2550 out_buff = xmlAllocOutputBuffer(conv_hdlr);
2551 if (out_buff == NULL ) {
2552 xmlSaveErrMemory(NULL);
2553 return;
2554 }
2555
2556 memset(&ctxt, 0, sizeof(ctxt));
2557 ctxt.buf = out_buff;
2558 ctxt.level = 0;
2559 ctxt.encoding = (const xmlChar *) txt_encoding;
2560
2561 options = XML_SAVE_AS_XML;
2562 if (format)
2563 options |= XML_SAVE_FORMAT;
2564 xmlSaveCtxtInit(&ctxt, options);
2565
2566 xmlDocContentDumpOutput(&ctxt, out_doc);
2567 xmlOutputBufferFlush(out_buff);
2568
2569 if (!out_buff->error) {
2570 if (out_buff->conv != NULL) {
2571 *doc_txt_len = xmlBufUse(out_buff->conv);
2572 *doc_txt_ptr = xmlBufDetach(out_buff->conv);
2573 } else {
2574 *doc_txt_len = xmlBufUse(out_buff->buffer);
2575 *doc_txt_ptr = xmlBufDetach(out_buff->buffer);
2576 }
2577 }
2578
2579 xmlOutputBufferClose(out_buff);
2580 }
2581
2582 /**
2583 * xmlDocDumpMemory:
2584 * @cur: the document
2585 * @mem: OUT: the memory pointer
2586 * @size: OUT: the memory length
2587 *
2588 * Dump an XML document in memory and return the #xmlChar * and it's size
2589 * in bytes. It's up to the caller to free the memory with xmlFree().
2590 * The resulting byte array is zero terminated, though the last 0 is not
2591 * included in the returned size.
2592 */
2593 void
xmlDocDumpMemory(xmlDocPtr cur,xmlChar ** mem,int * size)2594 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2595 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2596 }
2597
2598 /**
2599 * xmlDocDumpFormatMemory:
2600 * @cur: the document
2601 * @mem: OUT: the memory pointer
2602 * @size: OUT: the memory length
2603 * @format: should formatting spaces been added
2604 *
2605 *
2606 * Dump an XML document in memory and return the #xmlChar * and it's size.
2607 * It's up to the caller to free the memory with xmlFree().
2608 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2609 * or xmlKeepBlanksDefault(0) was called
2610 */
2611 void
xmlDocDumpFormatMemory(xmlDocPtr cur,xmlChar ** mem,int * size,int format)2612 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2613 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2614 }
2615
2616 /**
2617 * xmlDocDumpMemoryEnc:
2618 * @out_doc: Document to generate XML text from
2619 * @doc_txt_ptr: Memory pointer for allocated XML text
2620 * @doc_txt_len: Length of the generated XML text
2621 * @txt_encoding: Character encoding to use when generating XML text
2622 *
2623 * Dump the current DOM tree into memory using the character encoding specified
2624 * by the caller. Note it is up to the caller of this function to free the
2625 * allocated memory with xmlFree().
2626 */
2627
2628 void
xmlDocDumpMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding)2629 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2630 int * doc_txt_len, const char * txt_encoding) {
2631 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2632 txt_encoding, 0);
2633 }
2634
2635 /**
2636 * xmlDocFormatDump:
2637 * @f: the FILE*
2638 * @cur: the document
2639 * @format: should formatting spaces been added
2640 *
2641 * Dump an XML document to an open FILE.
2642 *
2643 * returns: the number of bytes written or -1 in case of failure.
2644 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2645 * or xmlKeepBlanksDefault(0) was called
2646 */
2647 int
xmlDocFormatDump(FILE * f,xmlDocPtr cur,int format)2648 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2649 xmlSaveCtxt ctxt;
2650 xmlOutputBufferPtr buf;
2651 const char * encoding;
2652 xmlCharEncodingHandlerPtr handler = NULL;
2653 int ret;
2654 int options;
2655
2656 if (cur == NULL) {
2657 return(-1);
2658 }
2659 encoding = (const char *) cur->encoding;
2660
2661 if (encoding != NULL) {
2662 int res;
2663
2664 res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
2665 if (res != XML_ERR_OK) {
2666 xmlFree((char *) cur->encoding);
2667 encoding = NULL;
2668 }
2669 }
2670 buf = xmlOutputBufferCreateFile(f, handler);
2671 if (buf == NULL) return(-1);
2672 memset(&ctxt, 0, sizeof(ctxt));
2673 ctxt.buf = buf;
2674 ctxt.level = 0;
2675 ctxt.encoding = (const xmlChar *) encoding;
2676
2677 options = XML_SAVE_AS_XML;
2678 if (format)
2679 options |= XML_SAVE_FORMAT;
2680 xmlSaveCtxtInit(&ctxt, options);
2681
2682 xmlDocContentDumpOutput(&ctxt, cur);
2683
2684 ret = xmlOutputBufferClose(buf);
2685 return(ret);
2686 }
2687
2688 /**
2689 * xmlDocDump:
2690 * @f: the FILE*
2691 * @cur: the document
2692 *
2693 * Dump an XML document to an open FILE.
2694 *
2695 * returns: the number of bytes written or -1 in case of failure.
2696 */
2697 int
xmlDocDump(FILE * f,xmlDocPtr cur)2698 xmlDocDump(FILE *f, xmlDocPtr cur) {
2699 return(xmlDocFormatDump (f, cur, 0));
2700 }
2701
2702 /**
2703 * xmlSaveFileTo:
2704 * @buf: an output I/O buffer
2705 * @cur: the document
2706 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2707 *
2708 * Dump an XML document to an I/O buffer.
2709 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2710 * after this call.
2711 *
2712 * returns: the number of bytes written or -1 in case of failure.
2713 */
2714 int
xmlSaveFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding)2715 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2716 xmlSaveCtxt ctxt;
2717 int ret;
2718
2719 if (buf == NULL) return(-1);
2720 if (cur == NULL) {
2721 xmlOutputBufferClose(buf);
2722 return(-1);
2723 }
2724 memset(&ctxt, 0, sizeof(ctxt));
2725 ctxt.buf = buf;
2726 ctxt.level = 0;
2727 ctxt.encoding = (const xmlChar *) encoding;
2728
2729 xmlSaveCtxtInit(&ctxt, XML_SAVE_AS_XML);
2730
2731 xmlDocContentDumpOutput(&ctxt, cur);
2732 ret = xmlOutputBufferClose(buf);
2733 return(ret);
2734 }
2735
2736 /**
2737 * xmlSaveFormatFileTo:
2738 * @buf: an output I/O buffer
2739 * @cur: the document
2740 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2741 * @format: should formatting spaces been added
2742 *
2743 * Dump an XML document to an I/O buffer.
2744 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2745 * after this call.
2746 *
2747 * returns: the number of bytes written or -1 in case of failure.
2748 */
2749 int
xmlSaveFormatFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding,int format)2750 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2751 const char *encoding, int format)
2752 {
2753 xmlSaveCtxt ctxt;
2754 int ret;
2755 int options;
2756
2757 if (buf == NULL) return(-1);
2758 if ((cur == NULL) ||
2759 ((cur->type != XML_DOCUMENT_NODE) &&
2760 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2761 xmlOutputBufferClose(buf);
2762 return(-1);
2763 }
2764 memset(&ctxt, 0, sizeof(ctxt));
2765 ctxt.buf = buf;
2766 ctxt.level = 0;
2767 ctxt.encoding = (const xmlChar *) encoding;
2768
2769 options = XML_SAVE_AS_XML;
2770 if (format)
2771 options |= XML_SAVE_FORMAT;
2772 xmlSaveCtxtInit(&ctxt, options);
2773
2774 xmlDocContentDumpOutput(&ctxt, cur);
2775 ret = xmlOutputBufferClose(buf);
2776 return (ret);
2777 }
2778
2779 /**
2780 * xmlSaveFormatFileEnc:
2781 * @filename: the filename or URL to output
2782 * @cur: the document being saved
2783 * @encoding: the name of the encoding to use or NULL.
2784 * @format: should formatting spaces be added.
2785 *
2786 * Dump an XML document to a file or an URL.
2787 *
2788 * Returns the number of bytes written or -1 in case of error.
2789 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2790 * or xmlKeepBlanksDefault(0) was called
2791 */
2792 int
xmlSaveFormatFileEnc(const char * filename,xmlDocPtr cur,const char * encoding,int format)2793 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2794 const char * encoding, int format ) {
2795 xmlSaveCtxt ctxt;
2796 xmlOutputBufferPtr buf;
2797 xmlCharEncodingHandlerPtr handler = NULL;
2798 int ret;
2799 int options;
2800
2801 if (cur == NULL)
2802 return(-1);
2803
2804 if (encoding == NULL)
2805 encoding = (const char *) cur->encoding;
2806
2807 if (encoding != NULL) {
2808 int res;
2809
2810 res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler);
2811 if (res != XML_ERR_OK)
2812 return(-1);
2813 }
2814
2815 #ifdef LIBXML_ZLIB_ENABLED
2816 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2817 #endif
2818 /*
2819 * save the content to a temp buffer.
2820 */
2821 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2822 if (buf == NULL) return(-1);
2823 memset(&ctxt, 0, sizeof(ctxt));
2824 ctxt.buf = buf;
2825 ctxt.level = 0;
2826 ctxt.encoding = (const xmlChar *) encoding;
2827
2828 options = XML_SAVE_AS_XML;
2829 if (format)
2830 options |= XML_SAVE_FORMAT;
2831 xmlSaveCtxtInit(&ctxt, options);
2832
2833 xmlDocContentDumpOutput(&ctxt, cur);
2834
2835 ret = xmlOutputBufferClose(buf);
2836 return(ret);
2837 }
2838
2839
2840 /**
2841 * xmlSaveFileEnc:
2842 * @filename: the filename (or URL)
2843 * @cur: the document
2844 * @encoding: the name of an encoding (or NULL)
2845 *
2846 * Dump an XML document, converting it to the given encoding
2847 *
2848 * returns: the number of bytes written or -1 in case of failure.
2849 */
2850 int
xmlSaveFileEnc(const char * filename,xmlDocPtr cur,const char * encoding)2851 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2852 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2853 }
2854
2855 /**
2856 * xmlSaveFormatFile:
2857 * @filename: the filename (or URL)
2858 * @cur: the document
2859 * @format: should formatting spaces been added
2860 *
2861 * Dump an XML document to a file. Will use compression if
2862 * compiled in and enabled. If @filename is "-" the stdout file is
2863 * used. If @format is set then the document will be indented on output.
2864 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2865 * or xmlKeepBlanksDefault(0) was called
2866 *
2867 * returns: the number of bytes written or -1 in case of failure.
2868 */
2869 int
xmlSaveFormatFile(const char * filename,xmlDocPtr cur,int format)2870 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2871 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2872 }
2873
2874 /**
2875 * xmlSaveFile:
2876 * @filename: the filename (or URL)
2877 * @cur: the document
2878 *
2879 * Dump an XML document to a file. Will use compression if
2880 * compiled in and enabled. If @filename is "-" the stdout file is
2881 * used.
2882 * returns: the number of bytes written or -1 in case of failure.
2883 */
2884 int
xmlSaveFile(const char * filename,xmlDocPtr cur)2885 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2886 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2887 }
2888
2889 #endif /* LIBXML_OUTPUT_ENABLED */
2890
2891