]> Lady’s Gitweb - Shushe/commitdiff
Apply parser attributes before reparsing current
authorLady <redacted>
Sat, 30 Aug 2025 17:24:24 +0000 (13:24 -0400)
committerLady <redacted>
Sat, 30 Aug 2025 17:24:24 +0000 (13:24 -0400)
This supports the beneficial case of wanting to match on
`@书社:parsed-by` or `@书社:media-type` in (re)parsers.

13 files changed:
.metadata-format-changed-since
GNUmakefile
README.markdown
lib/archive2extractor.xslt
lib/catalog2parser.xslt
lib/catalog2transform.xslt
lib/expandmetadata.xslt
parsers/plain.xslt
parsers/record-jar.xslt
parsers/tsv.xslt
transforms/asset.xslt
transforms/metadata.xslt
transforms/serialization.xslt

index 6cf4047917a5feb4fd9637e12153c6aa0feff88a..b6faef57639ca664c7b0abe4a6feccd08e3d156d 100644 (file)
@@ -1,10 +1,10 @@
-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
 
 The following hash indicates a commit in which the metadata format
 generated by ⛩📰 书社 was different than it currently is :⁠—
 
 SPDX-License-Identifier: CC0-1.0
 
 The following hash indicates a commit in which the metadata format
 generated by ⛩📰 书社 was different than it currently is :⁠—
 
-c84c2b38caf34807fd1c52a8f19fcf0af7e9807e
+338b26f8c92351bad03a180ad4b4f88e4cfeab76
 
 The purpose of this file is to serve as a trackable dependency which
 will prompt a rebuild of metadata when the generation mechanism
 
 The purpose of this file is to serve as a trackable dependency which
 will prompt a rebuild of metadata when the generation mechanism
index 95fcbf4e40304c0d75d1197b5fb0c1b59118208f..8f62e4697d79709db8fbced4f9ab2e1e0d2e301f 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
@@ -83,7 +83,7 @@ override define makefileinfo
 ║╰────────────────────────────────────────────────────────────╯║
 ╟┬ ¶ Copyright & License ─────────────────────────────────────┬╢
 ║│                                                            │║
 ║╰────────────────────────────────────────────────────────────╯║
 ╟┬ ¶ Copyright & License ─────────────────────────────────────┬╢
 ║│                                                            │║
-║│ Copyright © 2023–2024 Lady [@ Ladys Computer].             │║
+║│ Copyright © 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  │║
 ║│                                                            │║
 ║│ 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  │║
@@ -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´))
 
@@ -443,7 +488,7 @@ override includepath = $(or $(firstword $(foreach directory,$(INCLUDEDIR),$(if $
 # (callable) Get base64 data u·r·i’s for the given files.
 #
 # ☡ This variable creates a subshell every time it is computed.
 # (callable) Get base64 data u·r·i’s for the given files.
 #
 # ☡ This variable creates a subshell every time it is computed.
-override datauri = $(foreach file,$1,data:$(call typeoffile,$(file));base64,$(shell $(UUENCODE) -m $(call quote,$(file)) _ | $(SED) '2,$$!d;$$d' | $(TR) -d ' \n'))
+override datauri = $(foreach file,$1,data:$(call typeoffile,$(file));base64,$(shell $(UUENCODE) -m $(call quote,$(abspath $(file))) _ | $(SED) '2,$$!d;$$d' | $(TR) -d ' \n'))
 
 # Pair each source file and include with its local u·r·i.
 override sourcelocalpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|about:shushe?$(if $(filter $(file),$(sourceincludes)),include=$(call pathenc,$(call includepath,$(file))),source=$(call pathenc,$(call sourcepath,$(file)))))
 
 # Pair each source file and include with its local u·r·i.
 override sourcelocalpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|about:shushe?$(if $(filter $(file),$(sourceincludes)),include=$(call pathenc,$(call includepath,$(file))),source=$(call pathenc,$(call sourcepath,$(file)))))
@@ -463,25 +508,34 @@ override typeupdates := $(and $(wildcard $(BUILDDIR)/.update-types),FORCE)
 # Pair each source file and include with its metadata location.
 override sourcemetadatapair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes.metadata/$(call includepath,$(file)),sources.metadata/$(call sourcepath,$(file))))
 
 # Pair each source file and include with its metadata location.
 override sourcemetadatapair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes.metadata/$(call includepath,$(file)),sources.metadata/$(call sourcepath,$(file))))
 
-# (callable) Get the location of the transformed X·M·L files for the given source files.
+# (callable) Get the location of the metadata files for the given source files.
 override metadata = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourcemetadatapair))))
 
 override metadata = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourcemetadatapair))))
 
-# (callable) Get the source files for the given parsed file.
+# (callable) Get the source files for the given metadata files.
 override datadata = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourcemetadatapair))))
 
 # Pair each source file and include with its parsed location.
 override datadata = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourcemetadatapair))))
 
 # Pair each source file and include with its parsed location.
-override sourceparsedpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes/$(call includepath,$(file)),sources/$(call sourcepath,$(file))))
+override sourceparsedpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes.parsed/$(call includepath,$(file)),sources.parsed/$(call sourcepath,$(file))))
 
 # (callable) Get the location of the transformed X·M·L files for the given source files.
 override parsed = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourceparsedpair))))
 
 
 # (callable) Get the location of the transformed X·M·L files for the given source files.
 override parsed = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourceparsedpair))))
 
-# (callable) Get the source files for the given parsed file.
+# (callable) Get the source files for the given parsed files.
 override unparsed = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourceparsedpair))))
 
 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 source file and include with its parsed result location.
+override parsepair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes/$(call includepath,$(file)),sources/$(call sourcepath,$(file))))
 
 
-# (callable) Get the file u·r·is for the given parsers, transforms, source file or parsed files.
+# (callable) Get the location of the parsed results for the given source files.
+override parseresult = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(parsepair))))
+
+# (callable) Get the source files for the given parsed results.
+override parsesource = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(parsepair))))
+
+# Pair each build directory, transform, source file, or parsed file, parse result file with its file u·r·i.
+override fileuripairs := $(join $(patsubst %,%|,$(BUILDDIR) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)) $(call parseresult,$(sourcefiles) $(sourceincludes))),$(call pathenc,$(foreach uriable,$(BUILDDIR) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)) $(call parseresult,$(sourcefiles) $(sourceincludes)),file://$(abspath $(uriable)))))
+
+# (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,38 +604,21 @@ 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´.
 # (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>'; }
+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,$(abspath $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) Check if the provided X·M·L file is X·M·L 1.1, and if so, coerce to X·M·L 1.0 as best as possible, printing the result (or the original file contents) to `stdout´.
 #
 # The X·M·L declaration will be dropped and character escapes for C0 control codes will be replaced with a literal `U+0091 PRIVATE USE ONE´, which is invalid in X·M·L 1.1, but valid X·M·L 1.0 (making the replacement obvious).
 #
 # 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.
 
 # (callable) Check if the provided X·M·L file is X·M·L 1.1, and if so, coerce to X·M·L 1.0 as best as possible, printing the result (or the original file contents) to `stdout´.
 #
 # The X·M·L declaration will be dropped and character escapes for C0 control codes will be replaced with a literal `U+0091 PRIVATE USE ONE´, which is invalid in X·M·L 1.1, but valid X·M·L 1.0 (making the replacement obvious).
 #
 # 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)
+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,$(abspath $1)) | $(SED) "$$($(PRINTF) '%b' ':a\n/^\\n*$$/{ $$d\n N\n ba\n}')"
 
 # ─ ¶ 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 +632,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 +653,48 @@ 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.
-#
-# 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 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,$@))
-
 # 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"?><parsed xmlns="urn:fdc:ladys.computer:20231231:Shu1She4"><result><object xmlns="http://www.w3.org/1999/xhtml" type="$(call typeoffile,$<)" data="$(call datauri,$<)"/></result></parsed>) >|$(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,$(abspath $(BUILDDIR)/parser.xslt)) - >|$(call quote,$@))
+
+# Extract the results from the parsed files.
+$(call parseresult,$(sourcefiles) $(sourceincludes)) : % : $$(call parsed,$$(call parsesource,$$@))
+       $(silent)$(call ensuredirectory,$(dir $@))
+       $(silent)$(PRINTF) '%s\n' '<transform xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:s="urn:fdc:ladys.computer:20231231:Shu1She4" version="1.0"><template match="/"><copy-of select="s:parsed/s:result/node()"/></template></transform>' | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite - $(call quote,$<) >|$(call quote,$@)
+
+# Generate R·D·F metadata for files.
+$(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(THISDIR)/.metadata-format-changed-since
+       $(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"/><书社vocab:hasParsedFileWithMetadata 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 parseresult,$<)))) $(call quote,$(call attresc,$(call fileuri,$(call parsed,$<)))) >|$(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
-       $(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))
+$(BUILDDIR)/dependencies $(BUILDDIR)/destinations $(BUILDDIR)/metadata : $(call diffprereqs,metadatas,$(call metadata,$(sort $(sourcefiles) $(sourceincludes)))) $$(call parseresult,$(sourcefiles) $(sourceincludes)) $(THISDIR)/lib/expandmetadata.xslt
+       $(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 +704,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 parseresult,$$(call uncompiled,$$@)) $$(call parseresult,$$(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 +801,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..0b4c4b4ef135c7375368a9f78e859964998e4cf2 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
 -->
 # ⛩📰 书社
@@ -188,6 +188,7 @@ This document uses a few name·space prefixes, with the following
 |    `exsl:` | `http://exslt.org/common`                     |
 | `exslstr:` | `http://exslt.org/strings`                    |
 |    `html:` | `http://www.w3.org/1999/xhtml`                |
 |    `exsl:` | `http://exslt.org/common`                     |
 | `exslstr:` | `http://exslt.org/strings`                    |
 |    `html:` | `http://www.w3.org/1999/xhtml`                |
+|     `rdf:` | `http://www.w3.org/1999/02/22-rdf-syntax-ns#` |
 |     `svg:` | `http://www.w3.org/2000/svg`                  |
 |   `xlink:` | `http://www.w3.org/1999/xlink`                |
 |    `xslt:` | `http://www.w3.org/1999/XSL/Transform`        |
 |     `svg:` | `http://www.w3.org/2000/svg`                  |
 |   `xlink:` | `http://www.w3.org/1999/xlink`                |
 |    `xslt:` | `http://www.w3.org/1999/XSL/Transform`        |
@@ -307,6 +308,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 +326,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`).
@@ -515,6 +548,18 @@ These include :⁠—
 - A `@书社:media-type` attribute, giving the identified media type of
     the plaintext node.
 
 - A `@书社:media-type` attribute, giving the identified media type of
     the plaintext node.
 
+### Parsed metadata
+
+It is possible to extract metadata from a document at the same time as
+  it is being parsed.
+This is done by creating result elements in the `书社:about` mode;
+  these should be R·D·F property elements which apply to the conceptual
+  entity that is the document being parsed.
+
+During transformation, metadata for the file with identifier `$FILE`
+  can be read from the children of
+  `$书社:about//*[@rdf:about=$FILE]/nie:interpretedAs/*`.
+
 ## Output Redirection
 
 By default, ⛩📰 书社 installs files to the same location in `DESTDIR`
 ## Output Redirection
 
 By default, ⛩📰 书社 installs files to the same location in `DESTDIR`
@@ -585,7 +630,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 +649,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
@@ -658,6 +703,25 @@ The following params are made available globally in parsers and
 - **`THISREV`:**
   The value of the `THISREV` variable (if present).
 
 - **`THISREV`:**
   The value of the `THISREV` variable (if present).
 
+In transforms, the following params are additionally available :⁠—
+
+- **`书社:about`:**
+  R·D·F metadata about all of the documents ⛩📰 书社 knows about.
+  Use `$书社:about//*[@rdf:about=$IDENTIFIER]` to get the metadata for
+    the current document.
+
+- **`书社:source`:**
+  The parsed source document being transformed, prior to any expansion.
+
+- **`书社:expansion`:**
+  The document after the all embeds have been expanded.
+  Unavailable during the `书社:expand` stage.
+
+- **`书社:result`:**
+  The document after the main set of transformations have been applied.
+  Only available during the `书社:finalize` stage, where it is used to
+    apply output wrapping and other clean·up.
+
 ## Output Wrapping
 
 Provided at least one toplevel result element belongs to the H·T·M·L
 ## Output Wrapping
 
 Provided at least one toplevel result element belongs to the H·T·M·L
@@ -730,8 +794,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 a2eab127f482dd3915bec5b167a087f2cfd5ec0c..b24495c0ccb9ddbd910b92ecd7a48ade74832162 100644 (file)
@@ -1,24 +1,25 @@
 <?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/catalog2parser.xslt
 
 SPDX-License-Identifier: MPL-2.0
 -->
 <!--
 ⁌ ⛩📰 书社 ∷ lib/catalog2parser.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/>.
 -->
 
 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 rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+]>
 <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:exslstr="http://exslt.org/strings"
        xmlns:html="http://www.w3.org/1999/xhtml"
 <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:exslstr="http://exslt.org/strings"
        xmlns:html="http://www.w3.org/1999/xhtml"
-       xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#"
-       xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xslt="http://www.w3.org/1999/XSL/TransformAlias"
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xslt="http://www.w3.org/1999/XSL/TransformAlias"
        xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
@@ -122,28 +123,48 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                </xslt:for-each>
                        </xslt:template>
                        <xslt:template match="/">
                                </xslt:for-each>
                        </xslt:template>
                        <xslt:template match="/">
-                               <xslt:apply-templates select="node()" mode="书社:parse"/>
+                               <xslt:variable name="about">
+                                       <xslt:apply-templates select="node()" mode="书社:about"/>
+                               </xslt:variable>
+                               <xslt:variable name="result">
+                                       <xslt:apply-templates select="node()" mode="书社:parse"/>
+                               </xslt:variable>
+                               <xslt:element name="书社:parsed">
+                                       <xslt:element name="书社:result">
+                                               <xslt:copy-of select="$result"/>
+                                       </xslt:element>
+                                       <xslt:element name="书社:metadata">
+                                               <xslt:element name="rdf:RDF" namespace="&rdf;">
+                                                       <xslt:element name="rdf:Description" namespace="&rdf;">
+                                                               <xslt:copy-of select="$about"/>
+                                                       </xslt:element>
+                                               </xslt:element>
+                                       </xslt:element>
+                               </xslt:element>
                        </xslt:template>
                        <xslt:template match="@*|node()" priority="-1">
                                <xslt:copy>
                                        <xslt:apply-templates select="@*|node()" mode="书社:parse"/>
                                </xslt:copy>
                        </xslt:template>
                        </xslt:template>
                        <xslt:template match="@*|node()" priority="-1">
                                <xslt:copy>
                                        <xslt:apply-templates select="@*|node()" mode="书社:parse"/>
                                </xslt:copy>
                        </xslt:template>
+                       <xslt:template match="@*|node()" mode="书社:about" priority="-1">
+                               <xslt:apply-templates select="@*|node()" mode="书社:about"/>
+                       </xslt:template>
                        <xslt:template match="html:script[@type]" mode="书社:parse" priority="1">
                                <xslt:variable name="parserdiv" select="exsl:node-set($书社:parsers)//html:div[html:dd=current()/@type]"/>
                                <xslt:choose>
                                        <xslt:when test="$parserdiv">
                        <xslt:template match="html:script[@type]" mode="书社:parse" priority="1">
                                <xslt:variable name="parserdiv" select="exsl:node-set($书社:parsers)//html:div[html:dd=current()/@type]"/>
                                <xslt:choose>
                                        <xslt:when test="$parserdiv">
-                                               <xslt:variable name="result">
+                                               <xslt:variable name="result-fragment">
                                                        <xslt:apply-templates select="."/>
                                                </xslt:variable>
                                                        <xslt:apply-templates select="."/>
                                                </xslt:variable>
-                                               <xslt:variable name="reparsed-result">
-                                                       <xslt:apply-templates select="exsl:node-set($result)/node()" mode="书社:parse"/>
+                                               <xslt:variable name="applied-fragment">
+                                                       <xslt:call-template name="书社:apply-parsed-by">
+                                                               <xslt:with-param name="id" select="string($parserdiv/html:dt)"/>
+                                                               <xslt:with-param name="media-type" select="string(@type)"/>
+                                                               <xslt:with-param name="result" select="exsl:node-set($result-fragment)"/>
+                                                       </xslt:call-template>
                                                </xslt:variable>
                                                </xslt:variable>
-                                               <xslt:call-template name="书社:apply-parsed-by">
-                                                       <xslt:with-param name="id" select="string($parserdiv/html:dt)"/>
-                                                       <xslt:with-param name="media-type" select="string(@type)"/>
-                                                       <xslt:with-param name="result" select="exsl:node-set($reparsed-result)"/>
-                                               </xslt:call-template>
+                                               <xslt:apply-templates select="exsl:node-set($applied-fragment)/node()" mode="书社:parse"/>
                                        </xslt:when>
                                        <xslt:otherwise>
                                                <xslt:apply-templates select="."/>
                                        </xslt:when>
                                        <xslt:otherwise>
                                                <xslt:apply-templates select="."/>
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 f2efd18209177db3a846d1053cf16955ed2a74f8..027aaa74d4b2ae67dd131efc179c9d7ca6ca8f1b 100644 (file)
@@ -1,12 +1,12 @@
 <?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/expandmetadata.xslt
 
 SPDX-License-Identifier: MPL-2.0
 -->
 <!--
 ⁌ ⛩📰 书社 ∷ lib/expandmetadata.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/>.
 
 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/>.
@@ -78,6 +78,14 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                        </otherwise>
                </choose>
        </template>
                        </otherwise>
                </choose>
        </template>
+       <template match="nie:interpretedAs/*" priority="1">
+               <copy>
+                       <apply-templates select="@*|node()"/>
+                       <for-each select="../../书社vocab:hasParsedFileWithMetadata">
+                               <copy-of select="document(@nfo:fileUrl)/书社:parsed/书社:metadata/rdf:RDF/rdf:Description/node()"/>
+                       </for-each>
+               </copy>
+       </template>
        <template match="//书社vocab:SourceFile[not(nie:interpretedAs/nfo:PlainTextDocument)]" priority="1">
                <copy>
                        <apply-templates select="@*"/>
        <template match="//书社vocab:SourceFile[not(nie:interpretedAs/nfo:PlainTextDocument)]" priority="1">
                <copy>
                        <apply-templates select="@*"/>
@@ -133,6 +141,7 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                        </for-each>
                </copy>
        </template>
                        </for-each>
                </copy>
        </template>
+       <template match="书社vocab:hasParsedFileWithMetadata" priority="1"/>
        <template match="/">
                <variable name="result-fragment">
                        <apply-templates/>
        <template match="/">
                <variable name="result-fragment">
                        <apply-templates/>
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..235cfec75b4e03b8fa43708c2187af8507f6e976 100644 (file)
@@ -1,21 +1,24 @@
 <?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
 -->
 <!--
 ⁌ ⛩📰 书社 ∷ parsers/tsv.xslt
 
 SPDX-License-Identifier: MPL-2.0
 -->
 <!--
 ⁌ ⛩📰 书社 ∷ parsers/tsv.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/>.
 -->
 
 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,74 @@ 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;">
+                                                                               <attribute name="data-tsv-header">
+                                                                                       <value-of select="."/>
+                                                                               </attribute>
+                                                                       </element>
+                                                               </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.468781 seconds and 4 git commands to generate.