mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2025-07-30 22:43:14 +03:00
Geez, this one was painful ! I still need to handle entities references
for the validation step but I have a clean way to add this without touching the algorithm: - valid.[ch] tree.h: worked *hard* to get non-determinist content validation without using an ugly NFA -> DFA algo in the source. Made a specific algorithm easier to maintain, using a single stack and without recursion. - Makefile.am test/VCM/*.xml: added more tests to "make Validtests" - hash.c: made the growing routine static - tree.h parser.c: added the parent information to an xmlElementContent node. Daniel
This commit is contained in:
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
|||||||
|
Fri Apr 20 14:52:44 CEST 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
|
||||||
|
|
||||||
|
* valid.[ch] tree.h: worked *hard* to get non-determinist content
|
||||||
|
validation without using an ugly NFA -> DFA algo in the source.
|
||||||
|
Made a specific algorithm easier to maintain, using a single
|
||||||
|
stack and without recursion.
|
||||||
|
* Makefile.am test/VCM/*.xml: added more tests to "make Validtests"
|
||||||
|
* hash.c: made the growing routine static
|
||||||
|
* tree.h parser.c: added the parent information to an
|
||||||
|
xmlElementContent node.
|
||||||
|
|
||||||
Wed Apr 18 23:33:11 CEST 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
|
Wed Apr 18 23:33:11 CEST 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
|
||||||
|
|
||||||
* SAX.c parser.c xpath.c: generating IDs when not validating
|
* SAX.c parser.c xpath.c: generating IDs when not validating
|
||||||
|
16
Makefile.am
16
Makefile.am
@ -296,6 +296,7 @@ XPtrtests : testXPath
|
|||||||
fi ; fi ; done ; fi ; done)
|
fi ; fi ; done ; fi ; done)
|
||||||
|
|
||||||
XIncludetests : xmllint
|
XIncludetests : xmllint
|
||||||
|
@(rm -f .memdump ; touch .memdump)
|
||||||
@echo "##"
|
@echo "##"
|
||||||
@echo "## XInclude regression tests"
|
@echo "## XInclude regression tests"
|
||||||
@echo "##"
|
@echo "##"
|
||||||
@ -351,6 +352,17 @@ SAXtests : testSAX
|
|||||||
|
|
||||||
|
|
||||||
Validtests : xmllint
|
Validtests : xmllint
|
||||||
|
@(rm -f .memdump ; touch .memdump)
|
||||||
|
@echo "##"
|
||||||
|
@echo "## Valid documents regression tests"
|
||||||
|
@echo "##"
|
||||||
|
@(for i in $(srcdir)/test/VCM/* ; do \
|
||||||
|
name=`basename $$i`; \
|
||||||
|
if [ ! -d $$i ] ; then \
|
||||||
|
echo Testing $$name ; \
|
||||||
|
$(top_builddir)/xmllint --valid --noout $$i ; \
|
||||||
|
grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0";\
|
||||||
|
fi ; done ; exit 0)
|
||||||
@echo "##"
|
@echo "##"
|
||||||
@echo "## Validity checking regression tests"
|
@echo "## Validity checking regression tests"
|
||||||
@echo "##"
|
@echo "##"
|
||||||
@ -363,11 +375,12 @@ Validtests : xmllint
|
|||||||
else \
|
else \
|
||||||
echo Testing $$name ; \
|
echo Testing $$name ; \
|
||||||
$(top_builddir)/xmllint --noout --valid $$i 2> result.$$name ; \
|
$(top_builddir)/xmllint --noout --valid $$i 2> result.$$name ; \
|
||||||
|
grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0";\
|
||||||
diff $(srcdir)/result/VC/$$name result.$$name ; \
|
diff $(srcdir)/result/VC/$$name result.$$name ; \
|
||||||
rm result.$$name ; \
|
rm result.$$name ; \
|
||||||
fi ; fi ; done)
|
fi ; fi ; done)
|
||||||
@echo "##"
|
@echo "##"
|
||||||
@echo "## Valid documents regression tests"
|
@echo "## General documents valid regression tests"
|
||||||
@echo "##"
|
@echo "##"
|
||||||
@(for i in $(srcdir)/test/valid/* ; do \
|
@(for i in $(srcdir)/test/valid/* ; do \
|
||||||
name=`basename $$i`; \
|
name=`basename $$i`; \
|
||||||
@ -378,6 +391,7 @@ Validtests : xmllint
|
|||||||
else \
|
else \
|
||||||
echo Testing $$name ; \
|
echo Testing $$name ; \
|
||||||
$(top_builddir)/xmllint --valid $$i > result.$$name 2>error.$$name ; \
|
$(top_builddir)/xmllint --valid $$i > result.$$name 2>error.$$name ; \
|
||||||
|
grep "MORY ALLO" .memdump | grep -v "MEMORY ALLOCATED : 0";\
|
||||||
diff $(srcdir)/result/valid/$$name result.$$name ; \
|
diff $(srcdir)/result/valid/$$name result.$$name ; \
|
||||||
diff $(srcdir)/result/valid/$$name.err error.$$name ; \
|
diff $(srcdir)/result/valid/$$name.err error.$$name ; \
|
||||||
rm result.$$name error.$$name ; \
|
rm result.$$name error.$$name ; \
|
||||||
|
2
hash.c
2
hash.c
@ -125,7 +125,7 @@ xmlHashCreate(int size) {
|
|||||||
*
|
*
|
||||||
* Returns 0 in case of success, -1 in case of failure
|
* Returns 0 in case of success, -1 in case of failure
|
||||||
*/
|
*/
|
||||||
int
|
static int
|
||||||
xmlHashGrow(xmlHashTablePtr table, int size) {
|
xmlHashGrow(xmlHashTablePtr table, int size) {
|
||||||
unsigned long key;
|
unsigned long key;
|
||||||
int oldsize, i;
|
int oldsize, i;
|
||||||
|
@ -169,6 +169,7 @@ struct _xmlElementContent {
|
|||||||
const xmlChar *name; /* Element name */
|
const xmlChar *name; /* Element name */
|
||||||
struct _xmlElementContent *c1; /* first child */
|
struct _xmlElementContent *c1; /* first child */
|
||||||
struct _xmlElementContent *c2; /* second child */
|
struct _xmlElementContent *c2; /* second child */
|
||||||
|
struct _xmlElementContent *parent; /* parent */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -17,6 +17,12 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validation state added for non-determinist content model
|
||||||
|
*/
|
||||||
|
typedef struct _xmlValidState xmlValidState;
|
||||||
|
typedef xmlValidState *xmlValidStatePtr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* an xmlValidCtxt is used for error reporting when validating
|
* an xmlValidCtxt is used for error reporting when validating
|
||||||
*/
|
*/
|
||||||
@ -40,6 +46,12 @@ struct _xmlValidCtxt {
|
|||||||
int finishDtd; /* finished validating the Dtd ? */
|
int finishDtd; /* finished validating the Dtd ? */
|
||||||
xmlDocPtr doc; /* the document */
|
xmlDocPtr doc; /* the document */
|
||||||
int valid; /* temporary validity check result */
|
int valid; /* temporary validity check result */
|
||||||
|
|
||||||
|
/* state state used for non-determinist content validation */
|
||||||
|
xmlValidState *vstate; /* current state */
|
||||||
|
int vstateNr; /* Depth of the validation stack */
|
||||||
|
int vstateMax; /* Max depth of the validation stack */
|
||||||
|
xmlValidState *vstateTab; /* array of validation states */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
23
parser.c
23
parser.c
@ -3926,12 +3926,18 @@ xmlParseElementMixedContentDecl(xmlParserCtxtPtr ctxt) {
|
|||||||
ret = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_OR);
|
ret = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_OR);
|
||||||
if (ret == NULL) return(NULL);
|
if (ret == NULL) return(NULL);
|
||||||
ret->c1 = cur;
|
ret->c1 = cur;
|
||||||
|
if (cur != NULL)
|
||||||
|
cur->parent = ret;
|
||||||
cur = ret;
|
cur = ret;
|
||||||
} else {
|
} else {
|
||||||
n = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_OR);
|
n = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_OR);
|
||||||
if (n == NULL) return(NULL);
|
if (n == NULL) return(NULL);
|
||||||
n->c1 = xmlNewElementContent(elem, XML_ELEMENT_CONTENT_ELEMENT);
|
n->c1 = xmlNewElementContent(elem, XML_ELEMENT_CONTENT_ELEMENT);
|
||||||
|
if (n->c1 != NULL)
|
||||||
|
n->c1->parent = n;
|
||||||
cur->c2 = n;
|
cur->c2 = n;
|
||||||
|
if (n != NULL)
|
||||||
|
n->parent = cur;
|
||||||
cur = n;
|
cur = n;
|
||||||
xmlFree(elem);
|
xmlFree(elem);
|
||||||
}
|
}
|
||||||
@ -3954,6 +3960,8 @@ xmlParseElementMixedContentDecl(xmlParserCtxtPtr ctxt) {
|
|||||||
if (elem != NULL) {
|
if (elem != NULL) {
|
||||||
cur->c2 = xmlNewElementContent(elem,
|
cur->c2 = xmlNewElementContent(elem,
|
||||||
XML_ELEMENT_CONTENT_ELEMENT);
|
XML_ELEMENT_CONTENT_ELEMENT);
|
||||||
|
if (cur->c2 != NULL)
|
||||||
|
cur->c2->parent = cur;
|
||||||
xmlFree(elem);
|
xmlFree(elem);
|
||||||
}
|
}
|
||||||
ret->ocur = XML_ELEMENT_CONTENT_MULT;
|
ret->ocur = XML_ELEMENT_CONTENT_MULT;
|
||||||
@ -4098,10 +4106,16 @@ xmlParseElementChildrenContentDecl
|
|||||||
}
|
}
|
||||||
if (last == NULL) {
|
if (last == NULL) {
|
||||||
op->c1 = ret;
|
op->c1 = ret;
|
||||||
|
if (ret != NULL)
|
||||||
|
ret->parent = op;
|
||||||
ret = cur = op;
|
ret = cur = op;
|
||||||
} else {
|
} else {
|
||||||
cur->c2 = op;
|
cur->c2 = op;
|
||||||
|
if (op != NULL)
|
||||||
|
op->parent = cur;
|
||||||
op->c1 = last;
|
op->c1 = last;
|
||||||
|
if (last != NULL)
|
||||||
|
last->parent = op;
|
||||||
cur =op;
|
cur =op;
|
||||||
last = NULL;
|
last = NULL;
|
||||||
}
|
}
|
||||||
@ -4143,10 +4157,16 @@ xmlParseElementChildrenContentDecl
|
|||||||
}
|
}
|
||||||
if (last == NULL) {
|
if (last == NULL) {
|
||||||
op->c1 = ret;
|
op->c1 = ret;
|
||||||
|
if (ret != NULL)
|
||||||
|
ret->parent = op;
|
||||||
ret = cur = op;
|
ret = cur = op;
|
||||||
} else {
|
} else {
|
||||||
cur->c2 = op;
|
cur->c2 = op;
|
||||||
|
if (op != NULL)
|
||||||
|
op->parent = cur;
|
||||||
op->c1 = last;
|
op->c1 = last;
|
||||||
|
if (last != NULL)
|
||||||
|
last->parent = op;
|
||||||
cur =op;
|
cur =op;
|
||||||
last = NULL;
|
last = NULL;
|
||||||
}
|
}
|
||||||
@ -4213,6 +4233,8 @@ xmlParseElementChildrenContentDecl
|
|||||||
}
|
}
|
||||||
if ((cur != NULL) && (last != NULL)) {
|
if ((cur != NULL) && (last != NULL)) {
|
||||||
cur->c2 = last;
|
cur->c2 = last;
|
||||||
|
if (last != NULL)
|
||||||
|
last->parent = cur;
|
||||||
}
|
}
|
||||||
ctxt->entity = ctxt->input;
|
ctxt->entity = ctxt->input;
|
||||||
NEXT;
|
NEXT;
|
||||||
@ -8883,7 +8905,6 @@ xmlParseExternalEntity(xmlDocPtr doc, xmlSAXHandlerPtr sax, void *user_data,
|
|||||||
xmlParserCtxtPtr ctxt;
|
xmlParserCtxtPtr ctxt;
|
||||||
xmlDocPtr newDoc;
|
xmlDocPtr newDoc;
|
||||||
xmlSAXHandlerPtr oldsax = NULL;
|
xmlSAXHandlerPtr oldsax = NULL;
|
||||||
int oldexternal = ctxt->external;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (depth > 40) {
|
if (depth > 40) {
|
||||||
|
8
test/VCM/21.xml
Normal file
8
test/VCM/21.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a, (b | c)*, d*)? >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
<!ELEMENT c EMPTY>
|
||||||
|
<!ELEMENT d EMPTY>
|
||||||
|
]>
|
||||||
|
<doc><a/><d/></doc>
|
4
test/VCM/v1.xml
Normal file
4
test/VCM/v1.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc EMPTY>
|
||||||
|
]>
|
||||||
|
<doc/>
|
5
test/VCM/v10.xml
Normal file
5
test/VCM/v10.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)* >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc></doc>
|
5
test/VCM/v11.xml
Normal file
5
test/VCM/v11.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)* >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc><a/></doc>
|
9
test/VCM/v12.xml
Normal file
9
test/VCM/v12.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)* >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<a/>
|
||||||
|
<a/>
|
||||||
|
<a/>
|
||||||
|
</doc>
|
7
test/VCM/v13.xml
Normal file
7
test/VCM/v13.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)+ >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<a/>
|
||||||
|
</doc>
|
9
test/VCM/v14.xml
Normal file
9
test/VCM/v14.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)+ >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<a/>
|
||||||
|
<a/>
|
||||||
|
<a/>
|
||||||
|
</doc>
|
9
test/VCM/v15.xml
Normal file
9
test/VCM/v15.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc ((a | b | c)*) >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
<!ELEMENT c EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<b/>
|
||||||
|
</doc>
|
8
test/VCM/v16.xml
Normal file
8
test/VCM/v16.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a | b)>
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<b/>
|
||||||
|
</doc>
|
7
test/VCM/v17.xml
Normal file
7
test/VCM/v17.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a? | b?) >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
<!ELEMENT c EMPTY>
|
||||||
|
]>
|
||||||
|
<doc></doc>
|
7
test/VCM/v18.xml
Normal file
7
test/VCM/v18.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a? | b?) >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
<!ELEMENT c EMPTY>
|
||||||
|
]>
|
||||||
|
<doc><b/></doc>
|
7
test/VCM/v19.xml
Normal file
7
test/VCM/v19.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a? | b+) >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
<!ELEMENT c EMPTY>
|
||||||
|
]>
|
||||||
|
<doc><b/></doc>
|
4
test/VCM/v2.xml
Normal file
4
test/VCM/v2.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (#PCDATA)>
|
||||||
|
]>
|
||||||
|
<doc>text</doc>
|
10
test/VCM/v20.xml
Normal file
10
test/VCM/v20.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc ((a | b)*, a, b) >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
<!ELEMENT c EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<a/>
|
||||||
|
<b/>
|
||||||
|
</doc>
|
8
test/VCM/v3.xml
Normal file
8
test/VCM/v3.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (#PCDATA)>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<?pi value?>
|
||||||
|
text
|
||||||
|
<!-- and comments -->
|
||||||
|
</doc>
|
5
test/VCM/v4.xml
Normal file
5
test/VCM/v4.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)>
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc><a/></doc>
|
7
test/VCM/v5.xml
Normal file
7
test/VCM/v5.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)>
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<a/>
|
||||||
|
</doc>
|
9
test/VCM/v6.xml
Normal file
9
test/VCM/v6.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a, b)>
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<a/>
|
||||||
|
<b/>
|
||||||
|
</doc>
|
8
test/VCM/v7.xml
Normal file
8
test/VCM/v7.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a | b)>
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
<!ELEMENT b EMPTY>
|
||||||
|
]>
|
||||||
|
<doc>
|
||||||
|
<a/>
|
||||||
|
</doc>
|
5
test/VCM/v8.xml
Normal file
5
test/VCM/v8.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)? >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc></doc>
|
5
test/VCM/v9.xml
Normal file
5
test/VCM/v9.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE doc [
|
||||||
|
<!ELEMENT doc (a)? >
|
||||||
|
<!ELEMENT a EMPTY>
|
||||||
|
]>
|
||||||
|
<doc><a/></doc>
|
1
tree.h
1
tree.h
@ -169,6 +169,7 @@ struct _xmlElementContent {
|
|||||||
const xmlChar *name; /* Element name */
|
const xmlChar *name; /* Element name */
|
||||||
struct _xmlElementContent *c1; /* first child */
|
struct _xmlElementContent *c1; /* first child */
|
||||||
struct _xmlElementContent *c2; /* second child */
|
struct _xmlElementContent *c2; /* second child */
|
||||||
|
struct _xmlElementContent *parent; /* parent */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
656
valid.c
656
valid.c
@ -28,6 +28,8 @@
|
|||||||
#include <libxml/xmlerror.h>
|
#include <libxml/xmlerror.h>
|
||||||
#include <libxml/list.h>
|
#include <libxml/list.h>
|
||||||
|
|
||||||
|
#define ALLOW_UNDETERMINISTIC_MODELS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generic function for accessing stacks in the Validity Context
|
* Generic function for accessing stacks in the Validity Context
|
||||||
*/
|
*/
|
||||||
@ -61,84 +63,220 @@ scope type name##VPop(xmlValidCtxtPtr ctxt) { \
|
|||||||
return(ret); \
|
return(ret); \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
|
#ifdef ALLOW_UNDETERMINISTIC_MODELS
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I will use a home made algorithm less complex and easier to
|
||||||
|
* debug/maintin than a generic NFA -> DFA state based algo. The
|
||||||
|
* only restriction is on the deepness of the tree limited by the
|
||||||
|
* size of the occurs bitfield
|
||||||
|
*
|
||||||
|
* this is the content of a saved state for rollbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ROLLBACK_OR 0
|
||||||
|
#define ROLLBACK_PARENT 1
|
||||||
|
|
||||||
|
struct _xmlValidState {
|
||||||
|
xmlElementContentPtr cont; /* pointer to the content model subtree */
|
||||||
|
xmlNodePtr node; /* pointer to the current node in the list */
|
||||||
|
long occurs;/* bitfield for multiple occurences */
|
||||||
|
unsigned char depth; /* current depth in the overall tree */
|
||||||
|
unsigned char state; /* ROLLBACK_XXX */
|
||||||
|
} _xmlValidState;
|
||||||
|
|
||||||
|
#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
|
||||||
|
#define CONT ctxt->vstate->cont
|
||||||
|
#define NODE ctxt->vstate->node
|
||||||
|
#define DEPTH ctxt->vstate->depth
|
||||||
|
#define OCCURS ctxt->vstate->occurs
|
||||||
|
#define STATE ctxt->vstate->state
|
||||||
|
|
||||||
|
#define OCCURENCE (ctxt->vstate->occurs & (1 << DEPTH))
|
||||||
|
#define PARENT_OCCURENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
|
||||||
|
|
||||||
|
#define SET_OCCURENCE ctxt->vstate->occurs |= (1 << DEPTH)
|
||||||
|
#define RESET_OCCURENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
|
||||||
|
|
||||||
|
static int
|
||||||
|
vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
|
||||||
|
xmlNodePtr node, unsigned char depth, long occurs,
|
||||||
|
unsigned char state) {
|
||||||
|
if (ctxt->vstateNr >= ctxt->vstateMax) {
|
||||||
|
ctxt->vstateMax *= 2;
|
||||||
|
ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
|
||||||
|
ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
|
||||||
|
if (ctxt->vstateTab == NULL) {
|
||||||
|
xmlGenericError(xmlGenericErrorContext,
|
||||||
|
"realloc failed !n");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctxt->vstateTab[ctxt->vstateNr].cont = cont;
|
||||||
|
ctxt->vstateTab[ctxt->vstateNr].node = node;
|
||||||
|
ctxt->vstateTab[ctxt->vstateNr].depth = depth;
|
||||||
|
ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
|
||||||
|
ctxt->vstateTab[ctxt->vstateNr].state = state;
|
||||||
|
return(ctxt->vstateNr++);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vstateVPop(xmlValidCtxtPtr ctxt) {
|
||||||
|
if (ctxt->vstateNr <= 1) return(-1);
|
||||||
|
ctxt->vstateNr--;
|
||||||
|
ctxt->vstate = &ctxt->vstateTab[0];
|
||||||
|
ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
|
||||||
|
ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
|
||||||
|
ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
|
||||||
|
ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
|
||||||
|
ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
|
||||||
|
return(ctxt->vstateNr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ALLOW_UNDETERMINISTIC_MODELS */
|
||||||
|
|
||||||
PUSH_AND_POP(static, xmlNodePtr, node)
|
PUSH_AND_POP(static, xmlNodePtr, node)
|
||||||
|
|
||||||
/* #define DEBUG_VALID_ALGO */
|
/* #define DEBUG_VALID_ALGO */
|
||||||
|
|
||||||
#ifdef DEBUG_VALID_ALGO
|
#ifdef DEBUG_VALID_ALGO
|
||||||
void xmlValidPrintNodeList(xmlNodePtr cur) {
|
static void
|
||||||
|
xmlValidPrintNode(xmlNodePtr cur) {
|
||||||
|
if (cur == NULL) {
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (cur->type) {
|
||||||
|
case XML_ELEMENT_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
|
||||||
|
break;
|
||||||
|
case XML_TEXT_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "text ");
|
||||||
|
break;
|
||||||
|
case XML_CDATA_SECTION_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "cdata ");
|
||||||
|
break;
|
||||||
|
case XML_ENTITY_REF_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
|
||||||
|
break;
|
||||||
|
case XML_PI_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
|
||||||
|
break;
|
||||||
|
case XML_COMMENT_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "comment ");
|
||||||
|
break;
|
||||||
|
case XML_ATTRIBUTE_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?attr? ");
|
||||||
|
break;
|
||||||
|
case XML_ENTITY_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?ent? ");
|
||||||
|
break;
|
||||||
|
case XML_DOCUMENT_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?doc? ");
|
||||||
|
break;
|
||||||
|
case XML_DOCUMENT_TYPE_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?doctype? ");
|
||||||
|
break;
|
||||||
|
case XML_DOCUMENT_FRAG_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?frag? ");
|
||||||
|
break;
|
||||||
|
case XML_NOTATION_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?nota? ");
|
||||||
|
break;
|
||||||
|
case XML_HTML_DOCUMENT_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?html? ");
|
||||||
|
break;
|
||||||
|
case XML_DTD_NODE:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?dtd? ");
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_DECL:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?edecl? ");
|
||||||
|
break;
|
||||||
|
case XML_ATTRIBUTE_DECL:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?adecl? ");
|
||||||
|
break;
|
||||||
|
case XML_ENTITY_DECL:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
|
||||||
|
break;
|
||||||
|
case XML_NAMESPACE_DECL:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
|
||||||
|
break;
|
||||||
|
case XML_XINCLUDE_START:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "incstart ");
|
||||||
|
break;
|
||||||
|
case XML_XINCLUDE_END:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "incend ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xmlValidPrintNodeList(xmlNodePtr cur) {
|
||||||
if (cur == NULL)
|
if (cur == NULL)
|
||||||
xmlGenericError(xmlGenericErrorContext, "null ");
|
xmlGenericError(xmlGenericErrorContext, "null ");
|
||||||
while (cur != NULL) {
|
while (cur != NULL) {
|
||||||
switch (cur->type) {
|
xmlValidPrintNode(cur);
|
||||||
case XML_ELEMENT_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
|
|
||||||
break;
|
|
||||||
case XML_TEXT_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "text ");
|
|
||||||
break;
|
|
||||||
case XML_CDATA_SECTION_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "cdata ");
|
|
||||||
break;
|
|
||||||
case XML_ENTITY_REF_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
|
|
||||||
break;
|
|
||||||
case XML_PI_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
|
|
||||||
break;
|
|
||||||
case XML_COMMENT_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "comment ");
|
|
||||||
break;
|
|
||||||
case XML_ATTRIBUTE_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?attr? ");
|
|
||||||
break;
|
|
||||||
case XML_ENTITY_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?ent? ");
|
|
||||||
break;
|
|
||||||
case XML_DOCUMENT_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?doc? ");
|
|
||||||
break;
|
|
||||||
case XML_DOCUMENT_TYPE_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?doctype? ");
|
|
||||||
break;
|
|
||||||
case XML_DOCUMENT_FRAG_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?frag? ");
|
|
||||||
break;
|
|
||||||
case XML_NOTATION_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?nota? ");
|
|
||||||
break;
|
|
||||||
case XML_HTML_DOCUMENT_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?html? ");
|
|
||||||
break;
|
|
||||||
case XML_DTD_NODE:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?dtd? ");
|
|
||||||
break;
|
|
||||||
case XML_ELEMENT_DECL:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?edecl? ");
|
|
||||||
break;
|
|
||||||
case XML_ATTRIBUTE_DECL:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?adecl? ");
|
|
||||||
break;
|
|
||||||
case XML_ENTITY_DECL:
|
|
||||||
xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cur = cur->next;
|
cur = cur->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
|
static void
|
||||||
|
xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
|
||||||
char expr[1000];
|
char expr[1000];
|
||||||
|
|
||||||
expr[0] = 0;
|
expr[0] = 0;
|
||||||
xmlGenericError(xmlGenericErrorContext, "valid: ");
|
xmlGenericError(xmlGenericErrorContext, "valid: ");
|
||||||
xmlValidPrintNodeList(cur);
|
xmlValidPrintNodeList(cur);
|
||||||
xmlGenericError(xmlGenericErrorContext, "against ");
|
xmlGenericError(xmlGenericErrorContext, "against ");
|
||||||
xmlSprintfElementContent(expr, cont, 0);
|
xmlSprintfElementContent(expr, cont, 1);
|
||||||
xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
|
xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xmlValidDebugState(xmlValidStatePtr state) {
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "(");
|
||||||
|
if (state->cont == NULL)
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "null,");
|
||||||
|
else
|
||||||
|
switch (state->cont->type) {
|
||||||
|
case XML_ELEMENT_CONTENT_PCDATA:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "pcdata,");
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_ELEMENT:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "%s,",
|
||||||
|
state->cont->name);
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_SEQ:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "seq,");
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_OR:
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "or,");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
xmlValidPrintNode(state->node);
|
||||||
|
xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
|
||||||
|
state->depth, state->occurs, state->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "state: ");
|
||||||
|
xmlValidDebugState(ctxt->vstate);
|
||||||
|
xmlGenericError(xmlGenericErrorContext, " stack: %d ",
|
||||||
|
ctxt->vstateNr - 1);
|
||||||
|
for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
|
||||||
|
xmlValidDebugState(&ctxt->vstateTab[j]);
|
||||||
|
xmlGenericError(xmlGenericErrorContext, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****
|
||||||
#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
|
#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
|
||||||
|
*****/
|
||||||
|
|
||||||
|
#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
|
||||||
#define DEBUG_VALID_MSG(m) \
|
#define DEBUG_VALID_MSG(m) \
|
||||||
xmlGenericError(xmlGenericErrorContext, "%s\n", m);
|
xmlGenericError(xmlGenericErrorContext, "%s\n", m);
|
||||||
|
|
||||||
@ -271,7 +409,7 @@ xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
|
|||||||
ret->name = xmlStrdup(name);
|
ret->name = xmlStrdup(name);
|
||||||
else
|
else
|
||||||
ret->name = NULL;
|
ret->name = NULL;
|
||||||
ret->c1 = ret->c2 = NULL;
|
ret->c1 = ret->c2 = ret->parent = NULL;
|
||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +434,11 @@ xmlCopyElementContent(xmlElementContentPtr cur) {
|
|||||||
}
|
}
|
||||||
ret->ocur = cur->ocur;
|
ret->ocur = cur->ocur;
|
||||||
if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
|
if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
|
||||||
|
if (ret->c1 != NULL)
|
||||||
|
ret->c1->parent = ret;
|
||||||
if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
|
if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
|
||||||
|
if (ret->c2 != NULL)
|
||||||
|
ret->c2->parent = ret;
|
||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3086,6 +3228,8 @@ xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
|
|||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ALLOW_UNDETERMINISTIC_MODELS
|
||||||
|
|
||||||
/* Find the next XML_ELEMENT_NODE, subject to the content constraints.
|
/* Find the next XML_ELEMENT_NODE, subject to the content constraints.
|
||||||
* Return -1 if we found something unexpected, or 1 otherwise.
|
* Return -1 if we found something unexpected, or 1 otherwise.
|
||||||
*/
|
*/
|
||||||
@ -3346,6 +3490,386 @@ xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
|
|||||||
return(xmlValidateFindNextElement(ctxt, child, cont));
|
return(xmlValidateFindNextElement(ctxt, child, cont));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else /* ALLOW_UNDETERMINISTIC_MODELS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xmlValidateSkipIgnorable:
|
||||||
|
* @ctxt: the validation context
|
||||||
|
* @child: the child list
|
||||||
|
*
|
||||||
|
* Skip ignorable elements w.r.t. the validation process
|
||||||
|
*
|
||||||
|
* returns the first element to consider for validation of the content model
|
||||||
|
*/
|
||||||
|
|
||||||
|
static xmlNodePtr
|
||||||
|
xmlValidateSkipIgnorable(xmlValidCtxtPtr ctxt, xmlNodePtr child) {
|
||||||
|
while (child != NULL) {
|
||||||
|
switch (child->type) {
|
||||||
|
/*
|
||||||
|
* If there is an entity declared and it's not empty
|
||||||
|
* Push the current node on the stack and process with the
|
||||||
|
* entity content.
|
||||||
|
*/
|
||||||
|
case XML_ENTITY_REF_NODE:
|
||||||
|
if ((child->children != NULL) &&
|
||||||
|
(child->children->children != NULL)) {
|
||||||
|
nodeVPush(ctxt, child);
|
||||||
|
child = child->children->children;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* These things are ignored (skipped) during validation. */
|
||||||
|
case XML_PI_NODE:
|
||||||
|
case XML_COMMENT_NODE:
|
||||||
|
case XML_XINCLUDE_START:
|
||||||
|
case XML_XINCLUDE_END:
|
||||||
|
child = child->next;
|
||||||
|
break;
|
||||||
|
case XML_TEXT_NODE:
|
||||||
|
if (xmlIsBlankNode(child))
|
||||||
|
child = child->next;
|
||||||
|
else
|
||||||
|
return(child);
|
||||||
|
break;
|
||||||
|
/* keep current node */
|
||||||
|
default:
|
||||||
|
return(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xmlValidateElementType:
|
||||||
|
* @ctxt: the validation context
|
||||||
|
*
|
||||||
|
* Try to validate the content model of an element internal function
|
||||||
|
*
|
||||||
|
* returns 1 if valid or 0 and -1 if PCDATA stuff is found,
|
||||||
|
* also update child and content values in-situ.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
xmlValidateElementType(xmlValidCtxtPtr ctxt) {
|
||||||
|
int ret = -1;
|
||||||
|
int consumed = 1;
|
||||||
|
|
||||||
|
NODE = xmlValidateSkipIgnorable(ctxt, NODE);
|
||||||
|
if ((NODE == NULL) && (CONT == NULL))
|
||||||
|
return(1);
|
||||||
|
if ((NODE == NULL) &&
|
||||||
|
((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
|
||||||
|
(CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
if (CONT == NULL) return(-1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We arrive here when more states need to be examined
|
||||||
|
*/
|
||||||
|
cont:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We just recovered from a rollback generated by a possible
|
||||||
|
* epsilon transition, go directly to the analysis phase
|
||||||
|
*/
|
||||||
|
if (STATE == ROLLBACK_PARENT) {
|
||||||
|
DEBUG_VALID_MSG("restaured parent branch");
|
||||||
|
DEBUG_VALID_STATE(NODE, CONT)
|
||||||
|
ret = 1;
|
||||||
|
consumed = 0;
|
||||||
|
goto analyze;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_VALID_STATE(NODE, CONT)
|
||||||
|
/*
|
||||||
|
* we may have to save a backup state here. This is the equivalent
|
||||||
|
* of handling epsilon transition in NFAs.
|
||||||
|
*/
|
||||||
|
if ((consumed) && (CONT != NULL) &&
|
||||||
|
(CONT->parent != NULL) &&
|
||||||
|
((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
|
||||||
|
(CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
|
||||||
|
DEBUG_VALID_MSG("saving parent branch");
|
||||||
|
vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check first if the content matches
|
||||||
|
*/
|
||||||
|
switch (CONT->type) {
|
||||||
|
case XML_ELEMENT_CONTENT_PCDATA:
|
||||||
|
if (NODE == NULL) {
|
||||||
|
DEBUG_VALID_MSG("pcdata failed no node");
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NODE->type == XML_TEXT_NODE) {
|
||||||
|
DEBUG_VALID_MSG("pcdata found, skip to next");
|
||||||
|
/*
|
||||||
|
* go to next element in the content model
|
||||||
|
* skipping ignorable elems
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
NODE = NODE->next;
|
||||||
|
NODE = xmlValidateSkipIgnorable(ctxt, NODE);
|
||||||
|
} while ((NODE != NULL) &&
|
||||||
|
((NODE->type != XML_ELEMENT_NODE) &&
|
||||||
|
(NODE->type != XML_TEXT_NODE)));
|
||||||
|
consumed = 1;
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
DEBUG_VALID_MSG("pcdata failed");
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_ELEMENT:
|
||||||
|
if (NODE == NULL) {
|
||||||
|
DEBUG_VALID_MSG("element failed no node");
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = (xmlStrEqual(NODE->name, CONT->name));
|
||||||
|
if (ret == 1) {
|
||||||
|
DEBUG_VALID_MSG("element found, skip to next");
|
||||||
|
/*
|
||||||
|
* go to next element in the content model
|
||||||
|
* skipping ignorable elems
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
NODE = NODE->next;
|
||||||
|
NODE = xmlValidateSkipIgnorable(ctxt, NODE);
|
||||||
|
} while ((NODE != NULL) &&
|
||||||
|
((NODE->type != XML_ELEMENT_NODE) &&
|
||||||
|
(NODE->type != XML_TEXT_NODE)));
|
||||||
|
consumed = 1;
|
||||||
|
} else {
|
||||||
|
DEBUG_VALID_MSG("element failed");
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_OR:
|
||||||
|
/*
|
||||||
|
* save the second branch 'or' branch
|
||||||
|
*/
|
||||||
|
DEBUG_VALID_MSG("saving 'or' branch");
|
||||||
|
vstateVPush(ctxt, CONT->c2, NODE, DEPTH + 1, OCCURS, ROLLBACK_OR);
|
||||||
|
|
||||||
|
DEPTH++;
|
||||||
|
CONT = CONT->c1;
|
||||||
|
goto cont;
|
||||||
|
case XML_ELEMENT_CONTENT_SEQ:
|
||||||
|
DEPTH++;
|
||||||
|
CONT = CONT->c1;
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point handle going up in the tree
|
||||||
|
*/
|
||||||
|
if (ret == -1) {
|
||||||
|
DEBUG_VALID_MSG("error found returning");
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
analyze:
|
||||||
|
while (CONT != NULL) {
|
||||||
|
/*
|
||||||
|
* First do the analysis depending on the occurence model at
|
||||||
|
* this level.
|
||||||
|
*/
|
||||||
|
if (ret == 0) {
|
||||||
|
switch (CONT->ocur) {
|
||||||
|
case XML_ELEMENT_CONTENT_ONCE:
|
||||||
|
DEBUG_VALID_MSG("Once branch failed, rollback");
|
||||||
|
if (vstateVPop(ctxt) < 0 ) {
|
||||||
|
DEBUG_VALID_MSG("exhaustion, failed");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
consumed = 0;
|
||||||
|
goto cont;
|
||||||
|
case XML_ELEMENT_CONTENT_PLUS:
|
||||||
|
if (OCCURENCE == 0) {
|
||||||
|
DEBUG_VALID_MSG("Plus branch failed, rollback");
|
||||||
|
if (vstateVPop(ctxt) < 0 ) {
|
||||||
|
DEBUG_VALID_MSG("exhaustion, failed");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
consumed = 0;
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
DEBUG_VALID_MSG("Plus branch found");
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_MULT:
|
||||||
|
#ifdef DEBUG_VALID_ALGO
|
||||||
|
if (OCCURENCE == 0) {
|
||||||
|
DEBUG_VALID_MSG("Mult branch failed");
|
||||||
|
} else {
|
||||||
|
DEBUG_VALID_MSG("Mult branch found");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_OPT:
|
||||||
|
DEBUG_VALID_MSG("Option branch failed");
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (CONT->ocur) {
|
||||||
|
case XML_ELEMENT_CONTENT_OPT:
|
||||||
|
DEBUG_VALID_MSG("Option branch succeeded");
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_ONCE:
|
||||||
|
DEBUG_VALID_MSG("Once branch succeeded");
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_PLUS:
|
||||||
|
if (STATE == ROLLBACK_PARENT) {
|
||||||
|
DEBUG_VALID_MSG("Plus branch rollback");
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NODE == NULL) {
|
||||||
|
DEBUG_VALID_MSG("Plus branch exhausted");
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DEBUG_VALID_MSG("Plus branch succeeded, continuing");
|
||||||
|
SET_OCCURENCE;
|
||||||
|
goto cont;
|
||||||
|
case XML_ELEMENT_CONTENT_MULT:
|
||||||
|
if (STATE == ROLLBACK_PARENT) {
|
||||||
|
DEBUG_VALID_MSG("Mult branch rollback");
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NODE == NULL) {
|
||||||
|
DEBUG_VALID_MSG("Mult branch exhausted");
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DEBUG_VALID_MSG("Mult branch succeeded, continuing");
|
||||||
|
SET_OCCURENCE;
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
STATE = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Then act accordingly at the parent level
|
||||||
|
*/
|
||||||
|
RESET_OCCURENCE;
|
||||||
|
if (CONT->parent == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (CONT->parent->type) {
|
||||||
|
case XML_ELEMENT_CONTENT_PCDATA:
|
||||||
|
DEBUG_VALID_MSG("Error: parent pcdata");
|
||||||
|
return(-1);
|
||||||
|
case XML_ELEMENT_CONTENT_ELEMENT:
|
||||||
|
DEBUG_VALID_MSG("Error: parent element");
|
||||||
|
return(-1);
|
||||||
|
case XML_ELEMENT_CONTENT_OR:
|
||||||
|
if (ret == 1) {
|
||||||
|
DEBUG_VALID_MSG("Or succeeded");
|
||||||
|
CONT = CONT->parent;
|
||||||
|
DEPTH--;
|
||||||
|
} else {
|
||||||
|
DEBUG_VALID_MSG("Or failed");
|
||||||
|
CONT = CONT->parent;
|
||||||
|
DEPTH--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_CONTENT_SEQ:
|
||||||
|
if (ret == 0) {
|
||||||
|
DEBUG_VALID_MSG("Sequence failed");
|
||||||
|
CONT = CONT->parent;
|
||||||
|
DEPTH--;
|
||||||
|
} else if (CONT == CONT->parent->c1) {
|
||||||
|
DEBUG_VALID_MSG("Sequence testing 2nd branch");
|
||||||
|
CONT = CONT->parent->c2;
|
||||||
|
goto cont;
|
||||||
|
} else {
|
||||||
|
DEBUG_VALID_MSG("Sequence succeeded");
|
||||||
|
CONT = CONT->parent;
|
||||||
|
DEPTH--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (NODE != NULL) {
|
||||||
|
DEBUG_VALID_MSG("Failed, remaining input, rollback");
|
||||||
|
if (vstateVPop(ctxt) < 0 ) {
|
||||||
|
DEBUG_VALID_MSG("exhaustion, failed");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
consumed = 0;
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
if (ret == 0) {
|
||||||
|
DEBUG_VALID_MSG("Failure, rollback");
|
||||||
|
if (vstateVPop(ctxt) < 0 ) {
|
||||||
|
DEBUG_VALID_MSG("exhaustion, failed");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
consumed = 0;
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xmlValidateElementContent:
|
||||||
|
* @ctxt: the validation context
|
||||||
|
* @child: the child list
|
||||||
|
* @cont: pointer to the content declaration
|
||||||
|
*
|
||||||
|
* Try to validate the content model of an element
|
||||||
|
*
|
||||||
|
* returns 1 if valid or 0 if not and -1 in case of error
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
|
||||||
|
xmlElementContentPtr cont) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: handle the fact that entities references
|
||||||
|
* may appear at this level.
|
||||||
|
*/
|
||||||
|
ctxt->vstateMax = 8;
|
||||||
|
ctxt->vstateTab = (xmlValidState *) xmlMalloc(
|
||||||
|
ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
|
||||||
|
if (ctxt->vstateTab == NULL) {
|
||||||
|
xmlGenericError(xmlGenericErrorContext,
|
||||||
|
"malloc failed !n");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The first entry in the stack is reserved to the current state
|
||||||
|
*/
|
||||||
|
ctxt->vstate = &ctxt->vstateTab[0];
|
||||||
|
ctxt->vstateNr = 1;
|
||||||
|
CONT = cont;
|
||||||
|
NODE = child;
|
||||||
|
DEPTH = 0;
|
||||||
|
OCCURS = 0;
|
||||||
|
STATE = 0;
|
||||||
|
ret = xmlValidateElementType(ctxt);
|
||||||
|
ctxt->vstateMax = 0;
|
||||||
|
xmlFree(ctxt->vstateTab);
|
||||||
|
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
#endif /* ALLOW_UNDETERMINISTIC_MODELS */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* xmlSprintfElementChilds:
|
* xmlSprintfElementChilds:
|
||||||
* @buf: an output buffer
|
* @buf: an output buffer
|
||||||
@ -3612,6 +4136,23 @@ child_ok:
|
|||||||
case XML_ELEMENT_TYPE_ELEMENT:
|
case XML_ELEMENT_TYPE_ELEMENT:
|
||||||
child = elem->children;
|
child = elem->children;
|
||||||
cont = elemDecl->content;
|
cont = elemDecl->content;
|
||||||
|
#ifdef ALLOW_UNDETERMINISTIC_MODELS
|
||||||
|
ret = xmlValidateElementContent(ctxt, child, cont);
|
||||||
|
if (ret != 1) {
|
||||||
|
char expr[1000];
|
||||||
|
char list[2000];
|
||||||
|
|
||||||
|
expr[0] = 0;
|
||||||
|
xmlSprintfElementContent(expr, cont, 1);
|
||||||
|
list[0] = 0;
|
||||||
|
xmlSprintfElementChilds(list, elem, 1);
|
||||||
|
|
||||||
|
VERROR(ctxt->userData,
|
||||||
|
"Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
|
||||||
|
elem->name, expr, list);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
ret = xmlValidateElementTypeElement(ctxt, &child, cont);
|
ret = xmlValidateElementTypeElement(ctxt, &child, cont);
|
||||||
while ((child != NULL) && (child->type == XML_TEXT_NODE) &&
|
while ((child != NULL) && (child->type == XML_TEXT_NODE) &&
|
||||||
(xmlIsBlankNode(child))) {
|
(xmlIsBlankNode(child))) {
|
||||||
@ -3633,6 +4174,7 @@ child_ok:
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* [ VC: Required Attribute ] */
|
/* [ VC: Required Attribute ] */
|
||||||
|
12
valid.h
12
valid.h
@ -17,6 +17,12 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validation state added for non-determinist content model
|
||||||
|
*/
|
||||||
|
typedef struct _xmlValidState xmlValidState;
|
||||||
|
typedef xmlValidState *xmlValidStatePtr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* an xmlValidCtxt is used for error reporting when validating
|
* an xmlValidCtxt is used for error reporting when validating
|
||||||
*/
|
*/
|
||||||
@ -40,6 +46,12 @@ struct _xmlValidCtxt {
|
|||||||
int finishDtd; /* finished validating the Dtd ? */
|
int finishDtd; /* finished validating the Dtd ? */
|
||||||
xmlDocPtr doc; /* the document */
|
xmlDocPtr doc; /* the document */
|
||||||
int valid; /* temporary validity check result */
|
int valid; /* temporary validity check result */
|
||||||
|
|
||||||
|
/* state state used for non-determinist content validation */
|
||||||
|
xmlValidState *vstate; /* current state */
|
||||||
|
int vstateNr; /* Depth of the validation stack */
|
||||||
|
int vstateMax; /* Max depth of the validation stack */
|
||||||
|
xmlValidState *vstateTab; /* array of validation states */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user