From 9b5fc292cda45fc6e7bde8d18afbbde18810e816 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Tue, 4 Aug 2026 19:45:34 +0200 Subject: [PATCH 1/4] add standalone manual tree support with --standalone flag --- configure.php | 73 +++++++++++++++++++++++++-------------- scripts/file-entities.php | 17 ++++++--- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/configure.php b/configure.php index 68f2d4cbd1..ba848eac7a 100755 --- a/configure.php +++ b/configure.php @@ -84,6 +84,9 @@ function usage() // {{{ --disable-libxml-check Disable the libxml 2.7.4+ requirement check --with-php=PATH Path to php CLI executable [detect] --with-lang=LANG Language to build [{$acd['LANG']}] + --standalone Treat the language directory as a complete, + authoritative manual tree instead of a + translation overlay of en [{$acd['STANDALONE']}] --with-partial=my-xml-id Root ID to build (e.g. ) [{$acd['PARTIAL']}] --with-jing=auto|yes|no Validate with Jing (requires Java) instead of libxml [{$acd['JING']}] @@ -211,7 +214,7 @@ function print_dom_errors() function print_xml_errors() { global $ac; - $report = $ac['LANG'] == 'en' || $ac['XPOINTER_REPORTING'] == 'yes'; + $report = lang_is_base() || $ac['XPOINTER_REPORTING'] == 'yes'; $output = ( $ac['STDERR_TO_STDOUT'] == 'yes' ) ? STDOUT : STDERR ; $errors = libxml_get_errors(); @@ -297,6 +300,8 @@ function find_xml_files($path) // {{{ 'CHMONLY_INCL_END' => '-->', 'LANG' => 'en', 'LANGDIR' => "{$rootdir}/en", + 'EN_DIR' => 'en', + 'STANDALONE' => 'no', 'ENCODING' => 'utf-8', 'PARTIAL' => 'no', 'DETAILED_ERRORMSG' => 'no', @@ -382,6 +387,10 @@ function find_xml_files($path) // {{{ $ac['LANG'] = $v; break; + case 'standalone': + $ac['STANDALONE'] = $v; + break; + case 'partial': if ($v == "yes") { if (isset($_SERVER['argv'][$k+1])) { @@ -510,13 +519,13 @@ function find_xml_files($path) // {{{ } else if ($ac['LANG'] == 'yes') { $ac['LANG'] = 'en'; } -if ($ac["LANG"] == "en") { +if ($ac["LANG"] == "en" || $ac['STANDALONE'] == 'yes') { $ac["TRANSLATION_ONLY_INCL_BEGIN"] = ""; } + checkvalue($ac['LANG']); file_put_contents( __DIR__ . "/temp/lang" , $ac['LANG'] ); - checking("whether the language is supported"); $LANGDIR = "{$ac['rootdir']}/{$ac['LANG']}"; if (!file_exists($LANGDIR) || !is_readable($LANGDIR)) { @@ -524,9 +533,18 @@ function find_xml_files($path) // {{{ } $ac['LANGDIR'] = basename($LANGDIR); -$ac['EN_DIR'] = 'en'; +if ($ac['STANDALONE'] == 'yes') { + // The language directory is itself the base tree; no en overlay. + $ac['EN_DIR'] = $ac['LANGDIR']; +} checkvalue("yes"); +function lang_is_base() : bool +{ + global $ac; + return $ac['LANG'] === $ac['EN_DIR']; +} + checking("for partial build"); checkvalue($ac['PARTIAL']); @@ -560,9 +578,9 @@ function git_status() return; $repos = array(); - $repos['doc-base'] = $ac['basedir']; - $repos['en'] = "{$ac['rootdir']}/{$ac['EN_DIR']}"; - $repos[$ac['LANG']] = "{$ac['rootdir']}/{$ac['LANG']}"; + $repos['doc-base'] = $ac['basedir']; + $repos[$ac['EN_DIR']] = "{$ac['rootdir']}/{$ac['EN_DIR']}"; + $repos[$ac['LANG']] = "{$ac['rootdir']}/{$ac['LANG']}"; $output = ""; foreach ( $repos as $name => $path ) @@ -609,11 +627,13 @@ function dtd_pe_body( string $filename = '' ) $baseEnt2 = dtd_pe_body( __DIR__ . '/temp/file-entities.ent' ); $baseEnt3 = dtd_pe_body( __DIR__ . '/temp/entities.ent' ); - $langOne1 = dtd_pe_body( __DIR__ . '/../en/language-defs.ent' ); - $langOne2 = dtd_pe_body( __DIR__ . '/../en/language-snippets.ent' ); - $langOne3 = dtd_pe_body( __DIR__ . '/../en/extensions.ent' ); + $base = $ac['EN_DIR']; + + $langOne1 = dtd_pe_body( __DIR__ . "/../$base/language-defs.ent" ); + $langOne2 = dtd_pe_body( __DIR__ . "/../$base/language-snippets.ent" ); + $langOne3 = dtd_pe_body( __DIR__ . "/../$base/extensions.ent" ); - if ( $lang == 'en ' ) + if ( lang_is_base() ) { $langTwo1 = dtd_pe_body(); $langTwo2 = dtd_pe_body(); @@ -647,7 +667,7 @@ function dtd_pe_body( string $filename = '' ) $conf[] = ""; $conf[] = ""; - $outdir = __DIR__ . '/../en/temp'; + $outdir = __DIR__ . "/../$base/temp"; realpain( $outdir , mkdir: true ); file_put_contents( "{$outdir}/conf.dtd" , implode( "\n" , $conf ) ); } @@ -662,7 +682,8 @@ function dtd_file_entities() $parts = array(); $parts[] = $withphp; $parts[] = __DIR__ . "/scripts/file-entities.php"; - if ( $lang != "en" ) + $parts[] = $ac['EN_DIR']; + if ( $lang != $ac['EN_DIR'] ) $parts[] = $lang; if ( $withchm ) $parts[] = '--chmonly'; @@ -688,8 +709,8 @@ function dtd_text_entities() $parts = [ $php , __DIR__ . "/scripts/text-entities.php" - , "en" ]; - if ( $lang != "en" ) + , $ac['EN_DIR'] ]; + if ( $lang != $ac['EN_DIR'] ) $parts[] = $lang; foreach ( $parts as & $part ) @@ -739,7 +760,7 @@ function dom_saveload( DOMDocument $dom , string $filename = "" ) : string echo "Creating monolithic temp/manual.xml... "; $dom = new DOMDocument(); -if ( dom_load( $dom , __DIR__ . '/../en/manual.xml' , true ) ) +if ( dom_load( $dom , __DIR__ . "/../{$ac['EN_DIR']}/manual.xml" , true ) ) { echo " done.\n"; print_dom_errors(); @@ -795,10 +816,10 @@ function xinclude_no_fallback( DOMDocument $dom ) if ( $xifallbacks->length > 0 ) { - if ( $GLOBALS['ac']['LANG'] == 'en' ) + if ( lang_is_base() ) { print "\n cause silent errors in all languages.\n"; - print "Not allowed on doc-en.\n"; + print "Not allowed on base manual trees.\n"; errors_are_bad( 1 ); } else @@ -1012,9 +1033,9 @@ function xinclude_residual_fixup( DOMDocument $dom ) echo "\n\n"; } - // Duplicated structural xml:ids are fatal on doc-en + // Duplicated structural xml:ids are fatal on base manual trees - $fatal = $GLOBALS['ac']['LANG'] == 'en'; + $fatal = lang_is_base(); if ( $structural && $fatal ) errors_are_bad( 1 ); @@ -1134,7 +1155,7 @@ function xml_validate_jing() if ( preg_match( '/IDREF "[^"]+" without matching ID/', $line ) ) $countFatal--; - if ( $GLOBALS['ac']['LANG'] === 'en' || $countFatal > 0 ) + if ( lang_is_base() || $countFatal > 0 ) errors_are_bad( 1 ); } @@ -1143,7 +1164,7 @@ function xml_validate_libxml( $dom ) echo "Validating temp/manual.xml (libxml)... "; $ok = $dom->relaxNGValidate( RNG_SCHEMA_FILE ); - if ( ! $ok && $GLOBALS['ac']['LANG'] != 'en' ) + if ( ! $ok && ! lang_is_base() ) { $errors = libxml_get_errors(); $warnings = 0; @@ -1240,7 +1261,7 @@ function php_history() echo 'PhD history:'; - $lang_mod_file = (($ac['LANG'] !== 'en') ? ("{$ac['rootdir']}/{$ac['EN_DIR']}") : ("{$ac['rootdir']}/{$ac['LANGDIR']}")) . "/fileModHistory.php"; + $lang_mod_file = "{$ac['rootdir']}/{$ac['EN_DIR']}/fileModHistory.php"; $doc_base_mod_file = __DIR__ . "/fileModHistory.php"; $history_file = null; @@ -1278,9 +1299,9 @@ function phd_sources() $en_dir = "{$ac['rootdir']}/{$ac['EN_DIR']}"; $source_langs = array( array('base', $ac['srcdir'], array('manual.xml', 'funcindex.xml')), - array('en', $en_dir, find_xml_files($en_dir)), + array($ac['EN_DIR'], $en_dir, find_xml_files($en_dir)), ); - if ($ac['LANG'] !== 'en') { + if (!lang_is_base()) { $lang_dir = "{$ac['rootdir']}/{$ac['LANGDIR']}"; $source_langs[] = array($ac['LANG'], $lang_dir, find_xml_files($lang_dir)); } @@ -1339,7 +1360,7 @@ function phd_version() $globdir = dirname($ac["GENERATE"]) . "/{../../}versions.xml"; } else { - $globdir = $ac['rootdir'] . '/en'; + $globdir = "{$ac['rootdir']}/{$ac['EN_DIR']}"; $globdir .= "/*/*/versions.xml"; } echo ' transforming,'; diff --git a/scripts/file-entities.php b/scripts/file-entities.php index 62f96f454d..b6368c23b8 100644 --- a/scripts/file-entities.php +++ b/scripts/file-entities.php @@ -62,18 +62,27 @@ // Usage -$lang = ""; -$langs = [ "en" ]; +$langs = []; $langBase = realpain( __DIR__ . "/../.." ); $chmonly = false; array_shift( $argv ); foreach( $argv as $arg ) { - $lang = rtrim( $arg , "\\/" ); - $langs[] = $lang; + if ( $arg == '--chmonly' ) + { + $chmonly = true; + continue; + } + $langs[] = rtrim( $arg , "\\/" ); } +// Languages are passed base first, translation second. +// Without arguments, default to the base manual tree. + +if ( count( $langs ) == 0 ) + $langs = [ "en" ]; + // Generation print "Running file-entities.php... "; From 79ce0ed8511eee6cdc9a8e039945c5a2464ec893 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Tue, 4 Aug 2026 20:04:17 +0200 Subject: [PATCH 2/4] Don't define LANG in standalone mode --- configure.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/configure.php b/configure.php index ba848eac7a..407450a679 100755 --- a/configure.php +++ b/configure.php @@ -652,7 +652,13 @@ function dtd_pe_body( string $filename = '' ) $chmpath = dtd_pe_body(); $conf = []; - $conf[] = ""; + + // Standalone trees are not named after a language, so they must + // declare xml:lang themselves. Not defining ⟨ makes any + // accidental use of it fail. + if ( $ac['STANDALONE'] != 'yes' ) + $conf[] = ""; + $conf[] = ""; $conf[] = ""; From 40b39f3e1af8edea3798034cb4aac85edb1c096c Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Tue, 4 Aug 2026 20:11:11 +0200 Subject: [PATCH 3/4] Remove unused --chmonly command line option --- scripts/file-entities.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/file-entities.php b/scripts/file-entities.php index b6368c23b8..4e4211cf06 100644 --- a/scripts/file-entities.php +++ b/scripts/file-entities.php @@ -64,16 +64,10 @@ $langs = []; $langBase = realpain( __DIR__ . "/../.." ); -$chmonly = false; array_shift( $argv ); foreach( $argv as $arg ) { - if ( $arg == '--chmonly' ) - { - $chmonly = true; - continue; - } $langs[] = rtrim( $arg , "\\/" ); } From 624b94b14a9e3f3f759bdc41b9ef477d7cb0f7e4 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Tue, 4 Aug 2026 21:37:17 +0200 Subject: [PATCH 4/4] Replace standalone mode with base language configuration --- configure.php | 82 ++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/configure.php b/configure.php index 407450a679..e244f9b14d 100755 --- a/configure.php +++ b/configure.php @@ -84,9 +84,8 @@ function usage() // {{{ --disable-libxml-check Disable the libxml 2.7.4+ requirement check --with-php=PATH Path to php CLI executable [detect] --with-lang=LANG Language to build [{$acd['LANG']}] - --standalone Treat the language directory as a complete, - authoritative manual tree instead of a - translation overlay of en [{$acd['STANDALONE']}] + --with-base-lang=LANG Language directory that is the base + (authoritative) manual tree [{$acd['LANG_BASE_DIR']}] --with-partial=my-xml-id Root ID to build (e.g. ) [{$acd['PARTIAL']}] --with-jing=auto|yes|no Validate with Jing (requires Java) instead of libxml [{$acd['JING']}] @@ -214,7 +213,7 @@ function print_dom_errors() function print_xml_errors() { global $ac; - $report = lang_is_base() || $ac['XPOINTER_REPORTING'] == 'yes'; + $report = is_single_language() || $ac['XPOINTER_REPORTING'] == 'yes'; $output = ( $ac['STDERR_TO_STDOUT'] == 'yes' ) ? STDOUT : STDERR ; $errors = libxml_get_errors(); @@ -299,9 +298,9 @@ function find_xml_files($path) // {{{ 'CHMONLY_INCL_BEGIN' => '', 'LANG' => 'en', + 'LANGS' => ['en'], 'LANGDIR' => "{$rootdir}/en", - 'EN_DIR' => 'en', - 'STANDALONE' => 'no', + 'LANG_BASE_DIR' => 'en', 'ENCODING' => 'utf-8', 'PARTIAL' => 'no', 'DETAILED_ERRORMSG' => 'no', @@ -387,8 +386,8 @@ function find_xml_files($path) // {{{ $ac['LANG'] = $v; break; - case 'standalone': - $ac['STANDALONE'] = $v; + case 'base-lang': + $ac['LANG_BASE_DIR'] = $v; break; case 'partial': @@ -519,7 +518,18 @@ function find_xml_files($path) // {{{ } else if ($ac['LANG'] == 'yes') { $ac['LANG'] = 'en'; } -if ($ac["LANG"] == "en" || $ac['STANDALONE'] == 'yes') { +// Language dirs in consideration, in overwriting order (last wins): +// the base manual tree first, an optional translation overlay second. + +if (!in_array('LANG', $overridden_settings)) { + $ac['LANG'] = $ac['LANG_BASE_DIR']; +} +$ac['LANGS'] = [$ac['LANG_BASE_DIR']]; +if ($ac['LANG'] !== $ac['LANG_BASE_DIR']) { + $ac['LANGS'][] = $ac['LANG']; +} + +if (is_single_language()) { $ac["TRANSLATION_ONLY_INCL_BEGIN"] = ""; } @@ -533,16 +543,12 @@ function find_xml_files($path) // {{{ } $ac['LANGDIR'] = basename($LANGDIR); -if ($ac['STANDALONE'] == 'yes') { - // The language directory is itself the base tree; no en overlay. - $ac['EN_DIR'] = $ac['LANGDIR']; -} checkvalue("yes"); -function lang_is_base() : bool +function is_single_language() : bool { global $ac; - return $ac['LANG'] === $ac['EN_DIR']; + return count($ac['LANGS']) === 1; } checking("for partial build"); @@ -578,9 +584,9 @@ function git_status() return; $repos = array(); - $repos['doc-base'] = $ac['basedir']; - $repos[$ac['EN_DIR']] = "{$ac['rootdir']}/{$ac['EN_DIR']}"; - $repos[$ac['LANG']] = "{$ac['rootdir']}/{$ac['LANG']}"; + $repos['doc-base'] = $ac['basedir']; + $repos[$ac['LANG_BASE_DIR']] = "{$ac['rootdir']}/{$ac['LANG_BASE_DIR']}"; + $repos[$ac['LANG']] = "{$ac['rootdir']}/{$ac['LANG']}"; $output = ""; foreach ( $repos as $name => $path ) @@ -627,13 +633,13 @@ function dtd_pe_body( string $filename = '' ) $baseEnt2 = dtd_pe_body( __DIR__ . '/temp/file-entities.ent' ); $baseEnt3 = dtd_pe_body( __DIR__ . '/temp/entities.ent' ); - $base = $ac['EN_DIR']; + $base = $ac['LANG_BASE_DIR']; $langOne1 = dtd_pe_body( __DIR__ . "/../$base/language-defs.ent" ); $langOne2 = dtd_pe_body( __DIR__ . "/../$base/language-snippets.ent" ); $langOne3 = dtd_pe_body( __DIR__ . "/../$base/extensions.ent" ); - if ( lang_is_base() ) + if ( is_single_language() ) { $langTwo1 = dtd_pe_body(); $langTwo2 = dtd_pe_body(); @@ -653,11 +659,7 @@ function dtd_pe_body( string $filename = '' ) $conf = []; - // Standalone trees are not named after a language, so they must - // declare xml:lang themselves. Not defining ⟨ makes any - // accidental use of it fail. - if ( $ac['STANDALONE'] != 'yes' ) - $conf[] = ""; + $conf[] = ""; $conf[] = ""; @@ -688,8 +690,8 @@ function dtd_file_entities() $parts = array(); $parts[] = $withphp; $parts[] = __DIR__ . "/scripts/file-entities.php"; - $parts[] = $ac['EN_DIR']; - if ( $lang != $ac['EN_DIR'] ) + $parts[] = $ac['LANG_BASE_DIR']; + if ( $lang != $ac['LANG_BASE_DIR'] ) $parts[] = $lang; if ( $withchm ) $parts[] = '--chmonly'; @@ -715,8 +717,8 @@ function dtd_text_entities() $parts = [ $php , __DIR__ . "/scripts/text-entities.php" - , $ac['EN_DIR'] ]; - if ( $lang != $ac['EN_DIR'] ) + , $ac['LANG_BASE_DIR'] ]; + if ( $lang != $ac['LANG_BASE_DIR'] ) $parts[] = $lang; foreach ( $parts as & $part ) @@ -766,7 +768,7 @@ function dom_saveload( DOMDocument $dom , string $filename = "" ) : string echo "Creating monolithic temp/manual.xml... "; $dom = new DOMDocument(); -if ( dom_load( $dom , __DIR__ . "/../{$ac['EN_DIR']}/manual.xml" , true ) ) +if ( dom_load( $dom , __DIR__ . "/../{$ac['LANG_BASE_DIR']}/manual.xml" , true ) ) { echo " done.\n"; print_dom_errors(); @@ -822,7 +824,7 @@ function xinclude_no_fallback( DOMDocument $dom ) if ( $xifallbacks->length > 0 ) { - if ( lang_is_base() ) + if ( is_single_language() ) { print "\n cause silent errors in all languages.\n"; print "Not allowed on base manual trees.\n"; @@ -1041,7 +1043,7 @@ function xinclude_residual_fixup( DOMDocument $dom ) // Duplicated structural xml:ids are fatal on base manual trees - $fatal = lang_is_base(); + $fatal = is_single_language(); if ( $structural && $fatal ) errors_are_bad( 1 ); @@ -1161,7 +1163,7 @@ function xml_validate_jing() if ( preg_match( '/IDREF "[^"]+" without matching ID/', $line ) ) $countFatal--; - if ( lang_is_base() || $countFatal > 0 ) + if ( is_single_language() || $countFatal > 0 ) errors_are_bad( 1 ); } @@ -1170,7 +1172,7 @@ function xml_validate_libxml( $dom ) echo "Validating temp/manual.xml (libxml)... "; $ok = $dom->relaxNGValidate( RNG_SCHEMA_FILE ); - if ( ! $ok && ! lang_is_base() ) + if ( ! $ok && ! is_single_language() ) { $errors = libxml_get_errors(); $warnings = 0; @@ -1234,7 +1236,7 @@ function phd_conf_json() 'rootdir' => $ac['rootdir'], 'srcdir' => $ac['srcdir'], 'lang' => $ac['LANG'], - 'enDir' => $ac['EN_DIR'], + 'enDir' => $ac['LANG_BASE_DIR'], 'langDir' => $ac['LANGDIR'], 'generate' => $ac['GENERATE'], 'xpointerReporting' => $ac['XPOINTER_REPORTING'] === 'yes', @@ -1267,7 +1269,7 @@ function php_history() echo 'PhD history:'; - $lang_mod_file = "{$ac['rootdir']}/{$ac['EN_DIR']}/fileModHistory.php"; + $lang_mod_file = "{$ac['rootdir']}/{$ac['LANG_BASE_DIR']}/fileModHistory.php"; $doc_base_mod_file = __DIR__ . "/fileModHistory.php"; $history_file = null; @@ -1302,12 +1304,12 @@ function phd_sources() echo ' reading,'; $source_map = array(); - $en_dir = "{$ac['rootdir']}/{$ac['EN_DIR']}"; + $base_dir = "{$ac['rootdir']}/{$ac['LANG_BASE_DIR']}"; $source_langs = array( array('base', $ac['srcdir'], array('manual.xml', 'funcindex.xml')), - array($ac['EN_DIR'], $en_dir, find_xml_files($en_dir)), + array($ac['LANG_BASE_DIR'], $base_dir, find_xml_files($base_dir)), ); - if (!lang_is_base()) { + if (!is_single_language()) { $lang_dir = "{$ac['rootdir']}/{$ac['LANGDIR']}"; $source_langs[] = array($ac['LANG'], $lang_dir, find_xml_files($lang_dir)); } @@ -1366,7 +1368,7 @@ function phd_version() $globdir = dirname($ac["GENERATE"]) . "/{../../}versions.xml"; } else { - $globdir = "{$ac['rootdir']}/{$ac['EN_DIR']}"; + $globdir = "{$ac['rootdir']}/{$ac['LANG_BASE_DIR']}"; $globdir .= "/*/*/versions.xml"; } echo ' transforming,';