diff --git a/scripts/translation/README.md b/scripts/translation/README.md index 43c50be34..4aa8572ac 100644 --- a/scripts/translation/README.md +++ b/scripts/translation/README.md @@ -16,15 +16,19 @@ Because of the above, it's possible to silence each alert indempendly. These scripts will output `--add-ignore` commands that, if executed, will omit the specific alerts in future executions. -## First execution +## broken.php -The first execution of these scripts may generate an inordinate amount of -alerts. It's advised to initially run each command separately, and work the -alerts on a case by case basis. After all interesting cases are fixed, -it's possible to rerun the command and `grep` the output for `--add-ignore` -lines, run these commands, and by so, mass ignore the residual alerts. +`doc-base/scripts/broken.php` will test if individual XML files are +ill-formed. That is, if a file contains Unicode BOM, carriage returns (CR), +or if XML contents are not +[well-balanced](https://www.w3.org/TR/xml-fragment/#defn-well-balanced). + +Unbalanced XML contents are invalid XML and will result in a broken build. +BOM and CR marks may not result in broken builds, but *will* cause several +tools below to misbehave, as `libxml` behaviour changes if XML text contains +these bytes. -## qaxml-attributes.php (structural) +## qaxml-attributes.php `doc-base/scripts/translation/qaxml-attributes.php` checks if all translated files have the same tag-attribute-value triplets. Tag's attributes are @@ -35,7 +39,7 @@ This script accepts an `--urgent` option, to filter alerts related to `xml:id` attributes. This will help translators on languages that are failing to build, to focus on mismatches that are probably most related with build fails. -## qaxml-entities.php (structural) +## qaxml-entities.php `doc-base/scripts/translation/qaxml-entities.php` checks if all translated files contain the same XML Entities References as the original files. @@ -55,15 +59,99 @@ entities when generating alerts. This is handy in languages that use some `&zb;` and `&dh;` entities, and could run with `-zb -dh` to avoid generating alerts for these entities' differences. -## Old tools (below) +## qaxml-pi.php + +`doc-base/scripts/translation/qaxml-pi.php` checks if all translated files have +the same processing instructions (PI) as the original files. Unbalanced PIs may +cause compilation errors, as they are utilized in the manual build process. + +## qaxml-tags.php + +`doc-base/scripts/translation/qaxml-tags.php` checks if all translated files +have the same tags as the original files. Different number of tags between +source texts and translations indicated mismatched translated texts, and may +cause compilation errors + +This script accepts an `--detail` option, that will print lines of each +mismatched tag, to facilitate the work on big files. + +This script also accepts an `--content=` option, that will check the +*contents* of tags, to inspect tags where the contents are expected *not* to +be translated. Example below. + +## qaxml-ws.php + +`doc-base/scripts/translation/qaxml-ws.php` inspect whitespace usage inside +some known tags. Spurious whitespace may break manual linking or generate +visible artifacts. + +## qaxml-revtag.php + +`doc-base/scripts/translation/qaxml-revtag.php` checks if all translated +files have valid [revision tags](https://doc.php.net/guide/translating.md). +Files without revision tags in expected format will fail to generate pretty +diffs on [Translation status](https://doc.php.net/revcheck.php) website or +locally generated `revcheck.php` status pages. + +## Suggested execution + +The first execution of these scripts may generate an inordinate amount of +alerts. It's advised to initially run each command separately, and work the +alerts on a case by case basis. After all interesting cases are fixed, +it's possible to rerun the command and `grep` the output for `--add-ignore` +lines, run these commands, and by so, mass ignore the residual alerts. + +Structural checks: + +``` +php doc-base/scripts/broken.php +php doc-base/scripts/translation/qaxml-revtag.php + +php doc-base/scripts/translation/qaxml-attributes.php +php doc-base/scripts/translation/qaxml-entities.php +php doc-base/scripts/translation/qaxml-pi.php +php doc-base/scripts/translation/qaxml-tags.php --detail +php doc-base/scripts/translation/qaxml-ws.php +``` -The tools on `doc-base/scripts/translation/` are slowly being rewritten. While -this effort is not complete, the previous tools, document below, could be used -to supply for features yet not completed. +Tags where is expected no translations: + +``` +php doc-base/scripts/translation/qaxml-tags.php --content=acronym +php doc-base/scripts/translation/qaxml-tags.php --content=classname +php doc-base/scripts/translation/qaxml-tags.php --content=constant +php doc-base/scripts/translation/qaxml-tags.php --content=envar +php doc-base/scripts/translation/qaxml-tags.php --content=function +php doc-base/scripts/translation/qaxml-tags.php --content=interfacename +php doc-base/scripts/translation/qaxml-tags.php --content=parameter +php doc-base/scripts/translation/qaxml-tags.php --content=type +php doc-base/scripts/translation/qaxml-tags.php --content=classsynopsis +php doc-base/scripts/translation/qaxml-tags.php --content=constructorsynopsis +php doc-base/scripts/translation/qaxml-tags.php --content=destructorsynopsis +php doc-base/scripts/translation/qaxml-tags.php --content=fieldsynopsis +php doc-base/scripts/translation/qaxml-tags.php --content=funcsynopsis +php doc-base/scripts/translation/qaxml-tags.php --content=methodsynopsis +``` + +Tags where is expected few translations: + +``` +php doc-base/scripts/translation/qaxml-tags.php --content=code +php doc-base/scripts/translation/qaxml-tags.php --content=computeroutput +php doc-base/scripts/translation/qaxml-tags.php --content=filename +php doc-base/scripts/translation/qaxml-tags.php --content=literal +php doc-base/scripts/translation/qaxml-tags.php --content=varname +``` --- -Before using the old scripts, they need be configured: +## Old tools (below) + +Document below is the previous version of these tools. These tools are +deprecated, and scheduled for remotion very soon. + + +These old tools needed to be separated configured, before use: ``` php doc-base/scripts/translation/configure.php $LANG_DIR ``` @@ -107,44 +195,3 @@ contents, as some tag contents are expected *not* be translated. `--detail` will also print line definitions of each mismatched tag, to facilitate bitsecting. - -## Suggested execution - -Structural checks: - -``` -php doc-base/scripts/translation/configure.php $LANG_DIR - -php doc-base/scripts/translation/qarvt.php - -php doc-base/scripts/translation/qaxml.a.php -php doc-base/scripts/translation/qaxml.e.php -php doc-base/scripts/translation/qaxml.p.php -php doc-base/scripts/translation/qaxml.t.php -php doc-base/scripts/translation/qaxml.w.php -``` -Tags where is expected no translations: -``` -php doc-base/scripts/translation/qaxml.t.php acronym -php doc-base/scripts/translation/qaxml.t.php classname -php doc-base/scripts/translation/qaxml.t.php constant -php doc-base/scripts/translation/qaxml.t.php envar -php doc-base/scripts/translation/qaxml.t.php function -php doc-base/scripts/translation/qaxml.t.php interfacename -php doc-base/scripts/translation/qaxml.t.php parameter -php doc-base/scripts/translation/qaxml.t.php type -php doc-base/scripts/translation/qaxml.t.php classsynopsis -php doc-base/scripts/translation/qaxml.t.php constructorsynopsis -php doc-base/scripts/translation/qaxml.t.php destructorsynopsis -php doc-base/scripts/translation/qaxml.t.php fieldsynopsis -php doc-base/scripts/translation/qaxml.t.php funcsynopsis -php doc-base/scripts/translation/qaxml.t.php methodsynopsis -``` -Tags where is expected few translations: -``` -php doc-base/scripts/translation/qaxml.t.php code -php doc-base/scripts/translation/qaxml.t.php computeroutput -php doc-base/scripts/translation/qaxml.t.php filename -php doc-base/scripts/translation/qaxml.t.php literal -php doc-base/scripts/translation/qaxml.t.php varname -``` diff --git a/scripts/translation/libqa/ArgvParser.php b/scripts/translation/libqa/ArgvParser.php index a90570c8c..00c4ed9ce 100644 --- a/scripts/translation/libqa/ArgvParser.php +++ b/scripts/translation/libqa/ArgvParser.php @@ -26,7 +26,6 @@ class ArgvParser public function __construct( array $argv ) { $this->argv = array_values( array_filter( $argv ) ); - $this->used = []; $this->used = array_fill( 0 , count( $argv ) , false ); } @@ -58,6 +57,9 @@ public function consume( string $equals = null , string $prefix = null , int $po $this->argv[ $pos ] = null; $this->used[ $pos ] = true; + if ( $foundByPrefix ) + return substr( $arg , strlen( $prefix ) ); + return $arg; } } diff --git a/scripts/translation/libqa/OutputBuffer.php b/scripts/translation/libqa/OutputBuffer.php index c91b8c129..faa19f0f1 100644 --- a/scripts/translation/libqa/OutputBuffer.php +++ b/scripts/translation/libqa/OutputBuffer.php @@ -28,6 +28,8 @@ class OutputBuffer private OutputIgnore $ignore; private string $options; + public int $printCount = 0; + public function __construct( string $header , string $filename , OutputIgnore $ignore ) { $filename = str_replace( "/./" , "/" , $filename ); @@ -81,7 +83,7 @@ public function contains( string $text ) : bool return false; } - public function print( bool $useAlternatePrinting = false ) + public function print( bool $alternatePrinting = false ) { if ( count( $this->matter ) == 0 && count( $this->footer ) == 0 ) return; @@ -93,9 +95,11 @@ public function print( bool $useAlternatePrinting = false ) if ( $this->ignore->shouldIgnore( $this , $hashFile , $hashHead , $hashFull ) ) return; + $this->printCount++; + print $this->header; - if ( $useAlternatePrinting ) + if ( $alternatePrinting ) $this->printMatterAlternate(); else foreach( $this->matter as $text ) @@ -128,8 +132,8 @@ private function printMatterAlternate() : void for ( $idx = 0 ; $idx < count( $this->matter ) ; $idx++ ) { - if ( isset( $add[ $idx ] ) ) print $add[ $idx ]; if ( isset( $del[ $idx ] ) ) print $del[ $idx ]; + if ( isset( $add[ $idx ] ) ) print $add[ $idx ]; } foreach( $rst as $text ) diff --git a/scripts/translation/libqa/OutputIgnore.php b/scripts/translation/libqa/OutputIgnore.php index 99a2e2e89..fc1a9b031 100644 --- a/scripts/translation/libqa/OutputIgnore.php +++ b/scripts/translation/libqa/OutputIgnore.php @@ -20,11 +20,10 @@ class OutputIgnore { - private bool $appendIgnores = true; - private bool $showIgnore = true; private string $filename = ".qaxml.ignores"; private string $argv0 = ""; + public bool $appendIgnoreCommands = true; public ArgvParser $argv; public function __construct( ArgvParser $argv ) @@ -32,11 +31,10 @@ public function __construct( ArgvParser $argv ) $this->argv = $argv; $this->argv0 = escapeshellarg( $argv->consume( position: 0 ) ); - $arg = $argv->consume( prefix: "--add-ignore=" ); + $item = $argv->consume( prefix: "--add-ignore=" ); - if ( $arg != null ) + if ( $item != null ) { - $item = substr( $arg , 13 ); $list = $this->loadIgnores(); if ( ! in_array( $item , $list ) ) { @@ -46,10 +44,9 @@ public function __construct( ArgvParser $argv ) exit; } - $arg = $argv->consume( prefix: "--del-ignore=" ); - if ( $arg != null ) + $item = $argv->consume( prefix: "--del-ignore=" ); + if ( $item != null ) { - $item = substr( $arg , 13 ); $list = $this->loadIgnores(); $dels = 0; while ( in_array( $item , $list ) ) @@ -66,7 +63,7 @@ public function __construct( ArgvParser $argv ) } if ( $argv->consume( "--disable-ignore" ) != null ) - $this->showIgnore = false; + $this->appendIgnoreCommands = false; } private function loadIgnores() @@ -96,12 +93,12 @@ public function shouldIgnore( OutputBuffer $output , string $hashFile , string $ if ( in_array( $active , $marks ) ) $ret = true; else - if ( $this->showIgnore ) + if ( $this->appendIgnoreCommands ) $output->addFooter( " php {$this->argv0} --add-ignore=$active\n" ); // --del-ignore command - if ( $this->showIgnore ) + if ( $this->appendIgnoreCommands ) foreach ( $marks as $mark ) if ( str_starts_with( $mark , $prefix ) ) if ( $mark != $active ) diff --git a/scripts/translation/libqa/XmlFrag.php b/scripts/translation/libqa/XmlFrag.php index 42d2fbc6c..bf3f795e3 100644 --- a/scripts/translation/libqa/XmlFrag.php +++ b/scripts/translation/libqa/XmlFrag.php @@ -37,13 +37,13 @@ static function listNodesRecurse( DOMNode $node , int $type, array & $ret ) XmlFrag::listNodesRecurse( $child , $type, $ret ); } - static function loadXmlFragmentFile( string $filename ) + static function loadXmlFragmentFile( string $filename , bool $fakeDtdForMissingEntity = true ) { $contents = file_get_contents( $filename ); [ $doc , $ent , $err ] = XmlFrag::loadXmlFragmentText( $contents , "" ); - if ( count( $err ) == 0 ) + if ( count( $err ) == 0 || $fakeDtdForMissingEntity == false ) return [ $doc , $ent , $err ]; $dtd = "\n | ++----------------------------------------------------------------------+ + +# Description + +Compare processing instructions usage between two XML files. */ + +require_once __DIR__ . '/libqa/all.php'; + +$argv = new ArgvParser( $argv ); +$ignore = new OutputIgnore( $argv ); // may exit. +$argv->complete(); + +$list = SyncFileList::load(); + +foreach ( $list as $file ) +{ + $source = $file->sourceDir . '/' . $file->file; + $target = $file->targetDir . '/' . $file->file; + $output = new OutputBuffer( "# qaxml.p" , $target , $ignore ); + + [ $s , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $source ); + [ $t , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $target ); + + $s = XmlFrag::listNodes( $s , XML_PI_NODE ); + $t = XmlFrag::listNodes( $t , XML_PI_NODE ); + + $s = extractPiData( $s ); + $t = extractPiData( $t ); + + if ( implode( "\n" , $s ) == implode( "\n" , $t ) ) + continue; + + $sideCount = array(); + + foreach( $s as $v ) + $sideCount[$v] = [ 0 , 0 ]; + foreach( $t as $v ) + $sideCount[$v] = [ 0 , 0 ]; + + foreach( $s as $v ) + $sideCount[$v][0] += 1; + foreach( $t as $v ) + $sideCount[$v][1] += 1; + + foreach( $sideCount as $k => $v ) + if ( $v[0] != $v[1] ) + $output->addDiff( $k , $v[0] , $v[1] ); + + $output->print(); +} + +function extractPiData( array $list ) +{ + $ret = array(); + foreach( $list as $elem ) + $ret[] = "{$elem->target} {$elem->data}"; + return $ret; +} diff --git a/scripts/translation/qaxml-revtag.php b/scripts/translation/qaxml-revtag.php new file mode 100644 index 000000000..850b8cbd7 --- /dev/null +++ b/scripts/translation/qaxml-revtag.php @@ -0,0 +1,42 @@ + | ++----------------------------------------------------------------------+ + +# Description + +Inspect revision tag usage inside XML files. */ + +require_once __DIR__ . '/libqa/all.php'; +require_once __DIR__ . '/lib/RevtagParser.php'; + +$argv = new ArgvParser( $argv ); +$ignore = new OutputIgnore( $argv ); // may exit. +$ignore->appendIgnoreCommands = false; +$argv->complete(); + +$list = SyncFileList::load(); + +foreach ( $list as $file ) +{ + $target = $file->targetDir . '/' . $file->file; + $revtag = RevtagParser::parseFile( $target ); + + if ( count( $revtag->errors ) == 0 ) + continue; + + print "# qaxml.r: $target\n"; + foreach( $revtag->errors as $error ) + print " $error\n"; + print "\n"; +} diff --git a/scripts/translation/qaxml-tags.php b/scripts/translation/qaxml-tags.php new file mode 100644 index 000000000..7b0227de3 --- /dev/null +++ b/scripts/translation/qaxml-tags.php @@ -0,0 +1,313 @@ + | ++----------------------------------------------------------------------+ + +# Description + +Compare tags count/contents between two XML leaf/fragment files. */ + +require_once __DIR__ . '/libqa/all.php'; + +$argv = new ArgvParser( $argv ); +$ignore = new OutputIgnore( $argv ); // may exit. +$detail = $argv->consume( "--detail" ) != null; +$tags = explode( ',' , $argv->consume( prefix: "--content=" ) ?? "" ); + +$argv->complete(); + +if ( count( $tags ) == 1 && $tags[0] == "" ) + $tags = []; + +if ( $detail ) + $ignore->appendIgnoreCommands = false; + +$list = SyncFileList::load(); + +foreach ( $list as $file ) +{ + $source = $file->sourceDir . '/' . $file->file; + $target = $file->targetDir . '/' . $file->file; + $output = new OutputBuffer( "# qaxml.t" , $target , $ignore ); + + if ( count( $tags ) > 0 ) + { + // "Simple" tag contents check, by inner text + + [ $s , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $source ); + [ $t , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $target ); + + $s = XmlFrag::listNodes( $s , XML_ELEMENT_NODE ); + $t = XmlFrag::listNodes( $t , XML_ELEMENT_NODE ); + + typesNotCaseSensitive( $s ); + typesNotCaseSensitive( $t ); + + $s = extractTagsInnerText( $s , $tags ); + $t = extractTagsInnerText( $t , $tags ); + + if ( implode( "\n" , $s ) == implode( "\n" , $t ) ) + continue; + + $sideCount = array(); + + foreach( $s as $v ) + $sideCount[$v] = [ 0 , 0 ]; + foreach( $t as $v ) + $sideCount[$v] = [ 0 , 0 ]; + + foreach( $s as $v ) + $sideCount[$v][0] += 1; + foreach( $t as $v ) + $sideCount[$v][1] += 1; + + foreach( $sideCount as $k => $v ) + if ( $v[0] != $v[1] ) + $output->addDiff( $k , $v[0] , $v[1] ); + + if ( $detail ) + foreach( $sideCount as $tag => $v ) + printTagUsageDetail( $source , $target , $tag , $output ); + + $output->print( true ); + + if ( $output->printCount ) + continue; + + // "Complex" tag contents check, by inner XML + + [ $s , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $source ); + [ $t , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $target ); + + $s = XmlFrag::listNodes( $s , XML_ELEMENT_NODE ); + $t = XmlFrag::listNodes( $t , XML_ELEMENT_NODE ); + + typesNotCaseSensitive( $s ); + typesNotCaseSensitive( $t ); + + $s = extractTagsInnerXmls( $s , $tags ); + $t = extractTagsInnerXmls( $t , $tags ); + + if ( implode( "\n" , $s ) == implode( "\n" , $t ) ) + continue; + + $sideCount = array(); + + foreach( $s as $v ) + $sideCount[$v] = [ 0 , 0 ]; + foreach( $t as $v ) + $sideCount[$v] = [ 0 , 0 ]; + + foreach( $s as $v ) + $sideCount[$v][0] += 1; + foreach( $t as $v ) + $sideCount[$v][1] += 1; + + foreach( $sideCount as $k => $v ) + if ( $v[0] != $v[1] ) + $output->addDiff( $k , $v[0] , $v[1] ); + + if ( $detail ) + foreach( $sideCount as $tag => $v ) + printTagUsageDetail( $source , $target , $tag , $output ); + + $output->print( true ); + } + else + { + // Check tag count, not contents + + [ $s , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $source ); + [ $t , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $target ); + + $s = XmlFrag::listNodes( $s , XML_ELEMENT_NODE ); + $t = XmlFrag::listNodes( $t , XML_ELEMENT_NODE ); + + typesNotCaseSensitive( $s ); + typesNotCaseSensitive( $t ); + + $s = extractNodeName( $s , $tags ); + $t = extractNodeName( $t , $tags ); + + if ( implode( "\n" , $s ) == implode( "\n" , $t ) ) + continue; + + $sideCount = array(); + + foreach( $s as $v ) + $sideCount[$v] = [ 0 , 0 ]; + foreach( $t as $v ) + $sideCount[$v] = [ 0 , 0 ]; + + foreach( $s as $v ) + $sideCount[$v][0] += 1; + foreach( $t as $v ) + $sideCount[$v][1] += 1; + + foreach( $sideCount as $k => $v ) + if ( $v[0] != $v[1] ) + $output->addDiff( $k , $v[0] , $v[1] ); + + if ( $detail ) + foreach( $sideCount as $tag => $v ) + printTagUsageDetail( $source , $target , $tag , $output ); + + $output->print( true ); + } +} + +function extractNodeName( array $list , array $tags ) +{ + $ret = array(); + foreach( $list as $elem ) + if ( count( $tags ) == 0 || in_array( $elem->nodeName , $tags ) ) + $ret[] = $elem->nodeName; + return $ret; +} + +function typesNotCaseSensitive( array & $nodes ) +{ + // Types not case-sensitive: https://github.com/php/doc-en/issues/2658 + + if ( $nodes == null ) + return; + + foreach( $nodes as $node ) + { + if ( $node->nodeName == "type" ) + { + $text = trim( strtolower( $node->nodeValue ) ); + switch( $text ) + { + case "array": + case "string": + case "float": + case "bool": + case "null": + $node->nodeValue = $text; + break; + } + } + } +} + +function extractTagsInnerText( array $nodes , array $tags ) +{ + $ret = array(); + foreach( $nodes as $node ) + { + $tag = $node->nodeName; + if ( in_array( $tag , $tags ) == false ) + continue; + $text = $node->textContent; + while( true ) + { + $was = strlen( $text ); + $text = str_replace( "\n" , " " , $text ); + $text = str_replace( "\r" , " " , $text ); + $text = str_replace( " " , " " , $text ); + if ( strlen( $text ) == $was ) + break; + } + $ret[] = $tag . ">" . $text; + } + return $ret; +} + +function extractTagsInnerXmls( array $nodes , array $tags ) +{ + $ret = array(); + foreach( $nodes as $node ) + { + $tag = $node->nodeName; + if ( in_array( $tag , $tags ) == false ) + continue; + $text = $node->ownerDocument->saveXML( $node ); + while( true ) + { + $was = strlen( $text ); + $text = str_replace( "\n" , " " , $text ); + $text = str_replace( "\r" , " " , $text ); + $text = str_replace( " " , " " , $text ); + if ( strlen( $text ) == $was ) + break; + } + $ret[] = $text; + } + return $ret; +} + +function printTagUsageDetail( string $source , string $target , string $tag , OutputBuffer $output ) +{ + $source = collectTagLines( $source , $tag ); + $target = collectTagLines( $target , $tag ); + if ( count( $source ) == count($target) ) + return; + $output->addLine(); + $s = null; + $t = null; + while ( count( $source ) > 0 || count( $target ) > 0 ) + { + if ( $s == null ) + $s = array_shift( $source ); + if ( $t == null ) + $t = array_shift( $target ); + if ( $s != null && $t != null ) + { + if ( abs( $s - $t ) < 5 ) + { + $output->add( "\t{$tag}\t{$s}\t{$t}\n" ); + $s = null; + $t = null; + continue; + } + if ( $s < $t ) + { + array_unshift( $target , $t ); + $t = null; + } + else + { + array_unshift( $source , $s ); + $s = null; + } + } + if ( $s != null ) + { + $output->add( "\t{$tag}\t{$s}\t-\n" ); + $s = null; + } + if ( $t != null ) + { + $output->add( "\t{$tag}\t-\t{$t}\n" ); + $t = null; + } + } + $output->addLine(); +} + +function collectTagLines( string $file , string $tag ) +{ + $ret = array(); + + [ $s , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $file , false ); + $list = XmlFrag::listNodes( $s , XML_ELEMENT_NODE ); + + foreach( $list as $node ) + { + if ( $node->nodeName != $tag ) + continue; + $ret[] = $node->getLineNo(); + } + return $ret; +} diff --git a/scripts/translation/qaxml-ws.php b/scripts/translation/qaxml-ws.php new file mode 100644 index 000000000..2a8db8238 --- /dev/null +++ b/scripts/translation/qaxml-ws.php @@ -0,0 +1,69 @@ + | ++----------------------------------------------------------------------+ + +# Description + +Inspect white space usage inside some known tags. Spurious whitespace +may break manual linking or generate visible artifacts. */ + +require_once __DIR__ . '/libqa/all.php'; + +$argv = new ArgvParser( $argv ); +$ignore = new OutputIgnore( $argv ); // may exit. +$argv->complete(); + +$list = SyncFileList::load(); + +foreach ( $list as $file ) +{ + $source = $file->sourceDir . '/' . $file->file; + $target = $file->targetDir . '/' . $file->file; + + whitespaceCheckFile( $source , $ignore ); + whitespaceCheckFile( $target , $ignore ); +} + +function whitespaceCheckFile( string $filename , OutputIgnore $ignore ) +{ + if ( file_exists( $filename ) == false ) + return; + + $output = new OutputBuffer( "# qaxml.w" , $filename , $ignore ); + + [ $xml , $_ , $_ ] = XmlFrag::loadXmlFragmentFile( $filename ); + $nodes = XmlFrag::listNodes( $xml , XML_ELEMENT_NODE ); + + foreach( $nodes as $node ) + { + switch ( $node->nodeName ) + { + case "classname": + case "constant": + case "function": + case "methodname": + case "varname": + $text = $node->nodeValue; + $trim = trim( $text ); + if ( $text != $trim ) + { + $output->addLine(); + $output->add( " {$node->nodeName} {$trim}\n" ); + } + break; + } + } + + $output->print(); +}