📄 xmlwriter.c
字号:
/* * xmlwriter.c: XML text writer implementation * * For license and disclaimer see the license and disclaimer of * libxml2. * * alfred@mickautsch.de */#define IN_LIBXML#include <string.h>#include "libxml.h"#include <libxml/xmlmemory.h>#include <libxml/parser.h>#include <libxml/uri.h>#include <libxml/HTMLtree.h>#ifdef LIBXML_WRITER_ENABLED#include <libxml/xmlwriter.h>#define B64LINELEN 72#define B64CRLF "\r\n"/* * Types are kept private */typedef enum { XML_TEXTWRITER_NONE = 0, XML_TEXTWRITER_NAME, XML_TEXTWRITER_ATTRIBUTE, XML_TEXTWRITER_TEXT, XML_TEXTWRITER_PI, XML_TEXTWRITER_PI_TEXT, XML_TEXTWRITER_CDATA, XML_TEXTWRITER_DTD, XML_TEXTWRITER_DTD_TEXT, XML_TEXTWRITER_DTD_ELEM, XML_TEXTWRITER_DTD_ELEM_TEXT, XML_TEXTWRITER_DTD_ATTL, XML_TEXTWRITER_DTD_ATTL_TEXT, XML_TEXTWRITER_DTD_ENTY, /* entity */ XML_TEXTWRITER_DTD_ENTY_TEXT, XML_TEXTWRITER_DTD_PENT, /* parameter entity */ XML_TEXTWRITER_COMMENT} xmlTextWriterState;typedef struct _xmlTextWriterStackEntry xmlTextWriterStackEntry;struct _xmlTextWriterStackEntry { xmlChar *name; xmlTextWriterState state;};typedef struct _xmlTextWriterNsStackEntry xmlTextWriterNsStackEntry;struct _xmlTextWriterNsStackEntry { xmlChar *prefix; xmlChar *uri; xmlLinkPtr elem;};struct _xmlTextWriter { xmlOutputBufferPtr out; /* output buffer */ xmlListPtr nodes; /* element name stack */ xmlListPtr nsstack; /* name spaces stack */ int level; int indent; /* enable indent */ int doindent; /* internal indent flag */ xmlChar *ichar; /* indent character */ char qchar; /* character used for quoting attribute values */ xmlParserCtxtPtr ctxt; int no_doc_free;};static void xmlFreeTextWriterStackEntry(xmlLinkPtr lk);static int xmlCmpTextWriterStackEntry(const void *data0, const void *data1);static void xmlFreeTextWriterNsStackEntry(xmlLinkPtr lk);static int xmlCmpTextWriterNsStackEntry(const void *data0, const void *data1);static int xmlTextWriterWriteMemCallback(void *context, const xmlChar * str, int len);static int xmlTextWriterCloseMemCallback(void *context);static int xmlTextWriterWriteDocCallback(void *context, const xmlChar * str, int len);static int xmlTextWriterCloseDocCallback(void *context);static xmlChar *xmlTextWriterVSprintf(const char *format, va_list argptr);static int xmlOutputBufferWriteBase64(xmlOutputBufferPtr out, int len, const unsigned char *data);static void xmlTextWriterStartDocumentCallback(void *ctx);static int xmlTextWriterWriteIndent(xmlTextWriterPtr writer);static int xmlTextWriterHandleStateDependencies(xmlTextWriterPtr writer, xmlTextWriterStackEntry * p);/** * xmlWriterErrMsg: * @ctxt: a writer context * @error: the error number * @msg: the error message * * Handle a writer error */static voidxmlWriterErrMsg(xmlTextWriterPtr ctxt, xmlParserErrors error, const char *msg){ if (ctxt != NULL) { __xmlRaiseError(NULL, NULL, NULL, ctxt->ctxt, NULL, XML_FROM_WRITER, error, XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0, msg); } else { __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_WRITER, error, XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0, msg); }}/** * xmlWriterErrMsgInt: * @ctxt: a writer context * @error: the error number * @msg: the error message * @val: an int * * Handle a writer error */static voidxmlWriterErrMsgInt(xmlTextWriterPtr ctxt, xmlParserErrors error, const char *msg, int val){ if (ctxt != NULL) { __xmlRaiseError(NULL, NULL, NULL, ctxt->ctxt, NULL, XML_FROM_WRITER, error, XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, val, 0, msg, val); } else { __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_WRITER, error, XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, val, 0, msg, val); }}/** * xmlNewTextWriter: * @out: an xmlOutputBufferPtr * * Create a new xmlNewTextWriter structure using an xmlOutputBufferPtr * NOTE: the @out parameter will be deallocated when the writer is closed * (if the call succeed.) * * Returns the new xmlTextWriterPtr or NULL in case of error */xmlTextWriterPtrxmlNewTextWriter(xmlOutputBufferPtr out){ xmlTextWriterPtr ret; ret = (xmlTextWriterPtr) xmlMalloc(sizeof(xmlTextWriter)); if (ret == NULL) { xmlWriterErrMsg(NULL, XML_ERR_NO_MEMORY, "xmlNewTextWriter : out of memory!\n"); return NULL; } memset(ret, 0, (size_t) sizeof(xmlTextWriter)); ret->nodes = xmlListCreate((xmlListDeallocator) xmlFreeTextWriterStackEntry, (xmlListDataCompare) xmlCmpTextWriterStackEntry); if (ret->nodes == NULL) { xmlWriterErrMsg(NULL, XML_ERR_NO_MEMORY, "xmlNewTextWriter : out of memory!\n"); xmlFree(ret); return NULL; } ret->nsstack = xmlListCreate((xmlListDeallocator) xmlFreeTextWriterNsStackEntry, (xmlListDataCompare) xmlCmpTextWriterNsStackEntry); if (ret->nsstack == NULL) { xmlWriterErrMsg(NULL, XML_ERR_NO_MEMORY, "xmlNewTextWriter : out of memory!\n"); xmlListDelete(ret->nodes); xmlFree(ret); return NULL; } ret->out = out; ret->ichar = xmlStrdup(BAD_CAST " "); ret->qchar = '"'; if (!ret->ichar) { xmlListDelete(ret->nodes); xmlListDelete(ret->nsstack); xmlFree(ret); xmlWriterErrMsg(NULL, XML_ERR_NO_MEMORY, "xmlNewTextWriter : out of memory!\n"); return NULL; } ret->no_doc_free = 0; return ret;}/** * xmlNewTextWriterFilename: * @uri: the URI of the resource for the output * @compression: compress the output? * * Create a new xmlNewTextWriter structure with @uri as output * * Returns the new xmlTextWriterPtr or NULL in case of error */xmlTextWriterPtrxmlNewTextWriterFilename(const char *uri, int compression){ xmlTextWriterPtr ret; xmlOutputBufferPtr out; out = xmlOutputBufferCreateFilename(uri, NULL, compression); if (out == NULL) { xmlWriterErrMsg(NULL, XML_ERR_NO_MEMORY, "xmlNewTextWriterFilename : out of memory!\n"); return NULL; } ret = xmlNewTextWriter(out); if (ret == NULL) { xmlWriterErrMsg(NULL, XML_ERR_NO_MEMORY, "xmlNewTextWriterFilename : out of memory!\n"); xmlOutputBufferClose(out); return NULL; } ret->indent = 0; ret->doindent = 0; return ret;}/** * xmlNewTextWriterMemory: * @buf: xmlBufferPtr * @compression: compress the output? * * Create a new xmlNewTextWriter structure with @buf as output * TODO: handle compression * * Returns the new xmlTextWriterPtr or NULL in case of error */xmlTextWriterPtrxmlNewTextWriterMemory(xmlBufferPtr buf, int compression ATTRIBUTE_UNUSED){ xmlTextWriterPtr ret; xmlOutputBufferPtr out;/*::todo handle compression */ out = xmlOutputBufferCreateIO((xmlOutputWriteCallback) xmlTextWriterWriteMemCallback, (xmlOutputCloseCallback) xmlTextWriterCloseMemCallback, (void *) buf, NULL); if (out == NULL) { xmlWriterErrMsg(NULL, XML_ERR_NO_MEMORY, "xmlNewTextWriterMemory : out of memory!\n"); return NULL; } ret = xmlNewTextWriter(out); if (ret == NULL) { xmlWriterErrMsg(NULL, XML_ERR_NO_MEMORY, "xmlNewTextWriterMemory : out of memory!\n"); xmlOutputBufferClose(out); return NULL; } return ret;}/** * xmlNewTextWriterPushParser: * @ctxt: xmlParserCtxtPtr to hold the new XML document tree * @compression: compress the output? * * Create a new xmlNewTextWriter structure with @ctxt as output * NOTE: the @ctxt context will be freed with the resulting writer * (if the call succeeds). * TODO: handle compression * * Returns the new xmlTextWriterPtr or NULL in case of error */xmlTextWriterPtrxmlNewTextWriterPushParser(xmlParserCtxtPtr ctxt, int compression ATTRIBUTE_UNUSED){ xmlTextWriterPtr ret; xmlOutputBufferPtr out; if (ctxt == NULL) { xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterPushParser : invalid context!\n"); return NULL; } out = xmlOutputBufferCreateIO((xmlOutputWriteCallback) xmlTextWriterWriteDocCallback, (xmlOutputCloseCallback) xmlTextWriterCloseDocCallback, (void *) ctxt, NULL); if (out == NULL) { xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterPushParser : error at xmlOutputBufferCreateIO!\n"); return NULL; } ret = xmlNewTextWriter(out); if (ret == NULL) { xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterPushParser : error at xmlNewTextWriter!\n"); xmlOutputBufferClose(out); return NULL; } ret->ctxt = ctxt; return ret;}/** * xmlNewTextWriterDoc: * @doc: address of a xmlDocPtr to hold the new XML document tree * @compression: compress the output? * * Create a new xmlNewTextWriter structure with @*doc as output * * Returns the new xmlTextWriterPtr or NULL in case of error */xmlTextWriterPtrxmlNewTextWriterDoc(xmlDocPtr * doc, int compression){ xmlTextWriterPtr ret; xmlSAXHandler saxHandler; xmlParserCtxtPtr ctxt; memset(&saxHandler, '\0', sizeof(saxHandler)); xmlSAX2InitDefaultSAXHandler(&saxHandler, 1); saxHandler.startDocument = xmlTextWriterStartDocumentCallback; saxHandler.startElement = xmlSAX2StartElement; saxHandler.endElement = xmlSAX2EndElement; ctxt = xmlCreatePushParserCtxt(&saxHandler, NULL, NULL, 0, NULL); if (ctxt == NULL) { xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterDoc : error at xmlCreatePushParserCtxt!\n"); return NULL; } /* * For some reason this seems to completely break if node names * are interned. */ ctxt->dictNames = 0; ctxt->myDoc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION); if (ctxt->myDoc == NULL) { xmlFreeParserCtxt(ctxt); xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterDoc : error at xmlNewDoc!\n"); return NULL; } ret = xmlNewTextWriterPushParser(ctxt, compression); if (ret == NULL) { xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterDoc : error at xmlNewTextWriterPushParser!\n"); return NULL; } xmlSetDocCompressMode(ctxt->myDoc, compression); if (doc != NULL) { *doc = ctxt->myDoc; ret->no_doc_free = 1; } return ret;}/** * xmlNewTextWriterTree: * @doc: xmlDocPtr * @node: xmlNodePtr or NULL for doc->children * @compression: compress the output? * * Create a new xmlNewTextWriter structure with @doc as output * starting at @node * * Returns the new xmlTextWriterPtr or NULL in case of error */xmlTextWriterPtrxmlNewTextWriterTree(xmlDocPtr doc, xmlNodePtr node, int compression){ xmlTextWriterPtr ret; xmlSAXHandler saxHandler; xmlParserCtxtPtr ctxt; if (doc == NULL) { xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterTree : invalid document tree!\n"); return NULL; } memset(&saxHandler, '\0', sizeof(saxHandler)); xmlSAX2InitDefaultSAXHandler(&saxHandler, 1); saxHandler.startDocument = xmlTextWriterStartDocumentCallback; saxHandler.startElement = xmlSAX2StartElement; saxHandler.endElement = xmlSAX2EndElement; ctxt = xmlCreatePushParserCtxt(&saxHandler, NULL, NULL, 0, NULL); if (ctxt == NULL) { xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterDoc : error at xmlCreatePushParserCtxt!\n"); return NULL; } /* * For some reason this seems to completely break if node names * are interned. */ ctxt->dictNames = 0; ret = xmlNewTextWriterPushParser(ctxt, compression); if (ret == NULL) { xmlFreeParserCtxt(ctxt); xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, "xmlNewTextWriterDoc : error at xmlNewTextWriterPushParser!\n"); return NULL; } ctxt->myDoc = doc; ctxt->node = node; ret->no_doc_free = 1; xmlSetDocCompressMode(doc, compression); return ret;}/** * xmlFreeTextWriter: * @writer: the xmlTextWriterPtr * * Deallocate all the resources associated to the writer */voidxmlFreeTextWriter(xmlTextWriterPtr writer){ if (writer == NULL) return; if (writer->out != NULL) xmlOutputBufferClose(writer->out); if (writer->nodes != NULL) xmlListDelete(writer->nodes); if (writer->nsstack != NULL) xmlListDelete(writer->nsstack); if (writer->ctxt != NULL) { if ((writer->ctxt->myDoc != NULL) && (writer->no_doc_free == 0)) { xmlFreeDoc(writer->ctxt->myDoc); writer->ctxt->myDoc = NULL; } xmlFreeParserCtxt(writer->ctxt); } if (writer->ichar != NULL) xmlFree(writer->ichar); xmlFree(writer);}/** * xmlTextWriterStartDocument: * @writer: the xmlTextWriterPtr * @version: the xml version ("1.0") or NULL for default ("1.0") * @encoding: the encoding or NULL for default * @standalone: "yes" or "no" or NULL for default * * Start a new xml document * * Returns the bytes written (may be 0 because of buffering) or -1 in case of error
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -