~ecs/ecs.d2evs.net

e20c183323ae3ff5015a3d82f4f0609bb0fe618f — Eyal Sawady 24 days ago ef95c45
Add gemini support

The HTML version is automatically generated from gemtext
14 files changed, 264 insertions(+), 209 deletions(-)

M .gitignore
M Makefile
M _config.sh
M _templates/page.html.w
A index.gmi.w
M index.md.w
M nginx.conf
R posts/{2019-10-18-Installing-OpenWRT.md.w => 2019-10-18-Installing-OpenWRT.gmi.w}
A posts/2020-01-13-PowerBook-G4-OpenBSD.gmi.w
D posts/2020-01-13-PowerBook-G4-OpenBSD.md.w
A posts/2020-05-22-Selfhosting.gmi.w
D posts/2020-05-22-Selfhosting.md.w
A title.awk
A tohtml.awk
M .gitignore => .gitignore +2 -1
@@ 1,2 1,3 @@
_site
_html
_gemini
_templates/webring-out.html

M Makefile => Makefile +9 -4
@@ 11,19 11,24 @@ blogs=\
	https://tonsky.me/blog/atom.xml \

local: gen
	doas cp -r _site/* /var/www/localhost
	doas cp -r _html/* /var/www/localhost
	doas cp -r _gemini/* /srv/gemini/localhost

upload: gen
	rsync -r _site/ ecs@ecs.d2evs.net:/var/www/ecs.d2evs.net
	rsync -r _html/ ecs@ecs.d2evs.net:/var/www/ecs.d2evs.net
	rsync -r _gemini/ ecs@ecs.d2evs.net:/srv/gemini/ecs.d2evs.net

gen:
	@printf "openring <_templates/webring-in.html >_templates/webring-out.html\n"
	@openring $$(for blog in $(blogs); do echo "-s $$blog"; done) \
		<_templates/webring-in.html \
		>_templates/webring-out.html
	wersh . _site
	@echo "wersh . _html"
	@output_format=html wersh . _html
	@echo "wersh . _gemini"
	@output_format=gemini wersh . _gemini

clean:
	rm -rf _site
	rm -rf _html _gemini

.PHONY: clean

M _config.sh => _config.sh +15 -9
@@ 1,13 1,13 @@
title=
title=""
get_title() {
	_title="$title"
	file="$(mktemp)"
	# TODO: is this good enough?
	wersh.awk <"$1" | grep -v '^ *emit' >"$file"
	. "$file"
	rm "$file"
	printf "%s" "$title"
	title="$_title"
	case $1 in
		*.gmi.w)
			./title.awk <"$1"
			;;
		*.md.w)
			echo "ecs's blog"
			;;
	esac
}

get_description() {


@@ 45,4 45,10 @@ case $input in
		preprocessor="markdown -f fencedcode -f footnote"
		wsource _templates/page.html.w
		;;
	*.gmi.w)
		if [ "z$output_format" = "zhtml" ]; then
			preprocessor="./tohtml.awk"
			wsource _templates/page.html.w
		fi
		;;
esac

M _templates/page.html.w => _templates/page.html.w +1 -4
@@ 3,8 3,7 @@
<html lang="en" charset="utf-8">
<head>
	<meta name="viewport" content="width=device-width, initial-scale=1">
% echo "<meta name=\"description\" content=\"$(get_description "$input")\">"
% echo "<title>${title:-ecs's blog}</title>"
% echo "<title>$(get_title $input)</title>"
	<link type="text/css" rel="stylesheet" href="/static/style.css">
</head>
<body>


@@ 16,9 15,7 @@
% done
	</div>
	<main>
% echo "<h1>${title:-ecs's blog}</h1>"
% cat
	</main>
% cat "$root/_templates/webring-out.html"
</body>
% }

A index.gmi.w => index.gmi.w +20 -0
@@ 0,0 1,20 @@
# ecs's blog

I do stuff, most of which does not involve writing blog posts.

=> https://git.sr.ht/~ecs sourcehut: ~ecs on sr.ht
email: ecs@d2evs.net
=> https://social.d2evs.net fediverse: @ecs@social.d2evs.net
irc: ecs on most networks
=> /publickey.txt pgp: 9604D3459E53A9952

This site is licensed CC-BY-SA, you can view the code at
=> https://git.sr.ht/~ecs/ecs.d2evs.net
If you find a bug, please send a patch to ~ecs/public-inbox@lists.sr.ht:
=> https://git-send-email.io

=> /feed.xml RSS feed

% for i in $(ls "$root/posts" | sort -rn); do
%     emit "=> /posts/${i%\.w} $(printf "%s" "$i" | cut -d- -f-3): $(get_title "$root/posts/$i")"
% done

M index.md.w => index.md.w +6 -0
@@ 1,7 1,11 @@
<link rel="alternate" type="application/rss+xml" title="RSS feed for ecs's blog" href="/feed.xml">

# ecs's blog

I do stuff, most of which does not involve writing blog posts.

Note that the HTML version of this blog is automatically generated from the
Gemini version, [gemini://ecs.d2evs.net](gemini://ecs.d2evs.net) will probably provide a better UX.

sourcehut: [~ecs](https://git.sr.ht/~ecs) on [sr.ht](https://sr.ht)
<br>


@@ 28,3 32,5 @@ An RSS version of this site is available at [/feed.xml](/feed.xml).
  <br>
% done
</big>

% cat "$root/_templates/webring-out.html"

M nginx.conf => nginx.conf +4 -0
@@ 31,3 31,7 @@ server {
		internal;
	}
}

types {
	text/html				md gmi;
}

R posts/2019-10-18-Installing-OpenWRT.md.w => posts/2019-10-18-Installing-OpenWRT.gmi.w +20 -44
@@ 1,15 1,10 @@
% title="Adventures installing OpenWRT"
# Adventures installing OpenWRT

I recently got my
[MikroTik RB2011U](https://openwrt.org/toh/mikrotik/rb2011uias), and I
wanted to install OpenWRT on it.
I recently got my MikroTik RB2011U, and I wanted to install OpenWRT on it.

The MikroTik RouterBOARD has a "Netinstall" mode, in which it connects
to `eth1` and receives a kernel to boot from using TFTP[^0]. I ended up
with this small script, based on something from the OpenWRT
documentation:
=> https://openwrt.org/toh/mikrotik/rb2011uias MikroTik RB2011U

[^0]: The Trivial File Transfer Protocol.
The MikroTik RouterBOARD has a "Netinstall" mode, in which it connects to `eth1` and receives a kernel to boot from using TFTP (the Trivial File Transfer Protocol). I ended up with this small script, based on something from the OpenWRT documentation:

```
#!/bin/sh


@@ 28,14 23,11 @@ sudo dnsmasq -i enp2s0f1 --dhcp-range=192.168.1.100,192.168.1.200 \
  --enable-tftp -d -u $USER -p0 -K --log-dhcp --bootp-dynamic
```

Once the router attempted to connect to the DHCP server with the
hostname `OpenWrt`, I killed the TFTP server.
Once the router attempted to connect to the DHCP server with the hostname `OpenWrt`, I killed the TFTP server.

This worked - the router was now running OpenWRT from RAM.

Then, I disconnected my laptop from `eth1` and plugged it into `eth2`.
The OpenWRT initramfs is configured to act as a DHCP server on `eth2`
through `eth10`.
Then, I disconnected my laptop from `eth1` and plugged it into `eth2`. The OpenWRT initramfs is configured to act as a DHCP server on `eth2` through `eth10`.

At this point, I `ssh`ed into the router:



@@ 43,17 35,11 @@ At this point, I `ssh`ed into the router:
$ ssh root@OpenWrt
```

(In the future, commands starting with `$` are on the host computer, and
commands starting with `#` are on the router.)
(In the future, commands starting with `$` are on the host computer, and commands starting with `#` are on the router.)

I now had a root shell on the router! So far so good.

I then `scp`ed the OpenWRT sysupgrade[^1] onto the router:

[^1]: OpenWRT has a `sysupgrade` command which takes a firmware image
      and installs it. The install procedure for MikroTik RouterBOARDs
      is to run OpenWRT from RAM using TFTS, then use `sysupgrade` to
      flash a firmware upgrade.
I then `scp`ed the OpenWRT sysupgrade[1] onto the router:

```
$ scp ~/tmp/openwrt/stable.bin root@OpenWrt:/tmp/sysupgrade.bin


@@ 68,24 54,19 @@ Cannot save config while running from ramdisk.
Commencing upgrade. Closing all shell sessions.
```

At this point, the router closed the SSH connection and entered a boot
loop.
At this point, the router closed the SSH connection and entered a boot loop.

Huh.

That's interesting.

I tried repeating the previous steps, replacing stable (18.06.4) OpenWRT
with the snapshot. (The RB2011U v2 is listed as only supported by
OpenWRT snapshot.)
I tried repeating the previous steps, replacing stable (18.06.4) OpenWRT with the snapshot. (The RB2011U v2 is listed as only supported by OpenWRT snapshot.)

No luck.

In vain, I tried pinging the router as it boot looped. It never
responded.
In vain, I tried pinging the router as it boot looped. It never responded.

This meant that it was crashing and burning *before* networking was
initialized. (This also meant that OpenWRT failsafe mode wouldn't help.)
This meant that it was crashing and burning *before* networking was initialized. (This also meant that OpenWRT failsafe mode wouldn't help.)

So, I tried to read the kernel logs from flash memory:



@@ 102,8 83,7 @@ Maybe I need to specify the filesystem type? JFFS2 seems likely:
mount: mounting /dev/mtdblock6 on /mnt/ failed: Invalid argument
```

That last case was interesting, so I pulled up another SSH connection
and:
That last case was interesting, so I pulled up another SSH connection and:

```
# dmesg | tail -1


@@ 114,8 94,7 @@ with no valid JFFS2 nodes
[  855.220100] jffs2: empty_blocks 0, bad_blocks 3, c->nr_blocks 992
```

It appears that there's no JFFS2 filesystem on /dev/mtdblock6. That
makes sense, given:
It appears that there's no JFFS2 filesystem on /dev/mtdblock6. That makes sense, given:

```
# cat /proc/mtd | grep mtd6


@@ 135,8 114,7 @@ It would appear that `mtd6` has UBIFS on it, but:
mount: mounting /dev/mtdblock6 on /mnt/ failed: Invalid argument
```

At this point, I decided to dump `/dev/mtd6` to my laptop, and do some
forensics:
At this point, I decided to dump `/dev/mtd6` to my laptop, and do some forensics:

```
# dd if=/dev/mtd6 | ssh ecs@192.168.1.10 dd of=/tmp/mtd6


@@ 163,8 141,7 @@ What?!?!
brw-------    1 root     root       31,   6 Jan  1  1970 /dev/mtdblock6
```

By this point, I've installed `ubi_reader` on my laptop and I go back
there to do some diagnostics:
By this point, I've installed `ubi_reader` on my laptop and I go back there to do some diagnostics:

```
$ ubireader_list_files mtd6


@@ 175,11 152,10 @@ $ less mtd6
## <FF><FF><FF><FF><FF>...
```

Interesting. It appeared that OpenWRT did not successfully flash, but it
failed consistently. (By this time, I had re-flashed OpenWRT quite a few
times.)
Interesting. It appeared that OpenWRT did not successfully flash, but it failed consistently. (By this time, I had re-flashed OpenWRT quite a few times.)

At this point, it was 5:00 in the morning, so I decided to go to sleep.

Update: I returned that router and bought another one. Same problem. I
decided to give up.
Update: I returned that router and bought another one. Same problem. I decided to give up.

[1]: OpenWRT has a `sysupgrade` command which takes a firmware image and installs it. The install procedure for MikroTik RouterBOARDs is to run OpenWRT from RAM using TFTS, then use `sysupgrade` to flash a firmware upgrade.

A posts/2020-01-13-PowerBook-G4-OpenBSD.gmi.w => posts/2020-01-13-PowerBook-G4-OpenBSD.gmi.w +65 -0
@@ 0,0 1,65 @@
# Installing OpenBSD on a PowerBook G4

TL;DR:

* Download the OpenBSD install ISO
* If you have a CD and your CD drive works, do that
* If that doesn't work, flash the ISO onto a USB, and try booting to it
* If that doesn't work, flash the ISO onto the hard drive
* Install OpenBSD normally. Make sure to put / on the first partition.

### My ISO flashing procedure
* Remove the battery
* Unscrew all 18 screws
* Take off the memory bay
* Remove the RAM stick closest to the battery
* Unplug the ribbon cable beneath that RAM stick
* Unscrew both Torx T8 screws inside the memory bay
* Carefully remove the keyboard/plamrest/touchpad
* Unplug the ribbon cable going over the hard drive
* Unplug the ribbon cable coming from the hard drive
* Remove the ribbon cable from the hard drive
* The hard drive is a 2.5" IDE drive. Connect it to your computer and flash the ISO:

```
dd if=openbsd.iso of=/dev/sdb
```

* Follow these steps in reverse order to put everything back together.
* Boot to OpenFirmware, and run:

```
boot hd:,\ofwboot /6.6/macppc/bsd.rd
```

I recently found an old computer lying around in my house -- an old 17" PowerBook G4 (A1085, or 5,5). It had stopped working years ago, but with a new charger, I got it to boot to OS X. However, I wanted to run an open-source OS on it.

32-bit PowerPC is old enough that practically no-one still supports it. I ended up choosing OpenBSD, because I managed to boot it before I managed to boot NetBSD or FreeBSD, and I didn't want to have to deal with Gentoo's compilation hell on a computer that old.

I downloaded the OpenBSD install ISO, and got to work. The first challenge was booting to it. The CD drive wasn't working, so I couldn't boot from there, and for whatever reason, OpenFirmware would hang whenever I tried to boot from a USB.

So I booted to MacOS (which could read the USB) and used it to copy the contents of the ISO onto the HFS partition.

It worked... almost. I realized my mistake as soon as I had repartitioned the hard drive. The sets (gzipped tarballs that are untarred over the new root) were on the now-destroyed HFS partition. In addition, I had completely messed up `/dev` earlier (by running `sh /dev/MAKEDEV all` and running out of space on the ramdisk), so I wasn't able to copy over sets from my USB. I ended up accidentally killing my shell without a `/dev/console`, leading to a wedged system.

However, I still had the OpenBSD kernel running, so I was hoping that maybe, just maybe, if I hot-swapped the hard drive...

So I pulled out a stick of RAM, unplugged the internal keyboard, and it must have touched some circuit because there was a flash and magic smoke was leaking out of the logic board. I quickly unplugged it and removed the battery (thank God for easily removable batteries!).

Note to future self: DO NOT try to take apart a computer while it is running!

When I turned it on, it no longer chimed on boot, but was otherwise unharmed.

However, I still had a bit of a problem. I needed to boot to OpenBSD, but I had no way of booting a CD or USB, and the hard drive was wiped.

Thankfully, a quick `dd if=openbsd.iso of=/dev/sdb` after plugging the hard drive into my computer worked, and the installer booted just fine from there:

```
boot hd:,\ofwboot /6.6/macppc/bsd.rd
```

This time, I made sure that I could access the sets before I partitioned the hard drive. After putting the ISO in the first partition of an MBR, I was able to mount `/dev/sd0i`. Success! Now I just had to finish the installer, and reboot.

Not so fast. The install didn't work, and I wasn't able to debug because I wasn't able to mount the OpenBSD partition on Linux. So I re-installed the installer (by this point, I was quite proficient at taking the PowerBook apart), reinstalled OpenBSD, and got a shell after the install process, in order to see what was going on.

It went fine, but my custom partitioning had put the kernel in the second partition (with the first partition being swap), which apparently confused the bootloader. Once I fixed that, it worked!

D posts/2020-01-13-PowerBook-G4-OpenBSD.md.w => posts/2020-01-13-PowerBook-G4-OpenBSD.md.w +0 -87
@@ 1,87 0,0 @@
% title="Installing OpenBSD on a PowerBook G4"

TL;DR:

1. Download the OpenBSD install ISO
2. If you have a CD and your CD drive works, do that
3. If that doesn't work, flash the ISO onto a USB, and try booting to it
4. If that doesn't work, flash the ISO onto the hard drive
     - Remove the battery
     - Unscrew all 18 screws
     - Take off the memory bay
     - Remove the RAM stick closest to the battery
     - Unplug the ribbon cable beneath that RAM stick
     - Unscrew both Torx T8 screws inside the memory bay
     - Carefully remove the keyboard/plamrest/touchpad
     - Unplug the ribbon cable going over the hard drive
     - Unplug the ribbon cable coming from the hard drive
     - Remove the ribbon cable from the hard drive
     - The hard drive is a 2.5" IDE drive. Connect it to your computer
       and flash the ISO: `dd if=openbsd.iso of=/dev/sdb`
     - Follow these steps in reverse order to put everything back
       together.
     - Boot to OpenFirmware, and run
       `boot hd:,\ofwboot /6.6/macppc/bsd.rd`
5. Install OpenBSD normally. Make sure to put / on the first partition.

I recently found an old computer lying around in my house -- an old
17" PowerBook G4 (A1085, or 5,5). It had stopped working years ago, but
with a new charger, I got it to boot to OS X. However, I wanted to run an
open-source OS on it.

32-bit PowerPC is old enough that practically no-one still supports it.
I ended up choosing OpenBSD, because I managed to boot it before I
managed to boot NetBSD or FreeBSD, and I didn't want to have to deal
with Gentoo's compilation hell on a computer that old.

I downloaded the OpenBSD install ISO, and got to work. The first
challenge was booting to it. The CD drive wasn't working, so I couldn't
boot from there, and for whatever reason, OpenFirmware would hang
whenever I tried to boot from a USB.

So I booted to MacOS (which could read the USB) and used it to copy
the contents of the ISO onto the HFS partition.

It worked... almost. I realized my mistake as soon as I had
repartitioned the hard drive. The sets (gzipped tarballs that are
untarred over the new root) were on the now-destroyed HFS partition. In
addition, I had completely messed up `/dev` earlier (by running `sh
/dev/MAKEDEV all` and running out of space on the ramdisk), so I wasn't
able to copy over sets from my USB. I ended up accidentally killing my
shell without a `/dev/console`, leading to a wedged system.

However, I still had the OpenBSD kernel running, so I was hoping that
maybe, just maybe, if I hot-swapped the hard drive...

So I pulled out a stick of RAM, unplugged the internal keyboard, and it
must have touched some circuit because there was a flash and magic smoke
was leaking out of the logic board. I quickly unplugged it and removed
the battery (thank God for easily removable batteries!).

Note to future self: DO NOT try to take apart a computer while it is
running!

When I turned it on, it no longer chimed on boot, but was otherwise
unharmed.

However, I still had a bit of a problem. I needed to boot to OpenBSD,
but I had no way of booting a CD or USB, and the hard drive was wiped.

Thankfully, a quick `dd if=openbsd.iso of=/dev/sdb` after plugging the
hard drive into my computer worked, and the installer booted just fine
from there: `boot hd:,\ofwboot /6.6/macppc/bsd.rd`.

This time, I made sure that I could access the sets before I
partitioned the hard drive. After putting the ISO in the first partition
of an MBR, I was able to mount `/dev/sd0i`. Success! Now I just had to
finish the installer, and reboot.

Not so fast. The install didn't work, and I wasn't able to debug because
I wasn't able to mount the OpenBSD partition on Linux. So I re-installed
the installer (by this point, I was quite proficient at taking the
PowerBook apart), reinstalled OpenBSD, and got a shell after the install
process, in order to see what was going on.

It went fine, but my custom partitioning had put the kernel in the
second partition (with the first partition being swap), which apparently
confused the bootloader. Once I fixed that, it worked!

A posts/2020-05-22-Selfhosting.gmi.w => posts/2020-05-22-Selfhosting.gmi.w +44 -0
@@ 0,0 1,44 @@
# Self-hosting my life

Over the last few months, I've grown to despise the way most people now interact with computers. A typical person:

* Uses Gmail for their email. Some folks may still some other megacorp email, but rarely will folks even use their own domain name.
* Uses Google to search the web. Because we all know that Google can be trusted:

=> https://amp.dev
=> https://en.wikipedia.org/wiki/Dragonfly_(search_engine)
=> https://support.google.com/youtube/thread/19190975
=> https://en.wikipedia.org/wiki/Censorship_by_Google

* Uses Discord, Skype, Zoom, and other proprietary chat programs.
* Uses Spotify (or a competitor) for music.
* Uses Netflix to watch movies and TV shows.
* Uses Facebook, Twitter, or some competitor.
* Uses Windows or macOS. Some people don't even have non-mobile devices.

You get the idea.

Each service we use that's operated by someone other than ourselves is another point of failure in our lives. Each of these corporations is handling staggeringly large amounts of personal data. When (not if) these companies go belly-up, they inevitably will take peoples' online lives with them. I think it's clear to see that these are horrible companies, to be avoided at all cost. But what's the alternative?

Enter FOSS[0]. Over the past 30ish years, millions of man-hours have been put into creating an ecosystem of open-source[1] tools. It's feasible to run a daily-driver computer with 100% free software. But why stop there? There are open-source self-hostable solutions for practically all of these.

A quick overview, only including what I was able to set up within 20
days:

* Postfix/Dovecot for email, as well as alps webmail
* The Lounge for some the non-techies, and a normal IRC bouncer for myself
* I'm actually quite happy with a combination of mpd and mpv, though I'm considering setting up FunkWhale
* A Pleroma instance
* Finally, this blog

=> https://git.sr.ht/~emersion/alps alps webmail

Surprisingly enough, the last one took the most time, since I opted to use wersh instead of a more "normal" framework. I'm rather proud of where I ended up, check out the code if you're curious!

=> https://git.sr.ht/~ecs/wersh wersh
=> https://git.sr.ht/~ecs/ecs.d2evs.net This site's source code

While setting these up took some time, it's a fixed time-sink, with a low maintenance cost. If you'd like to migrate off of any of these services but don't have the technical experience, send me an email at ecs@d2evs.net!

[0]: Free and Open-Source Software. Loosely, this is where programmers make the secret sauce that powers their programs tick publicly available. Even better, they take contributions!
[1]: I use the terms "FOSS", "free software", and "open-source" interchangeably. Some people don't, and that's a topic for another day.

D posts/2020-05-22-Selfhosting.md.w => posts/2020-05-22-Selfhosting.md.w +0 -60
@@ 1,60 0,0 @@
% title="Self-hosting my life"

Over the last few months, I've grown to despise the way most people now
interact with computers. A typical person:

- Uses Gmail for their email. Some folks may still some other megacorp
  email, but rarely will folks even use their own domain name.
- Uses Google to search the web. Because we [all](amp) [know][dragonfly]
  [that][youtube-censorship] [Google][general-censorship] can be
  trusted.
- Uses Discord, Skype, Zoom, and other proprietary chat programs.
- Uses Spotify (or a competitor) for music.
- Uses Netflix to watch movies and TV shows.
- Uses Facebook, Twitter, or some competitor.
- Uses Windows or macOS. Some people don't even have non-mobile devices.

[amp]: https://amp.dev
[dragonfly]: https://en.wikipedia.org/wiki/Dragonfly_(search_engine)
[youtube-censorship]: https://support.google.com/youtube/thread/19190975?hl=en
[general-censorship]: https://en.wikipedia.org/wiki/Censorship_by_Google

You get the idea.

Each service we use that's operated by someone other than ourselves is
another point of failure in our lives. Each of these corporations is
handling staggeringly large amounts of personal data. When (not if)
these companies go belly-up, they inevitably will take peoples' online
lives with them. I think it's clear to see that these are horrible
companies, to be avoided at all cost. But what's the alternative?

Enter FOSS[^1]. Over the past 30ish years, millions of man-hours have
been put into creating an ecosystem of open-source[^2] tools. It's
feasible to run a daily-driver computer with 100% free software. But
why stop there? There are open-source self-hostable solutions for
practically all of these.

[^1]: Free and Open-Source Software. Loosely, this is where programmers make the secret sauce that powers their programs tick publicly available. Even better, they take contributions!

[^2]: I use the terms "FOSS", "free software", and "open-source" interchangeably. Some people don't, and that's a topic for another day.

A quick overview, only including what I was able to set up within 20
days:

- Postfix/Dovecot for email, as well as [alps
  webmail](https://git.sr.ht/~emersion/alps)
- The Lounge for some the non-techies, and a normal IRC bouncer for myself
- I'm actually quite happy with a combination of mpd and mpv, though
  I'm considering setting up FunkWhale
- A Pleroma instance
- Finally, this blog

Surprisingly enough, the last one took the most time, since I opted to
use [wersh](https://git.sr.ht/~ecs/wersh) instead of a more "normal"
framework. I'm rather proud of where I ended up, check out [the
code](https://git.sr.ht/~ecs/ecs.d2evs.net) if you're curious!

While setting these up took some time, it's a fixed time-sink, with a
low maintenance cost. If you'd like to migrate off of any of these
services but don't have the technical experience, [send me an
email](mailto:ecs@d2evs.net)!

A title.awk => title.awk +15 -0
@@ 0,0 1,15 @@
#!/usr/bin/awk -f
BEGIN { mode="normal" }
{
	if (mode == "normal") {
		if ($0 ~ /^#[^#]/) {
			sub(/^#[ \t]*/, "");
			print $0;
			exit 0;
		} else if ($0 ~ /^```/) {
			mode="preformatted";
		}
	} else if (mode == "preformatted" && $0 ~ /^```/) {
		mode="normal";
	}
}

A tohtml.awk => tohtml.awk +63 -0
@@ 0,0 1,63 @@
#!/usr/bin/awk -f
BEGIN { preformatted="off"; list="off"; title=""; }
{
	gsub(/&/, "\\&amp;");
	gsub(/</, "\\&lt;");
	gsub(/>/, "\\&gt;");
	gsub(/^=&gt;/, "=>");
	gsub(/"/, "\\&quot;");
	gsub(/'/, "\\&#39;");
	if (preformatted == "off") {
		if ($0 !~ /^\*/) {
			if (list == "on") {
				print "</ul>";
			}
			list="off";
		}
		if ($0 ~ /^\*/) {
			sub(/^\*[ \t]*/, "");
			if (list == "off") {
				print "<ul>";
			}
			print "<li>" $0 "</li>";
			list="on";
		} else if ($0 ~ /^###/) {
			sub(/^###[ \t]*/, "");
			print "<h3>" $0 "</h3>";
		} else if ($0 ~ /^##/) {
			sub(/^##[ \t]*/, "");
			print "<h2>" $0 "</h2>";
		} else if ($0 ~ /^#/) {
			sub(/^#[ \t]*/, "");
			if (title == "") {
				title = $0;
				print "<title>" $0 "</title>"
			}
			print "<h1>" $0 "</h1>";
		} else if ($0 ~ /^>/) {
			sub(/^>[ \t]*/, "");
			print "<q>" $0 "</q>";
		} else if ($0 ~ /^```/) {
			# TODO: alt text?
			preformatted="on";
			print "<pre>";
		} else if ($0 ~ /^=>/) {
			sub(/^=>[ \t]*/, "");
			printf "<a href=" $1 ">";
			sub(/^[^ \t]*[ \t*]/, "");
			print $0 "</a>";
		} else {
			print $0;
		}
		if (list == "off") {
			print "<br>";
		}
	} else {
		if ($0 ~ /^```/) {
			preformatted="off";
			print "</pre>";
		} else {
			print $0;
		}
	}
}