📄 ne_xml.c
字号:
/* Higher Level Interface to XML Parsers. Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA*/#include "config.h"#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_STRINGS_H#include <strings.h>#endif#include "ne_i18n.h"#include "ne_alloc.h"#include "ne_xml.h"#include "ne_utils.h"#include "ne_string.h"#if defined(HAVE_EXPAT)/* expat support: */#ifdef HAVE_XMLPARSE_H#include "xmlparse.h"#else#include <expat.h>#endiftypedef XML_Char ne_xml_char;#elif defined(HAVE_LIBXML)/* libxml2 support: */#include <libxml/xmlversion.h>#include <libxml/parser.h>typedef xmlChar ne_xml_char;#else /* not HAVE_LIBXML */# error need an XML parser#endif /* not HAVE_EXPAT *//* Approx. one screen of text: */#define ERR_SIZE (2048)struct handler { ne_xml_startelm_cb *startelm_cb; /* start-element callback */ ne_xml_endelm_cb *endelm_cb; /* end-element callback */ ne_xml_cdata_cb *cdata_cb; /* character-data callback. */ void *userdata; /* userdata for the above. */ struct handler *next; /* next handler in stack. */};#ifdef HAVE_LIBXMLstatic void sax_error(void *ctx, const char *msg, ...);#endifstruct element { const ne_xml_char *nspace; ne_xml_char *name; int state; /* opaque state integer */ /* Namespaces declared in this element */ ne_xml_char *default_ns; /* A default namespace */ struct namespace *nspaces; /* List of other namespace scopes */ struct handler *handler; /* Handler for this element */ struct element *parent; /* parent element, or NULL */ };/* We pass around a ne_xml_parser as the userdata in the parsing * library. This maintains the current state of the parse and various * other bits and bobs. Within the parse, we store the current branch * of the tree, i.e., the current element and all its parents, up to * the root, but nothing other than that. */struct ne_xml_parser_s { struct element *root; /* the root of the document */ struct element *current; /* current element in the branch */ struct handler *top_handlers; /* always points at the * handler on top of the stack. */ int valid; /* non-zero whilst parse should continue */ int prune; /* if non-zero, depth within a dead branch */#ifdef HAVE_EXPAT XML_Parser parser; char *encoding;#else xmlParserCtxtPtr parser;#endif char error[ERR_SIZE];};/* The callback handlers */static void start_element(void *userdata, const ne_xml_char *name, const ne_xml_char **atts);static void end_element(void *userdata, const ne_xml_char *name);static void char_data(void *userdata, const ne_xml_char *cdata, int len);static const char *resolve_nspace(const struct element *elm, const char *prefix, size_t pfxlen);/* Linked list of namespace scopes */struct namespace { ne_xml_char *name; ne_xml_char *uri; struct namespace *next;};#ifdef HAVE_LIBXML/* Could be const as far as we care, but libxml doesn't want that */static xmlSAXHandler sax_handler = { NULL, /* internalSubset */ NULL, /* isStandalone */ NULL, /* hasInternalSubset */ NULL, /* hasExternalSubset */ NULL, /* resolveEntity */ NULL, /* getEntity */ NULL, /* entityDecl */ NULL, /* notationDecl */ NULL, /* attributeDecl */ NULL, /* elementDecl */ NULL, /* unparsedEntityDecl */ NULL, /* setDocumentLocator */ NULL, /* startDocument */ NULL, /* endDocument */ start_element, /* startElement */ end_element, /* endElement */ NULL, /* reference */ char_data, /* characters */ NULL, /* ignorableWhitespace */ NULL, /* processingInstruction */ NULL, /* comment */ NULL, /* xmlParserWarning */ sax_error, /* xmlParserError */ sax_error, /* fatal error (never called by libxml2?) */ NULL, /* getParameterEntity */ char_data /* cdataBlock */};/* empty attributes array to mimic expat behaviour */static const char *empty_atts[] = {NULL, NULL};/* macro for determining the attributes array to pass */#define PASS_ATTS(atts) (atts ? (const char **)(atts) : empty_atts)#else#define PASS_ATTS(atts) ((const char **)(atts))/* XML declaration callback for expat. */static void decl_handler(void *userdata, const XML_Char *version, const XML_Char *encoding, int standalone){ ne_xml_parser *p = userdata; if (encoding) p->encoding = ne_strdup(encoding); }#endif /* HAVE_LIBXML */int ne_xml_currentline(ne_xml_parser *p) {#ifdef HAVE_EXPAT return XML_GetCurrentLineNumber(p->parser);#else return p->parser->input->line;#endif}const char *ne_xml_doc_encoding(const ne_xml_parser *p){#ifdef HAVE_LIBXML return p->parser->encoding;#else return p->encoding;#endif}/* Extract the namespace prefix declarations from 'atts'. */static int declare_nspaces(ne_xml_parser *p, struct element *elm, const ne_xml_char **atts){ int n; for (n = 0; atts && atts[n]; n += 2) { if (strcasecmp(atts[n], "xmlns") == 0) { /* New default namespace */ elm->default_ns = ne_strdup(atts[n+1]); } else if (strncasecmp(atts[n], "xmlns:", 6) == 0) { struct namespace *ns; if (atts[n][6] == '\0' || atts[n+1][0] == '\0') { ne_snprintf(p->error, ERR_SIZE, ("XML parse error at line %d: invalid namespace " "declaration"), ne_xml_currentline(p)); return -1; } /* New namespace scope */ ns = ne_calloc(sizeof(*ns)); ns->next = elm->nspaces; elm->nspaces = ns; ns->name = ne_strdup(atts[n]+6); /* skip the xmlns= */ ns->uri = ne_strdup(atts[n+1]); } } return 0;}/* Expand an XML qualified name, which may include a namespace prefix * as well as the local part. */static int expand_qname(ne_xml_parser *p, struct element *elm, const ne_xml_char *qname){ const ne_xml_char *pfx; pfx = strchr(qname, ':'); if (pfx == NULL) { struct element *e = elm; /* Find default namespace; guaranteed to terminate as the root * element always has default_ns="". */ while (e->default_ns == NULL) e = e->parent; elm->name = ne_strdup(qname); elm->nspace = e->default_ns; } else { const char *uri = resolve_nspace(elm, qname, pfx-qname); if (uri) { /* The name is everything after the ':' */ if (pfx[1] == '\0') { ne_snprintf(p->error, ERR_SIZE, ("XML parse error at line %d: element name missing" "after namespace prefix"), ne_xml_currentline(p)); return -1; } elm->name = ne_strdup(pfx+1); elm->nspace = uri; } else { ne_snprintf(p->error, ERR_SIZE, ("XML parse error at line %d: undeclared namespace"), ne_xml_currentline(p)); return -1; } } return 0;}/* Called with the start of a new element. */static void start_element(void *userdata, const ne_xml_char *name, const ne_xml_char **atts) { ne_xml_parser *p = userdata; struct element *elm; struct handler *hand; int state = NE_XML_DECLINE; if (!p->valid) return; if (p->prune) { p->prune++; return; } /* Create a new element */ elm = ne_calloc(sizeof *elm); elm->parent = p->current; p->current = elm; if (declare_nspaces(p, elm, atts) || expand_qname(p, elm, name)) { p->valid = 0; return; } /* Find a handler which will accept this element (or abort the parse) */ for (hand = elm->parent->handler; hand && state == NE_XML_DECLINE; hand = hand->next) { elm->handler = hand; state = hand->startelm_cb(hand->userdata, elm->parent->state, elm->nspace, elm->name, PASS_ATTS(atts)); } NE_DEBUG(NE_DBG_XMLPARSE, "XML: start-element (%d, {%s, %s}) => %d\n", elm->parent->state, elm->nspace, elm->name, state);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -