📄 debugxml.c
字号:
/*
* debugXML.c : This is a set of routines used for debugging the tree
* produced by the XML parser.
*
* See Copyright for the status of this software.
*
* Daniel Veillard <daniel@veillard.com>
*/
#define IN_LIBXML
#include "libxml.h"
#ifdef LIBXML_DEBUG_ENABLED
#include <string.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/valid.h>
#include <libxml/debugXML.h>
#include <libxml/HTMLtree.h>
#include <libxml/HTMLparser.h>
#include <libxml/xmlerror.h>
#include <libxml/globals.h>
#include <libxml/xpathInternals.h>
#include <libxml/uri.h>
#ifdef LIBXML_SCHEMAS_ENABLED
#include <libxml/relaxng.h>
#endif
typedef struct _xmlDebugCtxt xmlDebugCtxt;
typedef xmlDebugCtxt *xmlDebugCtxtPtr;
struct _xmlDebugCtxt {
FILE *output; /* the output file */
char shift[101]; /* used for indenting */
int depth; /* current depth */
xmlDocPtr doc; /* current document */
xmlNodePtr node; /* current node */
xmlDictPtr dict; /* the doc dictionnary */
int check; /* do just checkings */
int errors; /* number of errors found */
int nodict; /* if the document has no dictionnary */
};
static void xmlCtxtDumpNodeList(xmlDebugCtxtPtr ctxt, xmlNodePtr node);
static void
xmlCtxtDumpInitCtxt(xmlDebugCtxtPtr ctxt)
{
int i;
ctxt->depth = 0;
ctxt->check = 0;
ctxt->errors = 0;
ctxt->output = stdout;
ctxt->doc = NULL;
ctxt->node = NULL;
ctxt->dict = NULL;
ctxt->nodict = 0;
for (i = 0; i < 100; i++)
ctxt->shift[i] = ' ';
ctxt->shift[100] = 0;
}
static void
xmlCtxtDumpCleanCtxt(xmlDebugCtxtPtr ctxt ATTRIBUTE_UNUSED)
{
/* remove the ATTRIBUTE_UNUSED when this is added */
}
/**
* xmlNsCheckScope:
* @node: the node
* @ns: the namespace node
*
* Check that a given namespace is in scope on a node.
*
* Returns 1 if in scope, -1 in case of argument error,
* -2 if the namespace is not in scope, and -3 if not on
* an ancestor node.
*/
static int
xmlNsCheckScope(xmlNodePtr node, xmlNsPtr ns)
{
xmlNsPtr cur;
if ((node == NULL) || (ns == NULL))
return(-1);
if ((node->type != XML_ELEMENT_NODE) &&
(node->type != XML_ATTRIBUTE_NODE) &&
(node->type != XML_DOCUMENT_NODE) &&
(node->type != XML_TEXT_NODE) &&
(node->type != XML_HTML_DOCUMENT_NODE) &&
(node->type != XML_XINCLUDE_START))
return(-2);
while ((node != NULL) &&
((node->type == XML_ELEMENT_NODE) ||
(node->type == XML_ATTRIBUTE_NODE) ||
(node->type == XML_TEXT_NODE) ||
(node->type == XML_XINCLUDE_START))) {
if ((node->type == XML_ELEMENT_NODE) ||
(node->type == XML_XINCLUDE_START)) {
cur = node->nsDef;
while (cur != NULL) {
if (cur == ns)
return(1);
if (xmlStrEqual(cur->prefix, ns->prefix))
return(-2);
cur = cur->next;
}
}
node = node->parent;
}
/* the xml namespace may be declared on the document node */
if ((node != NULL) &&
((node->type == XML_DOCUMENT_NODE) ||
(node->type == XML_HTML_DOCUMENT_NODE))) {
xmlNsPtr oldNs = ((xmlDocPtr) node)->oldNs;
if (oldNs == ns)
return(1);
}
return(-3);
}
static void
xmlCtxtDumpSpaces(xmlDebugCtxtPtr ctxt)
{
if (ctxt->check)
return;
if ((ctxt->output != NULL) && (ctxt->depth > 0)) {
if (ctxt->depth < 50)
fprintf(ctxt->output, &ctxt->shift[100 - 2 * ctxt->depth]);
else
fprintf(ctxt->output, ctxt->shift);
}
}
/**
* xmlDebugErr:
* @ctxt: a debug context
* @error: the error code
*
* Handle a debug error.
*/
static void
xmlDebugErr(xmlDebugCtxtPtr ctxt, int error, const char *msg)
{
ctxt->errors++;
__xmlRaiseError(NULL, NULL, NULL,
NULL, ctxt->node, XML_FROM_CHECK,
error, XML_ERR_ERROR, NULL, 0,
NULL, NULL, NULL, 0, 0,
msg);
}
static void
xmlDebugErr2(xmlDebugCtxtPtr ctxt, int error, const char *msg, int extra)
{
ctxt->errors++;
__xmlRaiseError(NULL, NULL, NULL,
NULL, ctxt->node, XML_FROM_CHECK,
error, XML_ERR_ERROR, NULL, 0,
NULL, NULL, NULL, 0, 0,
msg, extra);
}
static void
xmlDebugErr3(xmlDebugCtxtPtr ctxt, int error, const char *msg, const char *extra)
{
ctxt->errors++;
__xmlRaiseError(NULL, NULL, NULL,
NULL, ctxt->node, XML_FROM_CHECK,
error, XML_ERR_ERROR, NULL, 0,
NULL, NULL, NULL, 0, 0,
msg, extra);
}
/**
* xmlCtxtNsCheckScope:
* @ctxt: the debugging context
* @node: the node
* @ns: the namespace node
*
* Report if a given namespace is is not in scope.
*/
static void
xmlCtxtNsCheckScope(xmlDebugCtxtPtr ctxt, xmlNodePtr node, xmlNsPtr ns)
{
int ret;
ret = xmlNsCheckScope(node, ns);
if (ret == -2) {
if (ns->prefix == NULL)
xmlDebugErr(ctxt, XML_CHECK_NS_SCOPE,
"Reference to default namespace not in scope\n");
else
xmlDebugErr3(ctxt, XML_CHECK_NS_SCOPE,
"Reference to namespace '%s' not in scope\n",
(char *) ns->prefix);
}
if (ret == -3) {
if (ns->prefix == NULL)
xmlDebugErr(ctxt, XML_CHECK_NS_ANCESTOR,
"Reference to default namespace not on ancestor\n");
else
xmlDebugErr3(ctxt, XML_CHECK_NS_ANCESTOR,
"Reference to namespace '%s' not on ancestor\n",
(char *) ns->prefix);
}
}
/**
* xmlCtxtCheckString:
* @ctxt: the debug context
* @str: the string
*
* Do debugging on the string, currently it just checks the UTF-8 content
*/
static void
xmlCtxtCheckString(xmlDebugCtxtPtr ctxt, const xmlChar * str)
{
if (str == NULL) return;
if (ctxt->check) {
if (!xmlCheckUTF8(str)) {
xmlDebugErr3(ctxt, XML_CHECK_NOT_UTF8,
"String is not UTF-8 %s", (const char *) str);
}
}
}
/**
* xmlCtxtCheckName:
* @ctxt: the debug context
* @name: the name
*
* Do debugging on the name, for example the dictionnary status and
* conformance to the Name production.
*/
static void
xmlCtxtCheckName(xmlDebugCtxtPtr ctxt, const xmlChar * name)
{
if (ctxt->check) {
if (name == NULL) {
xmlDebugErr(ctxt, XML_CHECK_NO_NAME, "Name is NULL");
return;
}
if (xmlValidateName(name, 0)) {
xmlDebugErr3(ctxt, XML_CHECK_NOT_NCNAME,
"Name is not an NCName '%s'", (const char *) name);
}
if ((ctxt->dict != NULL) &&
(!xmlDictOwns(ctxt->dict, name))) {
xmlDebugErr3(ctxt, XML_CHECK_OUTSIDE_DICT,
"Name is not from the document dictionnary '%s'",
(const char *) name);
}
}
}
static void
xmlCtxtGenericNodeCheck(xmlDebugCtxtPtr ctxt, xmlNodePtr node) {
xmlDocPtr doc;
xmlDictPtr dict;
doc = node->doc;
if (node->parent == NULL)
xmlDebugErr(ctxt, XML_CHECK_NO_PARENT,
"Node has no parent\n");
if (node->doc == NULL) {
xmlDebugErr(ctxt, XML_CHECK_NO_DOC,
"Node has no doc\n");
dict = NULL;
} else {
dict = doc->dict;
if ((dict == NULL) && (ctxt->nodict == 0)) {
#if 0
/* desactivated right now as it raises too many errors */
if (doc->type == XML_DOCUMENT_NODE)
xmlDebugErr(ctxt, XML_CHECK_NO_DICT,
"Document has no dictionnary\n");
#endif
ctxt->nodict = 1;
}
if (ctxt->doc == NULL)
ctxt->doc = doc;
if (ctxt->dict == NULL) {
ctxt->dict = dict;
}
}
if ((node->parent != NULL) && (node->doc != node->parent->doc) &&
(!xmlStrEqual(node->name, BAD_CAST "pseudoroot")))
xmlDebugErr(ctxt, XML_CHECK_WRONG_DOC,
"Node doc differs from parent's one\n");
if (node->prev == NULL) {
if (node->type == XML_ATTRIBUTE_NODE) {
if ((node->parent != NULL) &&
(node != (xmlNodePtr) node->parent->properties))
xmlDebugErr(ctxt, XML_CHECK_NO_PREV,
"Attr has no prev and not first of attr list\n");
} else if ((node->parent != NULL) && (node->parent->children != node))
xmlDebugErr(ctxt, XML_CHECK_NO_PREV,
"Node has no prev and not first of parent list\n");
} else {
if (node->prev->next != node)
xmlDebugErr(ctxt, XML_CHECK_WRONG_PREV,
"Node prev->next : back link wrong\n");
}
if (node->next == NULL) {
if ((node->parent != NULL) && (node->type != XML_ATTRIBUTE_NODE) &&
(node->parent->last != node))
xmlDebugErr(ctxt, XML_CHECK_NO_NEXT,
"Node has no next and not last of parent list\n");
} else {
if (node->next->prev != node)
xmlDebugErr(ctxt, XML_CHECK_WRONG_NEXT,
"Node next->prev : forward link wrong\n");
if (node->next->parent != node->parent)
xmlDebugErr(ctxt, XML_CHECK_WRONG_PARENT,
"Node next->prev : forward link wrong\n");
}
if (node->type == XML_ELEMENT_NODE) {
xmlNsPtr ns;
ns = node->nsDef;
while (ns != NULL) {
xmlCtxtNsCheckScope(ctxt, node, ns);
ns = ns->next;
}
if (node->ns != NULL)
xmlCtxtNsCheckScope(ctxt, node, node->ns);
} else if (node->type == XML_ATTRIBUTE_NODE) {
if (node->ns != NULL)
xmlCtxtNsCheckScope(ctxt, node, node->ns);
}
if ((node->type != XML_ELEMENT_NODE) &&
(node->type != XML_ATTRIBUTE_NODE) &&
(node->type != XML_ELEMENT_DECL) &&
(node->type != XML_ATTRIBUTE_DECL) &&
(node->type != XML_DTD_NODE) &&
(node->type != XML_ELEMENT_DECL) &&
(node->type != XML_HTML_DOCUMENT_NODE) &&
(node->type != XML_DOCUMENT_NODE)) {
if (node->content != NULL)
xmlCtxtCheckString(ctxt, (const xmlChar *) node->content);
}
switch (node->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
xmlCtxtCheckName(ctxt, node->name);
break;
case XML_TEXT_NODE:
if ((node->name == xmlStringText) ||
(node->name == xmlStringTextNoenc))
break;
/* some case of entity substitution can lead to this */
if ((ctxt->dict != NULL) &&
(node->name == xmlDictLookup(ctxt->dict, BAD_CAST "nbktext",
7)))
break;
xmlDebugErr3(ctxt, XML_CHECK_WRONG_NAME,
"Text node has wrong name '%s'",
(const char *) node->name);
break;
case XML_COMMENT_NODE:
if (node->name == xmlStringComment)
break;
xmlDebugErr3(ctxt, XML_CHECK_WRONG_NAME,
"Comment node has wrong name '%s'",
(const char *) node->name);
break;
case XML_PI_NODE:
xmlCtxtCheckName(ctxt, node->name);
break;
case XML_CDATA_SECTION_NODE:
if (node->name == NULL)
break;
xmlDebugErr3(ctxt, XML_CHECK_NAME_NOT_NULL,
"CData section has non NULL name '%s'",
(const char *) node->name);
break;
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_NAMESPACE_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
break;
}
}
static void
xmlCtxtDumpString(xmlDebugCtxtPtr ctxt, const xmlChar * str)
{
int i;
if (ctxt->check) {
return;
}
/* TODO: check UTF8 content of the string */
if (str == NULL) {
fprintf(ctxt->output, "(NULL)");
return;
}
for (i = 0; i < 40; i++)
if (str[i] == 0)
return;
else if (IS_BLANK_CH(str[i]))
fputc(' ', ctxt->output);
else if (str[i] >= 0x80)
fprintf(ctxt->output, "#%X", str[i]);
else
fputc(str[i], ctxt->output);
fprintf(ctxt->output, "...");
}
static void
xmlCtxtDumpDtdNode(xmlDebugCtxtPtr ctxt, xmlDtdPtr dtd)
{
xmlCtxtDumpSpaces(ctxt);
if (dtd == NULL) {
if (!ctxt->check)
fprintf(ctxt->output, "DTD node is NULL\n");
return;
}
if (dtd->type != XML_DTD_NODE) {
xmlDebugErr(ctxt, XML_CHECK_NOT_DTD,
"Node is not a DTD");
return;
}
if (!ctxt->check) {
if (dtd->name != NULL)
fprintf(ctxt->output, "DTD(%s)", (char *) dtd->name);
else
fprintf(ctxt->output, "DTD");
if (dtd->ExternalID != NULL)
fprintf(ctxt->output, ", PUBLIC %s", (char *) dtd->ExternalID);
if (dtd->SystemID != NULL)
fprintf(ctxt->output, ", SYSTEM %s", (char *) dtd->SystemID);
fprintf(ctxt->output, "\n");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -