aboutsummaryrefslogtreecommitdiff
path: root/mk
diff options
context:
space:
mode:
Diffstat (limited to 'mk')
-rw-r--r--mk/HACKING.pdfbin0 -> 24550 bytes
-rw-r--r--mk/HACKING.t162
-rw-r--r--mk/Makefile14
-rw-r--r--mk/README.pdfbin0 -> 63483 bytes
-rw-r--r--mk/README.t862
-rw-r--r--mk/k.tmac417
-rw-r--r--mk/kx.tmac59
-rw-r--r--mk/t/Makefile18
-rw-r--r--mk/t/k.tmac414
-rw-r--r--mk/t/k.tmac.orig419
-rw-r--r--mk/t/ne.t147
-rw-r--r--mk/t/t.tmac32
12 files changed, 2544 insertions, 0 deletions
diff --git a/mk/HACKING.pdf b/mk/HACKING.pdf
new file mode 100644
index 0000000..04e2b86
--- /dev/null
+++ b/mk/HACKING.pdf
Binary files differ
diff --git a/mk/HACKING.t b/mk/HACKING.t
new file mode 100644
index 0000000..9084dd3
--- /dev/null
+++ b/mk/HACKING.t
@@ -0,0 +1,162 @@
+.so k.tmac
+.
+.de @h
+. sp |36p
+. if \\n%>1 .tl ''-%-''
+. sp |1i
+..
+.de @f
+.sp |\\n(.pu-36p
+.tl ''\\*(#e''
+..
+.
+.t
+Hacking on the
+.x mk
+macro package
+.d
+John Ankarström
+.
+.
+.h
+What is
+.x mk ?
+.p
+.i Mk
+or
+.i mu
+is a simple macro package for troff
+designed to abstract as little as possible from troff itself,
+while still providing a powerful framework for
+writing advanced documents.
+.
+.
+.h
+How is the source code of
+.x mk
+organized?
+.p
+If you run
+.c "grep -n [-]-"
+on the
+.i k.tmac
+source file, you are presented with an overview of
+.i mk 's
+macros:
+.l
+.eo
+!grep -n [-]- k.tmac
+.ec
+.p
+This is a sufficient summary of the entire
+.i mk
+source code, as nothing is performed outside of these macros.
+All initialization is performed in the
+.c @a
+macro, which is automatically called at the first invocation
+of any other macro.
+.p
+The above summary reflects a categorization in the macros defined by
+.i mk .
+There are internal and external macros.
+The former are to be used within
+.i k.tmac
+itself, while the latter are to be used in
+.i mk
+documents.
+Among the external macros, there are inline, environment (or block-level)
+and other macros.
+.p
+The inline macros all follow the same pattern.
+They take three arguments:
+the string to be formatted,
+an optional suffix
+and an optional prefix.
+.p
+The environment or block-level macros generally take no arguments
+(except
+.c d ).
+Instead, they activate a given environment,
+affecting the formatting of the following text.
+Each environment macro is associated with a specific environment,
+carrying the same one-letter name as the macro itself.
+.p
+As you can see,
+the macros in each category are arranged alphabetically.
+.
+.
+.h
+Where is document state stored?
+.p
+Most state is stored by troff itself within the different environments.
+In addition,
+.i mk
+associates three extra registers with each environment:
+.c sp ,
+the amount of space to be added by
+.c @e
+before an environment;
+.c sq ,
+the same (except the space is not added
+if the new environment is identical to the previous one); and
+.c ti ,
+the indentation of the first line in the
+.c p
+environment.
+These are stored in registers named
+.c @ENV_sp ,
+.c @ENV_sq
+and
+.c @ENV_ti ,
+where
+.c ENV
+is the name of the associated environment.
+.p
+The strings
+.c %env
+and
+.c %penv
+contain the name of the current and previous environment.
+.p
+The
+.c @a
+register is set to 1 if the document has been initialized
+(i.e. if
+.c @a
+has been invoked).
+.p
+The
+.c @m
+register is non-zero if
+." "manual footer"
+mode is active.
+If
+.c @m
+is non-zero,
+.c @tf
+decrements it by one and exits when invoked,
+unless called with the
+.c f
+(force) argument.
+This is useful if you want to trigger the footer manually,
+but do not want the printed footer to trigger the footer trap again.
+.p
+.c @.t
+contains the absolute vertical position of the first trap
+following the first footnote reference on a page;
+it is set and used by
+.c )
+to place the footnote trap in the correct vertical position.
+.c @dn
+contains the height of all collected footnotes on a page;
+it is set by
+.c )
+and reset to zero by
+.c @tn .
+.c @n
+contains the total number of collected footnotes.
+.p
+Note that none of these registers and strings should be
+directly accessed or modified by
+.i mk
+documents.
diff --git a/mk/Makefile b/mk/Makefile
new file mode 100644
index 0000000..069eab1
--- /dev/null
+++ b/mk/Makefile
@@ -0,0 +1,14 @@
+TROFF ?= troff
+PDFROFF ?= troff | dpost | ps2pdf -
+
+all: README.pdf HACKING.pdf
+
+HACKING.pdf: HACKING.t k.tmac
+ perl -pe '$$_=`$$_` if s/^\!//' HACKING.t |\
+ LC_ALL=en_US.UTF-8\
+ $(PDFROFF) > HACKING.pdf
+
+README.pdf: README.t k.tmac
+ TROFFONTS=/usr/pkg/share/fonts/X11/TTF/\
+ LC_ALL=en_US.UTF-8\
+ <README.t ../toc/toc ../list/list -p.l \| $(PDFROFF) > README.pdf
diff --git a/mk/README.pdf b/mk/README.pdf
new file mode 100644
index 0000000..9ab89af
--- /dev/null
+++ b/mk/README.pdf
Binary files differ
diff --git a/mk/README.t b/mk/README.t
new file mode 100644
index 0000000..7e36106
--- /dev/null
+++ b/mk/README.t
@@ -0,0 +1,862 @@
+.so k.tmac
+.so kx.tmac
+.so ../toc/toc.tmac
+.mediasize letter
+.fp 0 M DejaVuSansMono ttf
+.hw Eng-lish
+.
+.\" configure table of contents
+.eo
+.de he
+. bp
+. nr a +1
+. h
+\X'PDFMark: Bookmark 0 \\$*'\A'\na'
+. br
+. sp -1
+\$*
+. te .the \\\T'\na'\$*\t\n%\\T
+..
+.
+.de se
+. nr a +1
+. s
+\X'PDFMark: Bookmark 1 \\$*'\A'\na'
+. br
+. sp -1
+\$*
+. if '\$1 \$2 \$3'How do I' \{\
+. nr hdi +1
+. if \n[hdi]>1 \{\
+. shift 3
+. te .the \\\T'\na'\\h'36p'"\\h'20.4p'\$*\t\n%\\T
+. return
+. \}
+. \}
+. te .the \\\T'\na'\\h'18p'\$*\t\n%\\T
+..
+.
+.ec
+.
+.\" configure header and footer
+.eo
+.de @h
+. sp |36p
+. if \n%>1 .tl ''-%-''
+. sp |1i
+..
+.
+.de @f
+. sp |\n(.pu-48p
+. tl ''\*(#e''
+..
+.ec
+.
+.\" configure environments
+.@e l
+. ft M
+. vs +1p
+.@e
+.
+.\" start document
+.t
+.x Mk ,
+a simple macro package for troff
+.d
+John Ankarström
+.d e
+.
+.s
+.ce
+.i "TABLE OF CONTENTS"
+.sp 4p
+.@e
+\X'SetLinkBorder: 0 0 0'\c
+.to
+.sp 0.5i
+.ns
+.
+.he Introduction
+.p
+.i Mk
+is a simple macro package for troff with the following features:
+.n 1
+It is designed to be easy to understand and to customize
+by editing the source code.
+.n
+It makes use of the extended support for environments
+offered by modern troff implementations.
+.n
+It is designed to be practical and easy to use.
+Macros consist of a single lowercase letter.
+.p
+While
+.i mk
+does provide macros for many common tasks,
+including footnotes,
+it is at the end of the day an idiosyncratic macro package,
+written to serve the author's personal needs.
+Users of
+.i mk
+are encouraged to
+.n 1 a )
+modify the source code
+according to their own needs, as well as
+.n
+use built-in troff requests for some things
+that other packages might provide custom macros for.
+.p
+All in all,
+.i mk
+aspires to abstract as little as possible
+from the underlying troff requests and registers.
+In its author's humble opinion,
+it is the ideal macro package for learning troff.
+.p
+In this document, the fundamental concepts of
+.i mk
+are explained.
+The document itself also serves as a demonstration of
+.i mk .
+With a couple of exceptions, it uses
+.i mk 's
+default settings.
+The reader is encouraged to inspect the document's source code
+in order to see how the macro package is used in practice.
+.p
+And yes, that table of contents is automatically generated!
+See p. \n[&toc] for more information.
+.
+.
+.he Environments
+.te .nr &env \n%
+.p
+.i Mk
+makes heavy use of named environments,
+supported by implementations such as GNU troff, Heirloom troff and Neatroff.
+Environments obviate the need for many special registers
+that a macro package (and its user) would otherwise need to keep track of.
+For example,
+.i ms
+keeps track of the document's font size in the
+.c PS
+register.
+For the font size of headings,
+it has yet another register.
+.i mk
+has no such registers.
+If the user wishes to modify the default font size,
+he or she can simply switch to the relevant environment
+and set the font size as desired
+using regular troff requests:
+.l(
+.\" set heading font
+.h
+.fam H
+.ps +1p
+.l)
+.p
+Troff saves the font settings in the environment,
+so that the next time the environment is invoked,
+the desired font family and point size are automatically restored.
+.p
+The environments are initialized
+as soon as the first
+." block-level
+macro is called.
+At the initialization of each environment,
+the default environment (0) is copied,
+meaning that all environment-relevant settings
+defined before the first macro call
+are applied to all
+.i mk
+environments.
+It is thus remarkably simple and intuitive to set,
+for example, the default font of a document:
+.l(
+.fam N
+.t
+Document title
+.p
+First paragraph.
+.l)
+.p
+In addition to the normal environment-relevant settings,
+.i mk
+manually associates a few special registers with the current environment:
+.n 1
+.c sp ,
+the amount of vertical space to add before an environment
+.n
+.c sq ,
+the amount of vertical space to add before a different type of environment
+.n
+.c ti ,
+the indentation of the first line in some environments
+(currently only
+.c p )
+.p
+These can be set inside a given environment
+to control its behavior when invoked.
+The only exceptions are the margin and footnote environments
+.c @m , (
+.c @f ),
+which are treated specially
+and do not support these registers.
+.
+.
+.he External macros
+.p
+.i Mk
+defines a number of macros.
+Some of them are used internally by
+.i mk
+itself;
+these carry an at
+.c @ ) (
+prefix
+and are going to be explored later.
+For now, we will focus on the external macros provided by
+.i mk .
+.
+.se Inline macros
+.p
+There are a group of macros that provide
+convenient inline formatting.
+All take three arguments:
+the text to be formatted,
+the text to be placed immediately after
+and the text to be placed immediately before.
+The inline macros are the following:
+.n 1
+.c \(dq ,
+quotation (like
+." this )
+.n
+.c b ,
+bold font
+.n
+.c c ,
+constant-width font
+.n
+.c i ,
+italic font
+.n
+.c i ,
+bold italic font
+.p
+For example, the following request outputs
+.i mk .\(rq: \(lq
+.l(
+.i mk .
+.l)
+.
+.p
+Note that
+.c c
+uses the font family and point size
+set in the
+.c l
+environment (see below).
+.se Block-level macros
+.p
+There is a large group of macros that provide
+block-level formatting:
+.n 1
+.c d ,
+centered date or text
+.n
+.c h ,
+heading
+.n
+.c l ,
+literal display (for source code)
+.n
+.c p ,
+paragraph
+.n
+.c q ,
+indented quotation
+.n
+.c s ,
+subheading
+.n
+.c t ,
+centered title
+.p
+The
+.c t
+and
+.c d
+macros can be used at the beginning of a document
+to create a centered header:
+.l(
+.t
+Document title
+.d
+First author
+.br
+Second author
+.d i \" current date formatted as YYYY-MM-DD
+.l)
+.p
+In the example above, you can see that the
+.c d
+macro may be used for other things than just dates.
+This works because
+.c d
+displays the date only if given
+an argument describing the desired date format:
+.n 1 a )
+.c i :
+international date, like
+." 2021-06-21
+.n
+.c e :
+English date, like
+." "21 June 2021"
+.p
+The formatted dates are defined in strings prefixed with a hash symbol
+.c # ): (
+.c #i
+and
+.c #e
+are provided by default,
+but more may be added by the user.
+.
+.se Other macros
+.p
+Finally, there are a few macros
+that belong to neither category:
+.n 1
+.ne 2
+.c ( ,
+begin footnote
+.n
+.c ) ,
+end footnote
+.n
+.c w ,
+want space
+.p
+The macros
+.c (
+and
+.c )
+create footnotes,
+placing a numerical reference at the place of their invocation.
+Both
+.c (
+and
+.c )
+take an optional argument,
+which is output either immediately before or immediately after
+the inline reference.
+For example, the code
+.l(
+.q
+This is a quotation\c
+.(
+\!.ne 1
+This is a footnote.
+.)
+.l)
+.p
+generates the following output:
+.q
+This is a quotation\c
+.(
+This is a footnote.
+.) .
+.p
+If
+.c (
+and
+.c )
+are called at such a position that the collected footnotes
+cannot fit on the current page,
+.i mk
+will print the footnotes on the next page instead.
+.p
+The
+.c w
+macro is an alternative to troff's
+.c ne
+request,
+which ensures that a given amount of space is available on the page
+\(en otherwise, a line break is issued \(en
+but unlike
+.c ne ,
+which takes an exact amount of space as its argument,
+.c w
+takes a declarative specification
+describing the amount of desired space
+in terms of
+.i mk
+environments.
+For example:
+.l(
+.\" want space for...
+.w s p \" a subheading of one line + a paragraph of one line
+.w s pp \" a subheading of one line + a paragraph of two lines
+.w ss p \" a subheading of two lines + a paragraph of one line
+.l)
+.
+.
+.he Internal macros
+.p
+.i Mk 's
+internal macros are generally not meant to be used outside of
+.i k.tmac .
+Documented in this section are the exceptions to this rule.
+For examples of how these macros are used in practice,
+see the FAQ section on page \n[&faq].
+.
+.se Page header and footer
+.p
+The
+.c @h ,
+.c @f ,
+.c @th
+and
+.c @tf
+macros control the page header and footer.
+.p
+At document initialization,
+.i mk
+automatically creates traps for
+.c @th
+and
+.c @tf .
+When sprung,
+.c @th
+and
+.c @tf
+call
+.c @h
+and
+.c @t ,
+which control the text and spacing of the header and footer.
+.p
+.i Mk
+will avoid creating a trap for
+.c @tf
+if any trap has already been set before document initialization.
+This means that users can override the position of the
+.c @tf
+trap by setting it themselves.
+Likewise, users can redefine
+.c @h
+and
+.c @f
+to change the content of the header and footer.
+.
+.se Environments
+.p
+The environment-related macros
+.c (e ,
+.c @e
+and
+.c @c
+may be used in advanced documents to define custom environments.
+.p
+At the present,
+.c (e
+is nothing more than a wrapper around troff's built-in
+.c ev ,
+but it may eventually be redefined in order to offer
+compatibility with troff implementations
+without support for named environments.
+.p
+.c @e
+and
+.c @c
+are equivalent to troff's
+.c ev
+and
+.c evc ,
+but perform some extra work to keep track of
+.i mk 's
+special environment variables
+(see
+.i Environments ,
+p. \n[&env]).
+.
+.
+.he Frequently anticipated questions
+.te .nr &faq \n%
+.
+.se How do I customize the default appearance of a document?
+.p
+All environment settings,
+like point size, font family and indentation\c
+.(
+For a complete list of settings that are associated with the environment,
+see 5.26\~\c
+.i Environments
+in the full GNU troff manual
+.nh
+.c "info 'groff\:.info)Environments'" ). (
+.hy
+.) ,
+are configured with the standard troff requests.
+If you set the point size at the beginning of the document,
+before any
+.i mk
+macros have been called,
+it will be correctly set for the entire document.
+This works because
+.i mk 's
+environments initially copy all their settings from 0,
+the default environment.
+.p
+For example, if you wanted to write a document
+with the New Century Schoolbook font
+at 9 points
+and a vertical spacing of 12 points,
+you would start the document like this
+(before you call any
+.i mk
+macros):
+.l(
+.fam N
+.ps 9p
+.vs 12p
+.l)
+.
+.se How do I customize the default appearance of a given environment?
+.p
+To configure the layout and font settings of a specific environment,
+you can switch to that environment and use the relevant troff requests:
+.l(
+\!.ne 1
+.q
+.ps +1p
+.l)
+.p
+If you are merely configuring the environment
+without printing anything in it,
+you can also use
+.c @e :
+.l(
+.@e q
+.ps +1p
+.l)
+.
+.w s ll
+.se How do I customize the default appearance of the margin text?
+.l(
+.(e @m \" margin environment
+.ps +1p
+.l)
+.p
+You can also set such settings in your redefinition of
+.c @h
+or
+.c @f .
+.
+.w s ll
+.se How do I customize the default appearance of footnotes?
+.l(
+.(e @f \" footnote environment
+.ps +1p
+.l)
+.
+.w s ll
+.se How do I redefine the page header?
+.l(
+.de @h
+. \" set position of header text
+. sp |36p
+. \" set header text
+. tl 'left'center'right'
+. \" set position of page text
+\!.ne 1
+. sp |1i
+..
+.l)
+.
+.w s ll
+.se How do I redefine the page footer?
+.p
+To change the position of the footer:
+.l(
+.\" set position of footer trap
+.\" (must be done before any macros have been called)
+.wh -1i @tf
+.
+.l)
+.p
+To change the contents of the footer:
+.l(
+.de @f
+. \" set position of footer text
+. \" (must be below the footer trap)
+. sp |\\n(.pu-48p
+. \" set footer text
+. tl 'left'center'right'
+..
+.l)
+.
+.se How do I define my own environments?
+.p
+Environments are a feature built into troff,
+accessed via the
+.c ev
+request, but because
+.i mk
+extends environments with additional functionality,
+it provides special macros to be used instead of
+.c ev :
+.n 1
+.c (e ,
+set environment (same as
+.c ev )
+.n
+.c @e ,
+set extended environment
+.n
+.c @c ,
+copy environment (same as
+.c evc )
+.p
+The
+.c @e
+macro
+can be used to activate any environment that supports
+.i mk 's
+extensions (see
+.i Environments ,
+p. \n[&env]).
+The following code configures an environment called
+.c n
+and defines a corresponding macro:
+.l(
+.@e n
+. @c 0 \" copy default environment
+. ps -1p
+.@e
+.
+.de n
+. br \" finish current environment
+\!.ne 1
+. @e n \" activate new environment
+..
+.l)
+.
+.se How do I prevent a section from being broken up by a page break?
+.p
+Some macro packages have a concept of
+." keeps ,
+sections that are kept together across page breaks.
+.i Mk
+does not (at least yet) define any such macros by default.
+The simplest solution is to use troff's
+.c ne
+request:
+.l(
+.ne 7 \" break page if seven lines won't fit on this page
+.\" ... some text ...
+.l)
+.p
+If you want to keep text of various styles together,
+you can use
+.i mk 's
+own
+.c w
+macro:
+.l(
+.w s qq \" break page if a subheading and two lines of a quotation won't fit
+.\" ... some text ...
+.l)
+.ig
+.p
+For a more general solution, you can use a diversion:
+.l(
+.di keep
+.\" ... some text ...
+.br
+.di
+.ne \n(dnu
+.keep
+.l)
+..
+.
+.se How do I use alternative quotation marks?
+.p
+Redefine
+.c \(dq .
+The following code defines a Swedish quotation style:
+.l(
+.de "
+\\&\\$3”\\$1”\\$2
+..
+.l)
+.p
+Another option is to create new macros for alternative quotation styles:
+.l(
+.\" alternative swedish quotation styles
+.de ><
+\\&\\$3»\\$1«\\$2
+..
+.de >>
+\\&\\$3»\\$1»\\$2
+..
+.l)
+.
+.se How do I create nested inline quotations?
+.p
+Just use the backtick
+.c \(ga ) (
+and apostrophe
+.c \(aq ) (
+directly in the argument to
+.c \(dq .
+.p
+If you need to change the quotation style,
+for example when converting a document from US to British English,
+you can redefine
+.c "
+to translate backticks and apostrophes accordingly:
+.l(
+.eo
+.de "
+. char ` \(lq
+. char ' \(rq
+\&\$3\(oq\$1\(cq\$2
+. char ` \\(oq
+. char ' \\(cq
+..
+.ec
+.l)
+.
+.se How do I include a table of contents in my document?
+.te .nr &toc \n%
+.p
+Included with the
+.i mk
+distribution is a package called
+.i toc ,
+which includes the following files:
+.n 1
+.i toc.tmac ,
+which provides the
+.c te
+and
+.c to
+macros
+.n
+.i toc ,
+a script that invokes troff in three passes
+in order to allow a table of contents to be generated
+.p
+Use
+.c te
+to register a line to be inserted into the table of contents.
+Use
+.c to
+to insert the lines registered by
+.c te
+into the document.
+Note that the argument given to
+.c te
+will be interpreted as a troff input line.
+.p
+The following definitions provide a good starting point:
+.l(
+.eo
+.de he
+. h
+\$*
+. tm .the \$*\t\n%
+..
+.de the
+. ta 0 \n(.luR
+. tc .
+\$*
+. tc
+. br
+..
+.ec
+.l)
+.p
+Now, instead of
+.c h ,
+you may use
+.c he
+to create a heading to be included in the table of contents:
+.l(
+.he A heading
+.l)
+.p
+Wherever you want the table of contents to be inserted,
+you may simply invoke
+.c to .
+.p
+Just remember to use
+.c toc
+to run your processing pipeline:
+.l(
+<example.t toc refer \| troff -Tps >example.ps
+.l)
+.p
+Thanks to the multiple passes performed by
+.i toc ,
+.c to
+can be invoked at any place in the document,
+including the beginning.
+.p
+The macro definition listed above is included in
+.i ux.tmac .
+.
+.se How do I create a reference to a later page?
+.p
+Use the macros provided by
+.i toc.tmac
+in combination with the
+.i toc
+program.
+Near the beginning of your document, invoke
+.c to .
+Then, at each position you want to reference,
+invoke
+.c .te
+like this:
+.l(
+.te .nr &refname \n%
+.l)
+.p
+To refer to that position, interpolate the register:
+.l(
+See page \n[&refname].
+.l)
+.p
+(I prefer prefixing my references with an ampersand.)
+.
+.se How do I include source code without needing to escape it?
+.p
+The
+.i mk
+distribution includes a troff preprocessor called
+.i list ,
+which filters text delimited by
+.c .l(
+and
+.c .l) ,
+escaping standard troff syntax.
+.p
+To automatically add a
+.c .l
+request before each listing, set the
+.i -p
+(prefix) flag accordingly:
+.l(
+list -p.l
+.l)
+.p
+To transparently pass an input line to troff,
+prefix it with
+.c \\\\! .
+(To disable this behavior, use the
+.i -E
+flag.)
diff --git a/mk/k.tmac b/mk/k.tmac
new file mode 100644
index 0000000..d1ff918
--- /dev/null
+++ b/mk/k.tmac
@@ -0,0 +1,417 @@
+.\" "/.
+.\" mk macro package for troff "/.
+.\" created by John Ankarström "/.
+.\" "/.
+.\" Permission to use, copy, modify and/or "/.
+.\" distribute this software for any purpose "/.
+.\" with or without fee is hereby granted. "/.
+.\" "/.
+.\" For an index of the macros defined in "/.
+.\" this file, issue `grep -n [-]- k.tmac'. "/.
+.\" "/.
+.\" When editing the source of mk, keep the "/.
+.\" following rules in mind: "/.
+.\" "/.
+.\" * Prefix temporary strings and registers "/.
+.\" with _. "/.
+.\" * Prefix internal registers and environ- "/.
+.\" ments with @. "/.
+.\" * Prefix internal macros with @ (normal "/.
+.\" macros), * (variants of the same "/.
+.\" macro) or % (strings). "/.
+.\" * Place macros in alphabetical order. "/.
+.\" * Do not use .ev and .evc directly; use "/.
+.\" (e, @e and @c instead. "/.
+.\" "/.
+.
+.do xflag 3 \" enable modern extensions
+.eo \" disable backslash escapes
+.
+.\" Internal macros ----------------------------
+.
+.\" @a -- setup document
+.de @a
+. nr @a 1
+.
+. \" set header/footer traps
+. wh 0 @th
+. em @tf
+. if \n(nl<=0 .@th
+. nr _ \n(.p-\n(nl
+. if \n(.t=\n_ .wh -1i @tf
+.
+. \" configure environments
+. @e d \" date
+. @c 0
+. nr sp 0.4v
+. ad c
+. @e h \" heading
+. @c 0
+. nr sp 1.2v
+. ps +1p
+. ft B
+. @e l \" literal display
+. @c 0
+. nr sp 0.3v
+. nf
+. in 18p
+. ft CR
+. ps -1p
+. vs -1p
+. @e n \" name (of author)
+. @c 0
+. nr sp 0.4v
+. ad c
+. @e p \" paragraph
+. @c 0
+. nr sp 0.3v
+. nr ti 18p
+. ad b
+. @e q \" quotation
+. @c 0
+. nr sp 0.3v
+. nr ti 18p
+. in 18p
+. ps -1p
+. vs -1p
+. @e s \" section heading
+. @c 0
+. nr sp 1v
+. ft B
+. @e t \" title
+. @c 0
+. ps +4p
+. vs +2p
+. ft B
+. ad c
+. @e
+.
+. \" configure special environments
+. (e @f \" footnote
+. @c 0
+. ps -2p
+. vs -1p
+. (e @m \" margins (header, footer)
+. @c 0
+. (e
+.
+. \" create formatted dates
+. af mo 00
+. af dy 00
+. ds #i \n[year]-\n(mo-\n(dy
+. af mo 1
+. af dy 1
+. ds #e \n(dy
+. ie \n(mo=1 .ds #e \*(%e January
+. el .ie \n(mo=2 .ds #e \*(#e February
+. el .ie \n(mo=3 .ds #e \*(#e March
+. el .ie \n(mo=4 .ds #e \*(#e April
+. el .ie \n(mo=5 .ds #e \*(#e May
+. el .ie \n(mo=6 .ds #e \*(#e June
+. el .ie \n(mo=7 .ds #e \*(#e July
+. el .ie \n(mo=8 .ds #e \*(#e August
+. el .ie \n(mo=9 .ds #e \*(#e September
+. el .ie \n(mo=10 .ds #e \*(#e October
+. el .ie \n(mo=11 .ds #e \*(#e November
+. el .ds #e \*(#e December
+. ds #e \*(#e \n[year]
+..
+.
+.\" @c -- copy environment
+.de @c
+. evc \$1
+. nr sp \n[@\$1_sp]
+. nr sq \n[@\$1_sq]
+. nr ti \n[@\$1_ti]
+..
+.
+.\" (e -- set environment
+.de (e
+. ev \$1
+..
+.
+.\" @e -- set extended environment
+.de @e
+.
+. \" initialize document if not initialized
+. if '\n(@a'0' .@a
+.
+. \" save special environment variables
+. if !'\n[.ev]'0' \{\
+. nr @\n[.ev]_sp \n(sp \" vertical space before any environment
+. nr @\n[.ev]_sq \n(sq \" vertical space before different environment
+. nr @\n[.ev]_ti \n(ti \" first line indentation
+. ev
+. \}
+.
+. \" keep track of previous and new environment
+. ds %penv \*[%env]
+. ds %env \$1
+.
+. \" post-environment operations
+. if !'\$1'' \{\
+. \" add small space after title
+. if '\*[%penv]'t' .if '\*[%env]'d' .sp 0.1v
+.
+. \" add space after title block
+. nr _ 0
+. if !'\*[%env]'d' .nr _ 1
+. if !'\*[%penv]'t' .if !'\*[%penv]'d' .nr _ 0
+. if \n_ \{\
+. sp 0.5i
+. ns
+. \}
+.
+. \" activate new environment
+. ev \$1
+. nr sp \n[@\n[.ev]_sp]
+. nr sq \n[@\n[.ev]_sq]
+. nr ti \n[@\n[.ev]_ti]
+. sp \n(spu
+. if !'\*[%env]'\*[%penv]' .sp \n(squ
+. \}
+..
+.
+.\" @f -- footer
+.de @f
+. sp |\n(.pu-48p
+. tl ''%''
+..
+.
+.\" @h -- header
+.de @h
+. sp |36p
+. tl ''''
+. sp |1i
+..
+.
+.\" @tf -- footer trap
+.de @tf
+.
+. \" skip automatic trigger if manual mode active, unless forced
+. if \n(@m>0 .if !'\$1'f' \{\
+. nr @m -1
+. return
+. \}
+.
+. \" print footer
+. br
+. (e @m
+. @f
+' bp
+..
+.
+.\" @th -- header trap
+.de @th
+. (e @m
+. rt 0
+. @h
+. (e \*[%env]
+. ns
+..
+.
+.\" @tn -- footnote trap
+.de @tn
+.
+. nr @m 1
+. \" print collected footnotes
+. (e @m
+___________________
+. br
+. sp 1p
+. (e @f
+.
+. \" escape control characters
+. char . \&.
+. char ' \&'
+.
+. @n
+. br
+.
+. \" restore control characters
+. char . .
+. char ' '
+.
+. \" clear footnotes
+. ch @tn
+. nr @.t 0
+. nr @dn 0
+. rm @n
+. @tf f
+..
+.
+.
+.\" Inline macros ------------------------------
+.
+.\" " -- inline quotation
+.de "
+\&\$3\(lq\$1\(rq\$2
+..
+.
+.\" b -- bold font
+.de b
+\&\$3\fB\$1\fP\$2
+..
+.
+.\" c -- constant-width font
+.de c
+.
+. \" save original interword spacing
+. nr _ss \n[.ss]
+.
+. \" copy family and (relative) point size from l environment
+. (e 0
+. nr _s0 \n(.s
+. (e
+. (e l
+. ds _fl \n(.f
+. nr _sl \n(.s
+. (e
+.
+\&\$3\c
+. ft \*[_fl]
+. ss 7
+. ie d nrf .nrf _s (\n(.s*(\n[_sl]/(\n[_s0])))
+. el .nr _s \n(.s+(\n[_sl]-\n[_s0])
+. ps \n(_sp
+\&\$1\c
+. ft
+. ss \n[_ss]
+. ps
+\&\$2
+..
+.
+.\" i -- italic font
+.de i
+\&\$3\fI\$1\fP\$2
+..
+.
+.\" x -- bold italic font
+.de x
+\&\$3\f(BI\$1\fP\$2
+..
+.
+.
+.\" Environment macros -------------------------
+.
+.
+.\" d -- centered date
+.de d
+. br
+. @e d
+. if !'\$1'' \*[#\$1]
+..
+.
+.\" h -- heading
+.de h
+. br
+. w h p
+. @e h
+.
+..
+.\" l -- literal display
+.de l
+. br
+. w ll
+. @e l
+..
+.
+.\" p -- paragraph
+.de p
+. br
+. @e p
+. if '\*[%penv]'p' .ti \n(tiu
+..
+.
+.\" q -- quotation
+.de q
+. br
+. @e q
+. ti \n(tiu
+..
+.
+.\" s -- subheading
+.de s
+. br
+. w s p
+. @e s
+..
+.
+.\" t -- centered title
+.de t
+. br
+. @e t
+..
+.
+.
+.\" Other macros -------------------------------
+.
+.\" ( -- begin footnote
+.de (
+. if !'\$1'' \&\$1\c
+.
+. \" increment and print number
+. nr @n \n(@n+1
+\v'-.3m'\s'\En(.s*80/100'\n(@n\s0\v'.3m'\c
+.
+. \" switch environment and capture footnote
+. (e @f
+. boxa @n
+\h'9p'\v'-.3m'\s'\En(.s*80/100'\n(@n\s0\v'.3m'\h'2p'\c
+..
+.
+.\" ) -- end footnote
+.de )
+. br
+\!.br
+. boxa
+. (e \*[%env]
+. ie !'\$1'' \&\$1
+. el \&
+.
+. \" get position of footer (or next trap)
+. if \n[@.t]=0 .nr @.t \n(nl+\n(.t
+.
+.
+. \" print footnote at bottom of page
+. ch @tn
+. nr @dn +\n(dnu
+. nr _tn \n[@.t]u-\n[@dn]u-12p
+. wh \n[_tn]u @tn
+..
+.
+.\" w -- want space
+.de w
+. *w 0 \$@
+..
+.de *w
+.
+. \" parse specification -> _s : accumulated space
+. nr _s \$1 \" _e : environment name (e.g. eee -> e)
+. ds _e \$2 \" _n : number of lines specified (e.g. eee -> 3)
+. substring _e 0 0
+. length _n \$2
+.
+. \" add environment spacing
+. nr _s +\n[@\*(_e_sp]
+. nr _s +\n[@\*(_e_sq]
+.
+. \" add environment line height (for each line)
+. (e \*(_e
+. nr _v \n(.v
+. (e
+. nr _s +(\n(_n*\n(_v)
+. shift 2
+.
+. \" recurse or request space if done
+. ie \n(.$>0 .*w \n(_s \$@
+. el \{\
+. nr _s \n(_s-\n(_v+1u \" do not request too much
+. if (\n(_su)>\n(.tu \{\
+. br
+. bp
+. \}
+. \}
+..
+.
+.ec \" re-enable backslash escapes
diff --git a/mk/kx.tmac b/mk/kx.tmac
new file mode 100644
index 0000000..1122bf8
--- /dev/null
+++ b/mk/kx.tmac
@@ -0,0 +1,59 @@
+.\" "/.
+.\" extra stuff for mk "/.
+.\" "/.
+.\" Permission to use, copy, modify and/or "/.
+.\" distribute this software for any purpose "/.
+.\" with or without fee is hereby granted. "/.
+.\" "/.
+.
+.eo
+.
+.\" he -- toc-enabled heading
+.de he
+. h
+\$*
+. te .the \$*\t\n%
+..
+.
+.\" se -- toc-enabled subheading
+.de se
+. s
+\$*
+. te .the \\h'18p'\$*\t\n%
+..
+.
+.\" the -- toc entry
+.de the
+. ta \n(.luR
+. tc .
+\$*
+. tc
+. br
+..
+.
+.\" n -- numbered list item
+.de n
+. if !'\$1'' \{\
+. nr ni \$1-1
+. af ni 0
+. if !'\$2'' .af ni \$2
+. ds n. .
+. if !'\$3'' .ds n. \$3
+. \}
+. nr ni +1
+. br
+. @e n
+. ti \n(tiu
+\n(ni\*(n. \c
+..
+.
+.am @a
+. @e n
+. @c p
+. nr sq \n(sp
+. nr sp 0
+. ta 3n +3n +3n +3n
+. @e
+..
+.
+.ec
diff --git a/mk/t/Makefile b/mk/t/Makefile
new file mode 100644
index 0000000..e040b7a
--- /dev/null
+++ b/mk/t/Makefile
@@ -0,0 +1,18 @@
+.SUFFIXES: .t .T .pdf
+TEST != ls *.t | sed 's/t$$/T/'
+
+TROFF ?= troff
+PDFROFF ?= troff | dpost | ps2pdf -
+
+all: $(TEST)
+
+k.tmac: ../k.tmac
+ cp k.tmac k.tmac.orig || true
+ grep -v '^\. *w ' ../k.tmac > k.tmac
+
+.t.T: k.tmac t.tmac
+ @echo -- $< -----------------------
+ @<$< $(TROFF) >/dev/null
+
+.t.pdf: k.tmac t.tmac
+ <$< $(PDFROFF) > $@
diff --git a/mk/t/k.tmac b/mk/t/k.tmac
new file mode 100644
index 0000000..9d9f235
--- /dev/null
+++ b/mk/t/k.tmac
@@ -0,0 +1,414 @@
+.\" "/.
+.\" mu macro package for troff "/.
+.\" created by John Ankarström "/.
+.\" "/.
+.\" Permission to use, copy, modify and/or "/.
+.\" distribute this software for any purpose "/.
+.\" with or without fee is hereby granted. "/.
+.\" "/.
+.\" For an index of the macros defined in "/.
+.\" this file, issue `grep -n [-]- u.tmac'. "/.
+.\" "/.
+.\" When editing the source of mu, keep the "/.
+.\" following rules in mind: "/.
+.\" "/.
+.\" * Prefix temporary strings and registers "/.
+.\" with _. "/.
+.\" * Prefix internal registers and environ- "/.
+.\" ments with @. "/.
+.\" * Prefix internal macros with @ (normal "/.
+.\" macros), * (variants of the same "/.
+.\" macro) or % (strings). "/.
+.\" * Place macros in alphabetical order. "/.
+.\" * Do not use .ev and .evc directly; use "/.
+.\" (e, @e and @c instead. "/.
+.\" "/.
+.
+.do xflag 3 \" enable modern extensions
+.eo \" disable backslash escapes
+.
+.\" Internal macros ----------------------------
+.
+.\" @a -- setup document
+.de @a
+. nr @a 1
+.
+. \" set header/footer traps
+. wh 0 @th
+. em @tf
+. if \n(nl<=0 .@th
+. nr _ \n(.p-\n(nl
+. if \n(.t=\n_ .wh -1i @tf
+.
+. \" configure environments
+. @e d \" date
+. @c 0
+. nr sp 0.4v
+. ad c
+. @e h \" heading
+. @c 0
+. nr sp 1.2v
+. ps +1p
+. ft B
+. @e l \" literal display
+. @c 0
+. nr sp 0.3v
+. nf
+. in 18p
+. ft CR
+. ps -1p
+. vs -1p
+. @e n \" name (of author)
+. @c 0
+. nr sp 0.4v
+. ad c
+. @e p \" paragraph
+. @c 0
+. nr sp 0.3v
+. nr ti 18p
+. ad b
+. @e q \" quotation
+. @c 0
+. nr sp 0.3v
+. nr ti 18p
+. in 18p
+. ps -1p
+. vs -1p
+. @e s \" section heading
+. @c 0
+. nr sp 1v
+. ft B
+. @e t \" title
+. @c 0
+. ps +4p
+. vs +2p
+. ft B
+. ad c
+. @e
+.
+. \" configure special environments
+. (e @f \" footnote
+. @c 0
+. ps -2p
+. vs -1p
+. (e @m \" margins (header, footer)
+. @c 0
+. (e
+.
+. \" create formatted dates
+. af mo 00
+. af dy 00
+. ds #i \n[year]-\n(mo-\n(dy
+. af mo 1
+. af dy 1
+. ds #e \n(dy
+. ie \n(mo=1 .ds #e \*(%e January
+. el .ie \n(mo=2 .ds #e \*(#e February
+. el .ie \n(mo=3 .ds #e \*(#e March
+. el .ie \n(mo=4 .ds #e \*(#e April
+. el .ie \n(mo=5 .ds #e \*(#e May
+. el .ie \n(mo=6 .ds #e \*(#e June
+. el .ie \n(mo=7 .ds #e \*(#e July
+. el .ie \n(mo=8 .ds #e \*(#e August
+. el .ie \n(mo=9 .ds #e \*(#e September
+. el .ie \n(mo=10 .ds #e \*(#e October
+. el .ie \n(mo=11 .ds #e \*(#e November
+. el .ds #e \*(#e December
+. ds #e \*(#e \n[year]
+..
+.
+.\" @c -- copy environment
+.de @c
+. evc \$1
+. nr sp \n[@\$1_sp]
+. nr sq \n[@\$1_sq]
+. nr ti \n[@\$1_ti]
+..
+.
+.\" (e -- set environment
+.de (e
+. ev \$1
+..
+.
+.\" @e -- set extended environment
+.de @e
+.
+. \" initialize document if not initialized
+. if '\n(@a'0' .@a
+.
+. \" save special environment variables
+. if !'\n[.ev]'0' \{\
+. nr @\n[.ev]_sp \n(sp \" vertical space before any environment
+. nr @\n[.ev]_sq \n(sq \" vertical space before different environment
+. nr @\n[.ev]_ti \n(ti \" first line indentation
+. ev
+. \}
+.
+. \" keep track of previous and new environment
+. ds %penv \*[%env]
+. ds %env \$1
+.
+. \" post-environment operations
+. if !'\$1'' \{\
+. \" add small space after title
+. if '\*[%penv]'t' .if '\*[%env]'d' .sp 0.1v
+.
+. \" add space after title block
+. nr _ 0
+. if !'\*[%env]'d' .nr _ 1
+. if !'\*[%penv]'t' .if !'\*[%penv]'d' .nr _ 0
+. if \n_ \{\
+. sp 0.5i
+. ns
+. \}
+.
+. \" activate new environment
+. ev \$1
+. nr sp \n[@\n[.ev]_sp]
+. nr sq \n[@\n[.ev]_sq]
+. nr ti \n[@\n[.ev]_ti]
+. sp \n(spu
+. if !'\*[%env]'\*[%penv]' .sp \n(squ
+. \}
+..
+.
+.\" @f -- footer
+.de @f
+. sp |\n(.pu-48p
+. tl ''%''
+..
+.
+.\" @h -- header
+.de @h
+. sp |36p
+. tl ''''
+. sp |1i
+..
+.
+.\" @tf -- footer trap
+.de @tf
+.
+. \" skip automatic trigger if manual mode active, unless forced
+. if \n(@m>0 .if !'\$1'f' \{\
+. nr @m -1
+. return
+. \}
+.
+. \" print footer
+. br
+. (e @m
+. @f
+' bp
+..
+.
+.\" @th -- header trap
+.de @th
+. (e @m
+. rt 0
+. @h
+. (e \*[%env]
+. ns
+..
+.
+.\" @tn -- footnote trap
+.de @tn
+.
+. nr @m 1
+. \" print collected footnotes
+. (e @m
+___________________
+. br
+. sp 1p
+. (e @f
+.
+. \" escape control characters
+. char . \&.
+. char ' \&'
+.
+. @n
+. br
+.
+. \" restore control characters
+. char . .
+. char ' '
+.
+. \" clear footnotes
+. ch @tn
+. nr @.t 0
+. nr @dn 0
+. rm @n
+. @tf f
+..
+.
+.
+.\" Inline macros ------------------------------
+.
+.\" " -- inline quotation
+.de "
+\&\$3\(lq\$1\(rq\$2
+..
+.
+.\" b -- bold font
+.de b
+\&\$3\fB\$1\fP\$2
+..
+.
+.\" c -- constant-width font
+.de c
+.
+. \" save original interword spacing
+. nr _ss \n[.ss]
+.
+. \" copy family and (relative) point size from l environment
+. (e 0
+. nr _s0 \n(.s
+. (e
+. (e l
+. ds _fl \n(.f
+. nr _sl \n(.s
+. (e
+.
+\&\$3\c
+. ft \*[_fl]
+. ss 7
+. ie d nrf .nrf _s (\n(.s*(\n[_sl]/(\n[_s0])))
+. el .nr _s \n(.s+(\n[_sl]-\n[_s0])
+. ps \n(_sp
+\&\$1\c
+. ft
+. ss \n[_ss]
+. ps
+\&\$2
+..
+.
+.\" i -- italic font
+.de i
+\&\$3\fI\$1\fP\$2
+..
+.
+.\" x -- bold italic font
+.de x
+\&\$3\f(BI\$1\fP\$2
+..
+.
+.
+.\" Environment macros -------------------------
+.
+.
+.\" d -- centered date
+.de d
+. br
+. @e d
+. if !'\$1'' \*[#\$1]
+..
+.
+.\" h -- heading
+.de h
+. br
+. @e h
+.
+..
+.\" l -- literal display
+.de l
+. br
+. @e l
+..
+.
+.\" p -- paragraph
+.de p
+. br
+. @e p
+. if '\*[%penv]'p' .ti \n(tiu
+..
+.
+.\" q -- quotation
+.de q
+. br
+. @e q
+. ti \n(tiu
+..
+.
+.\" s -- subheading
+.de s
+. br
+. @e s
+..
+.
+.\" t -- centered title
+.de t
+. br
+. @e t
+..
+.
+.
+.\" Other macros -------------------------------
+.
+.\" ( -- begin footnote
+.de (
+. if !'\$1'' \&\$1\c
+.
+. \" increment and print number
+. nr @n \n(@n+1
+\v'-.3m'\s'\En(.s*80/100'\n(@n\s0\v'.3m'\c
+.
+. \" switch environment and capture footnote
+. (e @f
+. boxa @n
+\h'9p'\v'-.3m'\s'\En(.s*80/100'\n(@n\s0\v'.3m'\h'2p'\c
+..
+.
+.\" ) -- end footnote
+.de )
+. br
+\!.br
+. boxa
+. (e \*[%env]
+. ie !'\$1'' \&\$1
+. el \&
+.
+. \" get position of footer (or next trap)
+. if \n[@.t]=0 .nr @.t \n(nl+\n(.t
+.
+.
+. \" print footnote at bottom of page
+. ch @tn
+. nr @dn +\n(dnu
+. nr _tn \n[@.t]u-\n[@dn]u-12p
+. wh \n[_tn]u @tn
+..
+.
+.\" w -- want space
+.de w
+. *w 0 \$@
+..
+.de *w
+.
+. \" parse specification -> _s : accumulated space
+. nr _s \$1 \" _e : environment name (e.g. eee -> e)
+. ds _e \$2 \" _n : number of lines specified (e.g. eee -> 3)
+. substring _e 0 0
+. length _n \$2
+.
+. \" add environment spacing
+. nr _s +\n[@\*(_e_sp]
+. nr _s +\n[@\*(_e_sq]
+.
+. \" add environment line height (for each line)
+. (e \*(_e
+. nr _v \n(.v
+. (e
+. nr _s +(\n(_n*\n(_v)
+. shift 2
+.
+. \" recurse or request space if done
+. ie \n(.$>0 .*w \n(_s \$@
+. el \{\
+. nr _s \n(_s-\n(_v+1u \" do not request too much
+. if (\n(_su)>\n(.tu \{\
+. br
+. bp
+. \}
+. \}
+..
+.
+.ec \" re-enable backslash escapes
diff --git a/mk/t/k.tmac.orig b/mk/t/k.tmac.orig
new file mode 100644
index 0000000..ff5ef66
--- /dev/null
+++ b/mk/t/k.tmac.orig
@@ -0,0 +1,419 @@
+.\" "/.
+.\" mu macro package for troff "/.
+.\" created by John Ankarström "/.
+.\" "/.
+.\" Permission to use, copy, modify and/or "/.
+.\" distribute this software for any purpose "/.
+.\" with or without fee is hereby granted. "/.
+.\" "/.
+.\" For an index of the macros defined in "/.
+.\" this file, issue `grep -n [-]- u.tmac'. "/.
+.\" "/.
+.\" When editing the source of mu, keep the "/.
+.\" following rules in mind: "/.
+.\" "/.
+.\" * Prefix temporary strings and registers "/.
+.\" with _. "/.
+.\" * Prefix internal registers and environ- "/.
+.\" ments with @. "/.
+.\" * Prefix internal macros with @ (normal "/.
+.\" macros), * (variants of the same "/.
+.\" macro) or % (strings). "/.
+.\" * Place macros in alphabetical order. "/.
+.\" * Do not use .ev and .evc directly; use "/.
+.\" (e, @e and @c instead. "/.
+.\" "/.
+.
+.do xflag 3 \" enable modern extensions
+.eo \" disable backslash escapes
+.
+.\" Internal macros ----------------------------
+.
+.\" @a -- setup document
+.de @a
+. nr @a 1
+.
+. \" set header/footer traps
+. wh 0 @th
+. em @tf
+. if \n(nl<=0 .@th
+. nr _ \n(.p-\n(nl
+. if \n(.t=\n_ .wh -1i @tf
+.
+. \" configure environments
+. @e d \" date
+. @c 0
+. nr sp 0.4v
+. ad c
+. @e h \" heading
+. @c 0
+. nr sp 1.2v
+. ps +1p
+. ft B
+. @e l \" literal display
+. @c 0
+. nr sp 0.3v
+. nf
+. in 18p
+. ft CR
+. ps -1p
+. vs -1p
+. @e n \" name (of author)
+. @c 0
+. nr sp 0.4v
+. ad c
+. @e p \" paragraph
+. @c 0
+. nr sp 0.3v
+. nr ti 18p
+. ad b
+. @e q \" quotation
+. @c 0
+. nr sp 0.3v
+. nr ti 18p
+. in 18p
+. ps -1p
+. vs -1p
+. @e s \" section heading
+. @c 0
+. nr sp 1v
+. ft B
+. @e t \" title
+. @c 0
+. ps +4p
+. vs +2p
+. ft B
+. ad c
+. @e
+.
+. \" configure special environments
+. (e @f \" footnote
+. @c 0
+. ps -2p
+. vs -1p
+. (e @m \" margins (header, footer)
+. @c 0
+. (e
+.
+. \" create formatted dates
+. af mo 00
+. af dy 00
+. ds #i \n[year]-\n(mo-\n(dy
+. af mo 1
+. af dy 1
+. ds #e \n(dy
+. ie \n(mo=1 .ds #e \*(%e January
+. el .ie \n(mo=2 .ds #e \*(#e February
+. el .ie \n(mo=3 .ds #e \*(#e March
+. el .ie \n(mo=4 .ds #e \*(#e April
+. el .ie \n(mo=5 .ds #e \*(#e May
+. el .ie \n(mo=6 .ds #e \*(#e June
+. el .ie \n(mo=7 .ds #e \*(#e July
+. el .ie \n(mo=8 .ds #e \*(#e August
+. el .ie \n(mo=9 .ds #e \*(#e September
+. el .ie \n(mo=10 .ds #e \*(#e October
+. el .ie \n(mo=11 .ds #e \*(#e November
+. el .ds #e \*(#e December
+. ds #e \*(#e \n[year]
+..
+.
+.\" @c -- copy environment
+.de @c
+. evc \$1
+. nr sp \n[@\$1_sp]
+. nr sq \n[@\$1_sq]
+. nr ti \n[@\$1_ti]
+..
+.
+.\" (e -- set environment
+.de (e
+. ev \$1
+..
+.
+.\" @e -- set extended environment
+.de @e
+.
+. \" initialize document if not initialized
+. if '\n(@a'0' .@a
+.
+. \" save special environment variables
+. if !'\n[.ev]'0' \{\
+. nr @\n[.ev]_sp \n(sp \" vertical space before any environment
+. nr @\n[.ev]_sq \n(sq \" vertical space before different environment
+. nr @\n[.ev]_ti \n(ti \" first line indentation
+. ev
+. \}
+.
+. \" keep track of previous and new environment
+. ds %penv \*[%env]
+. ds %env \$1
+.
+. \" post-environment operations
+. if !'\$1'' \{\
+. \" add small space after title
+. if '\*[%penv]'t' .if '\*[%env]'d' .sp 0.1v
+.
+. \" add space after title block
+. nr _ 0
+. if !'\*[%env]'d' .nr _ 1
+. if !'\*[%penv]'t' .if !'\*[%penv]'d' .nr _ 0
+. if \n_ \{\
+. sp 0.5i
+. ns
+. \}
+.
+. \" activate new environment
+. ev \$1
+. nr sp \n[@\n[.ev]_sp]
+. nr sq \n[@\n[.ev]_sq]
+. nr ti \n[@\n[.ev]_ti]
+. sp \n(spu
+. if !'\*[%env]'\*[%penv]' .sp \n(squ
+. \}
+..
+.
+.\" @f -- footer
+.de @f
+. sp |\n(.pu-48p
+. tl ''%''
+..
+.
+.\" @h -- header
+.de @h
+. sp |36p
+. tl ''''
+. sp |1i
+..
+.
+.\" @tf -- footer trap
+.de @tf
+.
+. \" skip automatic trigger if manual mode active, unless forced
+. if \n(@m>0 .if !'\$1'f' \{\
+. nr @m -1
+. return
+. \}
+.
+. \" print footer
+. br
+. (e @m
+. @f
+' bp
+..
+.
+.\" @th -- header trap
+.de @th
+. (e @m
+. rt 0
+. @h
+. (e \*[%env]
+. ns
+..
+.
+.\" @tn -- footnote trap
+.de @tn
+.
+. nr @m 1
+. \" print collected footnotes
+. (e @m
+___________________
+. br
+. sp 1p
+. (e @f
+.
+. \" escape control characters
+. char . \&.
+. char ' \&'
+.
+. @n
+. br
+.
+. \" restore control characters
+. char . .
+. char ' '
+.
+. \" clear footnotes
+. ch @tn
+. nr @.t 0
+. nr @dn 0
+. rm @n
+. @tf f
+..
+.
+.
+.\" Inline macros ------------------------------
+.
+.\" b -- bold font
+.de b
+\&\$3\fB\$1\fP\$2
+..
+.
+.\" c -- constant-width font
+.de c
+.
+. \" save original interword spacing
+. nr _ss \n[.ss]
+.
+. \" copy family and (relative) point size from l environment
+. (e 0
+. nr _s0 \n(.s
+. (e
+. (e l
+. ds _fl \n(.f
+. nr _sl \n(.s
+. (e
+.
+\&\$3\c
+. ft \*[_fl]
+. ss 7
+. ie d nrf .nrf _s (\n(.s*(\n[_sl]/(\n[_s0])))
+. el .nr _s \n(.s+(\n[_sl]-\n[_s0])
+. ps \n(_sp
+\&\$1\c
+. ft
+. ss \n[_ss]
+. ps
+\&\$2
+..
+.
+.\" i -- italic font
+.de i
+\&\$3\fI\$1\fP\$2
+..
+.
+.\" x -- bold italic font
+.de x
+\&\$3\f(BI\$1\fP\$2
+..
+.
+.
+.\" Hybrid macros ------------------------------
+.
+.\" q -- quotation
+.de q
+.
+. \" inline
+. ie !'\$1'' \&\$3\(lq\$1\(rq\$2
+.
+. \" block
+. el \{\
+. br
+. @e q
+. ti \n(tiu
+. \}
+..
+.
+.
+.\" Environment macros -------------------------
+.
+.
+.\" d -- centered date
+.de d
+. br
+. @e d
+. if !'\$1'' \*[#\$1]
+..
+.
+.\" h -- heading
+.de h
+. br
+. @e h
+.
+..
+.\" l -- literal display
+.de l
+. br
+. @e l
+..
+.
+.\" p -- paragraph
+.de p
+. br
+. @e p
+. if '\*[%penv]'p' .ti \n(tiu
+..
+.
+.\" s -- subheading
+.de s
+. br
+. @e s
+..
+.
+.\" t -- centered title
+.de t
+. br
+. @e t
+..
+.
+.
+.\" Other macros -------------------------------
+.
+.\" ( -- begin footnote
+.de (
+. if !'\$1'' \&\$1\c
+.
+. \" increment and print number
+. nr @n \n(@n+1
+\v'-.3m'\s'\En(.s*80/100'\n(@n\s0\v'.3m'\c
+.
+. \" switch environment and capture footnote
+. (e @f
+. boxa @n
+\h'9p'\v'-.3m'\s'\En(.s*80/100'\n(@n\s0\v'.3m'\h'2p'\c
+..
+.
+.\" ) -- end footnote
+.de )
+. br
+\!.br
+. boxa
+. (e \*[%env]
+. ie !'\$1'' \&\$1
+. el \&
+.
+. \" get position of footer (or next trap)
+. if \n[@.t]=0 .nr @.t \n(nl+\n(.t
+.
+.
+. \" print footnote at bottom of page
+. ch @tn
+. nr @dn +\n(dnu
+. nr _tn \n[@.t]u-\n[@dn]u-12p
+. wh \n[_tn]u @tn
+..
+.
+.\" w -- want space
+.de w
+. *w 0 \$@
+..
+.de *w
+.
+. \" parse specification -> _s : accumulated space
+. nr _s \$1 \" _e : environment name (e.g. eee -> e)
+. ds _e \$2 \" _n : number of lines specified (e.g. eee -> 3)
+. substring _e 0 0
+. length _n \$2
+.
+. \" add environment spacing
+. nr _s +\n[@\*(_e_sp]
+. nr _s +\n[@\*(_e_sq]
+.
+. \" add environment line height (for each line)
+. (e \*(_e
+. nr _v \n(.v
+. (e
+. nr _s +(\n(_n*\n(_v)
+. shift 2
+.
+. \" recurse or request space if done
+. ie \n(.$>0 .*w \n(_s \$@
+. el \{\
+. nr _s \n(_s-\n(_v+1u \" do not request too much
+. if (\n(_su)>\n(.tu \{\
+. br
+. bp
+. \}
+. \}
+..
+.
+.ec \" re-enable backslash escapes
diff --git a/mk/t/ne.t b/mk/t/ne.t
new file mode 100644
index 0000000..ac318a7
--- /dev/null
+++ b/mk/t/ne.t
@@ -0,0 +1,147 @@
+.so k.tmac
+.so t.tmac
+.
+.nr mb 1i
+.
+.t
+ne.t: test space requests
+.p
+This file tests the
+.c w
+macro.
+.
+.\" helper macros
+.
+.\" make space at bottom of page
+.de @
+. rs
+. sp |\n(.pu-\n(mbu-\\$1-1u
+. nr a \\n%
+..
+.
+.\" write 's pppp'
+.de *spppp
+. s
+. nr s \\n%
+Test \\nt
+. p
+. nr p \\n%
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+eiusmod tempor incididunt ut labore et dolore magna aliqua.
+. nr pp \\n%
+Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
+nisi ut aliquip ex ea commodo consequat.
+Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+cupidatat non proident, sunt in culpa qui officia deserunt mollit
+anim id est laborum.
+..
+.de *sllll
+. s
+Test \\nt
+. nr s \\n%
+. l
+. nr l \\n%
+first line
+. nr ll \\n%
+second line
+. nr lll \\n%
+third line
+fourth line
+..
+.
+.\" run tests
+.
+.test s pp +0u
+.bp
+.@ 39600u
+.w s pp
+.*spppp
+.assert (`na=`ns)&(`ns=`np)&(`np=`n(pp)
+.
+.test s pp +1u
+.bp
+.@ 39600u
+.sp 1u
+.w s pp
+.*spppp
+.assert (`na<`ns)&(`ns=`np)&(`np=`n(pp)
+.
+.test s pp +2u
+.bp
+.@ 39600u
+.sp 2u
+.w s pp
+.*spppp
+.assert (`na<`ns)&(`ns=`np)&(`np=`n(pp)
+.
+.test s p +0u
+.bp
+.@ 27600u
+.w s p
+.*spppp
+.assert (`na=`ns)&(`ns=`np)&(`np<`n(pp)
+.
+.test s p +1u
+.bp
+.@ 27600u
+.sp 1u
+.w s p
+.*spppp
+.assert (`na<`ns)&(`ns=`np)&(`np=`n(pp)
+.
+.test s p +2u
+.bp
+.@ 27600u
+.sp 2u
+.w s p
+.*spppp
+.assert (`na<`ns)&(`ns=`np)&(`np=`n(pp)
+.
+.test s l +0u
+.bp
+.@ 27600u
+.w s l
+.*sllll
+.assert (`na=`ns)&(`ns=`nl)&(`nl<`n(ll)&(`n(ll=`n[lll])
+.
+.test s l +1u
+.bp
+.@ 27600u
+.sp 1u
+.w s l
+.*sllll
+.assert (`na<`ns)&(`ns=`nl)&(`nl=`n(ll)&(`n(ll=`n[lll])
+.
+.test s l +2u
+.bp
+.@ 27600u
+.sp 2u
+.w s l
+.*sllll
+.assert (`na<`ns)&(`ns=`nl)&(`nl=`n(ll)&(`n(ll=`n[lll])
+.
+.test s ll +0u
+.bp
+.@ 38600u
+.w s ll
+.*sllll
+.assert (`na=`ns)&(`ns=`nl)&(`nl=`n(ll)&(`n(ll<`n[lll])
+.
+.test s ll +1u
+.bp
+.@ 38600u
+.sp 1u
+.w s ll
+.*sllll
+.assert (`na<`ns)&(`ns=`nl)&(`nl=`n(ll)&(`n(ll=`n[lll])
+.
+.test s ll +2u
+.bp
+.@ 38600u
+.sp 2u
+.w s ll
+.*sllll
+.assert (`na<`ns)&(`ns=`nl)&(`nl=`n(ll)&(`n(ll=`n[lll])
+.
+.end
diff --git a/mk/t/t.tmac b/mk/t/t.tmac
new file mode 100644
index 0000000..223babe
--- /dev/null
+++ b/mk/t/t.tmac
@@ -0,0 +1,32 @@
+.\" start test
+.de test
+. nr t +1
+. tmc test \\nt (\\$*):
+..
+.
+.\" make assertion
+.de assert
+.ec `
+.ie `$1 \{\
+. tm success
+.\}
+.ec
+.el \{\
+. if \\nF<\\nt .tm failure:
+.ec `
+. ds *exp `$1
+.ec
+. tm { \\*[*exp]
+. if !'\\*[*exp]'\\$1' . tm } \\$1
+. nr f +1
+. nr F \\nt
+.\}
+..
+.\" end tests
+.de end
+. ie \\nf=0 .tm ** \\nt test(s) succeeded **
+. el \{
+. ab !! \\nf test(s) failed !!
+. \}
+..
+.