Annotations, combined with a webserver that supports basic auth and cgi scripts, builds and maintains weblogs. It maintains the weblog as static pages, keeps articles in folders that tell you the date the article was posted (YYYY/MM/DD/seq), and produces RSS 2.0 and Atom 1.0 syndication formats. It uses a variant on my theme program to do formatting of the weblog, and the default markup is labelled for easier use of CSS


This code is a horrible evil bodge that needs to be completely redesigned, and it’s highly recommended that you have garlic, a silver cross, and holy water on hand before you download it.

Source Code

Another year, another revision. This time my archive page was getting out of hand, so I added in calendar-style archives where each year is presented as a monthly calendar with entries only where there is actually content for that month. It’s enabled by the option calendararchive in your weblog.conf
You can’t keep Freddy dead, nor can you keep annotations in the grave. Many changes had piled up, so I had to get them into version control before it was Too Late:
  • In,
    1. FAIL if discount can’t be found
    2. FAIL if discount < 1.2.0
  • In format.c
    1. Remove the comment that describes the old (and now invalid) formatting.
    2. byline() now supplies the <p class="byline"></p> block wrapper
    3. byline() uses a rudimentary printf-style format for placing author name (%A), post date (%D – w/ url link, %d – without), and %% escape, plus \n -> newline and \\ -> \
    4. In article(), call mkd_basename() to set the root for all unqualified relative paths in urls.
  • In formatting.h, add a base field to struct fmt (to pass to mkd_basename) and change struct markup byline to char *byline
  • In indexer.c
    1. Always use a <FORM> block for the comment button even if there are already comments.
    2. everywhere where I call markdown(), call mkd_basename() first so that I can resolve url fragments.
  • In readconfig.c,
    1. byline is now a single-field configuration instead of a pair.
    2. Generate fmt.base from the provided url.
  • In rss.c, use fmt.base for mkd_basename() and atom-format headers.

The long national nightmare continues here; I discovered that my rss and atom feed generators weren’t producing valid feeds anymore, so I had to revise them to make them work properly.

Fortunately, the rss/atom syndication code is not written quite as horribly as the rest of this zombie, so I was able to fix the feed generation code without having to apply the head-desk user interface tool.

Version 1.0.4

This version rips out my old home-grown text markup language and replaces it with my implementation of John Gruber’s Markdown.

The rubble is still bouncing from this retrofit, but I’ve taken a snapshot of the explosion so I can store it in a safe place for a while.


I’ve added some more functionality to the wiki-like behavior I started to put into – the original code would strip the last component off pathnames when fabricating the control file, which meant that the path weblog/legos/surveyship couldn’t be easily reindexed. I went in and fixed this code, then went back in to fix some code I broke when I added “index a single page” to reindex, then went back in to fix some code I broke (resulting in terribly mangled pathnames in permalinks) when I “fixed” the non-weblog-path-with-slash reindex logic.

So, quick!, release the code before I break it again!


Yes, it’s been less than a month since the last release, but this is for a good reason. The machine I usually run tsfr on (a x86 box running SLS linux) has some fairly old SCSI disks on it, and they have finally reached the point where the badblocks list is filled up and badblocks are starting to appear in user-visible space) has developed some badblocks in the swap area, so the machine has taken to just falling over dead out of the clear blue sky. It did it most recently on a Saturday morning, so I had to move things from their comfortable home on pell over to a hostile workplace running Apache. So, to make this move actually work, I had to modify annotations to spit out error and redirect pages in a form that would not cause Apache to freak its tiny little brain out after someone posted an article or comment.

There are no new features here, just portability fixes.


After a brief six months, here’s another release. There are three little changes here that begin to make the code more versatile, so I will (eventually) be able to use it to run this website as a wiki. The changes are:

  1. Before regenerating a page, store the environment variable LOCATION, which is the path from the bbs root to the page in question.
  2. The filename for <?theme [include-file]?> can now include a variable. The only useful variable is $LOCATION, but you can use this to do something like <?theme [$LOCATION/message.css]?> if you wish to have page-specific css to override site-wide css. (If you do do this, you need to wrap the css with <STYLE>; and </STYLE>, because annotations doesn’t know you’re doing something fancy with it.)
  3. You can reindex and add comments to pages located anywhere under bbsroot. The pages need to be set up in the standard format, with a message.txt containing the text of the page and a message.ctl containing the metadata, but once you’ve done that “reindex $LOCATION-of-page” will properly generate an index.html for you, and commenting will then work as it does anywhere else.
A slightly less trivial patch, mainly for portability reasons, but doing some small cleanup of broken and awkward code at the same time. The main portability change is that I now carry along a copy of Steven Grimm’s uncgi program so that annotations will still build on machines where a libuncgi wasn’t hand-built beforehand.

A trivial patch, of interest only to people who care about portability; I tried to compile the code on a redhat 8.0 system, with somewhat less that complete success, so I did a round of patching and ended up with a version that does actually compile on that OS.

As a little something extra, I’ve started to put support for post categories into the code, so you can enter a list of comment-separated categories when you make your post and it will be nicely filed into those categories when I get around to writing the rest of the code support.

[Version] (annotations-
This release fixes the tiny bug of me not actually putting all of the files into version control, plus it takes another pass on the annoying “posting a comment resets the post page to the last month” feature, which appears to be because I was misusing localtime() by getting a pointer to one time, then getting a pointer to another time. But the big reason for the release is that rewrite.c wasn’t in version control. Ooops.
[Version] (annotations-

It’s a new year, and time for some new features in the code. Not very much has changed in this version; I’ve redone the way that comments are stored from the traditional One Big File to a directory containing the comments in a combined data/control format. The hooks for moderated comments have been put in (but not deliberatedly added), and there’s now a user-settable flag that governs whether comment email addresses are published or not (default: not.)

A couple of bug A couple of bugs were introduced, then deleted, and I’ve added (in what turns out to be a fairly hackish way) a procedure for doing article truncation on the homepage; if you put a <!more!> token into your article, the homepage will display the article up to that point, then a –more– (this text is user settable by the readmore= variable in weblog.conf) link to the whole article.

Finally, I tweaked the way that the weblog builds the index and post pages to show nrposts= posts, no matter how old they are.

Version 1.0.3

With 2005 rapidly coming to an end, a new release. This one includes the usual crop of undocumented features, but it also include DOCUMENTATION, which is woefully incomplete, but it’s there.

To go along with the documentation, I’ve also changed the config file name and format, plus added a setup.weblog script that will do the initial setup for a user and a xmlpost program that allows you to publish posts via xml.

This release adds a single whimsical feature to the weblog program. Today (19-sept) was talk like a pirate day, so I needed to redo the entire weblog in Aaar!-speak. I added a [filter] section to the (still undocumented) config file, which contains the full pathname to a program that can be run to mangle the html that’s generated. The raw text is not mangled, and can be filtered in different ways without having to do fancy recovery in between.
There were places all over the code where I got confused about whether tm_mon went from 0 to 11 or 1 to 12; these were making the month rollovers at the end of January pretty interesting.
Version 1.0.2
This one has features. It does syndication (in both rss 2 and atom .3 format), fixes an old bug that allows anonymous callers to leave comments, and fixes another old bug that screws up index wraparound over year boundaries. There may be some other features added in, but I don’t remember what they are – I made a 1.0.1 release, but I forgot to tag it in version control or publish it, and 1.0.2 only came along because all of my posts to [TSFR][] vanished when it rolled from 2004 to 2005.
Version 1.0
This is the first (and nonfunctional) public release, for people to gawk at in horror and amazement.


Like I’m doing with everything else, I’m tracking code bloat as the software gets older and more feature-ridden.