diff --git a/configure.php b/configure.php index 35b34eba4..86f0dc32e 100755 --- a/configure.php +++ b/configure.php @@ -19,9 +19,11 @@ +----------------------------------------------------------------------+ */ -error_reporting(-1); -$cvs_id = '$Id$'; +ini_set( 'display_errors' , 1 ); +ini_set( 'display_startup_errors' , 1 ); +error_reporting( E_ALL ); +$cvs_id = '$Id$'; echo "configure.php: $cvs_id\n"; const RNG_SCHEMA_DIR = __DIR__ . DIRECTORY_SEPARATOR . 'docbook' . DIRECTORY_SEPARATOR . 'docbook-v5.2-os' . DIRECTORY_SEPARATOR . 'rng' . DIRECTORY_SEPARATOR; @@ -774,29 +776,173 @@ function getFileModificationHistory(): array { } echo "done.\n"; -echo "Running XInclude/XPointer... "; -$status = $dom->xinclude(); -if ($status === -1) { - echo "failed.\n"; -} else { - /* For some dumb reason when no substitution are made it returns false instead of 0... */ - $status = (int) $status; - echo "done. Performed $status XIncludes\n"; -} -flush(); -if ( $ac['XPOINTER_REPORTING'] == 'yes' || $ac['LANG'] == 'en' ) -{ +{ // XInclude/XPointer + + echo "Running XInclude/XPointer... "; + + // Always report failures on doc-en. + // Translations can --disable-xpointer-reporting. + // Failures on doc-en are fatal. + + $report = $ac['XPOINTER_REPORTING'] == 'yes' || $ac['LANG'] == 'en'; + $print = $ac['STDERR_TO_STDOUT'] == 'yes' ? STDOUT : STDERR; + $fatal = $ac['LANG'] == 'en'; + $xpath = new DOMXPath( $dom ); + + // xinclude by xml:id pre run + + $nodes = $xpath->query( "//*[xiparent]" ); + if ( $nodes->length > 0 ) + { + echo "XML contains elements with 'xiparent' attribute. XInclude by xml:id will fail.\n"; + exit(-1); + } + + $stx = chr( 1 ); // invalid attribute chars + $etx = chr( 2 ); // cannot be used in text + + $nodes = $xpath->query( "//*[@xpointer]" ); + foreach( $nodes as $xinclude ) + { + $id = $xinclude->getAttribute( "xpointer" ); + $target = $dom->getElementById( $id ); + if ( $target == null ) + continue; + $parent = $target->parentNode; + if ( $parent->nodeType != XML_ELEMENT_NODE ) + continue; + // creates an 'xiparent' attribute on parent of xi:include xpointer target, + // so these targered elements are not to be changed on later fixup. + $mark = $parent->getAttribute( "xiparent" ); + $mark .= "{$stx}{$id}{$etx}"; + $parent->setAttribute( "xiparent" , $mark ); + } + + // automatic xi:fallback + + $nodes = $xpath->query( "//*[local-name()='include']" ); + foreach( $nodes as $node ) + { + $target = $node->getAttribute( "xpointer" ); + $failback = null; + + $elements = $node->getElementsByTagName( "fallback" ); + if ( count( $elements ) == 0 ) + { + $failback = $node->ownerDocument->createElementNS( "http://www.w3.org/2001/XInclude" , "fallback" ); + $node->append( $failback ); + } + else + $failback = $elements[0]; + + $comment = $failback->ownerDocument->createComment( "xi:fallback $target" ); + $failback->append( $comment ); + + unset( $failback ); // otherwise core dumps on PHP 8.1.2-1ubuntu2.19 + unset( $comment ); // otherwise core dumps on PHP 8.1.2-1ubuntu2.19 + } + + // run + + $ret = $dom->xinclude(); + $errors = libxml_get_errors(); - $output = ( $ac['STDERR_TO_STDOUT'] == 'yes' ) ? STDOUT : STDERR; - if ( count( $errors ) > 0 ) + libxml_clear_errors(); + + $status = $ret === -1 ? "failed" : "done"; + $intret = $ret === -1 ? 0 : (int) $ret; + echo "$status. Performed $intret XIncludes.\n"; + + // failures + + if ( $report ) { - fprintf( $output , "\n"); foreach( $errors as $error ) - fprintf( $output , "{$error->message}\n"); - if ( $ac['LANG'] == 'en' ) - errors_are_bad(1); + fprintf( $print , " Failed xi:include at line: %s\n" , $error->line ); + + $nodes = $xpath->query( "//comment()" ); + foreach( $nodes as $node ) + { + $payload = $node->data; + if ( str_starts_with( $payload , "xi:fallback " ) ) + { + $payload = trim( substr( $payload , 12 ) ); + fprintf( $print , " Used xi:fallback for target: $payload\n" ); + } + } + + if ( count( $errors ) > 0 ) + { + fprintf( $print , "\n\n"); + if ( $fatal ) + errors_are_bad( 1 ); + } } + + // xinclude by xml:id post run fixup + + $nodes = $xpath->query( "//*[@xml:id]" ); + $count = []; // id : count + + foreach( $nodes as $node ) + { + $id = $node->getAttribute( "xml:id" ); + isset( $count[$id] ) ? $count[$id]++ : $count[$id] = 1; + } + + foreach( $nodes as $node ) + { + $parent = $node->parentNode; + if ( $parent->nodeType != XML_ELEMENT_NODE ) + continue; + + $id = $node->getAttribute( "xml:id" ); + $mark = "$stx" . $id . $etx; + $list = $parent->getAttribute( "xiparent" ); + if ( str_contains( $list , $mark ) ) + { + if ( $count[ $id ] > 1 ) + { + $node->removeAttribute( "xml:id" ); + $count[ $id ]--; + } + else + echo "\tStrange, xml:id was to be over deleted: $id\n"; + } + } + + $list = array(); + $nodes = $xpath->query( "//*[@xml:id]" ); + foreach( $nodes as $node ) + { + $id = $node->getAttribute( "xml:id" ); + if ( in_array( $id , $list ) ) + { + // may happen while migratating xpointers from xpaths to xml:ids + echo " Random removing duplicated xml:id: $id\n"; + $node->removeAttribute( "xml:id" ); + } + $list[] = $id; + } + + $nodes = $xpath->query( "//*[@xiparent]" ); + foreach( $nodes as $node ) + $node->removeAttribute( "xiparent" ); + + flush(); +} + +function getNodePathWithIds( DOMNode|null $node ) : string +{ + if ( $node == null || $node->nodeType == XML_DOCUMENT_NODE ) + return ""; + $path = getNodePathWithIds( $node->parentNode ); + $path .= "/{$node->nodeName}"; + $id = $node->getAttribute( "xml:id" ); + if ( $id != "" ) + $path .= "($id)"; + return $path; } echo "Validating {$ac["INPUT_FILENAME"]}... ";