2020-08-01

The /bin/sh Blog. Part I: Timmy’s Templates

I love Unix shell scripting, in fact I may enjoy it more than “actual” programming. I say “shell scripts”, as opposed to “bash scripts”, simply because I don’t use bash for my scripts. The reason why is not relevant to this post. What is relevant is why make a blog using shell scripts?
Well, why not? After all, Makefile blogs are a thing that exist, and Makefiles are just one way of running commands to do something. Hmm. I wonder, what’s another way of running a bunch of commands?

Oh! I know!

Well, what is it Timmy?

Why, shell scripts of course!

Woah, thanks Timmy, why that’s correct.

But how will you ever manage a website’s HTML layout all in a shell script? Wouldn’t that get messy?

Why yes Timmy, it would get pretty messy. What we need is some kind of “template” which can take some input and output HTML which we can serve…

What about that old M4 thing?

“M4”, Timmy? What on earth is that?

RFTM dude: https://en.wikipedia.org/wiki/M4_(computer_language), https://man.openbsd.org/m4

Okay Timmy, no need to be rude now. We have guests, remember?

Ignoring Timmy’s rude nature, we now have a way of making “templates” of sorts, to make building the HTML a little easier. First, we’ll start with something (almost) every HTML document on the web has, a <head>:

define(`_HTMLHEAD', `<head>   
<title>My Blog Lol</title>
<link rel="stylsheet" href="/style.css"/>
</head>')

Okay so now we have a _HTMLHEAD macro defined. In our m4 file, whenever we type _HTMLHEAD, the <head> containing our blog’s title and a link to our stylesheet will be printed. There’s only one problem, currently on every page our title is exactly the same. That is fine for some things, but for actual posts we should be able to set our <title> to be the title of our post. With M4 this is a piece of cake:

define(`_HTMLHEAD', `<head>
<title>$1 - My Blog Lol</title>
<link rel="stylsheet" href="/style.css"/>
</head>')

Well, what changed?

See Timmy, on the second line, there is now a $1 - before the rest of the blog title.
In M4, macros can have arguments that can be placed into the macro’s content. Now, if we call _HTMLHEAD(`This is a post'), the resulting HTML will be:

<head>
<title>This is a post - My Blog Lol</title>
<link rel="stylsheet" href="/style.css"/>
</head>

The $1 has been replaced by our first argument, as expected. Nice. We can define more macros; for the navigation, footer, whatever you like. Here’s a simple one that includes a <head> and also a <nav> block containing our site’s navigation links:

define(`_HTMLHEAD',`<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>$1 - zakaria.org</title>
<link rel="stylesheet" href="/style.css"/>
</head>'
)

define(`_HTMLNAV', `<nav>
<p><a href="/">zakaria.org</a><br/><a href="/posts/">blog</a> / <a href="/rss.xml">rss</a> / <a href="https://github.com/e-zk/">gh</a></p> 
</nav>'
)

For the sake of neatness, I’ve placed this code in head.m4. For our individual posts, we need to write more M4. Here’s a simple post template, we’ll call it post.m4:

include(m4/head.m4)dnl
<!DOCTYPE html>
<html>
_HTMLHEAD(POSTTILE)
<body>
_HTMLNAV
<main>
POSTCONTENT
</main>
</body>
</html>

On the first line, we include the macros in our head.m4, then on line 4 we “call” our _HTMLHEAD macro, then shortly after we call our _HTMLNAV macro. M4 fills in these macros with whatever they’re defined as, in our case our <head> and <nav> blocks of HTML. Neat? Right?
Moving on-

I’m confused

About what, Timmy?

Those strange upper-case words littered throughout the file: POSTTILE and POSTCONTENT.

Oh! Thank you Timmy, I almost forgot. These are also macros, much the same as our _HTMLNAV and _HTMLHEAD ones, but they aren’t defined in any of the M4 files we’re using. That’s because we define these macros when we run m4 itself:

$ m4 -DPOSTTILE="Hello?" post.m4

The -D argument defines a symbol to have some value. In this case POSTTILE now becomes Hello? after M4 is done running. Try running:

$ m4 -DPOSTTILE="Test" -DPOSTCONTENT="<h1>Test :P</h1>" post.m4

M4 should output a “complete” HTML document, with the title “Test” and with a single heading reading “Test :P”.

Now that we have a (an?) M4 template set up, instead of echoing a bunch of <html> in our script, now all we have to do is call m4 and pass arguments to it for our content to show up in the resulting HTML.

Don’t be mad. But that’s the end of this post. I know, I know we didn’t actually write any shell script just yet, (Timmy is giving me the stink eye right now), but that’s for another time.
Stay tuned for part II.