aboutsummaryrefslogtreecommitdiffstats
path: root/build/latex/letter
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-03 20:53:54 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-08-03 20:53:54 +0200
commitb241b009646b36891e6b6c9d336357ed595d4dc7 (patch)
tree0a47390083f6994698bb71aa524e0642912e2ae1 /build/latex/letter
parentfeat(templates/latex/letter): Init (diff)
downloadflake-templates-b241b009646b36891e6b6c9d336357ed595d4dc7.zip
chore(build): Regenerate
Diffstat (limited to 'build/latex/letter')
-rw-r--r--build/latex/letter/%INIT_APPLICATION_NAME.tex86
-rw-r--r--build/latex/letter/.envrc9
-rw-r--r--build/latex/letter/.gitignore8
-rw-r--r--build/latex/letter/.reuse/templates/default.jinja21
-rw-r--r--build/latex/letter/README.md11
-rwxr-xr-xbuild/latex/letter/build.sh14
-rw-r--r--build/latex/letter/cog.toml24
-rw-r--r--build/latex/letter/flake.lock129
-rw-r--r--build/latex/letter/flake.nix100
-rw-r--r--build/latex/letter/init152
-rwxr-xr-xbuild/latex/letter/scripts/cprh.sh57
-rwxr-xr-xbuild/latex/letter/scripts/extract_text_from_all.sh8
-rw-r--r--build/latex/letter/shell_line_editor.sh247
-rw-r--r--build/latex/letter/treefmt.nix70
-rwxr-xr-xbuild/latex/letter/update.sh3
-rwxr-xr-xbuild/latex/letter/watch.sh70
16 files changed, 989 insertions, 0 deletions
diff --git a/build/latex/letter/%INIT_APPLICATION_NAME.tex b/build/latex/letter/%INIT_APPLICATION_NAME.tex
new file mode 100644
index 0000000..2037aa6
--- /dev/null
+++ b/build/latex/letter/%INIT_APPLICATION_NAME.tex
@@ -0,0 +1,86 @@
+% LTeX: language=%INIT_LANGUAGE
+\documentclass[a4paper, version=last]{scrlttr2}
+\usepackage{lmodern}
+\usepackage[utf8]{inputenc}
+\usepackage[T1]{fontenc}
+\usepackage[%INIT_BABLE_LANGUAGE]{babel}
+\usepackage{url}
+\usepackage[%INIT_DATE_TIME_LANGUAGE, showdow]{datetime2} % make handling dates easier
+
+\setkomafont{fromname}{\sffamily \LARGE}
+\setkomafont{fromaddress}{\sffamily}%% statt \small
+\setkomafont{pagenumber}{\sffamily}
+\setkomafont{subject}{\bfseries}
+\setkomafont{backaddress}{\mdseries}
+
+\LoadLetterOption{DIN}
+% \setkomavar{backaddressseparator}{\enspace\textperiodcentered\enspace}
+% \setkomavar{backaddress}{%INIT_AUTHOR_NAME\\ PO Box 8421\\ Bachbrecht\\10007\\ Namibia}
+\setkomavar{backaddressseparator}{ $\cdot$ }
+\setkomavar{customer}{2342}
+\setkomavar{date}{\DTMToday}
+\setkomavar{enclseparator}{: }
+\setkomavar{fromaddress}{Musterstraße 00\\00000 Musterort}
+\setkomavar{fromemail}{%INIT_AUTHOR_EMAIL}
+\setkomavar{fromname}{%INIT_AUTHOR_NAME}
+\setkomavar{fromphone}{+49 0000 00\,00\,00\,0}
+\setkomavar{place}{Musterort}
+\setkomavar{signature}{%INIT_AUTHOR_NAME}
+\setkomavar{subject}{%INIT_APPLICATION_NAME_STYLIZED}
+
+\renewcommand*{\raggedsignature}{\raggedright}
+
+\KOMAoptions{%
+ addrfield=true, % Adress field for envelope with window
+ backaddress=true, % Sender address in this window
+ enlargefirstpage=true, % More space on first page
+ foldmarks=true, % Print foldmarks?
+ footsepline=true, % separate the footer with a line on page >1
+ fromalign=center, % alignment of the address
+ % fromalign=right, % Placement of name in letter head
+ fromemail=true, % print sender e-mail address
+ fromfax=false, % print sender fax number
+ fromlogo=false, % print a logo (position depends on fromalign)
+ fromphone=true, % print sender phone number
+ fromrule=afteraddress, % separate the address with a line?
+ % fromrule=aftername, % Rule after sender name in letter head
+ fromurl=false, % print sender URL
+ headsepline=true, % separate the header with a line on page >1
+ locfield=narrow, % Additional field for sender
+ pagenumber=botright, % position of the page number (see docu)
+ paper=a4, % pagesize
+ parskip=half, % Use indent instead of skip
+ refline=wide, % layout of the refline
+ subject=beforeopening, % Placement of subject
+}
+
+\begin{document}
+
+\begin{letter}{Director \\ Doe \& Co \\ 35 Anthony Road \\ Newport \\ Ipswich IP3 5RT}
+ \opening{Dear Sir or Madam,}
+
+ I am writing to you on behalf of the Wikipedia project
+ (\url{http://www.wikipedia.org/}), an endeavour to build a
+ fully-fledged multilingual encyclopaedia in an entirely open
+ manner, to ask for permission to use your copyrighted material.
+
+ \ldots
+
+ That said, allow me to reiterate that your material will be used
+ to the noble end of providing a free collection of knowledge for
+ everyone; naturally enough, only if you agree. If that is the
+ case, could you kindly fill in the attached form and post it back
+ to me? We shall greatly appreciate it.
+
+ Thank you for your time and consideration.
+
+ I look forward to your reply.
+
+ \closing{Yours Faithfully,}
+ \ps{P.S. You can find the full text of GFDL license at
+ \url{http://www.gnu.org/copyleft/fdl.html}.}
+ \encl{Copyright permission form}
+
+\end{letter}
+
+\end{document}
diff --git a/build/latex/letter/.envrc b/build/latex/letter/.envrc
new file mode 100644
index 0000000..3bc1085
--- /dev/null
+++ b/build/latex/letter/.envrc
@@ -0,0 +1,9 @@
+use flake || use nix
+watch_file flake.nix
+
+PATH_add ./scripts
+
+if on_git_branch; then
+ echo && git status --short --branch &&
+ echo && git fetch --verbose
+fi
diff --git a/build/latex/letter/.gitignore b/build/latex/letter/.gitignore
new file mode 100644
index 0000000..539b891
--- /dev/null
+++ b/build/latex/letter/.gitignore
@@ -0,0 +1,8 @@
+# build
+/result
+/build
+
+/resources.local
+
+# dev env
+.direnv
diff --git a/build/latex/letter/.reuse/templates/default.jinja2 b/build/latex/letter/.reuse/templates/default.jinja2
new file mode 100644
index 0000000..7a2d08f
--- /dev/null
+++ b/build/latex/letter/.reuse/templates/default.jinja2
@@ -0,0 +1 @@
+<!-- This file will be replaced by the init script -->
diff --git a/build/latex/letter/README.md b/build/latex/letter/README.md
new file mode 100644
index 0000000..9c5c6da
--- /dev/null
+++ b/build/latex/letter/README.md
@@ -0,0 +1,11 @@
+# %INIT_APPLICATION_NAME_STYLIZED
+
+> %INIT_DESCRIPTION
+
+## Licensing
+This project complies with the REUSE v3.2 specification. This means that every file
+clearly states its copyright.
+Please run `./scripts/cprh.sh contributer NAME EMAIL FILES..` after you
+contributed to `FILES..` to record your contribution (obviously replacing
+the `NAME`, `EMAIL` and `FILES..` placeholders with your name, email, and
+the paths to the changed files respectively (see the `--help` output for more)).
diff --git a/build/latex/letter/build.sh b/build/latex/letter/build.sh
new file mode 100755
index 0000000..1206ee7
--- /dev/null
+++ b/build/latex/letter/build.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env sh
+
+# if no parameter is given, use 'dst' as destination directory
+if [ -z "$1" ]; then
+ dst=build
+else
+ dst="$1"
+fi
+
+# find all directories which are not the destination dir or inside it
+find . -type d -not -name "$dst" -not -path "./$dst/*" -printf '%P\n' | while IFS= read -r dir; do
+ mkdir --parents "$dst/$dir"
+done
+latexmk -outdir="$dst" -file-line-error -pdflatex -recorder "./%INIT_APPLICATION_NAME.tex"
diff --git a/build/latex/letter/cog.toml b/build/latex/letter/cog.toml
new file mode 100644
index 0000000..ccca764
--- /dev/null
+++ b/build/latex/letter/cog.toml
@@ -0,0 +1,24 @@
+tag_prefix = "v"
+branch_whitelist = ["main", "prime"]
+ignore_merge_commits = false
+
+pre_bump_hooks = [
+ "nix flake check", # verify the project builds
+ "./scripts/renew_copyright_header.sh", # update the license header in each file
+ "flake_version_update --version v{{version}} --input-file flake.nix", # update the version in the flake.nix file
+ "nix fmt", # format
+]
+post_bump_hooks = [
+ "git push",
+ "git push origin v{{version}}", # push the new tag to origin
+]
+
+[bump_profiles]
+
+[changelog]
+path = "NEWS.md"
+template = "remote"
+remote = "%INIT_REMOTE"
+repository = "%INIT_REPOSITORY"
+owner = "%INIT_OWNER"
+authors = [{ signature = "%INIT_AUTHOR_NAME", username = "%INIT_OWNER" }]
diff --git a/build/latex/letter/flake.lock b/build/latex/letter/flake.lock
new file mode 100644
index 0000000..544cbdf
--- /dev/null
+++ b/build/latex/letter/flake.lock
@@ -0,0 +1,129 @@
+{
+ "nodes": {
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1696426674,
+ "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "inputs": {
+ "systems": [
+ "systems"
+ ]
+ },
+ "locked": {
+ "lastModified": 1710146030,
+ "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake_version_update": {
+ "inputs": {
+ "flake-utils": [
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "systems": [
+ "systems"
+ ]
+ },
+ "locked": {
+ "lastModified": 1685288691,
+ "narHash": "sha256-oP6h34oJ8rm6KlUpyZrX+ww3hnoWny2ecrEXxkU7F3c=",
+ "ref": "refs/heads/prime",
+ "rev": "e9a97e01eca780bd16e1dbdbd8856b59558f4959",
+ "revCount": 5,
+ "type": "git",
+ "url": "https://codeberg.org/soispha/flake_version_update.git"
+ },
+ "original": {
+ "type": "git",
+ "url": "https://codeberg.org/soispha/flake_version_update.git"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1711715736,
+ "narHash": "sha256-9slQ609YqT9bT/MNX9+5k5jltL9zgpn36DpFB7TkttM=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "807c549feabce7eddbf259dbdcec9e0600a0660d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "flake-utils": "flake-utils",
+ "flake_version_update": "flake_version_update",
+ "nixpkgs": "nixpkgs",
+ "systems": "systems",
+ "treefmt-nix": "treefmt-nix"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1680978846,
+ "narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=",
+ "owner": "nix-systems",
+ "repo": "x86_64-linux",
+ "rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "x86_64-linux",
+ "type": "github"
+ }
+ },
+ "treefmt-nix": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1711803027,
+ "narHash": "sha256-Qic3OvsVLpetchzaIe2hJqgliWXACq2Oee6mBXa/IZQ=",
+ "owner": "numtide",
+ "repo": "treefmt-nix",
+ "rev": "1810d51a015c1730f2fe05a255258649799df416",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "treefmt-nix",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/build/latex/letter/flake.nix b/build/latex/letter/flake.nix
new file mode 100644
index 0000000..4847a9c
--- /dev/null
+++ b/build/latex/letter/flake.nix
@@ -0,0 +1,100 @@
+{
+ description = "%INIT_DESCRIPTION";
+
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+
+ treefmt-nix = {
+ url = "github:numtide/treefmt-nix";
+ inputs = {
+ nixpkgs.follows = "nixpkgs";
+ };
+ };
+ flake_version_update = {
+ url = "git+https://codeberg.org/soispha/flake_version_update.git";
+ inputs = {
+ systems.follows = "systems";
+ nixpkgs.follows = "nixpkgs";
+ flake-utils.follows = "flake-utils";
+ };
+ };
+
+ # inputs for following
+ systems = {
+ url = "github:nix-systems/x86_64-linux"; # only evaluate for this system
+ };
+ flake-compat = {
+ url = "github:edolstra/flake-compat";
+ flake = false;
+ };
+ flake-utils = {
+ url = "github:numtide/flake-utils";
+ inputs = {
+ systems.follows = "systems";
+ };
+ };
+ };
+
+ outputs = {
+ self,
+ nixpkgs,
+ flake-utils,
+ treefmt-nix,
+ flake_version_update,
+ ...
+ }:
+ flake-utils.lib.eachDefaultSystem (system: let
+ pkgs = nixpkgs.legacyPackages.${system};
+
+ # This version is set automatically on `cog bump --auto`;
+ version = "v%INIT_APPLICATION_VERSION"; # GUIDING VERSION STRING
+
+ # TODO reduce to the needed ones
+ texlive = pkgs.texlive.combined.scheme-full;
+
+ treefmtEval = import ./treefmt.nix {inherit treefmt-nix pkgs;};
+
+ build = pkgs.stdenv.mkDerivation {
+ pname = "%INIT_APPLICATION_NAME";
+ inherit version;
+ src = ./.;
+
+ buildInputs = [
+ texlive
+ ];
+
+ # Run local
+ preferLocalBuild = true;
+ allowSubstitutes = false;
+
+ buildPhase = ''
+ # TODO: I have no idea, why calling it with `./build.sh` does not work <2024-03-20>
+ bash ./build.sh
+ '';
+
+ installPhase = ''
+ install -D ./build/main.pdf "$out/%INIT_APPLICATION_NAME.pdf";
+ '';
+ };
+ in {
+ packages.default = build;
+
+ checks = {
+ inherit build;
+ formatting = treefmtEval.config.build.check self;
+ };
+
+ formatter = treefmtEval.config.build.wrapper;
+
+ devShells.default = pkgs.mkShell {
+ packages = with pkgs; [
+ cocogitto
+ reuse
+ flake_version_update.packages."${system}".default
+ texlive
+
+ zathura
+ ];
+ };
+ });
+}
diff --git a/build/latex/letter/init b/build/latex/letter/init
new file mode 100644
index 0000000..1ea1603
--- /dev/null
+++ b/build/latex/letter/init
@@ -0,0 +1,152 @@
+#!/usr/bin/env sh
+
+# shellcheck source=/dev/null
+. "$(realpath "$(dirname "$0")")/shell_line_editor.sh"
+
+replacement_file="$(mktemp)"
+
+trap cleanup INT
+trap "cleanup; remove_self" EXIT
+cleanup() {
+ rm "$replacement_file"
+}
+remove_self() {
+ rm "$(realpath "$0")"
+ rm "$(realpath "$(dirname "$0")")/shell_line_editor.sh"
+}
+require() {
+ program="$1"
+ if ! command -v "$program" >/dev/null; then
+ echo "Please install '$program' for this init script to work." 1>&2
+ exit 1
+ fi
+}
+
+# Prompt the user for a specific variable.
+# ## Args:
+# [1]: Name of the variable to populate the answer to
+# [2]: An optional description
+# [3]: An optionally suggested answer
+# [4]: If this is set, the user is not even asked.
+prompt() {
+ pr_variable_upper="$(echo "$1" | sed 's/\([a-z]\)/\U\1/')"
+ pr_description="$2"
+ pr_suggested_answer="$3"
+ pr_ask="$4"
+
+ if [ -n "$pr_ask" ]; then
+ REPLY="$pr_suggested_answer"
+ else
+ printf "\033[94;1mEnter %s\033[0m" "$pr_variable_upper"
+ if [ -n "$pr_description" ]; then
+ printf " (\033[93;1m%s\033[0m):\n" "$pr_description"
+ else
+ printf ":\n"
+ fi
+
+ # LE "> " 0 " " "$pr_suggested_answer" "yes_please_produce_debug_output"
+ LE "> " 0 " " "$pr_suggested_answer" ""
+ fi
+
+ pr_new_variable="$(printf '%s="%s"' "$pr_variable_upper" "$REPLY")"
+
+ eval "$pr_new_variable"
+ printf "%s\n" "$pr_new_variable" >>"$replacement_file"
+}
+
+require git
+
+require jq
+require curl
+
+require fd
+require mv
+require sed
+require chmod
+
+git init
+
+# necessary meta data
+prompt APPLICATION_NAME "The name of the application" "$(basename "$PWD")"
+prompt APPLICATION_NAME_STYLIZED "The stylized name of the application (for documentation)" "$(echo "$APPLICATION_NAME" | sed 's/[_-]/ /g' | sed 's/^\(\w\)/\U\1/g' | sed 's/ \(\w\)/ \U\1/g')"
+prompt APPLICATION_NAME_CAPITALIZED_MAN_PART "The capitalized name of the application (for documentation also with a man section part)" "$(echo "$APPLICATION_NAME" | sed 's/_/-/g' | sed 's/\(.*\)/\U\1(1)/')" "dont_ask"
+prompt APPLICATION_VERSION "The version of this program, without the prefix" "0.1.0"
+
+prompt AUTHOR_NAME "The name of the author (or authors)" "$(git config --get user.name)"
+prompt AUTHOR_EMAIL "The email of the author (or authors)" "$(git config --get user.email)"
+
+# cog change-log variables
+prompt REMOTE "The remote, this project will be pushed to" "git.vhack.eu"
+prompt REPOSITORY "The path of the repository on the remote" "$APPLICATION_NAME"
+prompt OWNER "The name of owner of the repository" "$AUTHOR_NAME"
+
+# nice meta data
+prompt DESCRIPTION "The description of this project" ""
+prompt CURRENT_DATE "The stylized version of the current date" "$(date +'%b %Y')"
+prompt YEAR "The year the work on this has begun (for copyright reasons)" "$(date +'%Y')"
+prompt APPLICATION_SOURCE_CODE_REPOSITORY "The package's source code repository URL" "https://$REMOTE/$OWNER/$REPOSITORY"
+prompt HOME_PAGE "The home page URL of the project" "https://$REPOSITORY.org/"
+prompt BUG_URL "The URL people should report bugs to" "$APPLICATION_SOURCE_CODE_REPOSITORY/issues"
+
+if [ -e ./lpm.toml ]; then
+ # Use a different default license in latex projects.
+ init_default_license="CC-BY-SA-4.0"
+else
+ init_default_license="AGPL-3.0-or-later"
+fi
+prompt SPDX_LICENSE_IDENTIFIER "THE SPDX identifer of your choosen license" "$init_default_license"
+
+default_license_url="$(curl --silent --show-error "https://spdx.org/licenses/$SPDX_LICENSE_IDENTIFIER.json" | jq --raw-output '.seeAlso[0]')"
+
+# Prefer possible text versions of the license
+if curl --fail --silent --show-error "$default_license_url.txt" >/dev/null; then
+ default_license_url="$default_license_url.txt"
+fi
+prompt LICENSE_URL "The url of the license" "$default_license_url"
+
+if [ -e ./.reuse/templates/default.jinja2 ]; then
+ cat <<EOF | fmt --uniform-spacing --width=75 >./.reuse/templates/default.jinja2
+{% for copyright_line in copyright_lines %}
+{{ copyright_line }}
+{% endfor %}
+{% for contributor_line in contributor_lines %}
+SPDX-FileContributor: {{ contributor_line }}
+{% endfor %}
+{% for expression in spdx_expressions %}
+SPDX-License-Identifier: {{ expression }}
+{% endfor %}
+
+This file is part of $APPLICATION_NAME_STYLIZED - $DESCRIPTION.
+
+You should have received a copy of the License along with this program.
+If not, see <$LICENSE_URL>.
+<!-- vim: ft=htmldjango -->
+EOF
+fi
+
+# Allow templates to add template specific prompts
+[ -e init.local ] && . ./init.local
+
+echo "$DESCRIPTION" >.git/description
+
+while read -r var; do
+ var_name="${var%=*}"
+ var_value="${var#*=\"}"
+ var_value="${var_value%\"}"
+
+ fd . --hidden --type file --exec sed --in-place "s|%\bINIT_$var_name\b|$var_value|g"
+
+ # Replace the variable in file paths
+ fd "%INIT_$var_name" . --hidden | while read -r file_path; do
+ new_file_path="$(echo "$file_path" | sed "s|%INIT_$var_name|$var_value|g")"
+ mv "$file_path" "$new_file_path"
+ done
+done <"$replacement_file"
+
+# HACK: Re-add the executable permissions to files, which the nix template has somehow
+# removed <2024-04-02>
+chmod +x scripts/*
+chmod +x update.sh
+[ -f ./build.sh ] && chmod +x build.sh
+
+# vim: ft=sh
diff --git a/build/latex/letter/scripts/cprh.sh b/build/latex/letter/scripts/cprh.sh
new file mode 100755
index 0000000..9582575
--- /dev/null
+++ b/build/latex/letter/scripts/cprh.sh
@@ -0,0 +1,57 @@
+#! /usr/bin/env sh
+
+die() {
+ echo "$@" 1>&2
+ exit 1
+}
+
+help() {
+ cat <<EOF
+A copyright header managment tool.
+
+USAGE:
+ cprh.sh [OPTIONS] contribute NAME EMAIL FILE..
+
+OPTIONS:
+ --help | -h
+ Display this help and exit.
+
+ARGUMENTS:
+ NAME := [[git config user.name]]
+ Your name.
+
+ NAME := [[git config user.email]]
+ Your email address.
+
+ FILE := [[git diff --name-only --cached]]
+ The file you want to change. This can be given multiple times.
+EOF
+}
+
+for arg in "$@"; do
+ case "$arg" in
+ "--help" | "-h")
+ help
+ exit 0
+ ;;
+ *)
+ echo "'$1' is not a recognized option. See --help for more!" 1>&2
+ exit 1
+ ;;
+ esac
+done
+
+user_name="$1"
+[ -z "$user_name" ] && die "No NAME set! See --help for more"
+
+user_email="$2"
+[ -z "$user_email" ] && die "No EMAIL set! See --help for more"
+shift 2
+
+styleOne=""
+styleTwo=""
+[ "$COMMENT_STYLE" ] && styleOne="--style" && styleTwo="$COMMENT_STYLE"
+
+# The styleTwo must be unquoted to avoid adding empty args to reuse
+# shellcheck disable=2086
+reuse annotate --copyright "$user_name <$user_email>" --copyright-prefix string-c --template default --multi-line $styleOne $styleTwo
diff --git a/build/latex/letter/scripts/extract_text_from_all.sh b/build/latex/letter/scripts/extract_text_from_all.sh
new file mode 100755
index 0000000..11b2ac4
--- /dev/null
+++ b/build/latex/letter/scripts/extract_text_from_all.sh
@@ -0,0 +1,8 @@
+#! /usr/bin/env sh
+
+grep 'INPUT ./' ./build/main.fls | uniq | sed 's/INPUT //' | while read -r file; do
+ if ! [ "$(basename "$file")" = preamble.tex ] && ! [ "$(basename "$file")" = gymnasium.png ]; then
+ printf "\n%% Filename: %s\n" "$file"
+ grep -v '^\s*%' "$file"
+ fi
+done
diff --git a/build/latex/letter/shell_line_editor.sh b/build/latex/letter/shell_line_editor.sh
new file mode 100644
index 0000000..8d6833a
--- /dev/null
+++ b/build/latex/letter/shell_line_editor.sh
@@ -0,0 +1,247 @@
+#! /usr/bin/env sh
+# Taken in verbatim from: https://unix.stackexchange.com/a/113450, and somewhat changed
+
+LE_print_debug() {
+ LE_debug="$1"
+ LE_debug_msg="$2"
+ [ -n "$LE_debug" ] && printf "\nDBG: (%s)\n" "$LE_debug_msg" >outfile.debug
+}
+
+LE() {
+ # Shell Line Editor. Extremely slow and stupid code. However it
+ # should work on ansi/vt100/linux derived terminals on POSIX
+ # systems.
+ # Understands some emacs key bindings: CTRL-(A,B,D,E,F,H,K,L)
+ # plus the CTRL-W and CTRL-U normal killword and kill.
+ # no Meta-X key, but handling of <Left>, <Right>, <Home>, <End>
+ # <Suppr>.
+ #
+ # Args:
+ # [1]: prompt (\x sequences recognized, defaults to "")
+ # [2]: max input length (unlimited if < 0, (default))
+ # [3]: fill character when erasing (defaults to space)
+ # [4]: initial value.
+ # [5]: whether to output debugfiles (outfile.debug and outfile.raw.debug)
+ # Returns:
+ # 0: OK
+ # 1: od(d) error or CTRL-C hit
+
+ LE_prompt="$1"
+ LE_max=${2--1}
+ LE_fill=${3-" "}
+ LE_debug="$5"
+
+ LE_backward() {
+ LE_substract="$1"
+ while [ -n "$LE_substract" ]; do
+ printf '\b%s' "$2"
+ LE_substract=${LE_substract%?}
+ done
+ }
+
+ LE_fill() {
+ LE_substract="$1"
+ while [ -n "$LE_substract" ]; do
+ printf '%s' "$LE_fill"
+ LE_substract=${LE_substract%?}
+ done
+ }
+
+ # Used but not right now
+ # shellcheck disable=2016
+ LE_restore='stty "$LE_tty"
+ LC_COLLATE='${LC_COLLATE-"; unset LC_COLLATE"}
+
+ # LE_tty is used in the restore above
+ # shellcheck disable=2034
+ LE_ret=1 LE_tty=$(stty -g) LC_COLLATE=C
+
+ # text on the right of the cursor
+ LE_left=$4
+ # text on the left of the cursor
+ LE_right=''
+
+ # Tell the terminal to show us every char inputted
+ stty -icanon -echo -isig min 3 time 1 -istrip
+ printf '%b%s' "$LE_prompt" "$LE_left"
+
+ # clear the output
+ [ -n "$LE_debug" ] && printf "" >outfile.debug
+ [ -n "$LE_debug" ] && printf "" >outfile.raw.debug
+
+ # The value needs to be split for it to work (and it's either way just numbers)
+ # shellcheck disable=2046
+ while set -- $(dd bs=3 count=1 2>/dev/null | od -vAn -to1); do
+ while [ "$#" -gt 0 ]; do
+ [ -n "$LE_debug" ] && printf "%b" "\0$1" >>outfile.debug
+ [ -n "$LE_debug" ] && printf "%s " "$1" >>outfile.raw.debug
+ LE_current_key=$1
+ shift
+
+ # 033 is ^[ (`printf "\\$1\n" | cat -v`)
+ if [ "$LE_current_key" = 033 ]; then
+ case "$1$2$3" in
+ # [ C | O C -> ^F forward
+ 133103* | 117103*)
+ shift 2
+ LE_current_key=006
+ ;;
+ # [ D | O D -> ^B backward
+ 133104* | 117104*)
+ shift 2
+ LE_current_key=002
+ ;;
+ # [ H | O H -> ^A beginning of line
+ 133110* | 117110*)
+ shift 2
+ LE_current_key=001
+ ;;
+ # [ P | O P -> ^D del char
+ 133120* | 117120*)
+ shift 2
+ LE_current_key=004
+ ;;
+ # [ F | O F -> ^E end of line
+ 133106* | 117106*)
+ shift 2
+ LE_current_key=005
+ ;;
+ # [ 1 ~ -> ^A beginning of line
+ 133061176)
+ shift 3
+ LE_current_key=001
+ ;;
+ # [ 4 ~ -> ^E end of line
+ 133064176)
+ shift 3
+ LE_current_key=005
+ ;;
+ # [ 3 ~ -> ^D del char
+ 133063176)
+ shift 3
+ LE_current_key=004
+ ;;
+ # [ | O
+ 133* | 117*)
+ shift
+ # Is $1 in ge 0 AND le 9 OR eq ';'?
+ # These are control sequences for things like colors; Ignore them
+ while [ "0$1" -ge 060 ] && [ "0$1" -le 071 ] ||
+ [ "0$1" -eq 073 ]; do
+ shift
+ done
+ ;;
+ esac
+ fi
+
+ case "$LE_current_key" in
+ 001) # ^A beginning of line
+ LE_backward "$LE_left"
+ LE_right="$LE_left$LE_right"
+ LE_left=
+ ;;
+ 002) # ^B backward
+ if [ "$LE_left" = "" ]; then
+ # bell
+ printf '\a'
+ LE_print_debug "$LE_debug" "backward with empty left"
+ else
+ printf '\b'
+ LE_tmp="${LE_left%?}"
+ LE_right="${LE_left#"$LE_tmp"}$LE_right"
+ LE_left="$LE_tmp"
+ fi ;;
+ 003) # CTRL-C
+ break 2 ;;
+ 004) # ^D del char
+ if [ "$LE_right" = "" ]; then
+ # bell (tell the user that the line is empty)
+ printf '\a'
+ LE_print_debug "$LE_debug" "delete with empty right"
+ else
+ LE_right="${LE_right#?}"
+ printf '%s\b' "$LE_right$LE_fill"
+ LE_backward "$LE_right"
+ fi ;;
+ 012 | 015) # NL or CR
+ LE_ret=0
+ break 2
+ ;;
+ 005) # ^E end of line
+ printf '%s' "$LE_right"
+ LE_left="$LE_left$LE_right"
+ LE_right=
+ ;;
+ 006) # ^F forward
+ if [ "$LE_right" = "" ]; then
+ # bell (tell the user that the line is empty)
+ printf '\a'
+ LE_print_debug "$LE_debug" "forward with empty right"
+ else
+ LE_tmp="${LE_right#?}"
+ LE_left="$LE_left${LE_right%"$LE_tmp"}"
+ printf %s "${LE_right%"$LE_tmp"}"
+ LE_right="$LE_tmp"
+ fi ;;
+ 010 | 177) # backspace or del
+ if [ "$LE_left" = "" ]; then
+ # bell
+ printf '\a'
+ LE_print_debug "$LE_debug" "backspace with empty left"
+ else
+ printf '\b%s\b' "$LE_right$LE_fill"
+ LE_backward "$LE_right"
+ LE_left="${LE_left%?}"
+ fi ;;
+ 013) # ^K kill to end of line
+ LE_fill "$LE_right"
+ LE_backward "$LE_right"
+ LE_right=""
+ ;;
+ 014) # ^L redraw
+ printf '\r%b%s' "$LE_prompt" "$LE_left$LE_right"
+ LE_backward "$LE_right"
+ ;;
+ 025) # ^U kill line
+ LE_backward "$LE_left"
+ LE_fill "$LE_left$LE_right"
+ LE_backward "$LE_left$LE_right"
+ LE_left=""
+ LE_right=""
+ ;;
+ 027) # ^W kill word
+ if [ "$LE_left" = "" ]; then
+ # bell
+ printf '\a'
+ else
+ LE_tmp="${LE_left% *}"
+ LE_backward "${LE_left#"$LE_tmp"}"
+ LE_fill "${LE_left#"$LE_tmp"}"
+ LE_backward "${LE_left#"$LE_tmp"}"
+ LE_left="$LE_tmp"
+ fi ;;
+ # Print the received key, as it did not match a special key
+ [02][4-7]? | [13]??) # 040 -> 177, 240 -> 377
+ # was assuming iso8859-x at the time
+ if [ "$LE_max" -gt 0 ] && LE_tmp="$LE_left$LE_right" &&
+ [ "${#LE_tmp}" -eq "$LE_max" ]; then
+ # bell, when the user is trying to cross the line limit
+ printf '\a'
+ LE_print_debug "$LE_debug" "max output reached"
+ else
+ LE_left="$LE_left$(printf '%b' "\0$LE_current_key")"
+ printf '%b%s' "\0$LE_current_key" "$LE_right"
+ LE_backward "$LE_right"
+ fi ;;
+ *)
+ LE_print_debug "$LE_debug" "key not recognized: $(printf "%b" "\0$LE_current_key")"
+ printf '\a'
+ ;;
+ esac
+ done
+ done
+ eval "$LE_restore"
+ REPLY=$LE_left$LE_right
+ echo
+ return "$LE_ret"
+}
diff --git a/build/latex/letter/treefmt.nix b/build/latex/letter/treefmt.nix
new file mode 100644
index 0000000..794e8fc
--- /dev/null
+++ b/build/latex/letter/treefmt.nix
@@ -0,0 +1,70 @@
+{
+ treefmt-nix,
+ pkgs,
+}:
+treefmt-nix.lib.evalModule pkgs (
+ {pkgs, ...}: {
+ # Used to find the project root
+ projectRootFile = "flake.nix";
+
+ programs = {
+ alejandra.enable = true;
+ rustfmt.enable = true;
+ clang-format.enable = true;
+ mdformat.enable = true;
+ shfmt = {
+ enable = true;
+ indent_size = 4;
+ };
+ shellcheck.enable = true;
+ prettier = {
+ enable = true;
+ settings = {
+ arrowParens = "always";
+ bracketSameLine = false;
+ bracketSpacing = true;
+ editorconfig = true;
+ embeddedLanguageFormatting = "auto";
+ endOfLine = "lf";
+ # experimentalTernaries = false;
+ htmlWhitespaceSensitivity = "css";
+ insertPragma = false;
+ jsxSingleQuote = true;
+ printWidth = 80;
+ proseWrap = "always";
+ quoteProps = "consistent";
+ requirePragma = false;
+ semi = true;
+ singleAttributePerLine = true;
+ singleQuote = false;
+ trailingComma = "all";
+ useTabs = false;
+ vueIndentScriptAndStyle = false;
+
+ tabWidth = 2;
+ };
+ };
+ stylua.enable = true;
+ ruff = {
+ enable = true;
+ format = true;
+ };
+ taplo.enable = true;
+ };
+
+ settings = {
+ global.excludes = [
+ "CHANGELOG.md"
+ "NEWS.md"
+ ];
+ formatter = {
+ clang-format = {
+ options = ["--style" "GNU"];
+ };
+ shfmt = {
+ includes = ["*.bash"];
+ };
+ };
+ };
+ }
+)
diff --git a/build/latex/letter/update.sh b/build/latex/letter/update.sh
new file mode 100755
index 0000000..49216b8
--- /dev/null
+++ b/build/latex/letter/update.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+nix flake update
diff --git a/build/latex/letter/watch.sh b/build/latex/letter/watch.sh
new file mode 100755
index 0000000..e5147f2
--- /dev/null
+++ b/build/latex/letter/watch.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env sh
+
+help() {
+ cat <<EOF
+An simple watch script, useful to see the changes in the document as it evolves.
+
+USAGE:
+ watch.sh [OPTIONS] [COMMAND]
+
+OPTIONS:
+ --help | -h
+ Display this help and exit.
+
+ --open [READER] | -o [READER]
+ Open the build PDF in READER before waiting for changes.
+ READER defaults to the READER environment variable or
+ 'zathura' if the env variable is unset.
+ --sleeptime [T] | -s [T]
+ How long to sleep between each build attempt in seconds. The
+ time defaults to 10 seconds.
+ARGUMENTS:
+ READER := [[echo "\${READER-zathura}"]]
+ The reader to open the build PDF with.
+
+ T := [[seq 1 100]]
+ The time to sleep between each build attempt.
+EOF
+}
+
+reader=""
+time="10"
+for arg in "$@"; do
+ case "$arg" in
+ "--help" | "-h")
+ help
+ exit 0
+ ;;
+
+ "--open" | "-o")
+ shift 1
+ reader="$1"
+ if [ -z "$reader" ]; then
+ reader="${READER-zathura}"
+ else
+ shift 1
+ fi
+ ;;
+
+ "--sleeptime" | "-s")
+ shift 1
+ time="$1"
+ if [ -z "$time" ]; then
+ time=10
+ else
+ shift 1
+ fi
+ ;;
+ *)
+ echo "'$1' is not a recognized option! See '--help' for more detail." 1>&2
+ exit 1
+ ;;
+ esac
+done
+
+[ -n "$reader" ] && "$reader" ./build/%INIT_APPLICATION_NAME.pdf &
+
+while true; do
+ ./build.sh
+ sleep "$time"
+done