]> Lady’s Gitweb - Shushe/commitdiff
Add DATAOPTS current 1.0.1
authorLady <redacted>
Sat, 25 Jan 2025 20:09:34 +0000 (15:09 -0500)
committerLady <redacted>
Sun, 26 Jan 2025 01:40:46 +0000 (20:40 -0500)
This is a bit hacky, but probably is still the simplest and most
elegant solution to the problem of ⛩📰 书社 needing to use different
variables for different stages of a two‐stage build. Note that the core
variables used to manage the two‐stage process (`INCLUDEDIR`,
`BUILDDIR`, `FINDRULES`, `FINDINCLUDERULES`, and `MODE`) can _not_ be
overridden thru this (or any) mechanism.

This commit also adds a new callable variable, `varquote`, for escaping
dollar signs when passing them thru to submakes.

GNUmakefile
README.markdown
lib/archive2extractor.xslt
lib/catalog2transform.xslt
parsers/plain.xslt
parsers/record-jar.xslt
parsers/tsv.xslt
transforms/asset.xslt
transforms/metadata.xslt
transforms/serialization.xslt

index 95fcbf4e40304c0d75d1197b5fb0c1b59118208f..80e1c4c970d713455c9eca229227f2ff36a48a28 100644 (file)
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2023, 2024 Lady <https://www.ladys.computer/about/#lady>
+# SPDX-FileCopyrightText: 2023, 2024, 2025 Lady <https://www.ladys.computer/about/#lady>
 # SPDX-License-Identifier: MPL-2.0
 
 SHELL = /bin/sh
 # SPDX-License-Identifier: MPL-2.0
 
 SHELL = /bin/sh
@@ -188,6 +188,11 @@ EXTRAFINDINCLUDERULES :=
 FINDRULES := '!' '(' '(' -name '[.-]*' -a '!' -name '.' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a '(' $(EXTRAFINDRULES) ')',)
 FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a '(' $(EXTRAFINDINCLUDERULES) ')',)
 
 FINDRULES := '!' '(' '(' -name '[.-]*' -a '!' -name '.' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a '(' $(EXTRAFINDRULES) ')',)
 FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a '(' $(EXTRAFINDINCLUDERULES) ')',)
 
+# Options to use when calling Make for the first build of a two‐stage build.
+#
+# This can be used to override variables which are only applicable to the second build.
+DATAOPTS :=
+
 # File extensions which indicate files in `SRCDIR´ which should be built as part of the first, rather than second, stage of the two‐stage `MODE´.
 DATAEXT := rdf
 
 # File extensions which indicate files in `SRCDIR´ which should be built as part of the first, rather than second, stage of the two‐stage `MODE´.
 DATAEXT := rdf
 
@@ -197,6 +202,46 @@ DATAEXT := rdf
 EXTRAFINDDATARULES :=
 FINDDATARULES := -name '.' $(foreach ext,$(DATAEXT), -o -name '$(subst ','"'"',[!.]*.$(ext))')$(if $(EXTRAFINDDATARULES), -a '(' $(EXTRAFINDDATARULES) ')',)
 
 EXTRAFINDDATARULES :=
 FINDDATARULES := -name '.' $(foreach ext,$(DATAEXT), -o -name '$(subst ','"'"',[!.]*.$(ext))')$(if $(EXTRAFINDDATARULES), -a '(' $(EXTRAFINDDATARULES) ')',)
 
+# A semicolon‐separated list of regular expressions which paths should be required to match when finding files.
+FINDFILTERONLY :=
+
+# A semicolon‐separated list of regular expressions for paths which should be filtered out when finding files.
+FINDFILTEROUT :=
+
+# A semicolon‐separated list of regular expressions for paths which paths should be required to match when finding includes.
+#
+# This is generally only useful when `SRCDIR´ and `INCLUDEDIR´ point to the same location.
+# In that situation, this variable can be used to select certain files as includes, leaving the others to be recognized as sources instead.
+#
+# Otherwise, appropriately constructing `FINDFILTERONLY´ to look at the base directory of the files it finds should be sufficient.
+FINDINCLUDEFILTERONLY :=
+
+# A semicolon‐separated list of regular expressions for paths which should be filtered out in addition to those in `FINDFILTEROUT´ when finding includes.
+#
+# This is generally only useful when `SRCDIR´ and `INCLUDEDIR´ point to the same location.
+# In that situation, this variable can be used to exclude certain files from being recognized as includes, which will make them recognized as sources instead.
+#
+# Otherwise, appropriately constructing `FINDFILTEROUT´ to look at the base directory of the files it finds should be sufficient.
+FINDINCLUDEFILTEROUT :=
+
+# If not empty, the regular expression provided by `FINDFILTERONLY´ is an extended regular expression.
+FINDFILTERONLYEXTENDED :=
+
+# If not empty, the regular expression provided by `FINDFILTEROUT´ is an extended regular expression.
+#
+# By default, this matches `FINDFILTERONLYEXTENDED´
+FINDFILTEROUTEXTENDED :=  $(FINDFILTERONLYEXTENDED)
+
+# If not empty, the regular expression provided by `FINDFILTEROUT´ is an extended regular expression.
+#
+# By default, this matches `FINDFILTERONLYEXTENDED´
+FINDINCLUDEFILTERONLYEXTENDED :=  $(FINDFILTERONLYEXTENDED)
+
+# If not empty, the regular expression provided by `FINDINCLUDEFILTEROUT´ is an extended regular expression.
+#
+# By default, this matches `FINDFILTEROUTEXTENDED´
+FINDINCLUDEFILTEROUTEXTENDED := $(and $(FINDFILTERONLYEXTENDED),$(FINDFILTEROUTEXTENDED),1)
+
 # The list of magic files to use when determining media types.
 #
 # Some are provided as part of this repository, but you can add more if you need different media type detection.
 # The list of magic files to use when determining media types.
 #
 # Some are provided as part of this repository, but you can add more if you need different media type detection.
@@ -259,9 +304,9 @@ endif
 # • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive ›:
 #   Generates archive files from parse results.
 #
 # • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive ›:
 #   Generates archive files from parse results.
 #
-# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:_2stage ›:
-#   Two‐stage build; runs ⛩📰 书社 twice.
-MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:$(if $(DATADIR),$(shell if $(TEST) -d $(call quote,$(DATADIR)); then $(PRINTF) '%s\n' '_2stage'; else $(PRINTF) '%s\n' 'default'; fi),default)
+# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:initial ›:
+#   The initial mode that this make·file starts in; this builds the magic file and parsers and then recursively calls into the default mode to build the files.
+MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:initial
 
 # Set to a non·empty value to silence informative messages.
 QUIET :=
 
 # Set to a non·empty value to silence informative messages.
 QUIET :=
@@ -275,7 +320,7 @@ export LC_ALL
 
 # Posix timezone information.
 TZ := UTC0
 
 # Posix timezone information.
 TZ := UTC0
-export UTC0
+export TZ
 
 # The default target for this makefile.
 .DEFAULT_GOAL := all
 
 # The default target for this makefile.
 .DEFAULT_GOAL := all
@@ -311,6 +356,54 @@ override not = $(if $1,,1)
 # (callable) Quote the given string for use within shell calls.
 override quote = '$(subst ','"'"',$1)'
 
 # (callable) Quote the given string for use within shell calls.
 override quote = '$(subst ','"'"',$1)'
 
+# (callable) Quote the given string for use defining a variable to send to a submake.
+override varquote = $(subst $$,$$$$,$(call quote,$1))
+
+# The command to use for percent‐decoding.
+override perdeccmd := $(SED) 's/|/%7C/g;s/[\]/%5C/g;s/%[0123456789ABCDEFabcdef]\{2\}/|&|/g' | $(TR) '|' '\n' | $(SED) '/^%[0123456789ABCDEFabcdef]\{2\}$$/!s/%/|%25|/' | $(TR) '|' '\n' | $(AWK) '$$0!~/%/{printf "%s",$$0}/%/{d="0123456789ABCDEF";v=substr(toupper($$0),2,2);printf "\\%04o",(index(d,substr(v,1,1))-1)*16+index(d,substr(v,2,1))-1}' | $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/;$$!s/$$/\\/) | $(XARGS) -E '' $(PRINTF) '%b'
+
+# (callable) Percent‐decode the given strings.
+#
+# ☡ This variable creates a subshell every time it is computed.
+override perdec = $(shell $(PRINTF) '%s\0450A' $(foreach encoded,$1,$(call quote,$(encoded))) | $(perdeccmd))
+
+# (callable) Percent‐encode the given strings.
+#
+# This singly‐encodes u·r·i characters and doubly‐encodes other characters, then calls `perdec´ to decode back to a single encoding.
+# The encoding assumes the input is a “u·r·i component”; e·g that the resulting string should only contain `pchar´ (but can contain any `sub-delim´).
+#
+# It is assumed that the given strings do not contain newlines.
+#
+# ☡ This variable creates a subshell every time it is computed.
+override perenc = $(shell $(PRINTF) '%s\n' $(foreach unencoded,$1,$(call quote,$(unencoded))) | $(OD) -t x1 | $(SED) 's/^[0123456789]*//;s/0[Aa]/%&/g;s/2[146789ABCDEabcde]/%&/g;s/3[0123456789AaBbDd]/%&/g;s/40/%&/g;s/[46][123456789ABCDEFabcdef]/%&/g;s/[57][0123456789Aa]/%&/g;s/5[Ff]/%&/g;s/7[Ee]/%&/g;s/[ ][0123456789ABCDEFabcdef]\{2\}/%25&/g' | $(TR) -d ' \n' | $(TR) 'abcdef' 'ABCDEF' | $(perdeccmd))
+
+# (callable) Percent‐encode each component in the given paths.
+#
+# ☡ This variable creates a subshell every time it is computed.
+override pathenc = $(subst %2F,/,$(call perenc,$1))
+
+# (callable) Create a unique name for the given file, based on its absolute path.
+#
+# ☡ This variable creates a subshell every time it is computed.
+namehash = $(shell $(PRINTF) '%s' $(call quote,$(abspath $1)) | $(CKSUM) | $(SED) 's/ .*//' | $(XARGS) -E '' $(PRINTF) '%X')-$(notdir $1)
+
+# The location of this make·file.
+override makefile := $(abspath $(THISDIR)/GNUmakefile)
+
+# Non·empty if `help´ or `clean´, and no targets other than `help´ or `clean´, were specified as goals on the commandline.
+override notbuilding := $(and $(filter help clean,$(MAKECMDGOALS)),$(call not,$(filter-out help clean,$(MAKECMDGOALS))))
+
+# (callable) Tests to see if the prerequisites provided by the second argument matches the value in the file corresponding to the first argument in `$(BUILDDIR)/lastprereqs´.
+# If not, saves the new value.
+# Returns the values plus the file in `$(BUILDDIR)/lastprereqs´, which will always be newer than the target if there was a change.
+#
+# Calling this variable is useful when a given target should be updated whenever its list of prerequisites changes in addition to whenever there is a change to one of its prerequisites.
+#
+# If `$(notbuilding)´ is non·empty, this variable produces no result to avoid unnecessary work.
+#
+# ☡ This variable creates at least one subshell every time it is computed.
+override diffprereqs = $(if $(notbuilding),,$(and $(or $(wildcard $(BUILDDIR)/lastprereqs/$1),$(shell $(TOUCH) $(call quote,$(BUILDDIR)/lastprereqs/$1)),1),$(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastprereqs/$1) 2>>/dev/null || :),,$2),$(shell $(call ensuredirectory,$(BUILDDIR)/lastprereqs) && $(PRINTF) '%s\n' $(call quote,$2) >|$(call quote,$(BUILDDIR)/lastprereqs/$1)),)$2 $(BUILDDIR)/lastprereqs/$1)
+
 # ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
 
 # Outputs an `@´ to silence rules, unless `VERBOSE´ is non·empty.
 # ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
 
 # Outputs an `@´ to silence rules, unless `VERBOSE´ is non·empty.
@@ -325,19 +418,22 @@ override ensuredirectory = if $(TEST) ! -d $(call quote,$1); then $(MKDIR) -p $(
 # Quote standard input in such a way that piping it to xargs will result in it being processed as a single argument.
 #
 # If standard input ends in a newline, it is stripped; all other newlines are preserved.
 # Quote standard input in such a way that piping it to xargs will result in it being processed as a single argument.
 #
 # If standard input ends in a newline, it is stripped; all other newlines are preserved.
-override xargsquote = $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/;$$!s/$$/\\/)
+override xargsquote := $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/;$$!s/$$/\\/)
 
 # Quote standard input in such a way that piping it to xargs will result in each line being processed as a single argument.
 
 # Quote standard input in such a way that piping it to xargs will result in each line being processed as a single argument.
-override xargsmultiquote = $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/)
+override xargsmultiquote := $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/)
 
 # (callable) Test if the provided xpath expression matches the provided document.
 override xpath = $(XMLLINT) --noent --nonet --xpath $(call quote,$1) $(call quote,$2) >>/dev/null 2>>/dev/null
 
 
 # (callable) Test if the provided xpath expression matches the provided document.
 override xpath = $(XMLLINT) --noent --nonet --xpath $(call quote,$1) $(call quote,$2) >>/dev/null 2>>/dev/null
 
+# (callable) Get the identifier for the given parser or transform.
+override id = $(XMLLINT) --noent --nonet --xpath '/*/*[local-name()="id" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"]/text()[1]' $(call quote,$1) 2>>/dev/null || $(PRINTF) '%s\n' $(call quote,about:shushe?$(or $2,unknown)=$(call pathenc,$(basename $(notdir $1))))
+
 # (callable) Extract the value of the text nodes in the provided X·M·L document and print them to `stdout´.
 override extracttext = $(PRINTF) '%s' '<transform xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0"><output method="text" encoding="UTF-8"/></transform>' | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite - $(call quote,$1)
 
 # (callable) Process the provided transformation result and output the result to the provided location, given the provided relative path.
 # (callable) Extract the value of the text nodes in the provided X·M·L document and print them to `stdout´.
 override extracttext = $(PRINTF) '%s' '<transform xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0"><output method="text" encoding="UTF-8"/></transform>' | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite - $(call quote,$1)
 
 # (callable) Process the provided transformation result and output the result to the provided location, given the provided relative path.
-override processresultto = if $(call xpath,/*[local-name()="raw-text" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(call extracttext,$1) >|$(call quote,$2); elif $(call xpath,/*[local-name()="base64-binary" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then { $(PRINTF) '%s\n' 'begin-base64 644 -'; $(call extracttext,$1) | $(TR) -d '\t\n\f\r '; $(PRINTF) '\n%s\n' '===='; } | $(UUDECODE) -o /dev/stdout >|$(call quote,$2); elif $(call xpath,/*[local-name()="archive" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(MAKE) -f $(call quote,$(abspath $(THISDIR)/GNUmakefile)) NAME=$(call quote,$3) SRC=$(call quote,$1) BUILDDIR=$(call quote,$(BUILDDIR)/archive/$3) DESTDIR=$(call quote,$(patsubst %/,%,$(dir $2))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive' $(call quote,$2); else $(FINALIZE) $(call quote,$1) >|$(call quote,$2); fi
+override processresultto = if $(call xpath,/*[local-name()="raw-text" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(call extracttext,$1) >|$(call quote,$2); elif $(call xpath,/*[local-name()="base64-binary" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then { $(PRINTF) '%s\n' 'begin-base64 644 -'; $(call extracttext,$1) | $(TR) -d '\t\n\f\r '; $(PRINTF) '\n%s\n' '===='; } | $(UUDECODE) -o /dev/stdout >|$(call quote,$2); elif $(call xpath,/*[local-name()="archive" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(MAKE) -f $(makefile) NAME=$(call varquote,$3) SRC=$(call varquote,$1) BUILDDIR=$(call varquote,$(BUILDDIR)/archive/$3) DESTDIR=$(call varquote,$(patsubst %/,%,$(dir $2))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive' $(call varquote,$2); else $(FINALIZE) $(call quote,$1) >|$(call quote,$2); fi
 
 # ━ § BEGIN DEFAULT MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 
 
 # ━ § BEGIN DEFAULT MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 
@@ -345,20 +441,6 @@ ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:default)
 
 # ─ ¶ Non‐Recipe Variable Definitions ─────────────────────────────────
 
 
 # ─ ¶ Non‐Recipe Variable Definitions ─────────────────────────────────
 
-# Non·empty if `help´ or `clean´, and no targets other than `help´ or `clean´, were specified as goals on the commandline.
-override notbuilding := $(and $(filter help clean,$(MAKECMDGOALS)),$(call not,$(filter-out help clean,$(MAKECMDGOALS))))
-
-# (callable) Tests to see if the prerequisites provided by the second argument matches the value in the file corresponding to the first argument in `$(BUILDDIR)/lastprereqs´.
-# If not, saves the new value.
-# Returns the values plus the file in `$(BUILDDIR)/lastprereqs´, which will always be newer than the target if there was a change.
-#
-# Calling this variable is useful when a given target should be updated whenever its list of prerequisites changes in addition to whenever there is a change to one of its prerequisites.
-#
-# If `$(notbuilding)´ is non·empty, this variable produces no result to avoid unnecessary work.
-#
-# ☡ This variable creates at least one subshell every time it is computed.
-override diffprereqs = $(if $(notbuilding),,$(and $(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastprereqs/$1) 2>>/dev/null || :),,$2),$(shell $(call ensuredirectory,$(BUILDDIR)/lastprereqs) && $(PRINTF) '%s\n' $(call quote,$2) >|$(BUILDDIR)/lastprereqs/$1),)$2 $(BUILDDIR)/lastprereqs/$1)
-
 # (callable) Escape special characters for use in X·M·L.
 override xmlesc = $(subst >,&gt;,$(subst <,&lt;,$(subst &,&amp;,$1)))
 
 # (callable) Escape special characters for use in X·M·L.
 override xmlesc = $(subst >,&gt;,$(subst <,&lt;,$(subst &,&amp;,$1)))
 
@@ -368,39 +450,11 @@ override attresc = $(subst ",&quot;,$(call xmlesc,$1))
 # (callable) Escape special characters for use in sed regular expressions.
 override sedesc = $(subst /,[/],$(subst $$,\$$,$(subst *,\*,$(subst .,\.,$(subst [,\[,$(subst ^,\^,$(subst \,\\,$1)))))))
 
 # (callable) Escape special characters for use in sed regular expressions.
 override sedesc = $(subst /,[/],$(subst $$,\$$,$(subst *,\*,$(subst .,\.,$(subst [,\[,$(subst ^,\^,$(subst \,\\,$1)))))))
 
-# The command to use for percent‐decoding.
-override perdeccmd := $(SED) 's/|/%7C/g;s/[\]/%5C/g;s/%[0123456789ABCDEFabcdef]\{2\}/|&|/g' | $(TR) '|' '\n' | $(SED) '/^%[0123456789ABCDEFabcdef]\{2\}$$/!s/%/|%25|/' | $(TR) '|' '\n' | $(AWK) '$$0!~/%/{printf "%s",$$0}/%/{d="0123456789ABCDEF";v=substr(toupper($$0),2,2);printf "\\%04o",(index(d,substr(v,1,1))-1)*16+index(d,substr(v,2,1))-1}' | $(xargsquote) | $(XARGS) -E '' $(PRINTF) '%b'
-
-# (callable) Percent‐decode the given strings.
-#
-# ☡ This variable creates a subshell every time it is computed.
-override perdec = $(shell $(PRINTF) '%s\0450A' $(foreach encoded,$1,$(call quote,$(encoded))) | $(perdeccmd))
-
-# (callable) Percent‐encode the given strings.
-#
-# This singly‐encodes u·r·i characters and doubly‐encodes other characters, then calls `perdec´ to decode back to a single encoding.
-# The encoding assumes the input is a “u·r·i component”; e·g that the resulting string should only contain `pchar´ (but can contain any `sub-delim´).
-#
-# It is assumed that the given strings do not contain newlines.
-#
-# ☡ This variable creates a subshell every time it is computed.
-override perenc = $(shell $(PRINTF) '%s\n' $(foreach unencoded,$1,$(call quote,$(unencoded))) | $(OD) -t x1 | $(SED) 's/^[0123456789]*//;s/0[Aa]/%&/g;s/2[146789ABCDEabcde]/%&/g;s/3[0123456789AaBbDd]/%&/g;s/40/%&/g;s/[46][123456789ABCDEFabcdef]/%&/g;s/[57][0123456789Aa]/%&/g;s/5[Ff]/%&/g;s/7[Ee]/%&/g;s/[ ][0123456789ABCDEFabcdef]\{2\}/%25&/g' | $(TR) -d ' \n' | $(TR) 'abcdef' 'ABCDEF' | $(perdeccmd))
-
-# (callable) Percent‐encode each component in the given paths.
-#
-# ☡ This variable creates a subshell every time it is computed.
-override pathenc = $(subst %2F,/,$(call perenc,$1))
-
-# (callable) Create a unique name for the given file, based on its absolute path.
-#
-# ☡ This variable creates a subshell every time it is computed.
-namehash = $(shell $(PRINTF) '%s' $(call quote,$(abspath $1)) | $(CKSUM) | $(SED) 's/ .*//' | $(XARGS) -E '' $(PRINTF) '%X')-$(notdir $1)
-
 # (overridable) Collect all of the applicable includes from the includes directory.
 # (overridable) Collect all of the applicable includes from the includes directory.
-sourceincludes := $(if $(and $(INCLUDEDIR),$(wildcard $(INCLUDEDIR))),$(patsubst ./%,%,$(shell $(FIND) $(foreach dir,$(INCLUDEDIR),$(call quote,$(dir))) '(' $(FINDINCLUDERULES) ')' -a -type f -a -print)),)
+sourceincludes := $(foreach dir,$(INCLUDEDIR),$(and $(dir),$(wildcard $(dir)),$(patsubst ./%,%,$(shell $(FIND) $(call quote,$(dir)) '(' $(FINDINCLUDERULES) ')' -a -type f -a -print$(and $(FINDFILTERONLY),$(space)| $(GREP)$(and $(FINDFILTERONLYEXTENDED),$(space)-E) $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDFILTERONLY)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))$(and $(FINDINCLUDEFILTERONLY),$(space)| $(GREP)$(and $(FINDINCLUDEFILTERONLYEXTENDED),$(space)-E) $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDINCLUDEFILTERONLY)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))$(and $(FINDFILTEROUT),$(space)| $(GREP)$(and $(FINDFILTEROUTEXTENDED),$(space)-E) -v $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDFILTEROUT)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))$(and $(FINDINCLUDEFILTEROUT),$(space)| $(GREP)$(and $(FINDINCLUDEFILTEROUTEXTENDED),$(space)-E) -v $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDINCLUDEFILTEROUT)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))))))
 
 # (overridable) Collect all of the applicable source files from the source directory, removing any which are also includes.
 
 # (overridable) Collect all of the applicable source files from the source directory, removing any which are also includes.
-sourcefiles := $(if $(and $(SRCDIR),$(wildcard $(SRCDIR))),$(filter-out $(sourceincludes),$(patsubst ./%,%,$(shell $(FIND) $(foreach dir,$(SRCDIR),$(call quote,$(dir))) '(' $(FINDRULES) ')' -a -type f -a -print))))
+sourcefiles := $(filter-out $(sourceincludes),$(foreach dir,$(SRCDIR),$(and $(dir),$(wildcard $(dir)),$(patsubst ./%,%,$(shell $(FIND) $(call quote,$(dir)) '(' $(FINDRULES) ')' -a -type f -a -print$(and $(FINDFILTERONLY),$(space)| $(GREP)$(and $(FINDFILTERONLYEXTENDED),$(space)-E) $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDFILTERONLY)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))$(and $(FINDFILTEROUT),$(space)| $(GREP)$(and $(FINDFILTEROUTEXTENDED),$(space)-E) -v $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDFILTEROUT)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter))))))))))
 
 # Figure out the file type of each source file and source include.
 ifneq ($(wildcard $(BUILDDIR)/magic.mgc),)
 
 # Figure out the file type of each source file and source include.
 ifneq ($(wildcard $(BUILDDIR)/magic.mgc),)
@@ -425,15 +479,6 @@ override assetfiles := $(filter-out $(xmlfiles) $(plaintextfiles),$(sourcefiles)
 # (callable) Get the types of the given files.
 override typeoffile = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(types))),application/octet-stream))
 
 # (callable) Get the types of the given files.
 override typeoffile = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(types))),application/octet-stream))
 
-# Pair each source magic file with its location in the build directory.
-override magicpair := $(foreach magicfile,$(MAGIC),$(magicfile)|$(BUILDDIR)/magic/$(call namehash,$(magicfile)))
-
-# (callable) Get the source file for the given magic files.
-override magicsource = $(foreach magicpath,$1,$(patsubst %|$(magicpath),%,$(firstword $(filter %|$(magicpath),$(magicpair)))))
-
-# (callable) Get the build file for the given magic files.
-override magicfile = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(magicpair))))
-
 # (callable) Get the local path for the given source file.
 override sourcepath = $(or $(firstword $(foreach directory,$(SRCDIR),$(if $(filter .,$(directory)),$(wildcard $1),$(if $(filter $(directory)/%,$1),$(patsubst $(directory)/%,%,$1),)))),$(error Unable to get local path for source file `$1´))
 
 # (callable) Get the local path for the given source file.
 override sourcepath = $(or $(firstword $(foreach directory,$(SRCDIR),$(if $(filter .,$(directory)),$(wildcard $1),$(if $(filter $(directory)/%,$1),$(patsubst $(directory)/%,%,$1),)))),$(error Unable to get local path for source file `$1´))
 
@@ -478,10 +523,10 @@ override parsed = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(
 # (callable) Get the source files for the given parsed file.
 override unparsed = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourceparsedpair))))
 
 # (callable) Get the source files for the given parsed file.
 override unparsed = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourceparsedpair))))
 
-# Pair each build directory, parser, transform, source file, or parsed file with its file u·r·i.
-override fileuripairs := $(join $(patsubst %,%|,$(BUILDDIR) $(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes))),$(call pathenc,$(foreach uriable,$(BUILDDIR) $(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)),file://$(abspath $(uriable)))))
+# Pair each build directory, transform, source file, or parsed file with its file u·r·i.
+override fileuripairs := $(join $(patsubst %,%|,$(BUILDDIR) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes))),$(call pathenc,$(foreach uriable,$(BUILDDIR) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)),file://$(abspath $(uriable)))))
 
 
-# (callable) Get the file u·r·is for the given parsers, transforms, source file or parsed files.
+# (callable) Get the file u·r·is for the given transforms, source file or parsed files.
 override fileuri = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(fileuripairs))),$(error Unable to get file u·r·i for `$(file)´)))
 
 ifneq ($(wildcard $(BUILDDIR)/dependencies),)
 override fileuri = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(fileuripairs))),$(error Unable to get file u·r·i for `$(file)´)))
 
 ifneq ($(wildcard $(BUILDDIR)/dependencies),)
@@ -550,9 +595,6 @@ override installed = $(foreach file,$1,$(DESTDIR)/$(call destination,$(file)))
 
 # ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
 
 
 # ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
 
-# (callable) Get the identifier for the given parser or transform.
-override id = $(XMLLINT) --noent --nonet --xpath '/*/*[local-name()="id" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"]/text()[1]' $(call quote,$1) 2>>/dev/null || $(PRINTF) '%s\n' $(call quote,about:shushe?$(or $2,unknown)=$(call pathenc,$(basename $(notdir $1))))
-
 # (callable) Sanitize and wrap the provided plaintext file in X·M·L, printing to `stdout´.
 override wrapplaintext = { $(PRINTF) '%s\n%s' '<?xml version="1.0"?>' '<script xmlns="http://www.w3.org/1999/xhtml" type="$(call typeoffile,$1)"><![CDATA['; $(TR) '\000\013\014' '\032\011\012' <$(call quote,$1) | $(SED) "$$($(PRINTF) '%b' 's/]]>/]]]]><!\\[CDATA\\[>/g\ns/\0357\0277\0276/�/g\ns/\0357\0277\0277/�/g\n$$!s/\\r$$//g\ns/\\r/\\n/g\n$$!s/\0302\0205$$//g\ns/\0302\0205/\\n/g\ns/\0342\0200\0250/\\n/g\ns/[\0001\0002\0003\0004\0005\0006\0007\0010]/�/g\ns/[\0016\0017\0020\0021\0022\0023\0024\0025\0026\0027\0031\0032\0033\0034\0035\0036\0037]/�/g')"; $(PRINTF) '%s\n' ']]></script>'; }
 
 # (callable) Sanitize and wrap the provided plaintext file in X·M·L, printing to `stdout´.
 override wrapplaintext = { $(PRINTF) '%s\n%s' '<?xml version="1.0"?>' '<script xmlns="http://www.w3.org/1999/xhtml" type="$(call typeoffile,$1)"><![CDATA['; $(TR) '\000\013\014' '\032\011\012' <$(call quote,$1) | $(SED) "$$($(PRINTF) '%b' 's/]]>/]]]]><!\\[CDATA\\[>/g\ns/\0357\0277\0276/�/g\ns/\0357\0277\0277/�/g\n$$!s/\\r$$//g\ns/\\r/\\n/g\n$$!s/\0302\0205$$//g\ns/\0302\0205/\\n/g\ns/\0342\0200\0250/\\n/g\ns/[\0001\0002\0003\0004\0005\0006\0007\0010]/�/g\ns/[\0016\0017\0020\0021\0022\0023\0024\0025\0026\0027\0031\0032\0033\0034\0035\0036\0037]/�/g')"; $(PRINTF) '%s\n' ']]></script>'; }
 
@@ -563,25 +605,11 @@ override wrapplaintext = { $(PRINTF) '%s\n%s' '<?xml version="1.0"?>' '<script x
 # This isn’t a perfect substitution (it makes some assumptions about the format of the underlying X·M·L), but it should be workable for most sensible, welformed files.
 override serializexml = $(SED) "$$($(PRINTF) '%b' '/<?xml[ \t]\\{1,\\}version=[\0042\0047]1.1/,$${ s/<?xml[^>]*?>/<!--<?xml version=\00421.1\0042?>-->/\n s/&\0043x0*[12345678BCEFbcef];/\0302\0221/g\n s/&\0043x0*1[0123456789ABCDEFabcdef];/\0302\0221/g\n s/&\00430*[12345678];/\0302\0221/g\n s/&\00430*1[12456789];/\0302\0221/g\n s/&\00430*2[0123456789];/\0302\0221/g\n s/&\00430*3[01];/\0302\0221/g\n}')" <$(call quote,$1) | $(SED) "$$($(PRINTF) '%b' ':a\n/^\\n*$$/{ $$d\n N\n ba\n}')"
 
 # This isn’t a perfect substitution (it makes some assumptions about the format of the underlying X·M·L), but it should be workable for most sensible, welformed files.
 override serializexml = $(SED) "$$($(PRINTF) '%b' '/<?xml[ \t]\\{1,\\}version=[\0042\0047]1.1/,$${ s/<?xml[^>]*?>/<!--<?xml version=\00421.1\0042?>-->/\n s/&\0043x0*[12345678BCEFbcef];/\0302\0221/g\n s/&\0043x0*1[0123456789ABCDEFabcdef];/\0302\0221/g\n s/&\00430*[12345678];/\0302\0221/g\n s/&\00430*1[12456789];/\0302\0221/g\n s/&\00430*2[0123456789];/\0302\0221/g\n s/&\00430*3[01];/\0302\0221/g\n}')" <$(call quote,$1) | $(SED) "$$($(PRINTF) '%b' ':a\n/^\\n*$$/{ $$d\n N\n ba\n}')"
 
-# (callable) Wraps the provided shell script in a conditional which checks whether a make restart caused by `$(BUILDDIR)/.update-types´ is imminent (i·e, it was created during the course of this run) and only executes the script if it is not.
-override unlesstypeswillupdate = $(if $(call not,$(typeupdates)),if $(TEST) ! -e $(call quote,$(BUILDDIR)/.update-types); then ,)$1$(and $(call not,$(typeupdates)),; fi)
-
 # ─ ¶ Phony Targets ───────────────────────────────────────────────────
 
 # Compile all files, or error if any are recursive.
 all : $(call built,$(recursivefiles) $(installablefiles)) ;
 
 # ─ ¶ Phony Targets ───────────────────────────────────────────────────
 
 # Compile all files, or error if any are recursive.
 all : $(call built,$(recursivefiles) $(installablefiles)) ;
 
-# Destroy buildfiles.
-clean :
-       $(if $(BUILDDIR),$(silent)$(RM) -f -R $(call quote,$(BUILDDIR)/),)
-
-# Destroy build directory and installed files.
-gone : clean uninstall ;
-
-# Provide help.
-help :
-       @$(PRINTF) '%b' '$(subst $(newline),\n,$(makefileinfo))'
-
 # Install the compiled files into `DESTDIR´, or error if any are recursive.
 install : $(call installed,$(recursivefiles) $(installablefiles)) ;
 
 # Install the compiled files into `DESTDIR´, or error if any are recursive.
 install : $(call installed,$(recursivefiles) $(installablefiles)) ;
 
@@ -595,7 +623,7 @@ listout :
 
 # Destroy installed files.
 uninstall :
 
 # Destroy installed files.
 uninstall :
-       $(foreach file,$(installablefiles),$(if $(wildcard $(call installed,$(file))),$(silent)$(PRINTF) '%s\n' $(call quote,Removing </$(patsubst $(DESTDIR)/%,%,$(call installed,$(file)))>…)$(newline)$(silent)$(RM) -f $(call quote,$(call installed,$(file)))$(newline),))
+       $(foreach file,$(installablefiles),$(if $(wildcard $(call installed,$(file))),$(silent)$(PRINTF) '%s\n' $(call quote,Removing </$(patsubst $(DESTDIR)/%,%,$(call installed,$(file)))>…)$(newline)$(silent)$(RM) -f -R $(call quote,$(call installed,$(file)))$(newline),))
 
 # Raise an error when attempting to build any files with recursive dependencies.
 $(call built,$(recursivefiles)) :
 
 # Raise an error when attempting to build any files with recursive dependencies.
 $(call built,$(recursivefiles)) :
@@ -616,71 +644,43 @@ FORCE : ;
 .PRECIOUS : $(THISDIR)/GNUmakefile ;
 
 # Phony rules; always consider these out·of·date.
 .PRECIOUS : $(THISDIR)/GNUmakefile ;
 
 # Phony rules; always consider these out·of·date.
-.PHONY : FORCE all clean gone help install list listout uninstall $(call built,$(recursivefiles)) ;
+.PHONY : FORCE all install list listout uninstall $(call built,$(recursivefiles)) ;
 
 
-ifeq ($(notbuilding),)
-# Reload this make·file if the magic file, parser, dependencies, or destinations have changed.
+# Reload this make·file if the transform has changed.
 #
 #
-# If the magic file or parser changed, then another restart will be required, as the dependencies and destinations will need to be regenerated.
-$(THISDIR)/GNUmakefile : $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt $(BUILDDIR)/dependencies $(BUILDDIR)/destinations
+# This also captures any changes to dependencies or destinations.
+$(THISDIR)/GNUmakefile : $(BUILDDIR)/transform.xslt
        $(silent)$(TOUCH) $(THISDIR)/GNUmakefile
        $(silent)$(TOUCH) $(THISDIR)/GNUmakefile
-       $(if $(filter $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt,$?),$(call inform,$(PRINTF) '%b\n' '\0033[1mMagic file or parsers have updated. Restarting…\0033[22m' >&2)$(newline)$(silent)$(SLEEP) 1,$(if $(typeupdates),$(silent)$(RM) $(call quote,$(BUILDDIR)/.update-types)$(newline),)$(call inform,$(PRINTF) '%b\n' '\0033[1mDependency graph and output destinations updated. Restarting…\0033[22m' >&2))
-endif
+       $(if $(typeupdates),$(silent)$(RM) $(call quote,$(BUILDDIR)/.update-types),)
+       $(call inform,$(PRINTF) '%b\n' '\0033[1mDependency graph$(comma) output destinations$(comma) and transforms were updated. Restarting…\0033[22m' >&2)
 
 # ─ ¶ Build Targets ───────────────────────────────────────────────────
 
 
 # ─ ¶ Build Targets ───────────────────────────────────────────────────
 
-# Create symbolic links from the build directory’s store of magic files to their corresponding sources.
-$(call magicfile,$(MAGIC)) : $(BUILDDIR)/magic/% : $$(call magicsource,$$@)
-       $(silent)$(call ensuredirectory,$(dir $@))
-       $(silent)$(LN) -sf $(call quote,$(realpath $<)) $(call quote,$@)
-
-# Generate the compiled magic file from its sources.
-#
-# It must be updated if any of the files in the magic directory change.
-$(BUILDDIR)/magic.mgc : $(call diffprereqs,magic,$(sort $(call magicfile,$(MAGIC))))
-       $(foreach outdated,$(filter-out $^,$(wildcard $(BUILDDIR)/magic/*)),$(silent)$(RM) $(call quote,$(outdated))$(newline))
-       $(call inform,$(PRINTF) '%s\n' 'Compiling new magic…' >&2)
-       $(silent)$(call ensuredirectory,$(dir $@))
-       $(silent)$(CD) $(call quote,$(BUILDDIR)) && $(FILE) -C -m $(call quote,$(realpath $(BUILDDIR)/magic))
-       $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types)
-
-# Generate the main parser.
-$(BUILDDIR)/parser.catalog : $(call diffprereqs,parsers,$(sort $(PARSERS)))
-       $(call inform,$(PRINTF) '%s\n' 'Generating catalog of parsers…' >&2)
-       $(silent)$(XMLCATALOG) --create --noout $(call quote,$@)
-       $(foreach parser,$(PARSERS),$(silent){ $(call id,$(parser)); $(PRINTF) '%s\n' $(call quote,$(call fileuri,$(parser))) '--noout' $(call quote,$@); } | $(xargsmultiquote) | $(XARGS) -E '' $(XMLCATALOG) --add uri$(newline))
-$(BUILDDIR)/parser.xslt : $(BUILDDIR)/parser.catalog $(THISDIR)/lib/catalog2parser.xslt
-       $(call inform,$(PRINTF) '%s\n' 'Generating main parser…' >&2)
-       $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite $(call quote,$(THISDIR)/lib/catalog2parser.xslt) $(call quote,$<) >|$(call quote,$@)
-       $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types)
-
 # Generate R·D·F metadata for files.
 # Generate R·D·F metadata for files.
-#
-# If the types have been updated but `typeupdates´ is not active, this recipe is skipped because a restart is forthcoming.
 $(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(THISDIR)/.metadata-format-changed-since $(typeupdates)
 $(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(THISDIR)/.metadata-format-changed-since $(typeupdates)
-       $(call inform,$(call unlesstypeswillupdate,$(PRINTF) '%s\n' $(call quote,Generating metadata for `$<´…) >&2))
-       $(silent)$(call unlesstypeswillupdate,$(call ensuredirectory,$(dir $@)); { if $(TEST) ! -f $(call quote,$(BUILDDIR)/.mtime); then $(PRINTF) '%b' '\n' >|$(call quote,$(BUILDDIR)/.mtime); fi; $(TOUCH) -r $(call quote,$<) $(call quote,$(BUILDDIR)/.mtime); $(DIFF) -u $(call quote,$(BUILDDIR)/.mtime) /dev/null | $(SED) '1!d;s/.*   \([^ ]*\) \([^ ]*\).*$$/\1T\2Z/'; $(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//'; } | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) '<?xml version="1.0"?><书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile) xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:" rdf:about="%s" 书社vocab:path="%s" nfo:fileUrl="%s"><nie:interpretedAs>$(if $(filter $<,$(assetfiles)),<nfo:InformationElement nie:mimeType="%s"/>,<nfo:PlainTextDocument nie:mimeType="%s"/>)</nie:interpretedAs><书社vocab:hasParsedFile nfo:fileUrl="%s"/><nfo:fileLastModified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">%s</nfo:fileLastModified><nfo:hasHash nfo:hashAlgorithm="CRC32" nfo:hashValue="%s"/></书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile)>' $(call quote,$(call attresc,$(call localuri,$<))) $(call quote,$(call attresc,$(if $(filter $<,$(sourceincludes)),$(call includepath,$<),$(call sourcepath,$<)))) $(call quote,$(call attresc,$(call fileuri,$<))) $(call quote,$(call attresc,$(call typeoffile,$<))) $(call quote,$(call attresc,$(call fileuri,$(call parsed,$<)))) >|$(call quote,$@))
+       $(call inform,$(PRINTF) '%s\n' $(call quote,Generating metadata for `$<´…) >&2)
+       $(silent)$(call ensuredirectory,$(dir $@))
+       $(silent){ if $(TEST) ! -f $(call quote,$(BUILDDIR)/.mtime); then $(PRINTF) '%b' '\n' >|$(call quote,$(BUILDDIR)/.mtime); fi; $(TOUCH) -r $(call quote,$<) $(call quote,$(BUILDDIR)/.mtime); $(DIFF) -u $(call quote,$(BUILDDIR)/.mtime) /dev/null | $(SED) '1!d;s/.*   \([^ ]*\) \([^ ]*\).*$$/\1T\2Z/'; $(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//'; } | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) '<?xml version="1.0"?><书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile) xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:" rdf:about="%s" 书社vocab:path="%s" nfo:fileUrl="%s"><nie:interpretedAs>$(if $(filter $<,$(assetfiles)),<nfo:InformationElement nie:mimeType="%s"/>,<nfo:PlainTextDocument nie:mimeType="%s"/>)</nie:interpretedAs><书社vocab:hasParsedFile nfo:fileUrl="%s"/><nfo:fileLastModified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">%s</nfo:fileLastModified><nfo:hasHash nfo:hashAlgorithm="CRC32" nfo:hashValue="%s"/></书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile)>' $(call quote,$(call attresc,$(call localuri,$<))) $(call quote,$(call attresc,$(if $(filter $<,$(sourceincludes)),$(call includepath,$<),$(call sourcepath,$<)))) $(call quote,$(call attresc,$(call fileuri,$<))) $(call quote,$(call attresc,$(call typeoffile,$<))) $(call quote,$(call attresc,$(call fileuri,$(call parsed,$<)))) >|$(call quote,$@)
 
 # Parse the files.
 #
 # Even plain X·M·L files are parsed, because they may contain X·H·T·M·L `<script>´ elements which contain other kinds of data.
 # Asset files are turned into H·T·M·L embeds pointing to `data:´ U·R·I’s.
 
 # Parse the files.
 #
 # Even plain X·M·L files are parsed, because they may contain X·H·T·M·L `<script>´ elements which contain other kinds of data.
 # Asset files are turned into H·T·M·L embeds pointing to `data:´ U·R·I’s.
-#
-# If the types have been updated but `typeupdates´ is not active, this recipe is skipped because a restart is forthcoming.
 $(call parsed,$(sourcefiles) $(sourceincludes)) : % : $$(call unparsed,$$@) $(BUILDDIR)/parser.xslt $(PARSERLIBS) $(typeupdates)
 $(call parsed,$(sourcefiles) $(sourceincludes)) : % : $$(call unparsed,$$@) $(BUILDDIR)/parser.xslt $(PARSERLIBS) $(typeupdates)
-       $(call inform,$(call unlesstypeswillupdate,$(PRINTF) '%s\n' $(call quote,Processing `$<´…) >&2))
-       $(silent)$(call unlesstypeswillupdate,$(call ensuredirectory,$(dir $@)); $(if $(filter $<,$(assetfiles)),$(PRINTF) '%s\n' $(call quote,<?xml version="1.0"?><object xmlns="http://www.w3.org/1999/xhtml" type="$(call typeoffile,$<)" data="$(call datauri,$<)"/>) >|$(call quote,$@),$(if $(filter $<,$(plaintextfiles)),$(call wrapplaintext,$<),$(call serializexml,$<)) | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$<))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/parser.xslt) - >|$(call quote,$@)))
+       $(call inform,$(PRINTF) '%s\n' $(call quote,Processing `$<´…) >&2)
+       $(silent)$(call ensuredirectory,$(dir $@))
+       $(silent)$(if $(filter $<,$(assetfiles)),$(PRINTF) '%s\n' $(call quote,<?xml version="1.0"?><object xmlns="http://www.w3.org/1999/xhtml" type="$(call typeoffile,$<)" data="$(call datauri,$<)"/>) >|$(call quote,$@),$(if $(filter $<,$(plaintextfiles)),$(call wrapplaintext,$<),$(call serializexml,$<)) | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$<))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/parser.xslt) - >|$(call quote,$@))
 
 # Collect the metadata into a single file, and generate the dependencies and destinations files as side·effects.
 #
 # Doing this all in one step reduces the number of calls to `xsltproc´ required, but requires that it be called from the build directory (necessitating a subshell).
 
 # Collect the metadata into a single file, and generate the dependencies and destinations files as side·effects.
 #
 # Doing this all in one step reduces the number of calls to `xsltproc´ required, but requires that it be called from the build directory (necessitating a subshell).
-#
-# If the types have been updated but `typeupdates´ is not active, this recipe is skipped because a restart is forthcoming.
 $(BUILDDIR)/dependencies $(BUILDDIR)/destinations $(BUILDDIR)/metadata : $(call diffprereqs,metadatas,$(call metadata,$(sort $(sourcefiles) $(sourceincludes)))) $(call parsed,$(filter-out $(assetfiles),$(sourcefiles) $(sourceincludes))) $(THISDIR)/lib/expandmetadata.xslt
 $(BUILDDIR)/dependencies $(BUILDDIR)/destinations $(BUILDDIR)/metadata : $(call diffprereqs,metadatas,$(call metadata,$(sort $(sourcefiles) $(sourceincludes)))) $(call parsed,$(filter-out $(assetfiles),$(sourcefiles) $(sourceincludes))) $(THISDIR)/lib/expandmetadata.xslt
-       $(call inform,$(call unlesstypeswillupdate,$(PRINTF) '%s\n' 'Compiling metadata…' >&2))
-       $(silent)$(call unlesstypeswillupdate,{ $(PRINTF) '<?xml version="1.0"?><rdf:RDF xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:"><书社vocab:BuildDirectory nfo:fileUrl="%s"/>' $(call quote,$(call attresc,$(call fileuri,$(BUILDDIR)))); {$(foreach meta,$(call metadata,$(sort $(sourcefiles) $(sourceincludes))), $(CAT) $(call quote,$(meta));) } | $(SED) 's/<?xml version="1.0"?>//g'; $(PRINTF) '%s\n' '</rdf:RDF>'; } | ( $(CD) $(call quote,$(BUILDDIR)); $(XSLTPROC) --nonet --novalid --nomkdir $(call quote,$(abspath $(THISDIR)/lib/expandmetadata.xslt)) - ) | $(XMLLINT) --nonet --nsclean - >|$(call quote,$(BUILDDIR)/metadata))
+       $(call inform,$(PRINTF) '%s\n' 'Compiling metadata…' >&2)
+       $(silent){ $(PRINTF) '<?xml version="1.0"?><rdf:RDF xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:"><书社vocab:BuildDirectory nfo:fileUrl="%s"/>' $(call quote,$(call attresc,$(call fileuri,$(BUILDDIR)))); {$(foreach meta,$(call metadata,$(sort $(sourcefiles) $(sourceincludes))), $(CAT) $(call quote,$(meta));) } | $(SED) 's/<?xml version="1.0"?>//g'; $(PRINTF) '%s\n' '</rdf:RDF>'; } | ( $(CD) $(call quote,$(BUILDDIR)); $(XSLTPROC) --nonet --novalid --nomkdir $(call quote,$(abspath $(THISDIR)/lib/expandmetadata.xslt)) - ) | $(XMLLINT) --nonet --nsclean - >|$(call quote,$(BUILDDIR)/metadata)
 
 # Generate the main transform.
 
 # Generate the main transform.
+#
+# Because this depends on the metadata, it will also update the dependencies and destinations.
 $(BUILDDIR)/transform.catalog : $(call diffprereqs,transforms,$(sort $(TRANSFORMS)))
        $(call inform,$(PRINTF) '%s\n' 'Generating catalog of transforms…' >&2)
        $(silent)$(XMLCATALOG) --create --noout $(call quote,$@)
 $(BUILDDIR)/transform.catalog : $(call diffprereqs,transforms,$(sort $(TRANSFORMS)))
        $(call inform,$(PRINTF) '%s\n' 'Generating catalog of transforms…' >&2)
        $(silent)$(XMLCATALOG) --create --noout $(call quote,$@)
@@ -690,13 +690,20 @@ $(BUILDDIR)/transform.xslt : $(BUILDDIR)/transform.catalog $(BUILDDIR)/metadata
        $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam METADATA $(call quote,$(call fileuri,$(BUILDDIR))/metadata) $(call quote,$(THISDIR)/lib/catalog2transform.xslt) $(call quote,$<) >|$(call quote,$@)
 
 # Compile the result files using the dependencies as necessary.
        $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam METADATA $(call quote,$(call fileuri,$(BUILDDIR))/metadata) $(call quote,$(THISDIR)/lib/catalog2transform.xslt) $(call quote,$<) >|$(call quote,$@)
 
 # Compile the result files using the dependencies as necessary.
-$(call compiled,$(compilablefiles)) : $(BUILDDIR)/results/% : $$(call parsed,$$(call uncompiled,$$@)) $$(call parsed,$$(call dependencies,$$(call uncompiled,$$@))) $(BUILDDIR)/transform.xslt $(TRANSFORMLIBS) $(BUILDDIR)/metadata
+#
+# Altho this (obviously) depends on the main transform, it is not listed as a prerequisite (rather, the main transform is listed as a prerequisite for the make·file writ large). This is because the transform will update after every metadata change (i·e, basically constantly), but compiled files should only actually be updated in two cases :⁠—
+#
+# • When one of the actual transforms are updated (tracked by `transform.catalog´), and
+# • When the metadata of ⹐source files they depend on⹑ change.
+#
+# This is to reduce the number of needless regenerations of files with no substantial change.
+$(call compiled,$(compilablefiles)) : $(BUILDDIR)/results/% : $$(call parsed,$$(call uncompiled,$$@)) $$(call parsed,$$(call dependencies,$$(call uncompiled,$$@))) $(BUILDDIR)/transform.catalog $(THISDIR)/lib/catalog2transform.xslt $(TRANSFORMLIBS) $$(call metadata,$$(call dependencies,$$(call uncompiled,$$@)))
        $(call inform,$(PRINTF) '%s\n' $(call quote,Compiling </$*>…) >&2)
        $(silent)$(call ensuredirectory,$(dir $@))
        $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam METADATA 'metadata' --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$(call uncompiled,$@)))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/transform.xslt) $(call quote,$<) >|$(call quote,$@)
 
 # Create the final files from the compiled results (or error in the case of recursive ones).
        $(call inform,$(PRINTF) '%s\n' $(call quote,Compiling </$*>…) >&2)
        $(silent)$(call ensuredirectory,$(dir $@))
        $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam METADATA 'metadata' --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$(call uncompiled,$@)))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/transform.xslt) $(call quote,$<) >|$(call quote,$@)
 
 # Create the final files from the compiled results (or error in the case of recursive ones).
-$(call built,$(compilablefiles)) : $(BUILDDIR)/public/% : $(BUILDDIR)/results/%
+$(call built,$(compilablefiles)) : $(BUILDDIR)/public/% : $(BUILDDIR)/results/% $(THISDIR)/lib/archive2extractor.xslt
        $(call inform,$(PRINTF) '%s\n' $(call quote,Building </$*>…) >&2)
        $(silent)$(call ensuredirectory,$(dir $@))
        $(silent)$(RM) -f -R $(call quote,$@)
        $(call inform,$(PRINTF) '%s\n' $(call quote,Building </$*>…) >&2)
        $(silent)$(call ensuredirectory,$(dir $@))
        $(silent)$(RM) -f -R $(call quote,$@)
@@ -780,90 +787,152 @@ $(DESTDIR)/% : $(SRC) $(foreach file,$(archivefiles),$(BUILDDIR)/files/$(file))
        $(silent)$(call ensuredirectory,$(BUILDDIR)/files)
        $(silent)$(CD) $(call quote,$(BUILDDIR)/files); if $(call xpath,/*/@*[local-name()="expanded" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$(abspath $<)); then $(MKDIR) -p $(call quote,$(abspath $@)); $(PRINTF) '$(if $(archivefiles),%s\n,)' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -r -w $(call quote,$(abspath $@)); else $(PRINTF) '$(if $(archivefiles),%s\n,)' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -w -x ustar >|$(call quote,$(abspath $@)); fi
 
        $(silent)$(call ensuredirectory,$(BUILDDIR)/files)
        $(silent)$(CD) $(call quote,$(BUILDDIR)/files); if $(call xpath,/*/@*[local-name()="expanded" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$(abspath $<)); then $(MKDIR) -p $(call quote,$(abspath $@)); $(PRINTF) '$(if $(archivefiles),%s\n,)' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -r -w $(call quote,$(abspath $@)); else $(PRINTF) '$(if $(archivefiles),%s\n,)' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -w -x ustar >|$(call quote,$(abspath $@)); fi
 
-# ━ § BEGIN TWO‐STEP MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# ━ § BEGIN INITIAL MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 
 
-else ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:_2stage)
+else ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:initial)
 
 # ─ ¶ Non‐Recipe Variable Definitions ─────────────────────────────────
 
 
 # ─ ¶ Non‐Recipe Variable Definitions ─────────────────────────────────
 
-override makefile := $(abspath $(THISDIR)/GNUmakefile)
+# Non·empty if this is a two‐step build.
+override twostep := $(if $(DATADIR),$(call not,$(shell for dir in $(foreach dir,$(DATADIR),$(call quote,$(dir))); do if $(TEST) '!' -d "$$dir"; then $(PRINTF) '%s\n' '0'; fi; done)),)
 
 
-# (overridable) Options to use when calling ⛩📰 书社 the first time.
-shushedataopts := INCLUDEDIR=$(call quote,$(DATADIR)) BUILDDIR=$(call quote,$(BUILDDIR)/stage1) DESTDIR=$(call quote,$(BUILDDIR)/data) FINDRULES=$(call quote,$(FINDRULES) -a '(' $(FINDDATARULES) ')') FINDINCLUDERULES=$(call quote,$(FINDINCLUDERULES)) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default'
+# Pair each source magic file with its location in the build directory.
+override magicpair := $(foreach magicfile,$(MAGIC),$(magicfile)|$(BUILDDIR)/magic/$(call namehash,$(magicfile)))
 
 
-# (overridable) Options to use when calling ⛩📰 书社 the second time.
-shushesiteopts := INCLUDEDIR=$(call quote,$(INCLUDEDIR) $(BUILDDIR)/data) BUILDDIR=$(call quote,$(BUILDDIR)/stage2) FINDRULES=$(call quote,$(FINDRULES) -a '!' '(' $(FINDDATARULES) ')') FINDINCLUDERULES=$(call quote,$(FINDINCLUDERULES)) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default'
+# (callable) Get the source file for the given magic files.
+override magicsource = $(foreach magicpath,$1,$(patsubst %|$(magicpath),%,$(firstword $(filter %|$(magicpath),$(magicpair)))))
 
 
-# ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
+# (callable) Get the build file for the given magic files.
+override magicfile = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(magicpair))))
 
 
-# ─ ¶ Phony Targets ───────────────────────────────────────────────────
+# Pair each parser with its file u·r·i.
+override fileuripairs := $(join $(patsubst %,%|,$(PARSERS)),$(call pathenc,$(foreach uriable,$(PARSERS),file://$(abspath $(uriable)))))
 
 
-# Compile all files, or error if any are recursive.
-all : data
-       @$(MAKE) -f $(call quote,$(makefile)) $(shushesiteopts)
+# (callable) Get the file u·r·is for the given parsers.
+override fileuri = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(fileuripairs))),$(error Unable to get file u·r·i for `$(file)´)))
 
 
-# Destroy buildfiles.
-clean :
-       $(if $(BUILDDIR),$(silent)$(RM) -f -R $(call quote,$(BUILDDIR)/),)
+ifeq ($(twostep),)
+# (overridable) Options to use when calling ⛩📰 书社.
+shusheopts := MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default'
+else
+# (overridable) Options to use when calling ⛩📰 书社 the first time.
+shushedataopts := $(and $(DATAOPTS),$(DATAOPTS)$(space))INCLUDEDIR=$(call varquote,$(DATADIR)) BUILDDIR=$(call varquote,$(BUILDDIR)/data) FINDRULES=$(call varquote,$(FINDRULES) -a '(' $(FINDDATARULES) ')') FINDINCLUDERULES=$(call varquote,$(FINDINCLUDERULES)) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default'
 
 
-# Build the data and remove outdated data files.
-data : $(BUILDDIR)/data.out
-       @$(MAKE) -f $(call quote,$(makefile)) install $(shushedataopts)
-       $(silent)$(FIND) $(call quote,$(BUILDDIR)/data) '!' -exec $(GREP) -F -q -x '{}' $(call quote,$<) ';' -a '(' -type d -o -print ')' | $(xargsmultiquote) | $(XARGS) -E '' $(RM)
+# (overridable) Options to use when calling ⛩📰 书社 the second time.
+shushesiteopts := INCLUDEDIR=$(call varquote,$(INCLUDEDIR) $(BUILDDIR)/data/public) BUILDDIR=$(call varquote,$(BUILDDIR)/site) FINDRULES=$(call varquote,$(FINDRULES) -a '!' '(' $(FINDDATARULES) ')') FINDINCLUDERULES=$(call varquote,$(FINDINCLUDERULES)) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default'
+endif
 
 
-# Destroy build directory and installed files.
-gone : clean uninstall ;
+# ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
 
 
-# Install the compiled files into `DESTDIR´.
-install : all
-       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
-       @$(MAKE) -f $(call quote,$(makefile)) $(foreach dest,$(patsubst $(BUILDDIR)/data/%,$(DESTDIR)/%,$(shell $(CAT) $(call quote,$(BUILDDIR)/data.out))),$(call quote,$(dest)))
+# ─ ¶ Phony Targets ───────────────────────────────────────────────────
 
 
-# List all source files and includes and their computed types.
-list : data
+ifeq ($(twostep),)
+all install list listout uninstall : $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt
+       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shusheopts)
+else
+all : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data
+       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
+install uninstall : all
+       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts)
+       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
+list : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data
        @$(PRINTF) '%b' $(call quote,\0033[1;7m||Stage|1:||\0033[22;27m\0033[4m||||||||||||||||||||\0033[24m ) | $(TR) ' |' '\n '
        @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts)
        @$(PRINTF) '%b' $(call quote, \0033[1;7m||Stage|2:||\0033[22;27m\0033[4m||||||||||||||||||||\0033[24m ) | $(TR) ' |' '\n '
        @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
        @$(PRINTF) '%b' $(call quote,\0033[1;7m||Stage|1:||\0033[22;27m\0033[4m||||||||||||||||||||\0033[24m ) | $(TR) ' |' '\n '
        @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts)
        @$(PRINTF) '%b' $(call quote, \0033[1;7m||Stage|2:||\0033[22;27m\0033[4m||||||||||||||||||||\0033[24m ) | $(TR) ' |' '\n '
        @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
-listout : data
+listout : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data
        @{ $(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts); $(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts); } | $(SED) '$$!s/$$/ /' | $(TR) -d '\n'
        @{ $(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts); $(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts); } | $(SED) '$$!s/$$/ /' | $(TR) -d '\n'
-list1 listout1 : %1 :
+list1 listout1 : %1 : $(BUILDDIR)/data/magic.mgc $(BUILDDIR)/data/parser.xslt
        @$(MAKE) -f $(call quote,$(makefile)) $* $(shushedataopts)
        @$(MAKE) -f $(call quote,$(makefile)) $* $(shushedataopts)
-list2 listout2 : %2 : data
+list2 listout2 : %2 : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data
        @$(MAKE) -f $(call quote,$(makefile)) $* $(shushesiteopts)
        @$(MAKE) -f $(call quote,$(makefile)) $* $(shushesiteopts)
+endif
 
 
-# Destroy installed files.
-uninstall :
-       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
+# Destroy buildfiles.
+clean :
+       $(if $(BUILDDIR),$(silent)$(RM) -f -R $(call quote,$(BUILDDIR)/),)
+
+ifneq ($(twostep),)
+# Build the data and remove outdated data files.
+data : $(BUILDDIR)/data.out
+       $(silent)$(FIND) $(call quote,$(BUILDDIR)/data/public) '!' '(' -exec $(GREP) -F -q -x '{}' $(call quote,$<) ';' -a -prune ')' -a '(' -type d -o -print ')' | $(xargsmultiquote) | $(XARGS) -E '' $(RM)
+endif
+
+# Provide help.
+help :
+       @$(PRINTF) '%b' '$(subst $(newline),\n,$(makefileinfo))'
+
+# Destroy build directory and installed files.
+gone : uninstall clean ;
 
 # Add as a prerequisite to treat the target as tho it were phony.
 FORCE : ;
 
 # ─ ¶ Special Targets ─────────────────────────────────────────────────
 
 
 # Add as a prerequisite to treat the target as tho it were phony.
 FORCE : ;
 
 # ─ ¶ Special Targets ─────────────────────────────────────────────────
 
+# Perform secondary expansion; this enables pattern rules to determine their prerequisites based on the matched pattern.
+.SECONDEXPANSION : ;
+
 # Don’t use any implicit rules.
 .SUFFIXES : ;
 
 # Phony rules; always consider these out·of·date.
 # Don’t use any implicit rules.
 .SUFFIXES : ;
 
 # Phony rules; always consider these out·of·date.
-.PHONY : FORCE all data clean gone install list listout uninstall ;
+.PHONY : FORCE all clean gone install list listout uninstall$(if $(DATADIR), data list1 listout1 list2 listout2,) ;
 
 # ─ ¶ Build Targets ───────────────────────────────────────────────────
 
 
 # ─ ¶ Build Targets ───────────────────────────────────────────────────
 
-$(BUILDDIR)/data.out : FORCE
-       @$(MAKE) -f $(call quote,$(makefile)) $(shushedataopts)
-       @$(MAKE) -s -f $(call quote,$(makefile)) listout QUIET=1 $(shushedataopts) | $(TR) ' ' '\n' | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) $(call quote,$(BUILDDIR)/data/%s\n) >$(call quote,$(BUILDDIR)/data.out)
+# Create symbolic links from the build directory’s store of magic files to their corresponding sources.
+$(call magicfile,$(MAGIC)) : $(BUILDDIR)/magic/% : $$(call magicsource,$$@)
+       $(silent)$(call ensuredirectory,$(dir $@))
+       $(silent)$(LN) -sf $(call quote,$(realpath $<)) $(call quote,$@)
 
 
-$(BUILDDIR)/stage1/% : FORCE
-       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts)
+# Generate the compiled magic file from its sources.
+#
+# It must be updated if any of the files in the magic directory change.
+$(BUILDDIR)/magic.mgc : $(call diffprereqs,magic,$(sort $(call magicfile,$(MAGIC))))
+       $(foreach outdated,$(filter-out $^,$(wildcard $(BUILDDIR)/magic/*)),$(silent)$(RM) $(call quote,$(outdated))$(newline))
+       $(call inform,$(PRINTF) '%s\n' 'Compiling new magic…' >&2)
+       $(silent)$(call ensuredirectory,$(dir $@))
+       $(silent)$(CD) $(call quote,$(BUILDDIR)) && $(FILE) -C -m $(call quote,$(realpath $(BUILDDIR)/magic))
+       $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types)
 
 
-$(BUILDDIR)/data/% : FORCE
-       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts)
+# Generate the main parser.
+$(BUILDDIR)/parser.catalog : $(call diffprereqs,parsers,$(sort $(PARSERS)))
+       $(call inform,$(PRINTF) '%s\n' 'Generating catalog of parsers…' >&2)
+       $(silent)$(XMLCATALOG) --create --noout $(call quote,$@)
+       $(foreach parser,$(PARSERS),$(silent){ $(call id,$(parser)); $(PRINTF) '%s\n' $(call quote,$(call fileuri,$(parser))) '--noout' $(call quote,$@); } | $(xargsmultiquote) | $(XARGS) -E '' $(XMLCATALOG) --add uri$(newline))
+$(BUILDDIR)/parser.xslt : $(BUILDDIR)/parser.catalog $(THISDIR)/lib/catalog2parser.xslt
+       $(call inform,$(PRINTF) '%s\n' 'Generating main parser…' >&2)
+       $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite $(call quote,$(THISDIR)/lib/catalog2parser.xslt) $(call quote,$<) >|$(call quote,$@)
+       $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types)
 
 
-$(BUILDDIR)/stage2/% : data
-       @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
+ifneq ($(twostep),)
+$(BUILDDIR)/data/magic.mgc $(BUILDDIR)/data/parser.xslt : $(BUILDDIR)/data/% : $(BUILDDIR)/$$*
+       $(silent)$(call ensuredirectory,$(dir $@))
+       $(silent)$(LN) -sf $(call quote,$(realpath $<)) $(call quote,$@)
+$(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt : $(BUILDDIR)/site/% : $(BUILDDIR)/$$*
+       $(silent)$(call ensuredirectory,$(dir $@))
+       $(silent)$(LN) -sf $(call quote,$(realpath $<)) $(call quote,$@)
+$(BUILDDIR)/data.out : $(BUILDDIR)/data/magic.mgc $(BUILDDIR)/data/parser.xslt FORCE
+       @$(MAKE) -f $(call quote,$(makefile)) $(shushedataopts)
+       @$(MAKE) -s -f $(call quote,$(makefile)) listout QUIET=1 $(shushedataopts) | $(TR) ' ' '\n' | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) $(call quote,$(BUILDDIR)/data/public/%s\n) >$(call quote,$(BUILDDIR)/data.out)
+endif
 
 
-$(DESTDIR)/% : data
-       $(if $(shell $(GREP) -F -x $(call quote,$(BUILDDIR)/data/$*) $(BUILDDIR)/data.out),$(call inform $(PRINTF) '%s\n' $(call quote,Copying over </$*>…) >&2)$(newline),$(CP) $(call quote,$(BUILDDIR)/data/$*) $(call quote,$@),@$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts))
+ifeq ($(twostep),)
+$(BUILDDIR)/lastprereqs/% : ;
+$(BUILDDIR)/% : $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt FORCE
+       @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(shusheopts)
+$(DESTDIR)/% : $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt FORCE
+       @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(shusheopts)
+else
+$(BUILDDIR)/data/% : $(BUILDDIR)/data/magic.mgc $(BUILDDIR)/data/parser.xslt FORCE
+       @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(shushedataopts)
+$(BUILDDIR)/site/% : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data
+       $(silent)$(call ensuredirectory,$(dir $@))
+       @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(shushesiteopts)
+$(DESTDIR)/% : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data
+       @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(if $(shell $(GREP) -F -x $(call quote,$(BUILDDIR)/data/$*) $(BUILDDIR)/data.out),$(shushedataopts),$(shushesiteopts))
+endif
 
 # ━ § END DEFINED MAKE·FILE MODES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 
 
 # ━ § END DEFINED MAKE·FILE MODES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 
index 503a16338dda83163e59c655e8c9a02cf00d66ec..70a9730182b25a89b8542b8a8bed91a3aa875931 100644 (file)
@@ -1,5 +1,5 @@
 <!--
 <!--
-SPDX-FileCopyrightText: 2024 Lady <https://www.ladys.computer/about/#lady>
+SPDX-FileCopyrightText: 2024, 2025 Lady <https://www.ladys.computer/about/#lady>
 SPDX-License-Identifier: CC0-1.0
 -->
 # ⛩📰 书社
 SPDX-License-Identifier: CC0-1.0
 -->
 # ⛩📰 书社
@@ -307,6 +307,12 @@ The following additional variables can be used to control the behaviour
     default, to enable additional rules without overriding the existing
     ones.
 
     default, to enable additional rules without overriding the existing
     ones.
 
+- **`DATAOPTS`:**
+  Additional options to use when calling Make during the first stage of a two‐stage build using `DATADIR`.
+
+  This can be used to override variables which are only applicable during the second stage.
+  Note that when supplying this variable on the shell, it will need to be double‐quoted.
+
 - **`DATAEXT`:**
   A list of file extensions which signify “data” files during a two‐stage build using `DATADIR`.
 
 - **`DATAEXT`:**
   A list of file extensions which signify “data” files during a two‐stage build using `DATADIR`.
 
@@ -319,6 +325,32 @@ The following additional variables can be used to control the behaviour
     default, to enable additional rules without overriding the existing
     ones.
 
     default, to enable additional rules without overriding the existing
     ones.
 
+- **`FINDFILTERONLY`:**
+  A semicolon‐separated list of regular expressions, at least one of which the paths for sources and includes are required to match, unless empty (default: empty).
+
+- **`FINDFILTEROUT`:**
+  A semicolon‐separated list of regular expressions, each of which matches paths that should _not_ be considered sources or includes (default: empty).
+
+- **`FINDINCLUDEFILTERONLY`:**
+  A semicolon‐separated list of regular expressions, at least one of which the paths for includes are required to match, unless empty (default: empty).
+
+  Note that only paths which already match `FINDFILTERONLY` are considered.
+
+- **`FINDINCLUDEFILTEROUT`:**
+  A semicolon‐separated list of regular expressions, each of which matches paths that should _not_ be considered includes, but may still be considered sources (default: empty).
+
+- **`FINDFILTERONLYEXTENDED`:**
+  If non·empty, `FINDFILTERONLY` is an extended regular expression; otherwise, it is basic (default: empty).
+
+- **`FINDFILTEROUTEXTENDED`:**
+  If non·empty, `FINDFILTEROUT` is an extended regular expression; otherwise, it is basic (default: matches `FINDFILTERONLYEXTENDED`).
+
+- **`FINDINCLUDEFILTERONLYEXTENDED`:**
+  If non·empty, `FINDINCLUDEFILTERONLY` is an extended regular expression; otherwise, it is basic (default: matches `FINDFILTERONLYEXTENDED`).
+
+- **`FINDINCLUDEFILTEROUTEXTENDED`:**
+  If non·empty, `FINDINCLUDEFILTEROUT` is an extended regular expression; otherwise, it is basic (default: `1` if either `FINDFILTEROUTEXTENDED` or `FINDINCLUDEFILTERONLYEXTENDED` is non·empty).
+
 - **`PARSERS`:**
   A white·space‐separated list of parsers to use (default:
     `$(THISDIR)/parsers/*.xslt`).
 - **`PARSERS`:**
   A white·space‐separated list of parsers to use (default:
     `$(THISDIR)/parsers/*.xslt`).
@@ -585,7 +617,7 @@ Transforms are used to convert X·M·L files into their final output,
     media types into the appropriate H·T·M·L elements, and deletes
     `<html:style>` elements from the body of the document and moves
     them to the head.
     media types into the appropriate H·T·M·L elements, and deletes
     `<html:style>` elements from the body of the document and moves
     them to the head.
-  This conversion happens during the application phase, after the main
+  This conversion happens during the finalization phase, after the main
     transformation.
 
 - **`transforms/metadata.xslt`:**
     transformation.
 
 - **`transforms/metadata.xslt`:**
@@ -604,7 +636,7 @@ Transforms are used to convert X·M·L files into their final output,
 - **`transforms/serialization.xslt`:**
   Replaces `<书社:serialize-xml>` elements with the (escaped)
     serialized X·M·L of their contents.
 - **`transforms/serialization.xslt`:**
   Replaces `<书社:serialize-xml>` elements with the (escaped)
     serialized X·M·L of their contents.
-  This replacement happens during the application phase, after most
+  This replacement happens during the finalization phase, after most
     other transformations have taken place.
 
   If a `@with-namespaces` attribute is provided, any name·space nodes
     other transformations have taken place.
 
   If a `@with-namespaces` attribute is provided, any name·space nodes
@@ -730,8 +762,8 @@ It is especially useful in combination with output wrapping.
 
 In both cases, attributes from various sources are combined with
   white·space between them.
 
 In both cases, attributes from various sources are combined with
   white·space between them.
-Attribute application takes place after all ordinary transforms have
-  completed.
+Attribute application takes place after each stage of the
+  transformation, including after the initial embedding phase.
 
 Both elements ignore attributes in the `xml:` name·space, except for
   `@xml:lang`, which ignores all but the first definition (including
 
 Both elements ignore attributes in the `xml:` name·space, except for
   `@xml:lang`, which ignores all but the first definition (including
index b07b734046fe3d5d6425a7ceea5df36e0195b0f6..f9c8bdf4bf688596bec0d02d1d1a0dc731f125b7 100644 (file)
@@ -1,12 +1,12 @@
 <?xml version="1.0"?>
 <!--
 <?xml version="1.0"?>
 <!--
-SPDX-FileCopyrightText: 2024 Lady <https://www.ladys.computer/about/#lady>
+SPDX-FileCopyrightText: 2024, 2025 Lady <https://www.ladys.computer/about/#lady>
 SPDX-License-Identifier: MPL-2.0
 -->
 <!--
 ⁌ ⛩📰 书社 ∷ lib/archive2extractor.xslt
 
 SPDX-License-Identifier: MPL-2.0
 -->
 <!--
 ⁌ ⛩📰 书社 ∷ lib/archive2extractor.xslt
 
-© 2024 Lady [@ Ladys Computer].
+© 2024–2025 Lady [@ Ladys Computer].
 
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
@@ -38,7 +38,11 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                                <xslt:variable name="href">
                                                        <value-of select="@书社:archived-as"/>
                                                </xslt:variable>
                                                <xslt:variable name="href">
                                                        <value-of select="@书社:archived-as"/>
                                                </xslt:variable>
-                                               <xslt:copy-of select="书社:archive/*[@书社:archived-as=$href][1]"/>
+                                               <xslt:for-each select="书社:archive/*[@书社:archived-as=$href][1]">
+                                                       <xslt:copy>
+                                                               <xslt:copy-of select="@*[not(namespace-uri()='urn:fdc:ladys.computer:20231231:Shu1She4' and local-name()='archived-as')]|node()"/>
+                                                       </xslt:copy>
+                                               </xslt:for-each>
                                        </exsl:document>
                                        <xslt:text>
                                                <value-of select="$href"/>
                                        </exsl:document>
                                        <xslt:text>
                                                <value-of select="$href"/>
index 2c555e32c189c0ade8b54990a7ed439910f40b46..ce94067fa445d9b88b3fd63ba1f37836d62c40e4 100644 (file)
@@ -1,26 +1,28 @@
 <?xml version="1.0"?>
 <!--
 <?xml version="1.0"?>
 <!--
-SPDX-FileCopyrightText: 2023, 2024 Lady <https://www.ladys.computer/about/#lady>
+SPDX-FileCopyrightText: 2023, 2024, 2025 Lady <https://www.ladys.computer/about/#lady>
 SPDX-License-Identifier: MPL-2.0
 -->
 <!--
 ⁌ ⛩📰 书社 ∷ lib/catalog2transform.xslt
 
 SPDX-License-Identifier: MPL-2.0
 -->
 <!--
 ⁌ ⛩📰 书社 ∷ lib/catalog2transform.xslt
 
-© 2023–2024 Lady [@ Ladys Computer].
+© 2023–2025 Lady [@ Ladys Computer].
 
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 -->
 <!DOCTYPE transform [
        <!ENTITY 书社 "urn:fdc:ladys.computer:20231231:Shu1She4">
 
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 -->
 <!DOCTYPE transform [
        <!ENTITY 书社 "urn:fdc:ladys.computer:20231231:Shu1She4">
+       <!ENTITY xhtml "http://www.w3.org/1999/xhtml">
        <!ENTITY xml "http://www.w3.org/XML/1998/namespace">
 ]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        xmlns:catalog="urn:oasis:names:tc:entity:xmlns:xml:catalog"
        xmlns:exsl="http://exslt.org/common"
        <!ENTITY xml "http://www.w3.org/XML/1998/namespace">
 ]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        xmlns:catalog="urn:oasis:names:tc:entity:xmlns:xml:catalog"
        xmlns:exsl="http://exslt.org/common"
+       xmlns:exslfunc="http://exslt.org/functions"
        xmlns:exslstr="http://exslt.org/strings"
        xmlns:exslstr="http://exslt.org/strings"
-       xmlns:html="http://www.w3.org/1999/xhtml"
+       xmlns:html="&xhtml;"
        xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#"
        xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#"
        xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
@@ -51,26 +53,52 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                </if>
                        </for-each>
                </variable>
                                </if>
                        </for-each>
                </variable>
-               <xslt:transform exclude-result-prefixes="nie nfo" extension-element-prefixes="exsl exslstr" version="1.0">
+               <xslt:transform exclude-result-prefixes="nie nfo" extension-element-prefixes="exsl exslfunc exslstr" version="1.0">
                        <xslt:param name="BUILDTIME" select="'1972-12-31T00:00:00Z'"/>
                        <xslt:param name="IDENTIFIER" select="false()"/>
                        <xslt:param name="SRCREV" select="false()"/>
                        <xslt:param name="THISREV" select="false()"/>
                        <xslt:param name="BUILDTIME" select="'1972-12-31T00:00:00Z'"/>
                        <xslt:param name="IDENTIFIER" select="false()"/>
                        <xslt:param name="SRCREV" select="false()"/>
                        <xslt:param name="THISREV" select="false()"/>
+                       <element name="exslfunc:function">
+                               <attribute name="name">
+                                       <text>书社:document-with-attributes-applied</text>
+                               </attribute>
+                               <xslt:param name="document" select="/.."/>
+                               <xslt:variable name="application-result-fragment">
+                                       <xslt:variable name="result-nodes" select="$document/node()[not(self::书社:apply-attributes-to-root)]|$document/书社:apply-attributes-to-root//node()[not(self::书社:apply-attributes-to-root) and not(ancestor::*[not(self::书社:apply-attributes-to-root)])]"/>
+                                       <xslt:variable name="root-with-attributes">
+                                               <xslt:call-template name="书社:apply-attributes">
+                                                       <xslt:with-param name="context-nodes" select="$document//书社:apply-attributes-to-root"/>
+                                                       <xslt:with-param name="destination-nodes" select="$result-nodes"/>
+                                               </xslt:call-template>
+                                       </xslt:variable>
+                                       <xslt:apply-templates select="exsl:node-set($root-with-attributes)" mode="书社:apply"/>
+                               </xslt:variable>
+                               <element name="exslfunc:result">
+                                       <attribute name="select">
+                                               <text>exsl:node-set($application-result-fragment)</text>
+                                       </attribute>
+                               </element>
+                       </element>
                        <xslt:variable name="书社:source" select="/"/>
                        <xslt:variable name="书社:about-fragment">
                                <apply-templates select="document($METADATA)" mode="书社:literally">
                        <xslt:variable name="书社:source" select="/"/>
                        <xslt:variable name="书社:about-fragment">
                                <apply-templates select="document($METADATA)" mode="书社:literally">
-                                       <with-param name="extension-element-namespaces" select="'http://exslt.org/common http://exslt.org/strings'"/>
+                                       <with-param name="extension-element-namespaces" select="'http://exslt.org/common http://exslt.org/functions http://exslt.org/strings'"/>
                                </apply-templates>
                        </xslt:variable>
                        <xslt:variable name="书社:about" select="exsl:node-set($书社:about-fragment)"/>
                        <xslt:variable name="书社:expansion-fragment">
                                <xslt:apply-templates select="$书社:source/node()" mode="书社:expand"/>
                        </xslt:variable>
                                </apply-templates>
                        </xslt:variable>
                        <xslt:variable name="书社:about" select="exsl:node-set($书社:about-fragment)"/>
                        <xslt:variable name="书社:expansion-fragment">
                                <xslt:apply-templates select="$书社:source/node()" mode="书社:expand"/>
                        </xslt:variable>
-                       <xslt:variable name="书社:expansion" select="exsl:node-set($书社:expansion-fragment)"/>
+                       <xslt:variable name="书社:expansion" select="书社:document-with-attributes-applied(exsl:node-set($书社:expansion-fragment))"/>
                        <xslt:variable name="书社:result-fragment">
                        <xslt:variable name="书社:result-fragment">
-                               <xslt:apply-templates select="$书社:expansion/node()"/>
+                               <xslt:element name="书社:apply-attributes-to-root">
+                                       <xslt:attribute name="书社:version">
+                                               <xslt:value-of select="$THISREV"/>
+                                       </xslt:attribute>
+                                       <xslt:apply-templates select="$书社:expansion/node()"/>
+                               </xslt:element>
                        </xslt:variable>
                        </xslt:variable>
-                       <xslt:variable name="书社:result" select="exsl:node-set($书社:result-fragment)"/>
+                       <xslt:variable name="书社:result" select="书社:document-with-attributes-applied(exsl:node-set($书社:result-fragment))"/>
                        <xslt:variable name="书社:destination" select="string($书社:about//*[@rdf:about=$IDENTIFIER]/@书社vocab:destination)"/>
                        <for-each select="//catalog:uri">
                                <xslt:include href="{@uri}">
                        <xslt:variable name="书社:destination" select="string($书社:about//*[@rdf:about=$IDENTIFIER]/@书社vocab:destination)"/>
                        <for-each select="//catalog:uri">
                                <xslt:include href="{@uri}">
@@ -80,7 +108,6 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                </xslt:include>
                        </for-each>
                        <xslt:template name="书社:apply-attributes">
                                </xslt:include>
                        </for-each>
                        <xslt:template name="书社:apply-attributes">
-                               <xslt:param name="and-version" select="false()"/>
                                <xslt:param name="context-nodes" select="/.."/>
                                <xslt:param name="destination-nodes" select="/.."/>
                                <xslt:variable name="additional-attributes" select="$context-nodes/@*"/>
                                <xslt:param name="context-nodes" select="/.."/>
                                <xslt:param name="destination-nodes" select="/.."/>
                                <xslt:variable name="additional-attributes" select="$context-nodes/@*"/>
@@ -124,11 +151,6 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                                                </xslt:choose>
                                                        </xslt:variable>
                                                        <xslt:copy>
                                                                </xslt:choose>
                                                        </xslt:variable>
                                                        <xslt:copy>
-                                                               <xslt:if test="$and-version and $THISREV">
-                                                                       <xslt:attribute name="书社:version">
-                                                                               <xslt:value-of select="$THISREV"/>
-                                                                       </xslt:attribute>
-                                                               </xslt:if>
                                                                <xslt:if test="string($lang)!=''">
                                                                        <xslt:if test="self::html:* or self::svg:*">
                                                                                <xslt:attribute name="lang">
                                                                <xslt:if test="string($lang)!=''">
                                                                        <xslt:if test="self::html:* or self::svg:*">
                                                                                <xslt:attribute name="lang">
@@ -143,8 +165,7 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                                                        <xslt:choose>
                                                                                <xslt:when test="@namespace-uri='&xml;'"/>
                                                                                <xslt:when test="$context[self::html:* or self::svg:*] and @namespace-uri='' and @local-name='lang'"/>
                                                                        <xslt:choose>
                                                                                <xslt:when test="@namespace-uri='&xml;'"/>
                                                                                <xslt:when test="$context[self::html:* or self::svg:*] and @namespace-uri='' and @local-name='lang'"/>
-                                                                               <xslt:when test="@namespace-uri='&书社;' and @local-name='archived-as' and $context-nodes/ancestor::*[not(self::书社:apply-attributes-to-root or self::书社:apply-attributes)][position()=1 and not(self::书社:archive)]"/>
-                                                                               <xslt:when test="@namespace-uri='&书社;' and (@local-name='destination' or @local-name='disable-output-wrapping' or @local-name='version')"/>
+                                                                               <xslt:when test="@namespace-uri='&书社;' and (@local-name='destination' or @local-name='disable-output-wrapping')"/>
                                                                                <xslt:when test="preceding-sibling::*[@local-name=current()/@local-name and @namespace-uri=current()/@namespace-uri]"/>
                                                                                <xslt:otherwise>
                                                                                        <xslt:attribute>
                                                                                <xslt:when test="preceding-sibling::*[@local-name=current()/@local-name and @namespace-uri=current()/@namespace-uri]"/>
                                                                                <xslt:otherwise>
                                                                                        <xslt:attribute>
@@ -200,7 +221,7 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                        <xslt:apply-templates select="$modalinput/node()" mode="书社:metadata"/>
                                </xslt:variable>
                                <xslt:variable name="metadata" select="exsl:node-set($metadata-fragment)"/>
                                        <xslt:apply-templates select="$modalinput/node()" mode="书社:metadata"/>
                                </xslt:variable>
                                <xslt:variable name="metadata" select="exsl:node-set($metadata-fragment)"/>
-                               <html:html>
+                               <xslt:element name="html" namespace="&xhtml;">
                                        <xslt:copy-of select="$nodes[self::html:html]/@*"/>
                                        <xslt:if test="not($nodes[self::html:html]/@书社:archived-as) and $nodes/@书社:archived-as">
                                                <xslt:attribute name="书社:archived-as">
                                        <xslt:copy-of select="$nodes[self::html:html]/@*"/>
                                        <xslt:if test="not($nodes[self::html:html]/@书社:archived-as) and $nodes/@书社:archived-as">
                                                <xslt:attribute name="书社:archived-as">
@@ -217,9 +238,9 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                                        <xslt:value-of select="$nodes[self::html:*]/@lang|$nodes[self::svg:*]/@lang|$nodes/@xml:lang"/>
                                                </xslt:attribute>
                                        </xslt:if>
                                                        <xslt:value-of select="$nodes[self::html:*]/@lang|$nodes[self::svg:*]/@lang|$nodes/@xml:lang"/>
                                                </xslt:attribute>
                                        </xslt:if>
-                                       <html:head>
+                                       <xslt:element name="head" namespace="&xhtml;">
                                                <xslt:copy-of select="$nodes[self::html:html]/html:head/@*|$nodes[self::html:head]/@*"/>
                                                <xslt:copy-of select="$nodes[self::html:html]/html:head/@*|$nodes[self::html:head]/@*"/>
-                                               <html:title>
+                                               <xslt:element name="title" namespace="&xhtml;">
                                                        <xslt:for-each select="$metadata/html:title">
                                                                <xslt:value-of select="."/>
                                                                <xslt:if test="position()!=last()">
                                                        <xslt:for-each select="$metadata/html:title">
                                                                <xslt:value-of select="."/>
                                                                <xslt:if test="position()!=last()">
@@ -228,9 +249,14 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                                                        </xslt:text>
                                                                </xslt:if>
                                                        </xslt:for-each>
                                                                        </xslt:text>
                                                                </xslt:if>
                                                        </xslt:for-each>
-                                               </html:title>
+                                               </xslt:element>
                                                <xslt:copy-of select="$metadata/node()[not(self::html:title or self::html:meta and @name='generator')]"/>
                                                <xslt:copy-of select="$metadata/node()[not(self::html:title or self::html:meta and @name='generator')]"/>
-                                               <html:meta name="generator">
+                                               <xslt:element name="meta" namespace="&xhtml;">
+                                                       <xslt:attribute name="name">
+                                                               <xslt:text>
+                                                                       <text>generator</text>
+                                                               </xslt:text>
+                                                       </xslt:attribute>
                                                        <xslt:attribute name="content">
                                                                <xslt:for-each select="$metadata/html:meta[@name='generator']">
                                                                        <xslt:value-of select="@content"/>
                                                        <xslt:attribute name="content">
                                                                <xslt:for-each select="$metadata/html:meta[@name='generator']">
                                                                        <xslt:value-of select="@content"/>
@@ -251,122 +277,31 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                                                        </xslt:text>
                                                                </xslt:if>
                                                        </xslt:attribute>
                                                                        </xslt:text>
                                                                </xslt:if>
                                                        </xslt:attribute>
-                                               </html:meta>
-                                       </html:head>
-                                       <html:body>
-                                               <xslt:copy-of select="$nodes[self::html:html]/html:body/@*|$nodes[self::html:body]/@*"/>
+                                               </xslt:element>
+                                       </xslt:element>
+                                       <xslt:element name="body" namespace="&xhtml;">
+                                               <xslt:copy-of select="($nodes[self::html:html]/html:body/@*|$nodes[self::html:body]/@*)[not(namespace-uri()='&书社;' and local-name()='archived-as')]"/>
                                                <xslt:apply-templates select="$modalinput/node()" mode="书社:header"/>
                                                <xslt:apply-templates select="$modalinput/node()" mode="书社:header"/>
-                                               <xslt:copy-of select="$nodes[not(self::html:html or self::html:head or self::html:body)]|$nodes[self::html:html]/node()[not(self::html:head or self::html:body)]|$nodes[self::html:html]/html:body/node()|$nodes[self::html:body]/node()"/>
+                                               <xslt:for-each select="$nodes[not(self::html:html or self::html:head or self::html:body)]|$nodes[self::html:html]/node()[not(self::html:head or self::html:body)]|$nodes[self::html:html]/html:body/node()|$nodes[self::html:body]/node()">
+                                                       <xslt:copy>
+                                                               <xslt:copy-of select="@*[not(namespace-uri()='&书社;' and local-name()='archived-as')]|node()"/>
+                                                       </xslt:copy>
+                                               </xslt:for-each>
                                                <xslt:apply-templates select="$modalinput/node()" mode="书社:footer"/>
                                                <xslt:apply-templates select="$modalinput/node()" mode="书社:footer"/>
-                                       </html:body>
-                               </html:html>
+                                       </xslt:element>
+                               </xslt:element>
                        </xslt:template>
                        </xslt:template>
-                       <xslt:template match="/" priority="1">
-                               <xslt:variable name="result-nodes" select="$书社:result/node()[not(self::书社:apply-attributes-to-root)]|$书社:result/书社:apply-attributes-to-root//node()[not(self::书社:apply-attributes-to-root) and not(ancestor::*[not(self::书社:apply-attributes-to-root)])]"/>
-                               <xslt:variable name="significant-nodes" select="$result-nodes/descendant-or-self::*[not(self::书社:apply-attributes) and not(ancestor::*[not(self::书社:apply-attributes)])]"/>
-                               <xslt:variable name="root-with-attributes">
-                                       <xslt:choose>
-                                               <xslt:when test="not($significant-nodes/self::html:*) or $significant-nodes/self::书社:*[local-name()='raw-text' or local-name()='base64-binary' or local-name()='archive'] or $significant-nodes/@书社:disable-output-wrapping or $书社:result//书社:apply-attributes-to-root/@书社:disable-output-wrapping">
-                                                       <xslt:call-template name="书社:apply-attributes">
-                                                               <xslt:with-param name="and-version" select="true()"/>
-                                                               <xslt:with-param name="context-nodes" select="$书社:result//书社:apply-attributes-to-root"/>
-                                                               <xslt:with-param name="destination-nodes" select="$result-nodes"/>
-                                                       </xslt:call-template>
-                                               </xslt:when>
-                                               <xslt:otherwise>
-                                                       <xslt:variable name="wrapped-result">
-                                                               <xslt:call-template name="书社:wrap">
-                                                                       <xslt:with-param name="nodes" select="$result-nodes"/>
-                                                               </xslt:call-template>
-                                                       </xslt:variable>
-                                                       <xslt:call-template name="书社:apply-attributes">
-                                                               <xslt:with-param name="and-version" select="true()"/>
-                                                               <xslt:with-param name="context-nodes" select="$书社:result//书社:apply-attributes-to-root"/>
-                                                               <xslt:with-param name="destination-nodes" select="exsl:node-set($wrapped-result)/node()"/>
-                                                       </xslt:call-template>
-                                               </xslt:otherwise>
-                                       </xslt:choose>
+                       <xslt:template match="/">
+                               <xslt:variable name="finalization-fragment">
+                                       <xslt:apply-templates select="$书社:result" mode="书社:finalize"/>
                                </xslt:variable>
                                </xslt:variable>
-                               <xslt:apply-templates select="exsl:node-set($root-with-attributes)/node()" mode="书社:apply"/>
+                               <xslt:copy-of select="书社:document-with-attributes-applied(exsl:node-set($finalization-fragment))"/>
                        </xslt:template>
                        <xslt:template match="@*|node()" priority="-1">
                                <xslt:copy>
                                        <xslt:apply-templates select="@*|node()"/>
                                </xslt:copy>
                        </xslt:template>
                        </xslt:template>
                        <xslt:template match="@*|node()" priority="-1">
                                <xslt:copy>
                                        <xslt:apply-templates select="@*|node()"/>
                                </xslt:copy>
                        </xslt:template>
-                       <xslt:template match="@书社:destination|@书社:disable-output-wrapping|@书社:archived-as[../ancestor::*[not(self::书社:apply-attributes-to-root or self::书社:apply-attributes)]]" mode="书社:apply" priority="1"/>
-                       <xslt:template match="书社:archive" mode="书社:apply" priority="1">
-                               <xslt:copy>
-                                       <xslt:for-each select="@*">
-                                               <xslt:choose>
-                                                       <xslt:when test="local-name()='archived-as' and namespace-uri()='&书社;' and ../ancestor::*[not(self::书社:apply-attributes-to-root or self::书社:apply-attributes)]"/>
-                                                       <xslt:otherwise>
-                                                               <xslt:apply-templates select="." mode="书社:apply"/>
-                                                       </xslt:otherwise>
-                                               </xslt:choose>
-                                       </xslt:for-each>
-                                       <xslt:for-each select="node()">
-                                               <xslt:choose>
-                                                       <xslt:when test="self::*">
-                                                               <xslt:variable name="component-fragment">
-                                                                       <xslt:copy>
-                                                                               <xslt:choose>
-                                                                                       <xslt:when test="self::html:*">
-                                                                                               <xslt:variable name="existing-types" select="exslstr:tokenize(@itemtype)"/>
-                                                                                               <xslt:attribute name="itemscope">itemscope</xslt:attribute>
-                                                                                               <xslt:attribute name="itemtype">
-                                                                                                       <xslt:for-each select="$existing-types/token">
-                                                                                                               <xslt:if test="position()!=1">
-                                                                                                                       <xslt:text>
-                                                                                                                               <text> </text>
-                                                                                                                       </xslt:text>
-                                                                                                               </xslt:if>
-                                                                                                               <xslt:value-of select="."/>
-                                                                                                       </xslt:for-each>
-                                                                                                       <xslt:if test="not($existing-types/token[string()='&书社;:embed' or string()='&书社;:document'])">
-                                                                                                               <xslt:if test="$existing-types/token">
-                                                                                                                       <xslt:text>
-                                                                                                                               <text> </text>
-                                                                                                                       </xslt:text>
-                                                                                                               </xslt:if>
-                                                                                                               <xslt:text>
-                                                                                                                       <text>&书社;:document</text>
-                                                                                                               </xslt:text>
-                                                                                                       </xslt:if>
-                                                                                               </xslt:attribute>
-                                                                                               <xslt:copy-of select="@*[not(namespace-uri()='' and (local-name()='itemscope' or local-name()='itemtype'))]|node()"/>
-                                                                                       </xslt:when>
-                                                                                       <xslt:otherwise>
-                                                                                               <xslt:copy-of select="@*|node()"/>
-                                                                                       </xslt:otherwise>
-                                                                               </xslt:choose>
-                                                                       </xslt:copy>
-                                                               </xslt:variable>
-                                                               <xslt:variable name="wrapped-component-fragment">
-                                                                       <xslt:for-each select="exsl:node-set($component-fragment)/*">
-                                                                               <xslt:variable name="result-nodes" select="self::*[not(self::书社:apply-attributes-to-root)]|self::书社:apply-attributes-to-root//node()[not(self::书社:apply-attributes-to-root) and not(ancestor::*[not(self::书社:apply-attributes-to-root)])]"/>
-                                                                               <xslt:variable name="significant-nodes" select="$result-nodes/descendant-or-self::*[not(self::书社:apply-attributes) and not(ancestor::*[not(self::书社:apply-attributes)])]"/>
-                                                                               <xslt:choose>
-                                                                                       <xslt:when test="not($significant-nodes/self::html:*) or $significant-nodes/self::书社:*[local-name()='raw-text' or local-name()='base64-binary' or local-name()='archive'] or $significant-nodes/@书社:disable-output-wrapping">
-                                                                                               <xslt:copy-of select="."/>
-                                                                                       </xslt:when>
-                                                                                       <xslt:otherwise>
-                                                                                               <xslt:call-template name="书社:wrap">
-                                                                                                       <xslt:with-param name="nodes" select="$result-nodes"/>
-                                                                                               </xslt:call-template>
-                                                                                       </xslt:otherwise>
-                                                                               </xslt:choose>
-                                                                       </xslt:for-each>
-                                                               </xslt:variable>
-                                                               <xslt:apply-templates select="exsl:node-set($wrapped-component-fragment)/node()" mode="书社:apply"/>
-                                                       </xslt:when>
-                                                       <xslt:otherwise>
-                                                               <xslt:apply-templates select="." mode="书社:apply"/>
-                                                       </xslt:otherwise>
-                                               </xslt:choose>
-                                       </xslt:for-each>
-                               </xslt:copy>
-                       </xslt:template>
                        <xslt:template match="书社:apply-attributes" mode="书社:apply" priority="1">
                                <xslt:variable name="children">
                                        <xslt:apply-templates mode="书社:apply"/>
                        <xslt:template match="书社:apply-attributes" mode="书社:apply" priority="1">
                                <xslt:variable name="children">
                                        <xslt:apply-templates mode="书社:apply"/>
@@ -510,6 +445,79 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                </xslt:copy>
                        </xslt:template>
                        <xslt:template match="text()" mode="书社:header"/>
                                </xslt:copy>
                        </xslt:template>
                        <xslt:template match="text()" mode="书社:header"/>
+                       <xslt:template match="书社:archive" mode="书社:finalize" priority="1">
+                               <xslt:copy>
+                                       <xslt:apply-templates select="@*" mode="书社:finalize"/>
+                                       <xslt:for-each select="node()">
+                                               <xslt:choose>
+                                                       <xslt:when test="self::*">
+                                                               <xslt:variable name="component-fragment">
+                                                                       <xslt:copy>
+                                                                               <xslt:choose>
+                                                                                       <xslt:when test="self::html:*">
+                                                                                               <xslt:variable name="existing-types" select="exslstr:tokenize(@itemtype)"/>
+                                                                                               <xslt:attribute name="itemscope">itemscope</xslt:attribute>
+                                                                                               <xslt:attribute name="itemtype">
+                                                                                                       <xslt:for-each select="$existing-types/token">
+                                                                                                               <xslt:if test="position()!=1">
+                                                                                                                       <xslt:text>
+                                                                                                                               <text> </text>
+                                                                                                                       </xslt:text>
+                                                                                                               </xslt:if>
+                                                                                                               <xslt:value-of select="."/>
+                                                                                                       </xslt:for-each>
+                                                                                                       <xslt:if test="not($existing-types/token[string()='&书社;:embed' or string()='&书社;:document'])">
+                                                                                                               <xslt:if test="$existing-types/token">
+                                                                                                                       <xslt:text>
+                                                                                                                               <text> </text>
+                                                                                                                       </xslt:text>
+                                                                                                               </xslt:if>
+                                                                                                               <xslt:text>
+                                                                                                                       <text>&书社;:document</text>
+                                                                                                               </xslt:text>
+                                                                                                       </xslt:if>
+                                                                                               </xslt:attribute>
+                                                                                               <xslt:copy-of select="@*[not(namespace-uri()='' and (local-name()='itemscope' or local-name()='itemtype'))]|node()"/>
+                                                                                       </xslt:when>
+                                                                                       <xslt:otherwise>
+                                                                                               <xslt:copy-of select="@*|node()"/>
+                                                                                       </xslt:otherwise>
+                                                                               </xslt:choose>
+                                                                       </xslt:copy>
+                                                               </xslt:variable>
+                                                               <xslt:variable name="finalization-fragment">
+                                                                       <xslt:apply-templates select="exsl:node-set($component-fragment)" mode="书社:finalize"/>
+                                                               </xslt:variable>
+                                                               <xslt:copy-of select="书社:document-with-attributes-applied(exsl:node-set($finalization-fragment))"/>
+                                                       </xslt:when>
+                                                       <xslt:otherwise>
+                                                               <xslt:apply-templates select="." mode="书社:finalize"/>
+                                                       </xslt:otherwise>
+                                               </xslt:choose>
+                                       </xslt:for-each>
+                               </xslt:copy>
+                       </xslt:template>
+                       <xslt:template match="@书社:destination|@书社:disable-output-wrapping" mode="书社:finalize" priority="1"/>
+                       <xslt:template match="/" mode="书社:finalize">
+                               <xslt:choose>
+                                       <xslt:when test="not(html:*) or 书社:raw-text or 书社:base64-binary or 书社:archive or */@书社:disable-output-wrapping">
+                                               <xslt:apply-templates mode="书社:finalize"/>
+                                       </xslt:when>
+                                       <xslt:otherwise>
+                                               <xslt:variable name="wrapped-result">
+                                                       <xslt:call-template name="书社:wrap">
+                                                               <xslt:with-param name="nodes" select="node()"/>
+                                                       </xslt:call-template>
+                                               </xslt:variable>
+                                               <xslt:apply-templates select="exsl:node-set($wrapped-result)/node()" mode="书社:finalize"/>
+                                       </xslt:otherwise>
+                               </xslt:choose>
+                       </xslt:template>
+                       <xslt:template match="@*|node()" mode="书社:finalize" priority="-1">
+                               <xslt:copy>
+                                       <xslt:apply-templates select="@*|node()" mode="书社:finalize"/>
+                               </xslt:copy>
+                       </xslt:template>
                        <xslt:template match="text()" mode="书社:footer"/>
                        <xslt:template match="text()" mode="书社:metadata"/>
                        <xslt:output method="xml" encoding="UTF-8" cdata-section-elements="html:script html:style html:textarea 书社:raw-output 书社:raw-text"/>
                        <xslt:template match="text()" mode="书社:footer"/>
                        <xslt:template match="text()" mode="书社:metadata"/>
                        <xslt:output method="xml" encoding="UTF-8" cdata-section-elements="html:script html:style html:textarea 书社:raw-output 书社:raw-text"/>
index 01e6f8d15beaed5f7af054b898e106761f1fa8f9..3430526d5c37378eb1242d18c0068aa8f9fed3be 100644 (file)
@@ -11,14 +11,19 @@ SPDX-License-Identifier: MPL-2.0
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 -->
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 -->
+<!DOCTYPE transform [
+       <!ENTITY xhtml 'http://www.w3.org/1999/xhtml'>
+]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
-       xmlns:html="http://www.w3.org/1999/xhtml"
+       xmlns:html="&xhtml;"
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        version="1.0"
 >
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:plain.xslt</书社:id>
        <template match="html:script[@type='text/plain']">
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        version="1.0"
 >
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:plain.xslt</书社:id>
        <template match="html:script[@type='text/plain']">
-               <html:pre><value-of select="."/></html:pre>
+               <element name="pre" namespace="&xhtml;">
+                       <value-of select="."/>
+               </element>
        </template>
 </transform>
        </template>
 </transform>
index bd047305f6d4d12376c2183579264bce40555ff0..b58505d3600da93e842ae752f1581a4fa30f6cc2 100644 (file)
@@ -11,11 +11,14 @@ SPDX-License-Identifier: MPL-2.0
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 -->
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 -->
+<!DOCTYPE transform [
+       <!ENTITY xhtml 'http://www.w3.org/1999/xhtml'>
+]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        xmlns:exsl="http://exslt.org/common"
        xmlns:exslstr="http://exslt.org/strings"
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        xmlns:exsl="http://exslt.org/common"
        xmlns:exslstr="http://exslt.org/strings"
-       xmlns:html="http://www.w3.org/1999/xhtml"
+       xmlns:html="&xhtml;"
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        extension-element-prefixes="exsl exslstr"
        version="1.0"
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        extension-element-prefixes="exsl exslstr"
        version="1.0"
@@ -23,23 +26,28 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:record-jar.xslt</书社:id>
        <template match="html:script[@type='text/record-jar']">
                <variable name="lines" select="exslstr:tokenize(., '&#xA;')"/>
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:record-jar.xslt</书社:id>
        <template match="html:script[@type='text/record-jar']">
                <variable name="lines" select="exslstr:tokenize(., '&#xA;')"/>
-               <html:div>
+               <element name="div" namespace="&xhtml;">
+                       <if test="starts-with($lines[1], '%%') and substring-after($lines[1], '%%')!=''">
+                               <comment>
+                                       <value-of select="substring-after($lines[1], '%%')"/>
+                               </comment>
+                       </if>
                        <for-each select="$lines[not(position()=1) and starts-with(., '%%')]">
                                <variable name="end" select="."/>
                                <variable name="start" select="preceding-sibling::*[starts-with(., '%%')][1]"/>
                                <variable name="fields" select="($start/following-sibling::*|$lines[not($start)])[not(preceding-sibling::*[generate-id()=generate-id($end)]) and not(starts-with(., '%%'))]"/>
                                <if test="$fields">
                        <for-each select="$lines[not(position()=1) and starts-with(., '%%')]">
                                <variable name="end" select="."/>
                                <variable name="start" select="preceding-sibling::*[starts-with(., '%%')][1]"/>
                                <variable name="fields" select="($start/following-sibling::*|$lines[not($start)])[not(preceding-sibling::*[generate-id()=generate-id($end)]) and not(starts-with(., '%%'))]"/>
                                <if test="$fields">
-                                       <html:dl>
+                                       <element name="dl" namespace="&xhtml;">
                                                <for-each select="$fields">
                                                        <choose>
                                                                <when test="starts-with(., ' ') and $fields[generate-id()=generate-id(current()/preceding-sibling::*[1])]"/>
                                                                <otherwise>
                                                                        <variable name="next" select="following-sibling::*[not(starts-with(., ' '))]"/>
                                                <for-each select="$fields">
                                                        <choose>
                                                                <when test="starts-with(., ' ') and $fields[generate-id()=generate-id(current()/preceding-sibling::*[1])]"/>
                                                                <otherwise>
                                                                        <variable name="next" select="following-sibling::*[not(starts-with(., ' '))]"/>
-                                                                       <html:div>
-                                                                               <html:dt>
+                                                                       <element name="div" namespace="&xhtml;">
+                                                                               <element name="dt" namespace="&xhtml;">
                                                                                        <value-of select="normalize-space(substring-before(., ':'))"/>
                                                                                        <value-of select="normalize-space(substring-before(., ':'))"/>
-                                                                               </html:dt>
-                                                                               <html:dd>
+                                                                               </element>
+                                                                               <element name="dd" namespace="&xhtml;">
                                                                                        <variable name="firstline">
                                                                                                <choose>
                                                                                                        <when test="contains(., ':')">
                                                                                        <variable name="firstline">
                                                                                                <choose>
                                                                                                        <when test="contains(., ':')">
@@ -69,14 +77,19 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                                                                                        </otherwise>
                                                                                                </choose>
                                                                                        </for-each>
                                                                                                        </otherwise>
                                                                                                </choose>
                                                                                        </for-each>
-                                                                               </html:dd>
-                                                                       </html:div>
+                                                                               </element>
+                                                                       </element>
                                                                </otherwise>
                                                        </choose>
                                                </for-each>
                                                                </otherwise>
                                                        </choose>
                                                </for-each>
-                                       </html:dl>
+                                       </element>
+                               </if>
+                               <if test="substring-after(., '%%')!=''">
+                                       <comment>
+                                               <value-of select="substring-after(., '%%')"/>
+                                       </comment>
                                </if>
                        </for-each>
                                </if>
                        </for-each>
-               </html:div>
+               </element>
        </template>
 </transform>
        </template>
 </transform>
index 21ad1e0b6bdccaaa52ddd2859c1fd4b1aa6d593c..90e156e28c776c3b51bf74b557c1e35a687e5396 100644 (file)
@@ -11,11 +11,14 @@ SPDX-License-Identifier: MPL-2.0
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 -->
 This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
 If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 -->
+<!DOCTYPE transform [
+       <!ENTITY xhtml 'http://www.w3.org/1999/xhtml'>
+]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        xmlns:exsl="http://exslt.org/common"
        xmlns:exslstr="http://exslt.org/strings"
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        xmlns:exsl="http://exslt.org/common"
        xmlns:exslstr="http://exslt.org/strings"
-       xmlns:html="http://www.w3.org/1999/xhtml"
+       xmlns:html="&xhtml;"
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        extension-element-prefixes="exsl exslstr"
        version="1.0"
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        extension-element-prefixes="exsl exslstr"
        version="1.0"
@@ -23,54 +26,70 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
        <import href="../lib/split.xslt"/>
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:tsv.xslt</书社:id>
        <template match="html:script[@type='text/tab-separated-values']">
        <import href="../lib/split.xslt"/>
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:tsv.xslt</书社:id>
        <template match="html:script[@type='text/tab-separated-values']">
-               <variable name="rows" select="exslstr:tokenize(., '&#xA;')[normalize-space(.)!='' and not(starts-with(., '#'))]"/>
-               <variable name="head" select="$rows[1]"/>
+               <variable name="lines" select="exslstr:tokenize(., '&#xA;')"/>
+               <variable name="head" select="$lines[normalize-space(.)!='' and not(starts-with(., '#'))][1]"/>
                <variable name="headcols">
                        <call-template name="书社:split">
                                <with-param name="source" select="string($head)"/>
                                <with-param name="separator" select="'&#x9;'"/>
                        </call-template>
                </variable>
                <variable name="headcols">
                        <call-template name="书社:split">
                                <with-param name="source" select="string($head)"/>
                                <with-param name="separator" select="'&#x9;'"/>
                        </call-template>
                </variable>
-               <variable name="body" select="$rows[not(position()=1)]"/>
-               <html:table>
-                       <html:thead>
-                               <html:tr>
+               <element name="table" namespace="&xhtml;">
+                       <for-each select="$lines[starts-with(., '#') and following-sibling::*[generate-id(.)=generate-id($head)]]">
+                               <comment>
+                                       <value-of select="substring-after(., '#')"/>
+                               </comment>
+                       </for-each>
+                       <element name="thead" namespace="&xhtml;">
+                               <element name="tr" namespace="&xhtml;">
                                        <for-each select="exsl:node-set($headcols)/*">
                                        <for-each select="exsl:node-set($headcols)/*">
-                                               <html:th scope="col">
+                                               <element name="th" namespace="&xhtml;">
+                                                       <attribute name="scope">
+                                                               <text>col</text>
+                                                       </attribute>
                                                        <value-of select="."/>
                                                        <value-of select="."/>
-                                               </html:th>
+                                               </element>
                                        </for-each>
                                        </for-each>
-                               </html:tr>
-                       </html:thead>
-                       <html:tbody>
-                               <for-each select="$body">
-                                       <variable name="cols">
-                                               <call-template name="书社:split">
-                                                       <with-param name="source" select="string(.)"/>
-                                                       <with-param name="separator" select="'&#x9;'"/>
-                                               </call-template>
-                                       </variable>
-                                       <html:tr>
-                                               <for-each select="exsl:node-set($cols)/*[count(exsl:node-set($headcols)/*)>=position()]">
-                                                       <html:td>
-                                                               <attribute name="data-tsv-header">
-                                                                       <value-of select="exsl:node-set($headcols)/*[count(current()/preceding-sibling::*)+1]"/>
-                                                               </attribute>
-                                                               <value-of select="."/>
-                                                               <if test="position()=count(exsl:node-set($headcols)/*)">
-                                                                       <for-each select="following-sibling::*">
-                                                                               <text>&#x9;</text>
+                               </element>
+                       </element>
+                       <element name="tbody" namespace="&xhtml;">
+                               <for-each select="$head/following-sibling::*[normalize-space(.)!='']">
+                                       <choose>
+                                               <when test="starts-with(., '#')">
+                                                       <comment>
+                                                               <value-of select="substring-after(., '#')"/>
+                                                       </comment>
+                                               </when>
+                                               <otherwise>
+                                                       <variable name="cols">
+                                                               <call-template name="书社:split">
+                                                                       <with-param name="source" select="string(.)"/>
+                                                                       <with-param name="separator" select="'&#x9;'"/>
+                                                               </call-template>
+                                                       </variable>
+                                                       <element name="tr" namespace="&xhtml;">
+                                                               <for-each select="exsl:node-set($cols)/*[count(exsl:node-set($headcols)/*)>=position()]">
+                                                                       <element name="td" namespace="&xhtml;">
+                                                                               <attribute name="data-tsv-header">
+                                                                                       <value-of select="exsl:node-set($headcols)/*[count(current()/preceding-sibling::*)+1]"/>
+                                                                               </attribute>
                                                                                <value-of select="."/>
                                                                                <value-of select="."/>
-                                                                       </for-each>
-                                                               </if>
-                                                       </html:td>
-                                               </for-each>
-                                               <for-each select="exsl:node-set($headcols)/*[position()>count(exsl:node-set($cols)/*)]">
-                                                       <html:td/>
-                                               </for-each>
-                                       </html:tr>
+                                                                               <if test="position()=count(exsl:node-set($headcols)/*)">
+                                                                                       <for-each select="following-sibling::*">
+                                                                                               <text>&#x9;</text>
+                                                                                               <value-of select="."/>
+                                                                                       </for-each>
+                                                                               </if>
+                                                                       </element>
+                                                               </for-each>
+                                                               <for-each select="exsl:node-set($headcols)/*[position()>count(exsl:node-set($cols)/*)]">
+                                                                       <element name="td" namespace="&xhtml;"/>
+                                                               </for-each>
+                                                       </element>
+                                               </otherwise>
+                                       </choose>
                                </for-each>
                                </for-each>
-                       </html:tbody>
-               </html:table>
+                       </element>
+               </element>
        </template>
 </transform>
        </template>
 </transform>
index 38d2e671cdde33299c648621e834abaf82b95ebc..89f1475598f78bf318947bd031cc53c2ecbaafb8 100644 (file)
@@ -13,42 +13,69 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
 -->
 <!DOCTYPE transform [
        <!ENTITY wrapped "ancestor::html:html[not(parent::* or @书社:disable-output-wrapping)]">
 -->
 <!DOCTYPE transform [
        <!ENTITY wrapped "ancestor::html:html[not(parent::* or @书社:disable-output-wrapping)]">
+       <!ENTITY xhtml "http://www.w3.org/1999/xhtml">
 ]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        xmlns:exsl="http://exslt.org/common"
 ]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        xmlns:exsl="http://exslt.org/common"
-       xmlns:html="http://www.w3.org/1999/xhtml"
+       xmlns:html="&xhtml;"
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        extension-element-prefixes="exsl"
        version="1.0"
 >
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:asset.xslt</书社:id>
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        extension-element-prefixes="exsl"
        version="1.0"
 >
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:asset.xslt</书社:id>
-       <template match="html:style[&wrapped; and not(ancestor::html:head)]|html:object[@type='text/css' and &wrapped;]" mode="书社:apply" priority="0"/>
-       <template match="html:object[@type='text/javascript']" mode="书社:apply" priority="0">
-               <html:script type="{@type}" src="{@data}">
+       <template match="html:style[&wrapped; and not(ancestor::html:head)]|html:object[@type='text/css' and &wrapped;]" mode="书社:finalize" priority="0"/>
+       <template match="html:object[@type='text/javascript']" mode="书社:finalize" priority="0">
+               <element name="script" namespace="&xhtml;">
+                       <attribute name="type">
+                               <value-of select="@type"/>
+                       </attribute>
+                       <attribute name="src">
+                               <value-of select="@data"/>
+                       </attribute>
                        <copy-of select="@书社:identifier"/>
                        <copy-of select="@书社:identifier"/>
-               </html:script>
+               </element>
        </template>
        </template>
-       <template match="html:object[starts-with(@type, 'audio/')]" mode="书社:apply" priority="0">
-               <html:audio controls="" src="{@data}">
+       <template match="html:object[starts-with(@type, 'audio/')]" mode="书社:finalize" priority="0">
+               <element name="audio" namespace="&xhtml;">
+                       <attribute name="controls"/>
+                       <attribute name="src">
+                               <value-of select="@data"/>
+                       </attribute>
                        <copy-of select="@书社:identifier"/>
                        <copy-of select="@书社:identifier"/>
-               </html:audio>
+               </element>
        </template>
        </template>
-       <template match="html:object[starts-with(@type, 'image/') and not(@type='image/svg+xml')]" mode="书社:apply" priority="0">
-               <html:img src="{@data}">
+       <template match="html:object[starts-with(@type, 'image/') and not(@type='image/svg+xml')]" mode="书社:finalize" priority="0">
+               <element name="img" namespace="&xhtml;">
+                       <attribute name="src">
+                               <value-of select="@data"/>
+                       </attribute>
                        <copy-of select="@书社:identifier|@width|@height"/>
                        <copy-of select="@书社:identifier|@width|@height"/>
-               </html:img>
+               </element>
        </template>
        </template>
-       <template match="html:object[starts-with(@type, 'video/')]" mode="书社:apply" priority="0">
-               <html:video controls="" src="{@data}">
+       <template match="html:object[starts-with(@type, 'video/')]" mode="书社:finalize" priority="0">
+               <element name="video" namespace="&xhtml;">
+                       <attribute name="controls"/>
+                       <attribute name="src">
+                               <value-of select="@data"/>
+                       </attribute>
                        <copy-of select="@书社:identifier|@width|@height"/>
                        <copy-of select="@书社:identifier|@width|@height"/>
-               </html:video>
+               </element>
        </template>
        <template match="书社:id[string(.)='urn:fdc:ladys.computer:20231231:Shu1She4:asset.xslt']" mode="书社:metadata">
                <for-each select="$书社:expansion//html:object[@type='text/css']">
        </template>
        <template match="书社:id[string(.)='urn:fdc:ladys.computer:20231231:Shu1She4:asset.xslt']" mode="书社:metadata">
                <for-each select="$书社:expansion//html:object[@type='text/css']">
-                       <html:link rel="stylesheet" type="text/css" href="{@data}">
+                       <element name="link" namespace="&xhtml;">
+                               <attribute name="rel">
+                                       <text>stylesheet</text>
+                               </attribute>
+                               <attribute name="type">
+                                       <text>text/css</text>
+                               </attribute>
+                               <attribute name="href">
+                                       <value-of select="@data"/>
+                               </attribute>
                                <copy-of select="@书社:identifier"/>
                                <copy-of select="@书社:identifier"/>
-                       </html:link>
+                       </element>
                </for-each>
                <for-each select="$书社:expansion//html:style[not(ancestor::html:head)]">
                        <copy-of select="."/>
                </for-each>
                <for-each select="$书社:expansion//html:style[not(ancestor::html:head)]">
                        <copy-of select="."/>
index 71a10c515ecf02a86b60f7e0c4f5c465e0d04584..c4817509323cab4149ecdbceb974984ee45c2953 100644 (file)
@@ -13,18 +13,19 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
 -->
 <!DOCTYPE transform [
        <!ENTITY attoplevel "not(ancestor::html:template) and ancestor::html:*[@itemscope and @itemtype='&书社;:document'] and not(ancestor::html:*[@itemscope]/ancestor::html:*[@itemscope and @itemtype='&书社;:document'] or preceding::html:*[@itemtype='&书社;:document'])">
 -->
 <!DOCTYPE transform [
        <!ENTITY attoplevel "not(ancestor::html:template) and ancestor::html:*[@itemscope and @itemtype='&书社;:document'] and not(ancestor::html:*[@itemscope]/ancestor::html:*[@itemscope and @itemtype='&书社;:document'] or preceding::html:*[@itemtype='&书社;:document'])">
+       <!ENTITY xhtml "http://www.w3.org/1999/xhtml">
        <!ENTITY 书社 "urn:fdc:ladys.computer:20231231:Shu1She4">
 ]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
        <!ENTITY 书社 "urn:fdc:ladys.computer:20231231:Shu1She4">
 ]>
 <transform
        xmlns="http://www.w3.org/1999/XSL/Transform"
-       xmlns:html="http://www.w3.org/1999/xhtml"
-       xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+       xmlns:html="&xhtml;"
+       xmlns:书社="&书社;"
        version="1.0"
 >
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:metadata.xslt</书社:id>
        <template match="html:meta[not(@name) and @itemprop='&书社;:title'][&attoplevel;]" mode="书社:metadata">
        version="1.0"
 >
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:metadata.xslt</书社:id>
        <template match="html:meta[not(@name) and @itemprop='&书社;:title'][&attoplevel;]" mode="书社:metadata">
-               <html:title>
+               <element name="title" namespace="&xhtml;">
                        <value-of select="@content"/>
                        <value-of select="@content"/>
-               </html:title>
+               </element>
        </template>
 </transform>
        </template>
 </transform>
index ce986ff7a991433594dc1d59d5ce7075f36f1a2a..13c47b8b01a733c2fa4175d10e526f679093752f 100644 (file)
@@ -22,10 +22,10 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
 >
        <import href="../lib/serialize.xslt"/>
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:serialization.xslt</书社:id>
 >
        <import href="../lib/serialize.xslt"/>
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:serialization.xslt</书社:id>
-       <template match="书社:serialize-xml" mode="书社:apply" priority="1">
+       <template match="书社:serialize-xml" mode="书社:finalize" priority="1">
                <variable name="namespaces" select="namespace::*"/>
                <variable name="contents">
                <variable name="namespaces" select="namespace::*"/>
                <variable name="contents">
-                       <apply-templates mode="书社:apply"/>
+                       <apply-templates mode="书社:finalize"/>
                </variable>
                <variable name="passthru-namespaces" select="exslstr:tokenize(string(@with-namespaces))"/>
                <apply-templates select="exsl:node-set($contents)" mode="书社:serialize">
                </variable>
                <variable name="passthru-namespaces" select="exslstr:tokenize(string(@with-namespaces))"/>
                <apply-templates select="exsl:node-set($contents)" mode="书社:serialize">
This page took 0.259024 seconds and 4 git commands to generate.