% \iffalse meta-comment % % nehgs-register.dtx % Copyright 2026 James P. Howard, II % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3c % of this license or (at your option) any later version. % The latest version of this license is in % https://www.latex-project.org/lppl.txt % and version 1.3c or later is part of all distributions of LaTeX % version 2008-05-04 or later. % % This work has the LPPL maintenance status `maintained'. % The Current Maintainer of this work is James P. Howard, II. % % This work consists of the file nehgs-register.dtx % and the derived file nehgs-register.sty. % %<*driver> \documentclass{ltxdoc} \usepackage[T1]{fontenc} \usepackage{lmodern} \usepackage{hyperref} \usepackage{enumitem} \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{nehgs-register.dtx} \end{document} % % \fi % % \changes{v1.0}{2026/07/27}{First public release.} % \changes{v1.0.1}{2026/07/29}{Fix CI and test infrastructure for TeX Live 2026.} % \changes{v1.0.2}{2026/07/29}{Rename package to nehgs-register for CTAN.} % \changes{v1.1}{2026/08/10}{Include example documents in the CTAN archive.} % % \GetFileInfo{nehgs-register.sty} % % \title{The \textsf{nehgs-register} package\thanks{This document % corresponds to \textsf{nehgs-register}~v1.1, % dated~2026/08/10.}} % \author{James P. Howard, II\\ \texttt{jh@jameshoward.us}} % \date{2026/08/10} % \maketitle % % \begin{abstract} % The \textsf{nehgs-register} package provides commands and environments % for typesetting genealogical registers in the style used by the % \emph{New England Historical and Genealogical Register} (NEHGS). % It manages a person registry with computed generation numbers, % ancestor chains, child lists with continuation markers, multiple % marriages, vital records, date formatting, gender-aware pronouns, % source citations, index generation, and cross-references. % \end{abstract} % % \tableofcontents % % \section{Introduction} % % The Register style, also called the NEHGS style, is the standard % format for presenting genealogical data in descending order. % Each person who continues the lineage receives a numbered main entry; % children are listed under their parents with roman numeral labels. % Children who appear later as main entries are marked with a ``+'' % continuation symbol. % % This package automates the mechanical parts of Register typesetting: % numbering, generation computation, ancestor chains, parent % validation, and index entries. The genealogical prose itself % remains the author's responsibility. % % \section{Quick start} % % \begin{verbatim} % \documentclass{article} % \usepackage[en-US]{datetime2} % \usepackage[date=en-US,abbrevparents]{nehgs-register} % % \definepersonkv{john-doe}{ % name = {John Doe}, % preferred = {John}, % parents = {}, % sex = {M}, % birth = {1820-03-15}, % birth-place = {Boston, Massachusetts} % } % % \definepersonkv{mary-smith}{ % name = {Mary Smith}, % preferred = {Mary}, % parents = {}, % sex = {F}, % birth = {1825-06-20} % } % % \definepersonkv{james-doe}{ % name = {James Doe}, % preferred = {James}, % parents = {john-doe,mary-smith}, % lineage-parent = {john-doe}, % sex = {M}, % birth = {1850-11-02} % } % % \addmarriage{john-doe}{ % spouse = mary-smith, % date = 1845-09-10, % place = {Springfield, Massachusetts} % } % % \begin{document} % \begin{registersection} % % \generationheading{john-doe} % \mainperson{john-doe} was born % \getpersonbirthdate{john-doe} in % \getpersonbirthplace{john-doe}. % \HeSheThey{john-doe} married % \getpersonname{mary-smith} on % \getmarriagedate{john-doe}{1} in % \getmarriageplace{john-doe}{1}. % % Children of John Doe and Mary Smith: % \begin{childrenof}{john-doe}{mary-smith} % \childref{james-doe}{b.~\getpersonbirthdate{james-doe}.} % \end{childrenof} % % \generationheading{james-doe} % \mainperson{james-doe} was born % \getpersonbirthdate{james-doe}. % % \end{registersection} % \checkregister % \end{document} % \end{verbatim} % % \section{Package options} % % The package accepts the following options: % % \begin{description}[style=nextline] % \item[\texttt{index} (default)] Load \textsf{imakeidx} and % generate index entries whenever a person's name is printed % by |\getpersonname|. % \item[\texttt{noindex}] Suppress all index generation. % \item[\texttt{fullparents} (default)] Print full names in % parenthetical ancestor chains. % \item[\texttt{abbrevparents}] Print preferred (short) names in % ancestor chains. % \item[\texttt{ngsq}] Use NGSQ (Modified Register) numbering. % \item[\texttt{datestyle=}\meta{style}] Set the \textsf{datetime2} % date style at |\begin{document}|. The synonym \texttt{date=} % is also accepted. % \item[\texttt{numbering=ngsq}] Equivalent to the \texttt{ngsq} % option. % \end{description} % % \section{Defining persons} % % \DescribeMacro{\definepersonkv} % |\definepersonkv|\marg{id}\marg{key=value pairs} % % The primary interface for declaring a person in the registry. % The \meta{id} is a short identifier (e.g., |john-doe|) used to % refer to this person throughout the document. Available keys: % % \begin{description}[style=nextline, font=\ttfamily] % \item[name] Full name as it should appear in the register. % \item[preferred] Short or preferred name for abbreviated contexts. % \item[parents] Comma-separated list of parent ids. Leave empty for % the founding generation. % \item[index] Index entry text. Defaults to the \texttt{name} value. % \item[lineage-parent] Single parent id for lineage chain display. % When set, ancestor chains follow only this parent rather than % listing all parents. % \item[sex] |M|, |F|, or empty. Used by pronoun helpers. % \item[birth] Birth date in ISO format (|YYYY-MM-DD|). % \item[birth-place] Birth place as free text. % \item[death] Death date in ISO format. % \item[death-place] Death place as free text. % \item[baptism] Baptism or christening date in ISO format. % \item[baptism-place] Baptism or christening place. % \item[burial] Burial date in ISO format. % \item[burial-place] Burial place. % \item[occupation] Occupation as free text. % \item[marriage-date] Date of (first) marriage in ISO format. % For multiple marriages, use |\addmarriage| instead. % \item[marriage-place] Place of (first) marriage. % \end{description} % % \DescribeMacro{\defineperson} % |\defineperson|\marg{id}\marg{parents}\marg{name}\marg{preferred}% % \oarg{index} % % Legacy positional interface. Prefer |\definepersonkv| for new work. % % \section{Marriage support} % % \DescribeMacro{\addmarriage} % |\addmarriage|\marg{person-id}\marg{key=value pairs} % % Add a marriage record to a previously defined person. Supports % multiple marriages per person. Available keys: % % \begin{description}[style=nextline, font=\ttfamily] % \item[date] Marriage date in ISO format. % \item[place] Marriage place. % \item[spouse] Person id of the spouse (must be separately defined). % \end{description} % % Marriages are numbered sequentially starting from~1 in the order % they are added. If \texttt{marriage-date} or \texttt{marriage-place} % is given inline in |\definepersonkv|, that counts as marriage~1. % % \DescribeMacro{\getmarriagedate} % |\getmarriagedate|\marg{person-id}\marg{number} % % Print the date of marriage \meta{number}, formatted through % \textsf{datetime2}. % % \DescribeMacro{\getmarriageplace} % |\getmarriageplace|\marg{person-id}\marg{number} % % Print the place of marriage \meta{number}. % % \DescribeMacro{\getmarriagespouse} % |\getmarriagespouse|\marg{person-id}\marg{number} % % Print the name of the spouse from marriage \meta{number} % (via |\getpersonname|, including index entry). % % \DescribeMacro{\getmarriagecount} % |\getmarriagecount|\marg{person-id} % % Expand to the number of recorded marriages for the person. % % \section{Name retrieval} % % \DescribeMacro{\getpersonname} % |\getpersonname|\marg{id} prints the full name in small caps and % generates an index entry. % % \DescribeMacro{\getpersonnameNI} % |\getpersonnameNI|\marg{id} prints the full name in small caps % without an index entry. % % \DescribeMacro{\getpersonpreferred} % |\getpersonpreferred|\marg{id} prints the preferred (short) name % in small caps. % % \DescribeMacro{\getpersonabbrevNI} % |\getpersonabbrevNI|\marg{id} prints the preferred name if set, % otherwise the full name, without an index entry. % % \DescribeMacro{\shortname} % |\shortname|\marg{id} is an alias for |\getpersonpreferred|. % % \section{Vital record accessors} % % Each accessor prints the stored value, or a bracketed placeholder % with a package warning if the value is missing. % % \DescribeMacro{\getpersonbirthdate} % \DescribeMacro{\getpersonbirthplace} % \DescribeMacro{\getpersondeathdate} % \DescribeMacro{\getpersondeathplace} % \DescribeMacro{\getpersonbaptismdate} % \DescribeMacro{\getpersonbaptismplace} % \DescribeMacro{\getpersonburialdate} % \DescribeMacro{\getpersonburialplace} % \DescribeMacro{\getpersonoccupation} % \DescribeMacro{\getpersonsex} % % |\getpersonbirthdate|\marg{id}, % |\getpersonbirthplace|\marg{id}, etc. % % Date accessors format the stored ISO date through |\registerdate|. % Place and occupation accessors print the stored text verbatim. % |\getpersonsex|\marg{id} expands to the raw stored value % (|M|, |F|, or empty). % % \section{Generation computation} % % \DescribeMacro{\getgeneration} % |\getgeneration|\marg{id} computes and prints the generation number. % Persons with no parents are generation~1; others are one more than % their highest-generation parent. Results are cached after the first % computation. Cycles are detected and produce a warning. % % \DescribeMacro{\generationheading} % |\generationheading|\marg{id} prints a bold heading such as % ``Third Generation'' based on the person's computed generation. % Supports generations 1--20 by name; higher generations use the % numeric form. % % \section{Register entries} % % \DescribeMacro{\mainperson} % |\mainperson|\marg{id} typesets a numbered main register entry: % the entry number in bold, the person's name in small caps with % a generation superscript, and the parenthetical ancestor chain. % Each call increments the |registerperson| counter and creates % a label |person:|\meta{id} for cross-references. % % \DescribeMacro{\xrefperson} % |\xrefperson|\marg{id} prints a hyperlinked cross-reference to % a person's main entry, showing their name and entry number. % % \DescribeMacro{\childname} % |\childname|\marg{id} prints a person's name with generation % superscript, without an index entry. Used in child lists. % % \DescribeMacro{\formatparents} % |\formatparents|\marg{id} prints the parenthetical ancestor chain. % Called automatically by |\mainperson| but available for standalone % use. % % \section{Child lists} % % \DescribeEnv{children} % |children| is an enumerated list with roman numeral labels, % accepting optional \textsf{enumitem} keys. % % \DescribeEnv{childrenof} % |childrenof|\marg{parent-a}\marg{parent-b}\oarg{enumitem keys} % is like |children| but validates that each |\child| entry has the % expected parent pair. % % \DescribeMacro{\child} % |\child|\marg{id}\marg{description} typesets a child entry. % The description text follows a comma after the child's name. % % \DescribeMacro{\childref} % |\childref|\marg{id}\marg{description} typesets a child entry % with a ``\textbf{+}'' continuation marker, indicating that this % child will appear later as a |\mainperson| entry. This is a % core convention of the Register style. % % \section{Approximate dates} % % Genealogists frequently work with uncertain dates. These commands % format common qualifiers used in genealogical writing: % % \DescribeMacro{\circa} % |\circa|\marg{date} prints ``c.\,\meta{date}''. % % \DescribeMacro{\before} % |\before|\marg{date} prints ``bef.\ \meta{date}''. % % \DescribeMacro{\after} % |\after|\marg{date} prints ``aft.\ \meta{date}''. % % \DescribeMacro{\between} % |\between|\marg{date1}\marg{date2} prints % ``bet.\ \meta{date1} and \meta{date2}''. % % \DescribeMacro{\aboutdate} % |\aboutdate|\marg{date} prints ``abt.\ \meta{date}''. % % \DescribeMacro{\probably} % |\probably|\marg{date} prints ``prob.\ \meta{date}''. % % \DescribeMacro{\say} % |\say|\marg{date} prints ``say \meta{date}''. % % These may be used both in prose and inside key values. % % \section{Gender-aware pronouns} % % These commands select pronouns based on the |sex| field % (|M|\,$\to$\,masculine, |F|\,$\to$\,feminine, % other/empty\,$\to$\,neutral). % % \DescribeMacro{\heshethey} % \DescribeMacro{\himherthem} % \DescribeMacro{\hishertheirargs} % \DescribeMacro{\HeSheThey} % \DescribeMacro{\HimHerThem} % \DescribeMacro{\HisHerTheir} % \DescribeMacro{\himselfherselfthemself} % % \noindent % All take a single \marg{person-id} argument. % Lowercase and capitalized variants are provided. % % \section{Source citations} % % \DescribeMacro{\registersource} % |\registersource|\marg{text} produces a footnote citation. % % \DescribeMacro{\registerendnote} % |\registerendnote|\marg{text} produces an endnote if the % \textsf{endnotes} package is loaded, otherwise falls back to % a footnote. % % \section{Date formatting} % % \DescribeMacro{\registerdate} % |\registerdate|\marg{YYYY-MM-DD} formats an ISO-8601 date using % the current \textsf{datetime2} style. Non-ISO input is passed % through with a warning. % % \DescribeMacro{\registerdatestyle} % |\registerdatestyle|\marg{style} changes the \textsf{datetime2} % date style mid-document. % % \section{Shorthands} % % \DescribeMacro{\born} % \DescribeMacro{\died} % \DescribeMacro{\married} % \DescribeMacro{\baptized} % \DescribeMacro{\buried} % \DescribeMacro{\place} % \DescribeMacro{\dated} % % \noindent % |\born|\marg{text} prints ``b.\ \meta{text}''; % |\died| prints ``d.''; % |\married| prints ``m.''; % |\baptized| prints ``bp.''; % |\buried| prints ``bur.''. % |\place|\marg{text} prints \meta{text} in italics; % |\dated|\marg{text} prints \meta{text} as-is. % % \section{Register section} % % \DescribeEnv{registersection} % The |registersection| environment sets up typographic parameters % suitable for register prose: paragraph indentation of 1.5em, % no paragraph skip, and compact lists. It also defines a local % |\generation|\marg{label} command for manual generation headings. % % \section{Validation} % % \DescribeMacro{\checkperson} % |\checkperson|\marg{id} runs validation checks on a single person: % verifies the name is non-empty, all listed parents are defined, % all dates are in ISO format, and places are not given without % corresponding dates. Problems produce package warnings. % % \DescribeMacro{\checkregister} % |\checkregister| runs |\checkperson| on every registered person. % Call this near |\end{document}| to catch data integrity issues. % % \StopEventually{% % \PrintChanges % \PrintIndex % } % % \section{Implementation} % % \subsection{Package identification and options} % % \begin{macrocode} %<*package> %% nehgs-register.sty --- NEHGS-style genealogical register %% Copyright 2026 James P. Howard, II %% %% This work may be distributed and/or modified under the %% conditions of the LaTeX Project Public License, either version 1.3c %% of this license or (at your option) any later version. %% The latest version of this license is in %% https://www.latex-project.org/lppl.txt %% and version 1.3c or later is part of all distributions of LaTeX %% version 2008-05-04 or later. %% %% This work has the LPPL maintenance status `maintained'. %% The Current Maintainer of this work is James P. Howard, II. \NeedsTeXFormat{LaTeX2e}[2020/10/01] \ProvidesPackage{nehgs-register}[2026/08/10 v1.1 NEHGS-style genealogical register] % \end{macrocode} % % Option booleans and key=value option parser. % % \begin{macrocode} \newif\ifr@index \r@indextrue \newif\ifr@option@abbrevparents \r@option@abbrevparentsfalse \newif\ifr@option@ngsq \r@option@ngsqfalse \newcommand{\r@datestyle}{} \newcommand{\r@optionkey}{} \newcommand{\r@optionvalue}{} \newcommand{\r@datestylekey}{datestyle} \newcommand{\r@datekey}{date} \newcommand{\r@numberingkey}{numbering} \newcommand{\r@splitoption}{} \def\r@splitoption#1=#2\@nil{% \renewcommand{\r@optionkey}{#1}% \renewcommand{\r@optionvalue}{#2}% } \newcommand{\r@ngsqvalue}{ngsq} \newcommand{\r@handleunknownoption}{% \@expandtwoargs\in@{=}{\CurrentOption}% \ifin@ \expandafter\r@splitoption\CurrentOption\@nil \ifx\r@optionkey\r@datestylekey \renewcommand{\r@datestyle}{\r@optionvalue}% \else \ifx\r@optionkey\r@datekey \renewcommand{\r@datestyle}{\r@optionvalue}% \else \ifx\r@optionkey\r@numberingkey \ifx\r@optionvalue\r@ngsqvalue \r@option@ngsqtrue \else \PackageWarning{nehgs-register}{Unknown numbering style `\r@optionvalue'}% \fi \else \PackageWarning{nehgs-register}{Unknown option `\CurrentOption'}% \fi \fi \fi \else \PackageWarning{nehgs-register}{Unknown option `\CurrentOption'}% \fi } \DeclareOption{index}{\r@indextrue} \DeclareOption{noindex}{\r@indexfalse} \DeclareOption{fullparents}{\r@option@abbrevparentsfalse} \DeclareOption{abbrevparents}{\r@option@abbrevparentstrue} \DeclareOption{ngsq}{\r@option@ngsqtrue} \DeclareOption*{\r@handleunknownoption} \ProcessOptions\relax % \end{macrocode} % % \subsection{Required packages} % % \begin{macrocode} \RequirePackage{xparse} \RequirePackage{enumitem} \RequirePackage{etoolbox} \RequirePackage{keyval} \RequirePackage{xspace} \RequirePackage{datetime2} \RequirePackage{hyperref} \ifr@index \RequirePackage{imakeidx} \makeindex \fi \makeatletter % \end{macrocode} % % \subsection{Internal helpers} % % \begin{macrocode} \newcommand{\r@trimid}[1]{\zap@space#1 \@empty} \newcommand{\r@warn}[1]{\PackageWarning{nehgs-register}{#1}} \newcommand{\r@indexperson}[1]{\ifr@index\index{#1}\fi} \newcommand{\r@unknowngeneration}{0} \newcommand{\r@csvadd}[2]{% \ifx#1\@empty \edef#1{#2}% \else \edef#1{#1,#2}% \fi } % \end{macrocode} % % \subsection{Person registry} % % \begin{macro}{\r@rememberperson} % Add a person id to the global person list if not already present. % \begin{macrocode} \newcommand{\r@personlist}{} \newcommand{\r@rememberperson}[1]{% \edef\r@id{\r@trimid{#1}}% \ifx\r@id\@empty\else \ifcsdef{r@registered@\r@id}{}{% \csdef{r@registered@\r@id}{1}% \r@csvadd{\r@personlist}{\r@id}% }% \fi } % \end{macrocode} % \end{macro} % % \begin{macro}{\r@ensureperson} % Conditional that branches on whether a person is defined. % \begin{macrocode} \newcommand{\r@ensureperson}[1]{% \ifcsdef{r@name@#1}{% \@firstoftwo }{% \r@warn{Undefined person `#1'}% \@secondoftwo }% } % \end{macrocode} % \end{macro} % % \begin{macro}{\r@definepersoninternal} % Internal 8-argument person definition used by |\defineperson|. % \begin{macrocode} \newcommand{\r@definepersoninternal}[8]{% \edef\r@id{\r@trimid{#1}}% \ifx\r@id\@empty \r@warn{Empty person id ignored}% \else \r@rememberperson{\r@id}% \ifcsdef{r@name@\r@id}{\r@warn{Redefining person `\r@id'}}{}% \csgdef{r@name@\r@id}{#3}% \csgdef{r@parents@\r@id}{#2}% \csgdef{r@preferred@\r@id}{#4}% \csgdef{r@index@\r@id}{#5}% \csgdef{r@lineageparent@\r@id}{#6}% \csgdef{r@sex@\r@id}{#7}% \csgdef{r@birth@\r@id}{#8}% \csgdef{r@birthplace@\r@id}{}% \csgdef{r@death@\r@id}{}% \csgdef{r@deathplace@\r@id}{}% \csgdef{r@baptism@\r@id}{}% \csgdef{r@baptismplace@\r@id}{}% \csgdef{r@burial@\r@id}{}% \csgdef{r@burialplace@\r@id}{}% \csgdef{r@occupation@\r@id}{}% \csgdef{r@marriagecount@\r@id}{0}% \csundef{r@generation@\r@id}% \csundef{r@generation@working@\r@id}% \fi } % \end{macrocode} % \end{macro} % % \begin{macro}{\defineperson} % Legacy positional person definition. % \begin{macrocode} \NewDocumentCommand{\defineperson}{mmmm o}{% \IfNoValueTF{#5}{% \r@definepersoninternal{#1}{#2}{#3}{#4}{#3}{}{}{}% }{% \r@definepersoninternal{#1}{#2}{#3}{#4}{#5}{}{}{}% }% } % \end{macrocode} % \end{macro} % % \begin{macro}{\definepersonkv} % Key-value person definition. % \begin{macrocode} \define@key{r@person}{name}{\def\r@kv@name{#1}} \define@key{r@person}{preferred}{\def\r@kv@preferred{#1}} \define@key{r@person}{parents}{\def\r@kv@parents{#1}} \define@key{r@person}{index}{\def\r@kv@index{#1}} \define@key{r@person}{lineage-parent}{\def\r@kv@lineageparent{#1}} \define@key{r@person}{sex}{\def\r@kv@sex{#1}} \define@key{r@person}{birth}{\def\r@kv@birth{#1}} \define@key{r@person}{birth-place}{\def\r@kv@birthplace{#1}} \define@key{r@person}{death}{\def\r@kv@death{#1}} \define@key{r@person}{death-place}{\def\r@kv@deathplace{#1}} \define@key{r@person}{baptism}{\def\r@kv@baptism{#1}} \define@key{r@person}{baptism-place}{\def\r@kv@baptismplace{#1}} \define@key{r@person}{burial}{\def\r@kv@burial{#1}} \define@key{r@person}{burial-place}{\def\r@kv@burialplace{#1}} \define@key{r@person}{occupation}{\def\r@kv@occupation{#1}} \define@key{r@person}{marriage-date}{\def\r@kv@marriagedate{#1}} \define@key{r@person}{marriage-place}{\def\r@kv@marriageplace{#1}} \NewDocumentCommand{\definepersonkv}{mm}{% \def\r@kv@name{}% \def\r@kv@preferred{}% \def\r@kv@parents{}% \def\r@kv@index{}% \def\r@kv@lineageparent{}% \def\r@kv@sex{}% \def\r@kv@birth{}% \def\r@kv@birthplace{}% \def\r@kv@death{}% \def\r@kv@deathplace{}% \def\r@kv@baptism{}% \def\r@kv@baptismplace{}% \def\r@kv@burial{}% \def\r@kv@burialplace{}% \def\r@kv@occupation{}% \def\r@kv@marriagedate{}% \def\r@kv@marriageplace{}% \setkeys{r@person}{#2}% \edef\r@id{\r@trimid{#1}}% \ifx\r@id\@empty \r@warn{Empty person id ignored}% \else \r@rememberperson{\r@id}% \ifx\r@kv@name\@empty \r@warn{Person `\r@id' defined without a name}% \fi \ifx\r@kv@index\@empty \let\r@kv@index\r@kv@name \fi \ifcsdef{r@name@\r@id}{\r@warn{Redefining person `\r@id'}}{}% \expandafter\global\expandafter\let\csname r@name@\r@id\endcsname\r@kv@name \expandafter\global\expandafter\let\csname r@parents@\r@id\endcsname\r@kv@parents \expandafter\global\expandafter\let\csname r@preferred@\r@id\endcsname\r@kv@preferred \expandafter\global\expandafter\let\csname r@index@\r@id\endcsname\r@kv@index \expandafter\global\expandafter\let\csname r@lineageparent@\r@id\endcsname\r@kv@lineageparent \expandafter\global\expandafter\let\csname r@sex@\r@id\endcsname\r@kv@sex \expandafter\global\expandafter\let\csname r@birth@\r@id\endcsname\r@kv@birth \expandafter\global\expandafter\let\csname r@birthplace@\r@id\endcsname\r@kv@birthplace \expandafter\global\expandafter\let\csname r@death@\r@id\endcsname\r@kv@death \expandafter\global\expandafter\let\csname r@deathplace@\r@id\endcsname\r@kv@deathplace \expandafter\global\expandafter\let\csname r@baptism@\r@id\endcsname\r@kv@baptism \expandafter\global\expandafter\let\csname r@baptismplace@\r@id\endcsname\r@kv@baptismplace \expandafter\global\expandafter\let\csname r@burial@\r@id\endcsname\r@kv@burial \expandafter\global\expandafter\let\csname r@burialplace@\r@id\endcsname\r@kv@burialplace \expandafter\global\expandafter\let\csname r@occupation@\r@id\endcsname\r@kv@occupation \csgdef{r@marriagecount@\r@id}{0}% \ifx\r@kv@marriagedate\@empty\else \csgdef{r@marriagecount@\r@id}{1}% \expandafter\global\expandafter\let\csname r@marriagedate@\r@id @1\endcsname\r@kv@marriagedate \csgdef{r@marriageplace@\r@id @1}{}% \fi \ifx\r@kv@marriageplace\@empty\else \ifcsdef{r@marriagedate@\r@id @1}{% \expandafter\global\expandafter\let\csname r@marriageplace@\r@id @1\endcsname\r@kv@marriageplace }{% \csgdef{r@marriagecount@\r@id}{1}% \csgdef{r@marriagedate@\r@id @1}{}% \expandafter\global\expandafter\let\csname r@marriageplace@\r@id @1\endcsname\r@kv@marriageplace }% \fi \csundef{r@generation@\r@id}% \csundef{r@generation@working@\r@id}% \fi } % \end{macrocode} % \end{macro} % % \subsection{Multiple marriage support} % % \begin{macro}{\addmarriage} % \begin{macrocode} \define@key{r@marriage}{date}{\def\r@mkv@date{#1}} \define@key{r@marriage}{place}{\def\r@mkv@place{#1}} \define@key{r@marriage}{spouse}{\def\r@mkv@spouse{#1}} \NewDocumentCommand{\addmarriage}{mm}{% \def\r@mkv@date{}% \def\r@mkv@place{}% \def\r@mkv@spouse{}% \setkeys{r@marriage}{#2}% \edef\r@id{\r@trimid{#1}}% \ifx\r@id\@empty \r@warn{Cannot add marriage to empty person id}% \else \ifcsdef{r@name@\r@id}{% \edef\r@mcnt{\csuse{r@marriagecount@\r@id}}% \edef\r@newmcnt{\the\numexpr\r@mcnt+1\relax}% \csgdef{r@marriagecount@\r@id}{\r@newmcnt}% \expandafter\global\expandafter\let\csname r@marriagedate@\r@id @\r@newmcnt\endcsname\r@mkv@date \expandafter\global\expandafter\let\csname r@marriageplace@\r@id @\r@newmcnt\endcsname\r@mkv@place \expandafter\global\expandafter\let\csname r@marriagespouse@\r@id @\r@newmcnt\endcsname\r@mkv@spouse }{% \r@warn{Cannot add marriage to undefined person `\r@id'}% }% \fi } % \end{macrocode} % \end{macro} % % \begin{macro}{\getmarriagecount} % \begin{macro}{\getmarriagedate} % \begin{macro}{\getmarriageplace} % \begin{macro}{\getmarriagespouse} % \begin{macrocode} \newcommand{\getmarriagecount}[1]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@marriagecount@\r@id}{\csuse{r@marriagecount@\r@id}}{0}% } \newcommand{\getmarriagedate}[2]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@marriagedate@\r@id @#2}{% \ifcsempty{r@marriagedate@\r@id @#2}{% \r@warn{No date stored for marriage #2 of `\r@id'}% \textbf{[marriage date unknown]}% }{% \edef\r@mdate{\csuse{r@marriagedate@\r@id @#2}}% \expandafter\registerdate\expandafter{\r@mdate}% }% }{% \r@warn{No marriage #2 for `\r@id'}% \textbf{[no marriage #2]}% }% } \newcommand{\getmarriageplace}[2]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@marriageplace@\r@id @#2}{% \ifcsempty{r@marriageplace@\r@id @#2}{% \r@warn{No place stored for marriage #2 of `\r@id'}% \textbf{[marriage place unknown]}% }{% \csuse{r@marriageplace@\r@id @#2}% }% }{% \r@warn{No marriage #2 for `\r@id'}% \textbf{[no marriage #2]}% }% } \newcommand{\getmarriagespouse}[2]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@marriagespouse@\r@id @#2}{% \ifcsempty{r@marriagespouse@\r@id @#2}{}{% \edef\r@spid{\csuse{r@marriagespouse@\r@id @#2}}% \getpersonname{\r@spid}% }% }{}% } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Name retrieval} % % \begin{macro}{\getpersonname} % \begin{macro}{\getpersonnameNI} % \begin{macro}{\getpersonpreferred} % \begin{macro}{\getpersonabbrevNI} % \begin{macro}{\shortname} % \begin{macrocode} \newcommand{\getpersonname}[1]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@name@\r@id}{% \leavevmode \textsc{\csuse{r@name@\r@id}}% \r@indexperson{\csuse{r@index@\r@id}}% }{% \r@warn{Undefined person `\r@id'}% \textbf{[undefined \r@id]}% }% } \newcommand{\getpersonnameNI}[1]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@name@\r@id}{% \textsc{\csuse{r@name@\r@id}}% }{% \r@warn{Undefined person `\r@id'}% \textbf{[undefined \r@id]}% }% } \newcommand{\getpersonpreferred}[1]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@preferred@\r@id}{% \ifcsempty{r@preferred@\r@id}{}{\textsc{\csuse{r@preferred@\r@id}}}% }{}% } \newcommand{\getpersonabbrevNI}[1]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@preferred@\r@id}{% \ifcsempty{r@preferred@\r@id}{% \getpersonnameNI{\r@id}% }{% \textsc{\csuse{r@preferred@\r@id}}% }% }{% \getpersonnameNI{\r@id}% }% } \newcommand{\shortname}[1]{\getpersonpreferred{#1}} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Vital record accessors} % % \begin{macro}{\r@getvitaldate} % \begin{macro}{\r@getvitalplace} % Shared helpers for date and place retrieval. % \begin{macrocode} \newcommand{\r@getvitaldate}[3]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@#2@\r@id}{% \ifcsempty{r@#2@\r@id}{% \r@warn{No #3 date stored for `\r@id'}% \textbf{[#3 date unknown for \r@id]}% }{% \edef\r@thedate{\csuse{r@#2@\r@id}}% \expandafter\registerdate\expandafter{\r@thedate}% }% }{% \r@warn{Undefined person `\r@id' while retrieving #3 date}% \textbf{[undefined \r@id]}% }% } \newcommand{\r@getvitalplace}[3]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@#2@\r@id}{% \ifcsempty{r@#2@\r@id}{% \r@warn{No #3 place stored for `\r@id'}% \textbf{[#3 place unknown for \r@id]}% }{% \csuse{r@#2@\r@id}% }% }{% \r@warn{Undefined person `\r@id' while retrieving #3 place}% \textbf{[undefined \r@id]}% }% } \newcommand{\getpersonbirthdate}[1]{\r@getvitaldate{#1}{birth}{birth}} \newcommand{\getpersonbirthplace}[1]{\r@getvitalplace{#1}{birthplace}{birth}} \newcommand{\getpersondeathdate}[1]{\r@getvitaldate{#1}{death}{death}} \newcommand{\getpersondeathplace}[1]{\r@getvitalplace{#1}{deathplace}{death}} \newcommand{\getpersonbaptismdate}[1]{\r@getvitaldate{#1}{baptism}{baptism}} \newcommand{\getpersonbaptismplace}[1]{\r@getvitalplace{#1}{baptismplace}{baptism}} \newcommand{\getpersonburialdate}[1]{\r@getvitaldate{#1}{burial}{burial}} \newcommand{\getpersonburialplace}[1]{\r@getvitalplace{#1}{burialplace}{burial}} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\getpersonoccupation} % \begin{macro}{\getpersonsex} % \begin{macrocode} \newcommand{\getpersonoccupation}[1]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@occupation@\r@id}{% \ifcsempty{r@occupation@\r@id}{% \r@warn{No occupation stored for `\r@id'}% \textbf{[occupation unknown for \r@id]}% }{% \csuse{r@occupation@\r@id}% }% }{% \r@warn{Undefined person `\r@id' while retrieving occupation}% \textbf{[undefined \r@id]}% }% } \newcommand{\getpersonsex}[1]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@sex@\r@id}{\csuse{r@sex@\r@id}}{}% } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Gender-aware pronouns} % % \begin{macro}{\r@pronoun} % Internal dispatcher: selects masculine, feminine, or neutral form. % \begin{macrocode} \newcommand{\r@pronoun}[4]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@sex@\r@id}{% \edef\r@thesex{\csuse{r@sex@\r@id}}% \def\r@male{M}\def\r@female{F}% \ifx\r@thesex\r@male #2% \else\ifx\r@thesex\r@female #3% \else #4% \fi\fi }{#4}% } \newcommand{\heshethey}[1]{\r@pronoun{#1}{he}{she}{they}} \newcommand{\himherthem}[1]{\r@pronoun{#1}{him}{her}{them}} \newcommand{\hishertheirargs}[1]{\r@pronoun{#1}{his}{her}{their}} \newcommand{\HeSheThey}[1]{\r@pronoun{#1}{He}{She}{They}} \newcommand{\HimHerThem}[1]{\r@pronoun{#1}{Him}{Her}{Them}} \newcommand{\HisHerTheir}[1]{\r@pronoun{#1}{His}{Her}{Their}} \newcommand{\himselfherselfthemself}[1]{\r@pronoun{#1}{himself}{herself}{themself}} % \end{macrocode} % \end{macro} % % \subsection{Approximate dates} % % \begin{macrocode} \newcommand{\circa}[1]{c.\,#1} \newcommand{\before}[1]{bef.\ #1} \newcommand{\after}[1]{aft.\ #1} \newcommand{\between}[2]{bet.\ #1 and #2} \newcommand{\aboutdate}[1]{abt.\ #1} \newcommand{\probably}[1]{prob.\ #1} \newcommand{\say}[1]{say #1} % \end{macrocode} % % \subsection{Generation computation} % % \begin{macro}{\r@computegeneration} % Recursive computation with cycle detection via a ``working'' flag. % \begin{macrocode} \newcommand{\r@computegeneration}[1]{% \begingroup \edef\r@target{\r@trimid{#1}}% \ifcsdef{r@generation@\r@target}{% }{% \ifcsdef{r@generation@working@\r@target}{% \r@warn{Cycle detected while computing generation for `\r@target'}% \expandafter\xdef\csname r@generation@\r@target\endcsname{\r@unknowngeneration}% }{% \csdef{r@generation@working@\r@target}{1}% \ifcsdef{r@parents@\r@target}{% \edef\r@parents{\csuse{r@parents@\r@target}}% \ifx\r@parents\@empty \expandafter\xdef\csname r@generation@\r@target\endcsname{1}% \else \def\r@max{0}% \def\r@checkparent##1{% \edef\r@pid{\r@trimid{##1}}% \ifx\r@pid\@empty \r@warn{Empty parent id listed for `\r@target'}% \else \ifcsdef{r@parents@\r@pid}{% \ifcsdef{r@generation@\r@pid}{}{\r@computegeneration{\r@pid}}% \edef\r@pg{\csuse{r@generation@\r@pid}}% \ifnum\r@pg>\r@max\relax \xdef\r@max{\r@pg}\fi }{% \r@warn{Undefined parent `\r@pid' listed for `\r@target'}% \expandafter\xdef\csname r@generation@\r@pid\endcsname{\r@unknowngeneration}% }% \fi }% \expandafter\forcsvlist\expandafter\r@checkparent\expandafter{\r@parents}% \edef\r@this{\the\numexpr\r@max+1\relax}% \expandafter\xdef\csname r@generation@\r@target\endcsname{\r@this}% \fi }{% \r@warn{Undefined person `\r@target'; assigning generation \r@unknowngeneration}% \expandafter\xdef\csname r@generation@\r@target\endcsname{\r@unknowngeneration}% }% \csundef{r@generation@working@\r@target}% }% }% \endgroup } % \end{macrocode} % \end{macro} % % \begin{macro}{\getgeneration} % \begin{macrocode} \newcommand{\getgeneration}[1]{% \begingroup \edef\r@target{\r@trimid{#1}}% \r@computegeneration{\r@target}% \xdef\r@generationresult{\csuse{r@generation@\r@target}}% \endgroup \r@generationresult } % \end{macrocode} % \end{macro} % % \subsection{Ancestor chain formatting} % % \begin{macro}{\r@collectancestors} % \begin{macro}{\r@addoneancestor} % \begin{macrocode} \newcommand{\r@alist}{} \newif\ifr@ancestorchain@lineage \newcommand{\r@collectancestors}[1]{% \edef\r@id{\r@trimid{#1}}% \ifcsdef{r@ancestor@seen@\r@id}{% \r@warn{Cycle detected while collecting ancestors for `\r@id'}% }{% \csdef{r@ancestor@seen@\r@id}{1}% \ifcsdef{r@parents@\r@id}{% \ifcsdef{r@lineageparent@\r@id}{% \ifcsempty{r@lineageparent@\r@id}{% \edef\r@plist{\csuse{r@parents@\r@id}}% \ifx\r@plist\@empty\else \expandafter\forcsvlist\expandafter\r@addoneancestor\expandafter{\r@plist}% \fi }{% \r@ancestorchain@lineagetrue \edef\r@lineagepid{\csuse{r@lineageparent@\r@id}}% \expandafter\r@addoneancestor\expandafter{\r@lineagepid}% }% }{% \edef\r@plist{\csuse{r@parents@\r@id}}% \ifx\r@plist\@empty\else \expandafter\forcsvlist\expandafter\r@addoneancestor\expandafter{\r@plist}% \fi }% }{% \r@warn{Undefined person `\r@id' while collecting ancestors}% }% }% } \newcommand{\r@addoneancestor}[1]{% \edef\r@pid{\r@trimid{#1}}% \ifx\r@pid\@empty \r@warn{Empty parent id in ancestor chain}% \else \ifcsdef{r@parents@\r@pid}{% \r@csvadd{\r@alist}{\r@pid}% \r@collectancestors{\r@pid}% }{% \r@warn{Undefined parent `\r@pid' in ancestor chain}% \r@csvadd{\r@alist}{\r@pid}% }% \fi } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\formatparents} % \begin{macrocode} \newif\ifr@abbrevparents \ifr@option@abbrevparents \r@abbrevparentstrue \else \r@abbrevparentsfalse \fi \newcount\r@pcnt \newcount\r@i \newcommand{\r@namegensNI}[1]{% \mbox{\getpersonnameNI{#1}\textsuperscript{\getgeneration{#1}}}% } \newcommand{\r@abbrevnamegensNI}[1]{% \mbox{\getpersonabbrevNI{#1}\textsuperscript{\getgeneration{#1}}}% } \newcommand{\formatparents}[1]{% \begingroup \def\r@alist{}% \r@ancestorchain@lineagefalse \r@collectancestors{#1}% \edef\r@plist{\r@alist}% \ifx\r@plist\@empty \else \r@pcnt=0\relax \def\r@countone##1{\advance\r@pcnt by 1\relax}% \expandafter\forcsvlist\expandafter\r@countone\expandafter{\r@plist}% \unskip\space(% \r@i=0\relax \def\r@printone##1{% \edef\r@pid{\r@trimid{##1}}% \advance\r@i by 1\relax \ifnum\r@i>1 \ifr@ancestorchain@lineage ,\space \else \ifnum\r@i=\r@pcnt \space and\space \else ,\space \fi \fi \fi \ifr@abbrevparents \r@abbrevnamegensNI{\r@pid}% \else \r@namegensNI{\r@pid}% \fi }% \expandafter\forcsvlist\expandafter\r@printone\expandafter{\r@plist}% )% \fi \endgroup } % \end{macrocode} % \end{macro} % % \subsection{Main register entries} % % \begin{macro}{\mainperson} % \begin{macrocode} \newcommand{\registerpersonsep}{1em} \newif\ifr@afterheading \r@afterheadingfalse \pretocmd{\section}{\global\r@afterheadingtrue}{}{} \pretocmd{\subsection}{\global\r@afterheadingtrue}{}{} \pretocmd{\subsubsection}{\global\r@afterheadingtrue}{}{} \pretocmd{\paragraph}{\global\r@afterheadingtrue}{}{} \pretocmd{\subparagraph}{\global\r@afterheadingtrue}{}{} \newcounter{registerperson} \NewDocumentCommand{\mainperson}{m}{% \edef\r@id{\r@trimid{#1}}% \par \ifr@afterheading \global\r@afterheadingfalse \else \addvspace{\registerpersonsep}% \fi \refstepcounter{registerperson}% \csgdef{r@mainentry@\r@id}{\theregisterperson}% \label{person:\r@id}% \noindent \textbf{\theregisterperson.\ }% \getpersonname{\r@id}\textsuperscript{\getgeneration{\r@id}}% \formatparents{\r@id}\xspace } % \end{macrocode} % \end{macro} % % NGSQ numbering override. % \begin{macrocode} \ifr@option@ngsq \renewcommand{\theregisterperson}{\arabic{registerperson}} \fi % \end{macrocode} % % \subsection{Cross-references} % % \begin{macro}{\xrefperson} % \begin{macro}{\childname} % \begin{macrocode} \NewDocumentCommand{\xrefperson}{m}{% \edef\r@id{\r@trimid{#1}}% \hyperref[person:\r@id]{% \getpersonname{\r@id}\textsuperscript{\getgeneration{\r@id}}% \space(\#\ref{person:\r@id})% }% } \NewDocumentCommand{\childname}{m}{% \edef\r@id{\r@trimid{#1}}% \getpersonnameNI{\r@id}\textsuperscript{\getgeneration{\r@id}}% } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Child lists} % % \begin{environment}{children} % \begin{environment}{childrenof} % \begin{macrocode} \newif\ifr@childrenof \r@childrenoffalse \newcommand{\r@childrenof@a}{} \newcommand{\r@childrenof@b}{} \NewDocumentEnvironment{children}{ O{} }{% \begin{enumerate}[label=\roman*., leftmargin=2em, itemsep=0pt, topsep=0pt,#1]% }{\end{enumerate}} \NewDocumentEnvironment{childrenof}{ m m O{} }{% \begingroup \edef\r@childrenof@a{\r@trimid{#1}}% \edef\r@childrenof@b{\r@trimid{#2}}% \r@childrenoftrue \begin{children}[#3]% }{% \end{children}% \endgroup } % \end{macrocode} % \end{environment} % \end{environment} % % \begin{macro}{\r@checkchildparents} % \begin{macro}{\child} % \begin{macro}{\childref} % \begin{macrocode} \newcommand{\r@checkchildparents}[1]{% \ifr@childrenof \edef\r@childid{\r@trimid{#1}}% \ifcsdef{r@parents@\r@childid}{% \edef\r@childparents{\csuse{r@parents@\r@childid}}% \edef\r@expectedparents{\r@childrenof@a,\r@childrenof@b}% \edef\r@expectedparentsrev{\r@childrenof@b,\r@childrenof@a}% \ifx\r@childparents\r@expectedparents \else \ifx\r@childparents\r@expectedparentsrev \else \r@warn{Child `\r@childid' listed under parents `\r@expectedparents' but has parents `\r@childparents'}% \fi \fi }{% \r@warn{Undefined child `\r@childid' listed in childrenof}% }% \fi } \NewDocumentCommand{\child}{m m}{% \r@checkchildparents{#1}% \item \childname{#1}% \IfBlankF{#2}{, #2}% } \NewDocumentCommand{\childref}{m m}{% \r@checkchildparents{#1}% \item \textbf{+}\space\childname{#1}% \IfBlankF{#2}{, #2}% } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Source and note support} % % \begin{macro}{\registersource} % \begin{macro}{\registerendnote} % \begin{macrocode} \NewDocumentCommand{\registersource}{m}{% \footnote{#1}% } \NewDocumentCommand{\registerendnote}{m}{% \@ifpackageloaded{endnotes}{\endnote{#1}}{\footnote{#1}}% } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Automatic generation headings} % % \begin{macro}{\generationheading} % \begin{macrocode} \newcommand{\r@generationword}[1]{% \ifcase#1 Zeroth\or First\or Second\or Third\or Fourth\or Fifth\or Sixth\or Seventh\or Eighth\or Ninth\or Tenth\or Eleventh\or Twelfth\or Thirteenth\or Fourteenth\or Fifteenth\or Sixteenth\or Seventeenth\or Eighteenth\or Nineteenth\or Twentieth\else #1th\fi } \newcommand{\generationheading}[1]{% \edef\r@id{\r@trimid{#1}}% \r@computegeneration{\r@id}% \edef\r@gen{\csuse{r@generation@\r@id}}% \par\medskip\noindent \textbf{\r@generationword{\r@gen}\space Generation}% \par\nobreak } % \end{macrocode} % \end{macro} % % \subsection{Date formatting} % % \begin{macro}{\registerdate} % \begin{macro}{\registerdatestyle} % \begin{macrocode} \newcommand{\registerdatestyle}[1]{% \DTMsetdatestyle{#1}% } \AtBeginDocument{% \ifdefempty{\r@datestyle}{}{\DTMsetdatestyle{\r@datestyle}}% } \ExplSyntaxOn \cs_new_protected:Npn \nehgs_register_date:n #1 { \regex_match:nnTF { \A \d{4} - \d{2} - \d{2} \Z } {#1} { \DTMdate{#1} } { \PackageWarning{nehgs-register}{Date~`#1'~is~not~in~YYYY-MM-DD~form} #1 } } \NewDocumentCommand{\registerdate}{m}{\nehgs_register_date:n{#1}} \ExplSyntaxOff % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Date validation} % % \begin{macrocode} \ExplSyntaxOn \cs_generate_variant:Nn \regex_match:nnF { neF } \cs_new_protected:Npn \nehgs_register_validate_iso_date:nn #1 #2 { \tl_if_blank:nF {#2} { \regex_match:neF { \A \d{4} - \d{2} - \d{2} \Z } {#2} { \PackageWarning{nehgs-register}{#1~date~`#2'~is~not~in~YYYY-MM-DD~form} } } } \NewDocumentCommand{\r@validateisodate}{m m} {\nehgs_register_validate_iso_date:nn {#1} {#2}} \ExplSyntaxOff % \end{macrocode} % % \subsection{Validation commands} % % \begin{macro}{\checkperson} % \begin{macro}{\checkregister} % \begin{macrocode} \newcommand{\r@checkdatefield}[3]{% \ifcsdef{r@#3@#1}{% \edef\r@datevalue{\csuse{r@#3@#1}}% \ifx\r@datevalue\@empty\else \r@validateisodate{#2 for `#1'}{\r@datevalue}% \fi }{}% } \newcommand{\r@checkplacewithoutdate}[5]{% \ifcsdef{r@#2@#1}{% \ifcsempty{r@#2@#1}{}{% \ifcsdef{r@#3@#1}{% \ifcsempty{r@#3@#1}{\r@warn{Person `#1' has #4 but no #5}}{}% }{\r@warn{Person `#1' has #4 but no #5}}% }% }{}% } \newcommand{\r@checkparentlist}[1]{% \ifcsdef{r@parents@#1}{% \edef\r@plist{\csuse{r@parents@#1}}% \ifx\r@plist\@empty\else \def\r@checkoneparent##1{% \edef\r@pid{\r@trimid{##1}}% \ifx\r@pid\@empty \r@warn{Person `#1' has an empty parent id}% \else \ifcsdef{r@name@\r@pid}{}{\r@warn{Person `#1' lists undefined parent `\r@pid'}}% \fi }% \expandafter\forcsvlist\expandafter\r@checkoneparent\expandafter{\r@plist}% \fi }{\r@warn{Person `#1' has no parent field}}% } \NewDocumentCommand{\checkperson}{m}{% \begingroup \edef\r@id{\r@trimid{#1}}% \ifx\r@id\@empty \r@warn{Cannot check an empty person id}% \else \ifcsdef{r@name@\r@id}{% \ifcsempty{r@name@\r@id}{\r@warn{Person `\r@id' has an empty name}}{}% \r@checkparentlist{\r@id}% \r@checkdatefield{\r@id}{Birth}{birth}% \r@checkdatefield{\r@id}{Death}{death}% \r@checkdatefield{\r@id}{Baptism}{baptism}% \r@checkdatefield{\r@id}{Burial}{burial}% \r@checkplacewithoutdate{\r@id}{birthplace}{birth}{birth place}{birth date}% \r@checkplacewithoutdate{\r@id}{deathplace}{death}{death place}{death date}% \r@checkplacewithoutdate{\r@id}{baptismplace}{baptism}{baptism place}{baptism date}% \r@checkplacewithoutdate{\r@id}{burialplace}{burial}{burial place}{burial date}% }{% \r@warn{Cannot check undefined person `\r@id'}% }% \fi \endgroup } \NewDocumentCommand{\checkregister}{}{% \begingroup \ifx\r@personlist\@empty \r@warn{No registered persons to check}% \else \def\r@checkoneperson##1{\checkperson{##1}}% \expandafter\forcsvlist\expandafter\r@checkoneperson\expandafter{\r@personlist}% \fi \endgroup } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Shorthands} % % \begin{macrocode} \newcommand{\born}[1]{b.\ #1} \newcommand{\died}[1]{d.\ #1} \newcommand{\married}[1]{m.\ #1} \newcommand{\baptized}[1]{bp.\ #1} \newcommand{\buried}[1]{bur.\ #1} \newcommand{\place}[1]{\textit{#1}} \newcommand{\dated}[1]{#1} % \end{macrocode} % % \subsection{Register section environment} % % \begin{environment}{registersection} % \begin{macrocode} \NewDocumentEnvironment{registersection}{ O{} }{% \begingroup \setlength{\parindent}{1.5em} \setlength{\parskip}{0pt} \setlist{nosep} \def\generation##1{\par\medskip\noindent\textbf{##1 Generation}\par\nobreak}% }{% \par\endgroup } % \end{macrocode} % \end{environment} % % \subsection{Backward compatibility} % % Old public names retained as aliases for one release cycle. % \begin{macrocode} \let\collectancestors\r@collectancestors \let\addoneancestor\r@addoneancestor \newif\ifabbrevparents \ifr@abbrevparents\abbrevparentstrue\else\abbrevparentsfalse\fi \makeatother % % \end{macrocode} % % \Finale