diff --git a/third_party/nixpkgs-exposed/OWNERS b/third_party/nixpkgs-exposed/OWNERS
index 32f9ac249..a5ea8a2ae 100644
--- a/third_party/nixpkgs-exposed/OWNERS
+++ b/third_party/nixpkgs-exposed/OWNERS
@@ -4,3 +4,4 @@
inherited: true
owners:
- Profpatsch
+ - sterni
diff --git a/third_party/nixpkgs-exposed/exposed/default.nix b/third_party/nixpkgs-exposed/exposed/default.nix
index 6f4441879..61ae3ff67 100644
--- a/third_party/nixpkgs-exposed/exposed/default.nix
+++ b/third_party/nixpkgs-exposed/exposed/default.nix
@@ -84,6 +84,7 @@
lutris
makeFontsConf
makeWrapper
+ mandoc
mdbook
meson
mime-types
diff --git a/users/sterni/htmlman/README.md b/users/sterni/htmlman/README.md
new file mode 100644
index 000000000..258233d4c
--- /dev/null
+++ b/users/sterni/htmlman/README.md
@@ -0,0 +1,36 @@
+# htmlman
+
+static site generator for man pages intended for
+rendering man page documentation viewable using
+a web browser.
+
+## usage
+
+If you have a nix expression, `doc.nix`, like this:
+
+```nix
+{ depot, ... }:
+
+depot.users.sterni.htmlman {
+ title = "foo project";
+ pages = [
+ {
+ name = "foo";
+ section = 1;
+ }
+ {
+ name = "foo";
+ section = 3;
+ path = ../devman/foo.3;
+ }
+ ];
+ manDir = ../man;
+}
+```
+
+You can run the following to directly deploy the resulting
+documentation output to a specific target directory:
+
+```sh
+nix-build -A deploy doc.nix && ./result target_directory
+```
diff --git a/users/sterni/htmlman/default.nix b/users/sterni/htmlman/default.nix
new file mode 100644
index 000000000..c528a2900
--- /dev/null
+++ b/users/sterni/htmlman/default.nix
@@ -0,0 +1,204 @@
+{ depot, lib, pkgs, ... }:
+
+let
+ inherit (depot.nix)
+ getBins
+ runExecline
+ ;
+
+ inherit (depot.tools)
+ cheddar
+ ;
+
+ inherit (pkgs)
+ mandoc
+ coreutils
+ fetchurl
+ writers
+ ;
+
+ bins = getBins cheddar [ "cheddar" ]
+ // getBins mandoc [ "mandoc" ]
+ // getBins coreutils [ "cat" "mv" "mkdir" ]
+ ;
+
+ normalizeDrv = fetchurl {
+ url = "https://necolas.github.io/normalize.css/8.0.1/normalize.css";
+ sha256 = "04jmvybwh2ks4dlnfa70sb3a3z3ig4cv0ya9rizjvm140xq1h22q";
+ };
+
+ execlineStdoutInto = target: line: [
+ "redirfd" "-w" "1" target
+ ] ++ line;
+
+ # I will not write a pure nix markdown renderer
+ # I will not write a pure nix markdown renderer
+ # I will not write a pure nix markdown renderer
+ # I will not write a pure nix markdown renderer
+ # I will not write a pure nix markdown renderer
+ markdown = md:
+ let
+ html = runExecline.local "rendered-markdown" {
+ stdin = md;
+ } ([
+ "importas" "-iu" "out" "out"
+ ] ++ execlineStdoutInto "$out" [
+ bins.cheddar "--about-filter" "description.md"
+ ]);
+ in builtins.readFile html;
+
+ indexTemplate = { title, description, pages ? [] }: ''
+
+
+
+
+ ${title}
+
+
+
+
+
${title}
+ ${markdown description}
+
man pages
+
+
+
+
+ '';
+
+ defaultStyle = import ./defaultStyle.nix { };
+
+ # This deploy script automatically copies the build result into
+ # a TARGET directory and marks it as writeable optionally.
+ # It is exposed as the deploy attribute of the result of
+ # htmlman, so an htmlman expression can be used like this:
+ # nix-build -A deploy htmlman.nix && ./result target_dir
+ deployScript = title: drv: writers.writeDash "deploy-${title}" ''
+ usage() {
+ printf 'Usage: %s [-w] TARGET\n\n' "$0"
+ printf 'Deploy htmlman documentation to TARGET directory.\n\n'
+ printf ' -h Display this help message\n'
+ printf ' -w Make TARGET directory writeable\n'
+ }
+
+ if test "$#" -lt 1; then
+ usage
+ exit 100
+ fi
+
+ writeable=false
+
+ while test "$#" -gt 0; do
+ case "$1" in
+ -h)
+ usage
+ exit 0
+ ;;
+ -w)
+ writeable=true
+ ;;
+ -*)
+ usage
+ exit 100
+ ;;
+ *)
+ if test -z "$target"; then
+ target="$1"
+ else
+ echo "Too many arguments"
+ exit 100
+ fi
+ ;;
+ esac
+
+ shift
+ done
+
+ if test -z "$target"; then
+ echo "Missing TARGET"
+ usage
+ exit 100
+ fi
+
+ set -ex
+
+ mkdir -p "$target"
+ cp -RTL --reflink=auto "${drv}" "$target"
+
+ if $writeable; then
+ chmod -R +w "$target"
+ fi
+ '';
+
+ htmlman =
+ { title
+ # title of the index page
+ , description ? ""
+ # description which is displayed after
+ # the main heading on the index page
+ , pages ? []
+ # man pages of the following structure:
+ # {
+ # name : string;
+ # section : int;
+ # path : either path string;
+ # }
+ # path is optional, if it is not given,
+ # the man page source must be located at
+ # "${manDir}/${name}.${toString section}"
+ , manDir ? null
+ # directory in which man page sources are located
+ , style ? defaultStyle
+ # CSS to use as a string
+ , normalizeCss ? true
+ # whether to include normalize.css before the custom CSS
+ }:
+
+ let
+ index = indexTemplate {
+ inherit title description pages;
+ };
+ resolvePath = { path ? null, name, section }:
+ if path != null
+ then path
+ else "${manDir}/${name}.${toString section}";
+ html =
+ runExecline.local "htmlman-${title}" {
+ derivationArgs = {
+ inherit index style;
+ passAsFile = [ "index" "style" ];
+ };
+ } ([
+ "multisubstitute" [
+ "importas" "-iu" "out" "out"
+ "importas" "-iu" "index" "indexPath"
+ "importas" "-iu" "style" "stylePath"
+ ]
+ "if" [ bins.mkdir "-p" "$out" ]
+ "if" [ bins.mv "$index" "\${out}/index.html" ]
+ "if" (execlineStdoutInto "\${out}/style.css" [
+ "if" ([
+ bins.cat
+ ] ++ lib.optional normalizeCss normalizeDrv
+ ++ [
+ "$style"
+ ])
+ ])
+ ] ++ lib.concatMap ({ name, section, ... }@p:
+ execlineStdoutInto "\${out}/${name}.${toString section}.html" [
+ "if" [
+ bins.mandoc
+ "-T" "html" "-mdoc"
+ "-O" "style=style.css"
+ (resolvePath p)
+ ]
+ ]) pages);
+ in html // {
+ deploy = deployScript title html;
+ };
+in
+ htmlman
diff --git a/users/sterni/htmlman/defaultStyle.nix b/users/sterni/htmlman/defaultStyle.nix
new file mode 100644
index 000000000..a44b5ef06
--- /dev/null
+++ b/users/sterni/htmlman/defaultStyle.nix
@@ -0,0 +1,49 @@
+{ ... }:
+
+''
+ body {
+ font-size: 1em;
+ line-height: 1.5;
+ font-family: serif;
+ background-color: #efefef;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ font-family: sans-serif;
+ font-size: 1em;
+ margin: 5px 0;
+ }
+
+ h1 {
+ margin-top: 0;
+ }
+
+ a:link, a:visited {
+ color: #3e7eff;
+ }
+
+ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+ text-decoration: none;
+ }
+
+ .manual-text, .index-text {
+ padding: 20px;
+ max-width: 800px;
+ background-color: white;
+ margin: 0 auto;
+ }
+
+ table.head, table.foot {
+ display: none;
+ }
+
+ .Nd {
+ display: inline;
+ }
+
+ /* use same as cheddar for man pages */
+ pre {
+ padding: 16px;
+ background-color: #f6f8fa;
+ }
+''