Praise to scdoc to generate man pages

Hey, its been a long time since my last blog post. It is mostly because I ran out of things to write, but I expected this. This is probably more likely how I am actually going to post from now. At least, it shows that my plan to have a blog for a long time, that is easy to go back when I wanted is working fine, but I digress.

Going back to the theme of the today blog post, I needed to write a man page for the first time in years. I hate troff, the typesetting system used for man pages (similar to LaTeX for documents). It is one of the weirdest languages that I ever saw, and even the example in Wikipedia shows that:

.ND "January 10, 1993"
.AU "Ms. Jane Smith"
.AT "Upcoming appointment"
.MT 5
.DS
Reference #A12345
.sp 4
Mr. Samuel Jones
Field director, Bureau of Inspections
1010 Government Plaza
Capitoltown, ST
.sp 3
Dear Mr. Jones,
.sp 2
.P
Making reference to the noted obligation to submit for state inspection our newly created production process, we request that you consider the possible inappropriateness of subjecting the  innovative technologies of tomorrow to the largely antiquated requirements of yesterday.  If our great state is to prosper in the twenty-first century, we must take steps
.B now ,
in
.I this
year of
.I this
decade, to prepare our industrial base for the interstate and international competition that is sure to appear.  Our new process does precisely that.  Please do not let it be undone by a regulatory environment that is no longer apt.
.P
Thank you for your consideration of our position.
.FC Sincerely
.SG

Keep in mind that the break lines are necessary every time you introduce a macro, like .I this (that I think it is for italics). Yes, this format is as illegible as hell, and it is worse that the format lacks good tooling (or at least I didn't find any good ones).

Most people when they need to write a man page nowadays ends up using some other format that generates a man page. For example, in the past I used Pandoc to convert Markdown to a man page, but even if Pandoc is a great project the result is sub-optimal at best: Markdowns are, at the end, designed for generating HTML (and a subset of it), and not man pages, so you basically ends up fighting the format for it to do what you want. Also, Pandoc is a big project, with a ~200MB binary (at least it is the default Pandoc binary in Nix).

For this specific project I needed something small. I am trying to replace one of the most essential pieces inside NixOS, nixos-rebuild, written in Bash, with a full rewritten in Python (sorry Rust zealots!), called nixos-rebuild-ng.

Since this project will eventually (if successful) be in the critical path for NixOS, I want to reduce the number of dependencies as much as possible, so something as big as Pandoc is out. I could use AsciiDoc, but it is a big complicated Python project (this may seem ironic, but nixos-rebuild-ng has only one runtime dependency, that is optional). And I also hated the last time I tried to use it to generate man pages: it more flexible than Markdown, but still far from optimal.

Thanks to Drew DeVault (creator of SwayWM) that seems it had the same issues in the past and created scdoc, a very simple man page generator using a DSL inspired in Markdown, but specific to generate man pages. The binary is written in C (and advantage in this case since it means it is easier to bootstrap), is small (~1 Kloc) and has no dependencies, so it fits the requirement.

While the language suffers from being a niche project for a niche segment, the man page for it is actually really nice. It is terse though and lacks examples, and this is what this blog post will try to accomplish.

To start, let's have a quick summary of the syntax, written in scdoc as comments:

; quick summary:
; # new section
; comments starts with ;
; - this is a list
; 	- sub-list
; - *bold*: _underline_, force a line break++
; - [tables], \[ can be used to force an actual [
; . numbered list
; please configure your editor to use hard tabs
; see `man 5 scdoc` for more information about syntax
; or https://man.archlinux.org/man/scdoc.5.en

I actually added this summary in the .scd (the scdoc extension) files that I wrote, so it is easy for someone that never saw the format to start collaborating.

And here an example of a (summarised) man page in .scd format:

nixos-rebuild-ng(8)

# NAME

nixos-rebuild - reconfigure a NixOS machine

# SYNOPSIS

_nixos-rebuild_ \[--upgrade] [--upgrade-all]++
		\[{switch,boot}]

# DESCRIPTION

This command has one required argument, which specifies the desired operation.
It must be one of the following:

*switch*
	Build and activate the new configuration, and make it the boot default.
	That is, the configuration is added to the GRUB boot menu as the
	default menu entry, so that subsequent reboots will boot the system
	into the new configuration. Previous configurations activated with
	nixos-rebuild switch or nixos-rebuild boot remain available in the GRUB
	menu.

*boot*
	Build the new configuration and make it the boot default (as with
	*nixos-rebuild switch*), but do not activate it. That is, the system
	continues to run the previous configuration until the next reboot.

# OPTIONS

*--upgrade, --upgrade-all*
	Update the root user's channel named 'nixos' before rebuilding the
	system.

	In addition to the 'nixos' channel, the root user's channels which have
	a file named '.update-on-nixos-rebuild' in their base directory will
	also be updated.

	Passing *--upgrade-all* updates all of the root user's channels.

See the Nix manual, *nix flake lock --help* or *nix-build --help* for details.

# ENVIRONMENT

NIXOS_CONFIG
	Path to the main NixOS configuration module. Defaults to
	_/etc/nixos/configuration.nix_.

# FILES

/etc/nixos/flake.nix
	If this file exists, then *nixos-rebuild* will use it as if the
	*--flake* option was given. This file may be a symlink to a
	flake.nix in an actual flake; thus _/etc/nixos_ need not be a
	flake.

# AUTHORS

Nixpkgs/NixOS contributors

And here is a screenshot of the result:

Man page rendered from scd
file

One of nice things that I found is how looking at the plain text looks kind like the man page result already. And if you know Markdown, you can basically understand most things that is happening. There are a few differences, like *bold* instead of **bold**, and while they're unfortunate they're not the end of the world.

Now, the format has its quirks. The first line being the name of the program and section in parenthesis is required, but this makes sense, since you need this information for the corners. But for one, it requires the usage of hard tabs to create indentation, and the error messages are awful, in a situation that kind remembers me of Makefile. Also the choice of [ to start a table means that the traditional app [command] needs in many cases to be escaped as app \[command]. I found this a strange choice since this is supposed to be a format that is only used for man pages, and using [command] to indicate an optional is common, but at least it is easy to escape.

In the end, I think all that matters is the result. And for the first time for all those years trying to write a man page, I am satisfied with the result. The man page looks exactly as I wanted once rendered, and the .scd file looks reasonable good that it can work as a documentation for someone that for one reason or another can't use the man page (can't say the same for the troff version). Also, it is really easy for someone to just go there and update the man page, even without experience in the format (except for maybe the requirement of tabs). So all in all, I really liked the format, and will use it again if I need to write another man page in the future.