python script to run regression tests against the XML Test suite of

* check-xml-test-suite.py: python script to run regression tests
  against the XML Test suite of W3C/OASis
* SAX.c: fixed a validation bug
* parser.c: fixed 3 errors pointed by the test suite
* doc/buildDocBookCatalog: fixed a typo pointed by drake
* python/Makefile.am: fixed a dependendy
Daniel
diff --git a/ChangeLog b/ChangeLog
index 0fc8225..037d9ef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Sun Feb 17 20:41:37 CET 2002 Daniel Veillard <daniel@veillard.com>
+
+	* check-xml-test-suite.py: python script to run regression tests
+	  against the XML Test suite of W3C/OASis
+	* SAX.c: fixed a validation bug
+	* parser.c: fixed 3 errors pointed by the test suite
+	* doc/buildDocBookCatalog: fixed a typo pointed by drake
+	* python/Makefile.am: fixed a dependendy
+
 Fri Feb 15 21:47:13 CET 2002 Daniel Veillard <daniel@veillard.com>
 
 	* xmlmemory.c: avoid a warning bug #71594
diff --git a/SAX.c b/SAX.c
index 399eed4..da13370 100644
--- a/SAX.c
+++ b/SAX.c
@@ -493,7 +493,7 @@
 	     "SAX.attributeDecl(%s) called while not in subset\n", name);
 	return;
     }
-    if (attr == 0) ctxt->valid = 0;
+    /* if (attr == 0) ctxt->valid = 0; */
     if (ctxt->validate && ctxt->wellFormed &&
         ctxt->myDoc && ctxt->myDoc->intSubset)
 	ctxt->valid &= xmlValidateAttributeDecl(&ctxt->vctxt, ctxt->myDoc,
diff --git a/check-xml-test-suite.py b/check-xml-test-suite.py
new file mode 100755
index 0000000..3bf9f9e
--- /dev/null
+++ b/check-xml-test-suite.py
@@ -0,0 +1,330 @@
+#!/usr/bin/python
+import sys
+import os
+import string
+sys.path.append("python")
+import libxml2
+
+#
+# the testsuite description
+#
+CONF="xml-test-suite/xmlconf/xmlconf.xml"
+LOG="check-xml-test-suite.log"
+
+log = open(LOG, "w")
+
+#
+# Error and warning handlers
+#
+error_nr = 0
+def errorHandler(ctx, str):
+    global error_nr
+
+    error_nr = error_nr + 1
+
+libxml2.registerErrorHandler(errorHandler, None)
+
+#warning_nr = 0
+#warning = ''
+#def warningHandler(ctx, str):
+#    global warning_nr
+#    global warning
+#
+#    warning_nr = warning_nr + 1
+#    warning = warning + str
+#
+#libxml2.registerWarningHandler(warningHandler, None)
+
+#
+# Used to load the XML testsuite description
+#
+def loadNoentDoc(filename):
+    ctxt = libxml2.createFileParserCtxt(filename)
+    if ctxt == None:
+        return None
+    ctxt.replaceEntities(1)
+    ctxt.parseDocument()
+    doc = ctxt.doc()
+    if ctxt.wellFormed() != 1:
+        doc.freeDoc()
+	return None
+    return doc
+
+#
+# The conformance testing routines
+#
+
+def testNotWf(filename, id):
+    global error_nr
+    global log
+
+    error_nr = 0
+
+    ctxt = libxml2.createFileParserCtxt(filename)
+    if ctxt == None:
+        return -1
+    ctxt.parseDocument()
+
+    doc = ctxt.doc()
+    if error_nr == 0 or ctxt.wellFormed() != 0:
+        print "%s: error: Well Formedness error not detected" % (id)
+	log.write("%s: error: Well Formedness error not detected\n" % (id))
+	doc.freeDoc()
+	return 0
+    return 1
+
+def testNotWfEnt(filename, id):
+    global error_nr
+    global log
+
+    error_nr = 0
+
+    ctxt = libxml2.createFileParserCtxt(filename)
+    if ctxt == None:
+        return -1
+    ctxt.replaceEntities(1)
+    ctxt.parseDocument()
+
+    doc = ctxt.doc()
+    if error_nr == 0 or ctxt.wellFormed() != 0:
+        print "%s: error: Well Formedness error not detected" % (id)
+	log.write("%s: error: Well Formedness error not detected\n" % (id))
+	doc.freeDoc()
+	return 0
+    return 1
+
+def testNotWfEntDtd(filename, id):
+    global error_nr
+    global log
+
+    error = ''
+    error_nr = 0
+
+    ctxt = libxml2.createFileParserCtxt(filename)
+    if ctxt == None:
+        return -1
+    ctxt.replaceEntities(1)
+    ctxt.loadSubset(1)
+    ctxt.parseDocument()
+
+    doc = ctxt.doc()
+    if error_nr == 0 or ctxt.wellFormed() != 0:
+        print "%s: error: Well Formedness error not detected" % (id)
+	log.write("%s: error: Well Formedness error not detected\n" % (id))
+	doc.freeDoc()
+	return 0
+    return 1
+
+def testWfEntDtd(filename, id):
+    global error_nr
+    global log
+
+    error = ''
+    error_nr = 0
+
+    ctxt = libxml2.createFileParserCtxt(filename)
+    if ctxt == None:
+        return -1
+    ctxt.replaceEntities(1)
+    ctxt.loadSubset(1)
+    ctxt.parseDocument()
+
+    doc = ctxt.doc()
+    if ctxt.wellFormed() == 0:
+        print "%s: error: wrongly failed to parse the document" % (id)
+	log.write("%s: error: wrongly failed to parse the document\n" % (id))
+	return 0
+    if error_nr != 0:
+        print "%s: warning: WF document generated an error msg" % (id)
+	log.write("%s: error: WF document generated an error msg\n" % (id))
+	doc.freeDoc()
+	return 2
+    doc.freeDoc()
+    return 1
+
+def testInvalid(filename, id):
+    global error_nr
+    global log
+
+    error_nr = 0
+
+    ctxt = libxml2.createFileParserCtxt(filename)
+    if ctxt == None:
+        return -1
+    ctxt.validate(1)
+    ctxt.parseDocument()
+
+    doc = ctxt.doc()
+    valid = ctxt.isValid()
+    if doc == None:
+        print "%s: error: wrongly failed to parse the document" % (id)
+	log.write("%s: error: wrongly failed to parse the document\n" % (id))
+	return 0
+    if valid == 1:
+        print "%s: error: Validity error not detected" % (id)
+	log.write("%s: error: Validity error not detected\n" % (id))
+	doc.freeDoc()
+	return 0
+    if error_nr == 0:
+        print "%s: warning: Validity error not reported" % (id)
+	log.write("%s: warning: Validity error not reported\n" % (id))
+	doc.freeDoc()
+	return 2
+        
+    doc.freeDoc()
+    return 1
+
+def testValid(filename, id):
+    global error_nr
+
+    error_nr = 0
+
+    ctxt = libxml2.createFileParserCtxt(filename)
+    if ctxt == None:
+        return -1
+    ctxt.validate(1)
+    ctxt.parseDocument()
+
+    doc = ctxt.doc()
+    valid = ctxt.isValid()
+    if doc == None:
+        print "%s: error: wrongly failed to parse the document" % (id)
+	log.write("%s: error: wrongly failed to parse the document\n" % (id))
+	return 0
+    if valid != 1:
+        print "%s: error: Validity check failed" % (id)
+	log.write("%s: error: Validity check failed\n" % (id))
+	doc.freeDoc()
+	return 0
+    if error_nr != 0 or valid != 1:
+        print "%s: warning: valid document reported an error" % (id)
+	log.write("%s: warning: valid document reported an error\n" % (id))
+	doc.freeDoc()
+	return 2
+    doc.freeDoc()
+    return 1
+
+test_nr = 0
+test_succeed = 0
+test_failed = 0
+test_error = 0
+def runTest(test):
+    global test_nr
+    global test_failed
+    global test_error
+    global test_succeed
+    global log
+
+    uri = test.prop('URI')
+    id = test.prop('ID')
+    if uri == None:
+        print "Test without ID:", uri
+	return -1
+    if id == None:
+        print "Test without URI:", id
+	return -1
+    base = test.getBase(None)
+    URI = libxml2.buildURI(uri, base)
+    if os.access(URI, os.R_OK) == 0:
+        print "Test %s missing: base %s uri %s" % (URI, base, uri)
+	return -1
+    type = test.prop('TYPE')
+    if type == None:
+        print "Test %s missing TYPE" % (id)
+	return -1
+
+    extra = None
+    if type == "invalid":
+        res = testInvalid(URI, id)
+    elif type == "valid":
+        res = testValid(URI, id)
+    elif type == "not-wf":
+        extra =  test.prop('ENTITIES')
+	# print URI
+	#if extra == None:
+	#    res = testNotWfEntDtd(URI, id)
+ 	#elif extra == 'none':
+	#    res = testNotWf(URI, id)
+	#elif extra == 'general':
+	#    res = testNotWfEnt(URI, id)
+	#elif extra == 'both' or extra == 'parameter':
+	res = testNotWfEntDtd(URI, id)
+	#else:
+	#    print "Unknow value %s for an ENTITIES test value" % (extra)
+	#    return -1
+    elif type == "error":
+	res = testWfEntDtd(URI, id)
+    else:
+        # TODO skipped for now
+	return -1
+
+    test_nr = test_nr + 1
+    if res > 0:
+	test_succeed = test_succeed + 1
+    elif res == 0:
+	test_failed = test_failed + 1
+    elif res < 0:
+	test_error = test_error + 1
+
+    # Log the ontext
+    if res != 1:
+	log.write("   File: %s\n" % (URI))
+	content = test.content
+	if extra != None:
+	    log.write("   %s:%s:%s\n\n" % (type, extra, content))
+	else:
+	    log.write("   %s:%s\n\n" % (type, content))
+
+    return 0
+	    
+
+def runTestCases(case):
+    profile = case.prop('PROFILE')
+    if profile != None and \
+       string.find(profile, "IBM XML Conformance Test Suite - Production") < 0:
+	print "=>", profile
+    test = case.children
+    while test != None:
+        if test.name == 'TEST':
+	    runTest(test)
+	if test.name == 'TESTCASES':
+	    runTestCases(test)
+        test = test.next
+        
+conf = loadNoentDoc(CONF)
+if conf == None:
+    print "Unable to load %s" % CONF
+    sys.exit(1)
+
+testsuite = conf.getRootElement()
+if testsuite.name != 'TESTSUITE':
+    print "Expecting TESTSUITE root element: aborting"
+    sys.exit(1)
+
+profile = testsuite.prop('PROFILE')
+if profile != None:
+    print profile
+
+case = testsuite.children
+while case != None:
+    global test_nr
+    global test_succeed
+    global test_failed
+    global test_error
+
+    if case.name == 'TESTCASES':
+	old_test_nr = test_nr
+	old_test_succeed = test_succeed
+	old_test_failed = test_failed
+	old_test_error = test_error
+        runTestCases(case)
+	print "   Ran %d tests: %d suceeded, %d failed and %d generated an error" % (
+	       test_nr - old_test_nr, test_succeed - old_test_succeed,
+	       test_failed - old_test_failed, test_error - old_test_error)
+    case = case.next
+
+conf.freeDoc()
+log.close()
+
+print "Ran %d tests: %d suceeded, %d failed and %d generated an error" % (
+      test_nr, test_succeed, test_failed, test_error)
diff --git a/doc/buildDocBookCatalog b/doc/buildDocBookCatalog
index 64cf931..513796a 100755
--- a/doc/buildDocBookCatalog
+++ b/doc/buildDocBookCatalog
@@ -51,7 +51,7 @@
     found=`find /usr/share/sgml -name docbookx.dtd`
 fi
 if [ "$found" = "" ] ; then
-    echo could not locate docbookx.dtd drom DocBook XML
+    echo could not locate docbookx.dtd for DocBook XML
     exit 1
 fi
 
diff --git a/parser.c b/parser.c
index 8a783ef..fcb9e85 100644
--- a/parser.c
+++ b/parser.c
@@ -469,7 +469,11 @@
 	SKIP(3);
 	GROW;
 	while (RAW != ';') { /* loop blocked by count */
-	    if ((RAW >= '0') && (RAW <= '9') && (count < 20)) 
+	    if (count++ > 20) {
+		count = 0;
+		GROW;
+	    }
+	    if ((RAW >= '0') && (RAW <= '9')) 
 	        val = val * 16 + (CUR - '0');
 	    else if ((RAW >= 'a') && (RAW <= 'f') && (count < 20))
 	        val = val * 16 + (CUR - 'a') + 10;
@@ -497,7 +501,11 @@
 	SKIP(2);
 	GROW;
 	while (RAW != ';') { /* loop blocked by count */
-	    if ((RAW >= '0') && (RAW <= '9') && (count < 20)) 
+	    if (count++ > 20) {
+		count = 0;
+		GROW;
+	    }
+	    if ((RAW >= '0') && (RAW <= '9')) 
 	        val = val * 10 + (CUR - '0');
 	    else {
 		ctxt->errNo = XML_ERR_INVALID_DEC_CHARREF;
@@ -2555,7 +2563,7 @@
 	in = ctxt->input->cur;
 	do {
 get_more:
-	    while (((*in >= 0x20) && (*in != '<') &&
+	    while (((*in >= 0x20) && (*in != '<') && (*in != ']') &&
 		    (*in != '&') && (*in <= 0x7F)) || (*in == 0x09))
 		in++;
 	    if (*in == 0xA) {
@@ -2567,6 +2575,20 @@
 		}
 		goto get_more;
 	    }
+	    if (*in == ']') {
+		if ((in[1] == ']') && (in[2] == '>')) {
+		    ctxt->errNo = XML_ERR_MISPLACED_CDATA_END;
+		    if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+			ctxt->sax->error(ctxt->userData,
+			   "Sequence ']]>' not allowed in content\n");
+		    ctxt->input->cur = in;
+		    ctxt->wellFormed = 0;
+		    ctxt->disableSAX = 1;
+		    return;
+		}
+		in++;
+		goto get_more;
+	    }
 	    nbchar = in - ctxt->input->cur;
 	    if (nbchar > 0) {
 		if (IS_BLANK(*ctxt->input->cur)) {
@@ -2848,7 +2870,7 @@
     while (IS_CHAR(cur) && /* checked */
            ((cur != '>') ||
 	    (r != '-') || (q != '-'))) {
-	if ((r == '-') && (q == '-') && (len > 1)) {
+	if ((r == '-') && (q == '-')) {
 	    ctxt->errNo = XML_ERR_HYPHEN_IN_COMMENT;
 	    if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
 	        ctxt->sax->error(ctxt->userData,
@@ -4814,7 +4836,7 @@
 	ctxt->disableSAX = 1;
 	ctxt->instate = XML_PARSER_IGNORE;
 
-	while (depth >= 0) {
+	while ((depth >= 0) && (RAW != 0)) {
 	  if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) {
 	    depth++;
 	    SKIP(3);
@@ -5149,6 +5171,8 @@
     } else {
 	ent = xmlParseEntityRef(ctxt);
 	if (ent == NULL) return;
+	if (!ctxt->wellFormed)
+	    return;
 	if ((ent->name != NULL) && 
 	    (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) {
 	    xmlNodePtr list = NULL;
@@ -5237,6 +5261,7 @@
 				"Detected entity reference loop\n");
 			ctxt->wellFormed = 0;
 			ctxt->disableSAX = 1;
+			return;
 		    } else if ((ret == 0) && (list != NULL)) {
 			if (((ent->etype == XML_INTERNAL_GENERAL_ENTITY) ||
 			 (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY))&&
diff --git a/python/Makefile.am b/python/Makefile.am
index a3fecfc..2978b70 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -38,7 +38,7 @@
 noinst_LTLIBRARIES = libxmlmodule.la
 libxmlmodule_la_SOURCES = libxml.c types.c libxml2-py.c
 
-libxml2mod.so: $(libxmlmodule_la_OBJECTS)
+libxml2mod.so: $(libxmlmodule_la_OBJECTS) $(mylibs)
 	$(LINK) -o $@ $(libxmlmodule_la_OBJECTS) $(libxml2mod_so_LDFLAGS)