Commits

Anonymous committed 66087c9

Modified Files:
Tag: mm_fix
LibXML.xs
[fix] memory leak in DTD parsing

perl-libxml-mm.c
+ handles HTML documents correctly
o frees DTDs now

lib/XML/LibXML/SAX/Builder.pm
more naming fixes

t/11memory.t
+ DTD parsing and validation tests
+ xpath test (leaks!!!)
seems to be a string, that is not freed.

t/12html.t t/13dtd.t
(whitespaces)

  • Participants
  • Parent commits fa4c571
  • Branches mm_fix

Comments (0)

Files changed (5)

     CODE:
         xmlSetDocCompressMode((xmlDocPtr)PmmSvNode(self), zLevel);
 
+
+int
+is_valid(self, ...)
+        SV * self
+    PREINIT:
+        xmlDocPtr doc = (xmlDocPtr)PmmSvNode(self);
+        xmlValidCtxt cvp;
+        xmlDtdPtr dtd;
+        SV * dtd_sv;
+    CODE:
+        LibXML_error = sv_2mortal(newSVpv("", 0));
+        cvp.userData = (void*)PerlIO_stderr();
+        cvp.error = (xmlValidityErrorFunc)LibXML_validity_error;
+        cvp.warning = (xmlValidityWarningFunc)LibXML_validity_warning;
+        if (items > 1) {
+            dtd_sv = ST(1);
+            if ( sv_isobject(dtd_sv) && (SvTYPE(SvRV(dtd_sv)) == SVt_PVMG) ) {
+                dtd = (xmlDtdPtr)PmmSvNode(dtd_sv);
+            }
+            RETVAL = xmlValidateDtd(&cvp, doc, dtd);
+        }
+        else {
+            RETVAL = xmlValidateDocument(&cvp, doc);
+        }
+    OUTPUT:
+        RETVAL
+
+int
+validate(self, ...)
+        SV * self
+    PREINIT:
+        xmlDocPtr doc = (xmlDocPtr)PmmSvNode(self);
+        xmlValidCtxt cvp;
+        xmlDtdPtr dtd;
+        SV * dtd_sv;
+        STRLEN n_a;
+    CODE:
+        LibXML_error = sv_2mortal(newSVpv("", 0));
+        cvp.userData = (void*)PerlIO_stderr();
+        cvp.error = (xmlValidityErrorFunc)LibXML_validity_error;
+        cvp.warning = (xmlValidityWarningFunc)LibXML_validity_warning;
+        if (items > 1) {
+            dtd_sv = ST(1);
+            if ( sv_isobject(dtd_sv) && (SvTYPE(SvRV(dtd_sv)) == SVt_PVMG) ) {
+                dtd = (xmlDtdPtr)PmmSvNode(dtd_sv);
+            }
+            else {
+                croak("is_valid: argument must be a DTD object");
+            }
+            RETVAL = xmlValidateDtd(&cvp, doc , dtd);
+        }
+        else {
+            RETVAL = xmlValidateDocument(&cvp, doc);
+        }
+        if (RETVAL == 0) {
+            croak(SvPV(LibXML_error, n_a));
+        }
+    OUTPUT:
+        RETVAL
+
+
 MODULE = XML::LibXML         PACKAGE = XML::LibXML::Node
 
 void
         }
 
 void
-_findnodes( pnode, xpath )
+_findnodes( pnode, perl_xpath )
         SV* pnode
-        char * xpath 
+        SV * perl_xpath 
     PREINIT:
         xmlNodePtr node = PmmSvNode(pnode);
         ProxyNodePtr owner = NULL;
         xmlNodeSetPtr nodelist = NULL;
         SV * element = NULL ;
         int len = 0 ;
+        xmlChar * xpath = nodeSv2C(perl_xpath, node);
+    INIT:
+        if ( !(xpath && xmlStrlen(xpath)) ) {
+            xs_warn( "bad xpath\n" );
+            if ( xpath ) 
+                xmlFree(xpath);
+            XSRETURN_UNDEF;
+        }
     PPCODE:
-       if ( node->doc ) {
+        if ( node->doc ) {
             domNodeNormalize( xmlDocGetRootElement(node->doc ) );
         }
         else {
         }
 
         nodelist = domXPathSelect( node, xpath );
+        xmlFree(xpath);
+
         if ( nodelist ) {
             if ( nodelist->nodeNr > 0 ) {
                 int i = 0 ;
     OUTPUT:
        RETVAL
 
+
+MODULE = XML::LibXML         PACKAGE = XML::LibXML::Dtd
+
+SV *
+new(CLASS, external, system)
+        char * CLASS
+        char * external
+        char * system
+    ALIAS:
+        parse_uri = 1
+    PREINIT:
+        xmlDtdPtr dtd = NULL;
+    CODE:
+        LibXML_error = sv_2mortal(newSVpv("", 0));
+        dtd = xmlParseDTD((const xmlChar*)external, (const xmlChar*)system);
+        if ( dtd == NULL ) {
+            XSRETURN_UNDEF;
+        }
+        RETVAL = PmmNodeToSv( (xmlNodePtr) dtd, NULL );
+    OUTPUT:
+        RETVAL
+
+SV *
+parse_string(CLASS, str, ...)
+        char * CLASS
+        char * str
+    PREINIT:
+        STRLEN n_a;
+        xmlDtdPtr res;
+        SV * encoding_sv;
+        xmlParserInputBufferPtr buffer;
+        xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
+        char * new_string;
+    CODE:
+        LibXML_error = sv_2mortal(newSVpv("", 0));
+        if (items > 2) {
+            encoding_sv = ST(2);
+            if (items > 3) {
+                croak("parse_string: too many parameters");
+            }
+            /* warn("getting encoding...\n"); */
+            enc = xmlParseCharEncoding(SvPV(encoding_sv, n_a));
+            if (enc == XML_CHAR_ENCODING_ERROR) {
+                croak("Parse of encoding %s failed: %s", SvPV(encoding_sv, n_a), SvPV(LibXML_error, n_a));
+            }
+        }
+        buffer = xmlAllocParserInputBuffer(enc);
+        /* buffer = xmlParserInputBufferCreateMem(str, xmlStrlen(str), enc); */
+        if ( !buffer)
+            croak("cant create buffer!\n" );
+
+        new_string = xmlStrdup(str);
+        xmlParserInputBufferPush(buffer, strlen(new_string), new_string);
+
+        res = xmlIOParseDTD(NULL, buffer, enc);
+
+        /* NOTE: For some reason freeing this InputBuffer causes a segfault! */
+        /* xmlFreeParserInputBuffer(buffer); */
+        xmlFree(new_string);
+        if (res != NULL) {
+            RETVAL = PmmNodeToSv((xmlNodePtr)res, NULL);
+        }
+        else {
+            croak("couldn't parse DTD: %s", SvPV(LibXML_error, n_a));
+        }
+    OUTPUT:
+        RETVAL

lib/XML/LibXML/SAX/Builder.pm

 sub end_element {
     my ($self, $el) = @_;
     return unless $self->{Parent};
-    $self->{Parent} = $self->{Parent}->getParentNode();
+    $self->{Parent} = $self->{Parent}->parentNode();
 }
 
 sub characters {
 {
     switch( node->type ) {
     case XML_DOCUMENT_NODE:
+    case XML_HTML_DOCUMENT_NODE:
         xs_warn("XML_DOCUMENT_NODE\n");
         xmlFreeDoc( (xmlDocPtr) node );
         break;
         if ( node->doc ) {
             if ( node->doc->extSubset != (xmlDtdPtr)node 
                  && node->doc->intSubset != (xmlDtdPtr)node ) {
-                xs_warn( "should free DTD\n");
+                xs_warn( "XML_DTD_NODE\n");
                 node->doc = NULL;
-                // xmlFreeDtd( (xmlDtdPtr)node );
+                xmlFreeDtd( (xmlDtdPtr)node );
             }
         }
         break;
 use Test;
 BEGIN { 
     if ($^O eq 'linux' && $ENV{MEMORY_TEST}) {
-        plan tests => 12;
+        plan tests => 18;
     }
     else {
         plan tests => 0;
         }
         ok(1);
         check_mem();
+
+        print("# DTD string parsing\n");
+
+        my $dtdstr;
+        {
+            local $/; local *DTD;
+            open(DTD, 'example/test.dtd') || die $!;
+            $dtdstr = <DTD>;
+            $dtdstr =~ s/\r//g;
+            $dtdstr =~ s/[\r\n]*$//;
+            close DTD;
+        }
+
+        ok($dtdstr);
+
+        for ( 1..$times_through ) {
+            my $dtd = XML::LibXML::Dtd->parse_string($dtdstr);
+        }
+        ok(1);
+        check_mem();
+
+        print( "# DTD URI parsing \n");
+        # parse a DTD from a SYSTEM ID
+        for ( 1..$times_through ) {
+            my $dtd = XML::LibXML::Dtd->new('ignore', 'example/test.dtd');
+        }
+        ok(1);
+        check_mem();
+
+        print("# Document validation\n");
+        {
+            print "# is_valid()\n";
+            my $dtd = XML::LibXML::Dtd->parse_string($dtdstr);
+            my $xml = XML::LibXML->new->parse_file('example/article_bad.xml');
+            for ( 1..$times_through ) {
+                $xml->is_valid($dtd);
+            }
+            ok(1);
+            check_mem();
+        
+            print "# validate() \n";
+            for ( 1..$times_through ) {
+                eval { $xml->validate($dtd);};
+            }
+            ok(1);
+            check_mem();
+                
+        }
+
+        print "# FIND NODES \n";
+        {
+            my $str = "<foo><bar><foo/></bar></foo>";
+            my $doc = XML::LibXML->new->parse_string( $str );
+            for ( 1..$times_through ) {
+                my @nodes = $doc->findnodes("/foo/bar/foo");
+            }
+            ok(1);
+            check_mem();
+
+        }
     }
 }
 
 my $html = "example/test.html";
 
 my $parser = XML::LibXML->new();
-my $doc = $parser->parse_html_file($html);
-ok($doc);
-
-undef $doc;
+{
+    my $doc = $parser->parse_html_file($html);
+    ok($doc);
+}
 
 my $fh = IO::File->new($html) || die "Can't open $html: $!";