Expanded archives support this need better.
# By default, `find´ will ignore files which begin with a period and those which are likely to cause problems for `make´.
EXTRAFINDRULES :=
EXTRAFINDINCLUDERULES :=
-FINDRULES := ! '(' '(' -name '[.-]*' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*"{*' -o -name '*}"*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a $(EXTRAFINDRULES),)
+FINDRULES := ! '(' '(' -name '[.-]*' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a $(EXTRAFINDRULES),)
FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a $(EXTRAFINDINCLUDERULES),)
# The list of magic files to use when determining media types.
#
# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive ›:
# Generates archive files from parse results.
-#
-# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:paged ›:
-# Generates paginated files from parse results.
MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:default
# Set to a non·empty value to print all commands as they run.
# (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 - $(call quote,$1)
-# (callable) List the files in the provided directory which are associated with the provided filename, one per line.
-override associatedfiles = $(LS) -1 $(call quote,$1) | $(SED) '/.*"{.*}".*/!d;s/^\(.*\)"{.*}"\(.*\)$$/\1\2|&/' | $(GREP) -F -e $(call quote,$2|) | $(SED) 's/^.*|//'
-
-# (callable) Remove files associated with the provided file.
-override removeassociatedfiles = ASSOCIATED="$$($(call associatedfiles,$(dir $1),$(notdir $1)) | $(TR) '\n' ' ')"; if $(TEST) -n "$$ASSOCIATED"; then cd $(call quote,$(dir $1)) && $(PRINTF) '%s\n' "$$ASSOCIATED" | $(SED) 's/ \{2,\}/ /g;s/^ //;s/ $$//' | $(TR) ' ' '\n' | $(xargsmultiquote) | $(XARGS) -E '' $(RM); fi
-
# (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); $(call removeassociatedfiles,$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); $(call removeassociatedfiles,$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)) $(call quote,$2) 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 removeassociatedfiles,$2); elif $(call xpath,//*[local-name()="page" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(MAKE) -f $(call quote,$(abspath $(THISDIR)/GNUmakefile)) $(call quote,$2) NAME=$(call quote,$3) SRC=$(call quote,$1) BUILDDIR=$(call quote,$(BUILDDIR)/paged/$3) DESTDIR=$(call quote,$(patsubst %/,%,$(dir $2))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:paged'; else $(XMLLINT) --nsclean $(call quote,$1) > $(call quote,$2); $(call removeassociatedfiles,$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 $(call quote,$(abspath $(THISDIR)/GNUmakefile)) $(call quote,$2) 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'; else $(XMLLINT) --nsclean $(call quote,$1) > $(call quote,$2); fi
# ━ § BEGIN DEFAULT MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
$(silent)$(call ensuredirectory,$(dir $@))
$(silent)$(RM) -f -R $(call quote,$@)
$(silent)$(CP) -R $(call quote,$<) $(call quote,$@)
- $(silent)for associated in $$($(call associatedfiles,$(dir $<),$(notdir $<))); do $(PRINTF) '%s\n' "$$associated" | $(SED) 's/^\(.*\)"{\(.*\)}"\(.*\)$$/\1\2\3/' | $(xargsquote) | $(XARGS) -E '' $(PRINTF) '%s%s\n' $(call quote,$(dir $@)) | $(xargsquote) | $(XARGS) -E '' $(CP) $(call quote,$(dir $<))"$$associated"; done
# ━ § BEGIN ARCHIVE MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
$(silent)$(RM) -f -R $(call quote,$@)
$(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) '%s\n' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -r -w $(call quote,$(abspath $@)); else $(PRINTF) '%s\n' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -w -x ustar > $(call quote,$(abspath $@)); fi
-# ━ § BEGIN PAGED MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-else ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:paged)
-
-# ─ ¶ Non‐Recipe Variable Definitions ─────────────────────────────────
-
-# Get the list of pages.
-ifneq ($(wildcard $(BUILDDIR)/index),)
-override pagefiles := $(patsubst ./pages/%,%,$(shell $(CAT) $(call quote,$(BUILDDIR)/index)))
-endif
-
-# ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
-
-# ─ ¶ Phony Targets ───────────────────────────────────────────────────
-
-# ─ ¶ Special Targets ─────────────────────────────────────────────────
-
-# Reload this make·file if the pages have changed.
-$(THISDIR)/GNUmakefile : $(BUILDDIR)/index
- $(silent)$(TOUCH) $(THISDIR)/GNUmakefile
- @$(PRINTF) '%b\n' $(call quote,\0033[1mPages for </$(NAME)> updated. Restarting…\0033[22m)
-
-# ─ ¶ Build Targets ───────────────────────────────────────────────────
-
-# Build the extractor transform for extracting the pages of the paged source file.
-$(BUILDDIR)/extractor.xslt : $(SRC) $(THISDIR)/lib/paged2extractor.xslt
- $(silent)$(call ensuredirectory,$(dir $@))
- $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) --stringparam NAME $(call quote,$(call notdir,$<)) $(call quote,$(THISDIR)/lib/paged2extractor.xslt) $(call quote,$<)
-
-# Use the extractor transform to extract the paged source file out into its pages.
-#
-# This target sleeps for 1 second to ensure the resulting index will always be newer than this make·file.
-$(BUILDDIR)/index : $(BUILDDIR)/extractor.xslt $(SRC)
- @$(PRINTF) '%s\n' $(call quote,Extracting pages for </$(NAME)>…)
- $(silent)$(SLEEP) 1
- $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) --writesubtree $(call quote,$(dir $@)) $(call quote,$<) $(call quote,$(SRC))
-
-# All other pages are extracted alongside the base.
-$(foreach file,$(pagefiles),$(BUILDDIR)/pages/$(file)) : $(BUILDDIR)/index ;
-
-# Process each page into its final form.
-$(foreach file,$(pagefiles),$(BUILDDIR)/public/$(file)) : $(BUILDDIR)/public/% : $(BUILDDIR)/pages/%
- @$(PRINTF) '%s\n' $(call quote,Building page `$(subst }",,$(subst "{,,$*))´ of </$(NAME)>…)
- $(silent)$(call ensuredirectory,$(dir $@))
- $(silent)$(call processresultto,$<,$@,$*)
-
-# Archive all components in the file to the destination.
-#
-# This is a match‐anything target, with the assumption that this make·file is being called recursively from the default mode.
-$(DESTDIR)/% : $(foreach file,$(pagefiles),$(BUILDDIR)/public/$(file))
- @$(PRINTF) '%s\n' $(call quote,Collecting pages for </$(NAME)>…)
- $(silent)$(call ensuredirectory,$(dir $@))
- $(silent)$(call removeassociatedfiles,$@)
- $(silent)cp $(foreach file,$(pagefiles),$(call quote,$(BUILDDIR)/public/$(file))) $(DESTDIR)
-
# ━ § END DEFINED MAKE·FILE MODES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
else
contain Ascii white·space, colons (`:`), semis (`;`), pipes (`|`),
bucks (`$`), percents (`%`), hashes (`#`), asterisks (`*`), brackets
(`[` or `]`), erotemes (`?`), backslashes (`\`), or control
- characters, must not begin with a hyphen‐minus (`-`), must not end
- with a cloparen (`)`), and must not contain quoted braces (`"{` or
- `}"`).**
+ characters, must not begin with a hyphen‐minus (`-`), and must not end
+ with a cloparen (`)`).**
The former characters have the potential to conflict with make syntax,
- a leading hyphen‐minus is confusable for a command‐line argument, a
+ a leading hyphen‐minus is confusable for a commandline argument, and a
trailing cloparen [activates a bug in G·N·U Make
- 3.81](https://stackoverflow.com/questions/17148468/capturing-filenames-including-parentheses-with-gnu-makes-wildcard-function#comment24825307_17148894),
- and quoted braces are used internally by the program.
+ 3.81](https://stackoverflow.com/questions/17148468/capturing-filenames-including-parentheses-with-gnu-makes-wildcard-function#comment24825307_17148894).
## Parsers
A plaintext (U·T·F‐8) file will be produced from the text nodes in
the transformation result.
-## Pagination
-
-It is possible to have a single source file produce multiple output
- files via `<书社:page>` elements, whose `@name` gives the name of the
- page.
-If a parsed document has a `@书社:destination` which contains `%s`,
- the `%s` will be replaced with the `@name` for each `<书社:page>` (and
- removed for the main output).
-Otherwise, the `@name` is inserted before the first period of the
- filename (or at the end of the filename for those with no period).
-If `<书社:page>`s do not have a `@name`, they are numbered
- sequentially.
-The destination of pages must be in the same directory as their parent.
-
-Pagination essentially forms a limited convenience for the more
- sophisticated technique of creating an archive with ⛩️📰 书社 and
- then unarchiving it.
-Pages are, from Make’s point of view, untracked side·effects of
- installing the main output, meaning they cannot be targeted directly
- and will not appear in `make list` or `make listout`.
-They are intended solely for the like of indices and feeds, for which
- convenience and necessity outweigh their flaws.
-
## License
This repository conforms to [REUSE][].
<text>|</text>
<choose>
<when test="$destination">
- <variable name="dir">
- <for-each select="exslstr:tokenize($destination, '/')[not(position()=last())]">
- <value-of select="."/>
- <text>/</text>
- </for-each>
- </variable>
- <variable name="name" select="string(exslstr:tokenize($destination, '/')[last()])"/>
- <value-of select="$dir"/>
- <choose>
- <when test="contains($name, '%s')">
- <value-of select="substring-before($name, '%s')"/>
- <value-of select="substring-after($name, '%s')"/>
- </when>
- <otherwise>
- <value-of select="$name"/>
- </otherwise>
- </choose>
+ <value-of select="$destination"/>
</when>
<otherwise>
<value-of select="substring-after(@name, 'about:shushe?source=')"/>
</xslt:otherwise>
</xslt:choose>
</xslt:variable>
- <xslt:variable name="书社:pagedestination">
- <xslt:variable name="dir">
- <xslt:for-each select="exslstr:tokenize($书社:destination, '/')[not(position()=last())]">
- <xslt:value-of select="."/>
- <xslt:text>/</xslt:text>
- </xslt:for-each>
- </xslt:variable>
- <xslt:variable name="name" select="string(exslstr:tokenize($书社:destination, '/')[last()])"/>
- <xslt:value-of select="$dir"/>
- <xslt:choose>
- <xslt:when test="contains($name, '%s')">
- <xslt:value-of select="$name"/>
- </xslt:when>
- <xslt:when test="contains($name, '.')">
- <xslt:value-of select="substring-before($name, '.')"/>
- <xslt:text>%s.</xslt:text>
- <xslt:value-of select="substring-after($name, '.')"/>
- </xslt:when>
- <xslt:otherwise>
- <xslt:value-of select="$name"/>
- <xslt:text>%s</xslt:text>
- </xslt:otherwise>
- </xslt:choose>
- </xslt:variable>
<for-each select="//catalog:uri">
<xslt:include href="{@uri}">
<if test="contains(@name, ':')">
<xslt:apply-templates select="@*|node()" mode="书社:application"/>
</xslt:copy>
</xslt:template>
- <xslt:template match="@书社:destination[not(../self::书社:page)]|@书社:disable-output-wrapping|@书社:archived-as[../ancestor::*[not(self::书社:apply-attributes-to-root or self::书社:apply-attributes)]]" mode="书社:application" priority="1"/>
- <xslt:template match="书社:archive|书社:page" mode="书社:application" priority="1">
+ <xslt:template match="@书社:destination|@书社:disable-output-wrapping|@书社:archived-as[../ancestor::*[not(self::书社:apply-attributes-to-root or self::书社:apply-attributes)]]" mode="书社:application" priority="1"/>
+ <xslt:template match="书社:archive" mode="书社:application" priority="1">
<xslt:copy>
<xslt:for-each select="@*">
<xslt:choose>
- <xslt:when test="../self::书社:archive and local-name()='archived-as' and namespace-uri()='&书社;' and ../ancestor::*[not(self::书社:apply-attributes-to-root or self::书社:apply-attributes)]"/>
+ <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="书社:application"/>
</xslt:otherwise>
</xslt:choose>
</xslt:for-each>
- <xslt:if test="self::书社:page and not(@书社:destination)">
- <xslt:attribute name="书社:destination">
- <xslt:value-of select="substring-before($书社:pagedestination, '%s')"/>
- <xslt:text>%22%7B</xslt:text>
- <xslt:choose>
- <xslt:when test="@name">
- <xslt:value-of select="@name"/>
- </xslt:when>
- <xslt:otherwise>
- <xslt:value-of select="count(ancestor::书社:page|preceding::书社:page)+1"/>
- </xslt:otherwise>
- </xslt:choose>
- <xslt:text>%7D%22</xslt:text>
- <xslt:value-of select="substring-after($书社:pagedestination, '%s')"/>
- </xslt:attribute>
- </xslt:if>
<xslt:for-each select="node()">
<xslt:choose>
<xslt:when test="self::*">
+++ /dev/null
-<?xml version="1.0"?>
-<!--
-SPDX-FileCopyrightText: 2024 Lady <https://www.ladys.computer/about/#lady>
-SPDX-License-Identifier: MPL-2.0
--->
-<!--
-⁌ ⛩️📰 书社 ∷ lib/paged2extractor.xslt
-
-© 2024 Lady [@ Lady’s 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/>.
--->
-<transform
- xmlns="http://www.w3.org/1999/XSL/Transform"
- xmlns:exsl="http://exslt.org/common"
- xmlns:exslstr="http://exslt.org/strings"
- xmlns:xslt="http://www.w3.org/1999/XSL/TransformAlias"
- xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
- version="1.0"
->
- <namespace-alias stylesheet-prefix="xslt" result-prefix="#default"/>
- <param name="NAME"/>
- <template match="/">
- <xslt:transform extension-element-prefixes="exsl exslstr" version="1.0">
- <xslt:template match="@*|node()">
- <xslt:param name="destination" select="''"/>
- <xslt:choose>
- <xslt:when test="self::书社:page">
- <xslt:apply-templates>
- <xslt:with-param name="destination" select="$destination"/>
- </xslt:apply-templates>
- </xslt:when>
- <xslt:when test="$destination='' and not(ancestor::书社:page) or string(ancestor::书社:page[1]/@书社:destination)=$destination">
- <xslt:copy>
- <xslt:apply-templates>
- <xslt:with-param name="destination" select="$destination"/>
- </xslt:apply-templates>
- </xslt:copy>
- </xslt:when>
- <xslt:otherwise>
- <xslt:apply-templates>
- <xslt:with-param name="destination" select="$destination"/>
- </xslt:apply-templates>
- </xslt:otherwise>
- </xslt:choose>
- </xslt:template>
- <xslt:template match="/" priority="1">
- <exsl:document href="./pages/{$NAME}" method="xml" encoding="UTF-8">
- <xslt:apply-templates/>
- </exsl:document>
- <xslt:text>
- <text>./pages/</text>
- <value-of select="$NAME"/>
- <text>
</text>
- </xslt:text>
- <for-each select="//书社:page[@书社:destination]">
- <if test="not(ancestor::书社:page[@书社:destination=current()/@书社:destination] or preceding::书社:page[@书社:destination=current()/@书社:destination])">
- <variable name="href">
- <text>./pages/</text>
- <value-of select="exslstr:tokenize(@书社:destination, '/')[last()]"/>
- </variable>
- <exsl:document href="{$href}" method="xml" encoding="UTF-8">
- <xslt:apply-templates>
- <xslt:with-param name="destination">
- <value-of select="@书社:destination"/>
- </xslt:with-param>
- </xslt:apply-templates>
- </exsl:document>
- <xslt:text>
- <choose>
- <when test="contains($href, '%22%7B')">
- <value-of select="substring-before($href, '%22%7B')"/>
- <text>"{</text>
- <value-of select="substring-before(substring-after($href, '%22%7B'), '%7D%22')"/>
- <text>}"</text>
- <value-of select="substring-after(substring-after($href, '%22%7B'), '%7D%22')"/>
- </when>
- <otherwise>
- <value-of select="$href"/>
- </otherwise>
- </choose>
- <text>
</text>
- </xslt:text>
- </if>
- </for-each>
- </xslt:template>
- <xslt:output method="text" encoding="UTF-8"/>
- </xslt:transform>
- </template>
- <output method="xml" encoding="UTF-8"/>
-</transform>