This article presents htmlcc, an HTML templating engine for C utilising static compilation.

Motivation

Web applications are not commonly written in C; one notable counterexample is cgit, a web frontend for git repositories. HTML output in cgit is implemented from scratch, using bespoke helper functions and hundreds of individual printf-esque calls. A representative example of HTML-related code in cgit looks like:

void cgit_print_docstart(void)
{
	/* ... */
	html("<html lang='en'>\n");
	html("<head>\n");
	html("<title>");
	html_txt(ctx.page.title);
	html("</title>\n");
	htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
	if (ctx.cfg.robots && *ctx.cfg.robots)
		htmlf("<meta name='robots' content='%s'/>\n", ctx.cfg.robots);
	/* ... */
}

While highly performant, this code is non-trivial to work with, particularly to make significant changes to the HTML layout. Typical support tooling (e.g. syntax highlighting) is also not available.

For this reason, I sought to create an HTML templating engine for C. In order to preserve performance and simplify implementation as a drop-in replacement, it was desired not to write a template interpreter in C. Rather, templates would be transpiled into C code (like that above) and statically compiled. Since there is no widespread standardised approach to writing web applications in C, it was also desired that emitted C code be easily customisable (e.g. printf-style as above, versus fill a buffer and return the result, etc.).

I am aware of limited prior art in this respect. TinyTemplate has similar syntax, but templates are dynamically rather than statically compiled, and interspersed C code is not possible so significant architectural changes would be required to any software that expects to output HTML while manipulating internal state. Omar Polo describes a similar templating engine, but it does not have an extensible architecture (though is designed to be easily modified).

About htmlcc

htmlcc is a command line Python application. It takes as input a template file (syntax inspired by Jinja, using double curly braces) and outputs C code in the above described style. An extensible framework is provided to customise the C code output; examples are provided implementing output to stdout, or cgit-style function calls. Although the transpiler is implemented in Python, the generated C code is pure C, cross-platform, has no inherent dependencies, and requires no memory allocations.

Supported language features include Jinja-like filter expressions (a limited number have been implemented), control flow (if, while, for), and interspersing raw C code.

Example

The following is a simple example of an htmlcc template:

{! char *my_name = "Alice"; !}
{% page hello_world %}
<html>
    <body>Hello world, my name is {{ my_name }}!</body>
</html>
{% endpage %}

Using the default C emitter (print to stdout), the template transpiles into the following C code:

#include <stdio.h>

void output_variable_as_text(char *variable) { /* escape special characters ... */ }

char *my_name = "Alice";

void hello_world(void) {
    printf("%s", "<html>\n");
    printf("%s", "    <body>Hello world, my name is ");
    output_variable_as_text(my_name);
    printf("%s", "!</body>\n");
    printf("%s", "</html>\n");
}

The following example demonstrates how control flow is supported:

{% page main %}
<html>
	<body>
		<p>Counting to 10:</p>
		<p>
			{% for int i = 1; i <= 10; i++ %}
			{{ i|%d }}{% if i != 10 %},{% endif %}
			{% endfor %}
		</p>
	</body>
</html>
{% endpage %}

Further details

Source code and further details on htmlcc are available at https://yingtongli.me/git/htmlcc. A further article discussing the application of htmlcc to a cgit fork is forthcoming.