diff options
Diffstat (limited to '')
75 files changed, 8232 insertions, 0 deletions
diff --git a/www/causal.agency/.gitignore b/www/causal.agency/.gitignore new file mode 100644 index 00000000..b00b1c3c --- /dev/null +++ b/www/causal.agency/.gitignore @@ -0,0 +1,4 @@ +index.html +leveler.html +scheme.css +scheme.png diff --git a/www/causal.agency/Makefile b/www/causal.agency/Makefile new file mode 100644 index 00000000..8c74f8f1 --- /dev/null +++ b/www/causal.agency/Makefile @@ -0,0 +1,23 @@ +WEBROOT = /var/www/causal.agency + +GEN = index.html scheme.css scheme.png +FILES = ${GEN} style.css alpha.html lands.html + +all: ${FILES} + +.SUFFIXES: .7 .html + +.7.html: + mandoc -T html -O style=style.css $< > $@ + +scheme.css: + scheme -st > scheme.css + +scheme.png: + scheme -g > scheme.png + +install: ${FILES} + install -C -m 644 ${FILES} ${WEBROOT} + +clean: + rm -f ${GEN} diff --git a/www/causal.agency/alpha.html b/www/causal.agency/alpha.html new file mode 100644 index 00000000..0d83f530 --- /dev/null +++ b/www/causal.agency/alpha.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>all 26 letters of the alphabet RANKED</title> +<style> +body, button { font-size: 200%; text-align: center; } +button { margin: 1em; padding: 1ch; } +button#shuffle { font-size: 100%; } +</style> + +which letter do you like more? +<p> +<button id="a">A</button> +<button id="b">B</button> +<p> +<details> +<summary>current ranking</summary> +<p> +<span id="ranking">ABCDEFGHIJKLMNOPQRSTUVWXYZ</span> +<p> +<button id="shuffle">reshuffle</button> +</details> + +<script> +let buttonA = document.getElementById("a"); +let buttonB = document.getElementById("b"); +let ranking = document.getElementById("ranking"); + +let alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); +let rand = (bound) => Math.floor(Math.random() * bound); +function shuffle() { + for (let i = alpha.length - 1; i > 0; --i) { + let j = rand(i + 1); + let x = alpha[i]; + alpha[i] = alpha[j]; + alpha[j] = x; + } +} +if (localStorage.getItem("alpha")) { + alpha = localStorage.getItem("alpha").split(""); +} else { + shuffle(); +} + +let index = 0; +let even = true; +function choose(o) { + if (o == "b") { + let x = alpha[index]; + alpha[index] = alpha[index + 1]; + alpha[index + 1] = x; + } + index += 2; + if (index > alpha.length - 2) { + even = !even; + index = (even ? 0 : 1); + } + update(); +} + +document.onkeydown = function(event) { + if (event.key.toUpperCase() == alpha[index]) { + choose("a"); + } else if (event.key.toUpperCase() == alpha[index + 1]) { + choose("b"); + } +} + +function update() { + localStorage.setItem("alpha", alpha.join("")); + ranking.innerText = alpha.join(""); + let a = buttonA; + let b = buttonB; + if (rand(2)) { + a = buttonB; + b = buttonA; + } + let lc = (c) => c; + if (rand(2)) lc = (c) => c.toLowerCase(); + a.innerText = lc(alpha[index]); + b.innerText = lc(alpha[index + 1]); + a.onclick = () => choose("a"); + b.onclick = () => choose("b"); +} +update(); + +document.getElementById("shuffle").onclick = function() { + if (confirm("Are you SURE you want to throw away all your hard work?")) { + shuffle(); + update(); + } +} +</script> diff --git a/www/causal.agency/index.7 b/www/causal.agency/index.7 new file mode 100644 index 00000000..d29c1a6b --- /dev/null +++ b/www/causal.agency/index.7 @@ -0,0 +1,74 @@ +.Dd November 28, 2023 +.Dt CAUSAL.AGENCY 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm june +.Nd computer enthusiast (she/her) +. +.Sh SYNOPSIS +.Nm mail +.Mt june@causal.agency +.Nm +in +.Li #ascii.town +on tilde.chat +. +.Sh DESCRIPTION +I make mostly IRC software in C. +I like +.Ox +but also the GPL. +I just want to read books +and try to learn to be kinder. +When I can I'd like to talk to strangers +and experience more magic. +. +.Pp +.Lk https://git.causal.agency code +\(em +.Lk https://text.causal.agency words +\(em +.Lk /list/ mailist +. +.Pp +These are some things I've done: +.Bl -tag -width Ds +.It Lk https://git.causal.agency/pounce/about pounce +a multi-client-first IRC bouncer +.It Lk https://git.causal.agency/catgirl/about catgirl +a cosy IRC client +.It Lk https://git.causal.agency/litterbox/about litterbox +a full-text search IRC logger +.It Lk https://git.causal.agency/scooper/about scooper +a web interface for litterbox +.It Lk https://git.causal.agency/kitd/about kitd +a process supervisor +.It Lk https://git.causal.agency/imbox/about "imbox & git-fetch-email" +a tool to pull patches out of IMAP +.It Lk https://git.causal.agency/bubger/about bubger +a mailing list archive generator for IMAP +.It Lk https://git.causal.agency/notemap/about notemap +a tool to mirror text files to IMAP notes +.It Lk https://ascii.town/explore.html torus@ascii.town +a collaborative ASCII art project +.It Lk ssh://play@ascii.town play@ascii.town +some games to play over +.Xr ssh 1 +.It Lk https://git.causal.agency/cards/about cards +a +.Pa CARDS.DLL +loader for SDL +.It Lk scheme.png scheme +an earthy terminal colour scheme +.El +. +.Sh SEE ALSO +.Bl -bullet +.It +.Lk /bin/ bin +.It +.Lk lands.html "Magic lands quiz" +.It +.Lk alpha.html "alphabet ranking game" +.El diff --git a/www/causal.agency/lands.html b/www/causal.agency/lands.html new file mode 100644 index 00000000..7aaadd80 --- /dev/null +++ b/www/causal.agency/lands.html @@ -0,0 +1,176 @@ +<!DOCTYPE html> +<title>Lands Quiz</title> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<style> +html { font: 14pt sans-serif; line-height: 1.5em; } +body { padding: 1em 1ch; max-width: 78ch; margin: auto; } +h1 { text-align: center; } +h2 { margin-top: 0; } +button { font-size: 100%; padding: 0.5em 1ch; } +img { max-width: 100%; } +div.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 2ch; } +</style> + +<h1 id="loading">Loading...</h1> +<h1 id="error" hidden>Failed to load cards :(</h1> + +<div id="game" hidden> +<h1>Magic Lands Quiz</h1> +<p>Try to guess the colours of mana each land produces!</p> +<div class="cols"> + <div> + <img id="back" src="https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg"> + <a id="link" target="_blank"> + <img id="image1" hidden> + <img id="image2" hidden> + </a> + </div> + <div> + <h2 id="name"></h2> + <input type="checkbox" id="w"> <label for="w">White</label><br> + <input type="checkbox" id="u"> <label for="u">Blue</label><br> + <input type="checkbox" id="b"> <label for="b">Black</label><br> + <input type="checkbox" id="r"> <label for="r">Red</label><br> + <input type="checkbox" id="g"> <label for="g">Green</label><br> + <p><button id="submit">Submit</button></p> + <h3>Score: <span id="score">0</span>/<span id="total">0</span></h3> + </div> +</div> +</div> + +<script> +function shuffle(arr) { + let rand = (bound) => Math.floor(Math.random() * bound); + for (let i = arr.length-1; i > 0; --i) { + let j = rand(i+1); + let x = arr[i]; + arr[i] = arr[j]; + arr[j] = x; + } +} + +const CardBack = +"https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg"; + +function hideCard() { + document.getElementById("back").hidden = false; + document.getElementById("image1").hidden = true; + document.getElementById("image2").hidden = true; +} + +function showCard(card) { + document.getElementById("back").hidden = true; + document.getElementById("link").href = card.scryfall_uri; + let image1 = document.getElementById("image1"); + let image2 = document.getElementById("image2"); + if (card.card_faces) { + image1.src = card.card_faces[0].image_uris.normal; + image2.src = card.card_faces[1].image_uris.normal; + image1.hidden = false; + image2.hidden = false; + } else { + image1.src = card.image_uris.normal; + image1.hidden = false; + } +} + +function resetChecks() { + for (let c of "wubrg") { + let input = document.getElementById(c); + input.checked = false; + input.disabled = false; + input.labels[0].style.fontWeight = "normal"; + } +} + +function checkChecks(card) { + let score = 0; + let total = 0; + let checked = 0; + for (let c of "wubrg") { + let input = document.getElementById(c); + let produced = card.produced_mana.includes(c.toUpperCase()); + if (produced) { + total++; + input.labels[0].style.fontWeight = "bold"; + if (input.checked) score++; + } + if (input.checked) checked++; + input.disabled = true; + } + if (checked > total) score -= (checked - total); + if (score < 0) score = 0; + return { score: score, total: total }; +} + +document.onkeydown = function(event) { + for (let c of "wubrg") { + if (event.key == c) { + let input = document.getElementById(c); + if (!input.disabled) input.checked ^= true; + } + } + if (event.key == "Enter") { + document.getElementById("submit").click(); + } +} + +let score = 0; +let total = 0; +let cards = []; +let card = null; + +function nextCard() { + hideCard(); + resetChecks(); + card = cards.shift(); + document.getElementById("name").innerText = card.name; +} + +document.getElementById("submit").onclick = function() { + if (card) { + let { score: cardScore, total: cardTotal } = checkChecks(card); + total += cardTotal; + score += cardScore; + document.getElementById("score").innerText = score; + document.getElementById("total").innerText = total; + showCard(card); + card = null; + if (cards.length) { + this.innerText = "Next card"; + } else { + this.disabled = true; + this.innerText = "No more cards"; + } + } else { + nextCard(); + this.innerText = "Submit"; + } +} + +function loadCards(resp) { + let loading = document.getElementById("loading"); + let error = document.getElementById("error"); + let game = document.getElementById("game"); + if (resp.status != 200) { + loading.hidden = true; + error.hidden = false; + } + resp.json().then((json) => { + cards.push(...json.data); + if (json.has_more) { + setTimeout(() => fetch(json.next_page).then(loadCards), 50); + } else { + loading.hidden = true; + game.hidden = false; + shuffle(cards); + nextCard(); + } + }); +} + +const Search = +"https://api.scryfall.com/cards/search?q=t:land+id>=2+produces>=2+produces!=wubrg"; +fetch(Search).then(loadCards); + +</script> diff --git a/www/causal.agency/style.css b/www/causal.agency/style.css new file mode 100644 index 00000000..ee218533 --- /dev/null +++ b/www/causal.agency/style.css @@ -0,0 +1,28 @@ +@import url("scheme.css"); + +table.head, table.foot { width: 100%; } +td.head-rtitle, td.foot-os { text-align: right; } +td.head-vol { text-align: center; } +div.Pp { margin: 1ex 0ex; } +div.Nd, div.Bf, div.Op { display: inline; } +span.Pa, span.Ad { font-style: italic; } +span.Ms { font-weight: bold; } +dl.Bl-diag > dt { font-weight: bold; } +code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn, +code.Cd { font-weight: bold; font-family: inherit; } + +div.head, div.foot { display: flex; justify-content: space-between; } +.head-ltitle, .foot-left { flex: 1; } +.head-vol, .foot-date { flex: 0 1 auto; text-align: center; } +.head-rtitle, .foot-os { flex: 1; text-align: right; } + +html { font-family: monospace; line-height: 1.25em; } +body { max-width: 80ch; margin: 1em auto; padding: 0 1ch; } +table { border-collapse: collapse; } +table.Nm code.Nm { padding-right: 1ch; } +table.foot { margin-top: 1em; } + +html { background-color: var(--ansi16); color: var(--ansi17); } +a { color: var(--ansi4); } +a:visited { color: var(--ansi5); } +a.permalink { color: var(--ansi3); text-decoration: none; } diff --git a/www/git.causal.agency/.gitignore b/www/git.causal.agency/.gitignore new file mode 100644 index 00000000..eaed8039 --- /dev/null +++ b/www/git.causal.agency/.gitignore @@ -0,0 +1,13 @@ +*.html +about-filter +compress +ctags +email-filter +filter +gzip +hilex +htagml +mandoc +mtags +owner-filter +source-filter diff --git a/www/git.causal.agency/Makefile b/www/git.causal.agency/Makefile new file mode 100644 index 00000000..86b9f3eb --- /dev/null +++ b/www/git.causal.agency/Makefile @@ -0,0 +1,53 @@ +PREFIX = /var/www +CONFDIR = ${PREFIX}/conf +DATADIR = ${PREFIX}/cgit +BINDIR = ${PREFIX}/bin +WEBROOT = ${PREIFX}/git.causal.agency + +CFLAGS += -Wall -Wextra +LDFLAGS = -static -pie + +BINS += about-filter +BINS += ctags +BINS += email-filter +BINS += gzip +BINS += hilex +BINS += htagml +BINS += mandoc +BINS += mtags +BINS += owner-filter +BINS += source-filter + +HTMLS = index.html + +all: ${BINS} ${HTMLS} + +compress ctags mandoc: + ${MAKE} -C /usr/src/usr.bin/$@ LDFLAGS='${LDFLAGS}' + mv /usr/src/usr.bin/$@/$@ $@ + ${MAKE} -C /usr/src/usr.bin/$@ clean + +gzip: compress + ln -f compress $@ + +hilex htagml mtags: + rm -f ../../bin/$@ + ${MAKE} -C ../../bin $@ LDFLAGS='${LDFLAGS}' + mv ../../bin/$@ $@ + +about-filter email-filter owner-filter source-filter: filter + ln -f filter $@ + +index.html: index.7 + mandoc -Thtml -Ostyle=https://causal.agency/style.css index.7 >index.html + +install: cgitrc custom.css ${BINS} + install -m 644 cgitrc ${CONFDIR} + install -m 644 custom.css ${DATADIR} + install -d -o www -g daemon ${PREFIX}/cache/cgit + install -d -m 1700 -o www -g daemon ${PREFIX}/tmp + install -s ${BINS} ${BINDIR} + install -m 644 ${HTMLS} ${WEBROOT} + +clean: + rm -f compress filter ${BINS} ${HTMLS} diff --git a/www/git.causal.agency/cgitrc b/www/git.causal.agency/cgitrc new file mode 100644 index 00000000..0666fd28 --- /dev/null +++ b/www/git.causal.agency/cgitrc @@ -0,0 +1,30 @@ +root-title=causal agency +root-desc=“I think some people from the Gentoo project are behind this.” +logo= + +clone-url=https://$HTTP_HOST/$CGIT_REPO_URL +snapshots=tar.gz zip + +enable-blame=1 +enable-commit-graph=1 +enable-subject-links=1 +enable-follow-links=1 +enable-index-owner=0 +repository-sort=age +branch-sort=age + +css=/custom.css +about-filter=/bin/about-filter +source-filter=/bin/source-filter +#owner-filter=/bin/owner-filter +email-filter=/bin/email-filter + +readme=:README.7 +readme=:README + +remove-suffix=1 +enable-git-config=1 +scan-path=/git.causal.agency + +cache-root=/cache/cgit +cache-size=1024 diff --git a/www/git.causal.agency/custom.css b/www/git.causal.agency/custom.css new file mode 100644 index 00000000..b3f4f425 --- /dev/null +++ b/www/git.causal.agency/custom.css @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +@import url("cgit.css"); + +* { line-height: 1.25em; } + +div#cgit { + max-width: 117ch; + margin: auto; + font-family: monospace; + -moz-tab-size: 4; + tab-size: 4; +} + +div#cgit table#header td.sub { + border-top: none; +} +div#cgit table#header td.sub.right { + padding-right: 1em; +} +div#cgit table.tabs { + border-bottom: none; +} +div#cgit div.content { + border-bottom: none; +} +div#cgit table.list th a { + color: inherit; +} +div#cgit table.list tr:nth-child(even) { + background: inherit; +} +div#cgit table.list tr:hover { + background: inherit; +} +div#cgit table.list tr.nohover-highlight:hover:nth-child(even) { + background: inherit; +} + +div#cgit table.blob td.linenumbers a:target { + color: goldenrod; + text-decoration: underline; + outline: none; +} + +div#cgit div#summary { + max-width: 80ch; +} + +/* for hilex(1) */ +div#cgit pre .Ke { color: dimgray; } +div#cgit pre .Ma { color: green; } +div#cgit pre .Co { color: navy; } +div#cgit pre .St { color: teal; } +div#cgit pre .Fo { color: teal; font-weight: bold; } +div#cgit pre .Su { color: olive; } + +/* for htagml(1) */ +div#cgit pre a.tag { color: inherit; text-decoration: underline; } +div#cgit pre a.tag:target { color: goldenrod; outline: none; } + +/* for mandoc(1) */ +table.head, table.foot { width: 100%; } +td.head-rtitle, td.foot-os { text-align: right; } +td.head-vol { text-align: center; } +div.Pp { margin: 1ex 0ex; } +div.Nd, div.Bf, div.Op { display: inline; } +span.Pa, span.Ad { font-style: italic; } +span.Ms { font-weight: bold; } +dl.Bl-diag > dt { font-weight: bold; } +code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn, +code.Cd { font-weight: bold; font-family: inherit; } + +h1.Sh { font-size: 1.5em; } +table.Nm td:first-child { padding-right: 1ch; } +code.Fl { white-space: nowrap; } +span.RsT { font-style: italic; } +dl.Bl-tag:not(.Bl-compact) > dt { margin-top: 1em; } +ul.Bl-bullet:not(.Bl-compact) > li { margin-top: 1em; } +div.Bd-indent { margin-left: 4ch; } +table.Bl-column { width: 100%; } +table.foot { margin-top: 1em; } + +div#cgit a.permalink { color: inherit; } diff --git a/www/git.causal.agency/filter.c b/www/git.causal.agency/filter.c new file mode 100644 index 00000000..7c7e9320 --- /dev/null +++ b/www/git.causal.agency/filter.c @@ -0,0 +1,158 @@ +#include <err.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +#define Q(...) #__VA_ARGS__ + +#define MANDOC_OPTIONS "fragment,man=%N.%S,includes=../tree/%I" + +static int about(int argc, char *argv[]) { + if (argc < 2) return 1; + if (!fnmatch("README.[1-9]", argv[1], 0)) { + execlp("mandoc", "mandoc", "-T", "html", "-O", MANDOC_OPTIONS, NULL); + err(127, "mandoc"); + } else if (!fnmatch("*.[1-9]", argv[1], 0)) { + execlp( + "mandoc", "mandoc", "-T", "html", "-O", "toc," MANDOC_OPTIONS, NULL + ); + err(127, "mandoc"); + } else { + execlp("hilex", "hilex", "-l", "text", "-f", "html", "-o", "pre", NULL); + err(127, "hilex"); + } +} + +static int email(void) { + size_t cap = 0; + char *buf = NULL; + if (getline(&buf, &cap, stdin) < 0) err(1, "getline"); + if (buf[0] == 'C' && !strncmp(&buf[strcspn(buf, " ")], " McEnroe", 8)) { + printf("June%s", &buf[strcspn(buf, " ")]); + } else { + printf("%s", buf); + } + return 0; +} + +static int owner(void) { + printf(Q(<a href="https://liberapay.com/june/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>)); + return 0; +} + +#define CTAGS_PATTERN "*.[chlmy]" +#define TEMPLATE "/tmp/filter.XXXXXXXXXX" + +static char tmp[PATH_MAX]; +static char tags[] = TEMPLATE; +static void cleanup(void) { + unlink(tmp); + unlink(tags); +} + +static int source(int argc, char *argv[]) { + if (argc < 2) return 1; + if ( + strcmp("Makefile", argv[1]) && + strcmp(".profile", argv[1]) && + strcmp(".shrc", argv[1]) && + fnmatch(CTAGS_PATTERN, argv[1], 0) && + fnmatch("*.mk", argv[1], 0) && + fnmatch("*.[1-9]", argv[1], 0) && + fnmatch("*.sh", argv[1], 0) + ) { + execlp("hilex", "hilex", "-t", "-n", argv[1], "-f", "html", NULL); + err(127, "hilex"); + } + + const char *ext = strrchr(argv[1], '.'); + if (!strcmp(argv[1], ".profile") || !strcmp(argv[1], ".shrc")) { + ext = ".sh"; + } else if (!strcmp(argv[1], "Makefile")) { + ext = ".mk"; + } else if (!ext) { + ext = ""; + } + + snprintf(tmp, sizeof(tmp), TEMPLATE "%s", ext); + int fd = mkstemps(tmp, strlen(ext)); + if (fd < 0) err(1, "%s", tmp); + atexit(cleanup); + + char buf[4096]; + for (ssize_t len; 0 < (len = read(STDIN_FILENO, buf, sizeof(buf)));) { + if (write(fd, buf, len) < 0) err(1, "%s", tmp); + } + if (close(fd) < 0) err(1, "%s", tmp); + + fd = mkstemp(tags); + if (fd < 0) err(1, "%s", tags); + close(fd); + pid_t pid = fork(); + if (pid < 0) err(1, "fork"); + if (!pid) { + if (!fnmatch(CTAGS_PATTERN, argv[1], 0)) { + execlp("ctags", "ctags", "-w", "-f", tags, tmp, NULL); + warn("ctags"); + } else { + execlp("mtags", "mtags", "-f", tags, tmp, NULL); + warn("mtags"); + } + _exit(127); + } + int status; + if (wait(&status) < 0) err(1, "wait"); + + int rw[2]; + if (pipe(rw) < 0) err(1, "pipe"); + pid = fork(); + if (pid < 0) err(1, "fork"); + if (!pid) { + dup2(rw[1], STDOUT_FILENO); + close(rw[0]); + close(rw[1]); + execlp("hilex", "hilex", "-f", "html", tmp, NULL); + warn("hilex"); + _exit(127); + } + pid = fork(); + if (pid < 0) err(1, "fork"); + if (!pid) { + dup2(rw[0], STDIN_FILENO); + close(rw[0]); + close(rw[1]); + execlp("htagml", "htagml", "-im", "-f", tags, tmp, NULL); + warn("htagml"); + _exit(127); + } + close(rw[0]); + close(rw[1]); + + if (wait(&status) < 0) err(1, "wait"); + if (wait(&status) < 0) err(1, "wait"); + return status; +} + +int main(int argc, char *argv[]) { +#ifdef __OpenBSD__ + int error; + switch (getprogname()[0]) { + break; case 'a': error = pledge("stdio exec", NULL); + break; case 's': error = pledge("stdio tmppath proc exec", NULL); + break; default: error = pledge("stdio", NULL); + } + if (error) err(1, "pledge"); +#endif + switch (getprogname()[0]) { + case 'a': return about(argc, argv); + case 'e': return email(); + case 'o': return owner(); + case 's': return source(argc, argv); + default: return 1; + } +} diff --git a/www/git.causal.agency/index.7 b/www/git.causal.agency/index.7 new file mode 100644 index 00000000..58a40dfe --- /dev/null +++ b/www/git.causal.agency/index.7 @@ -0,0 +1,81 @@ +.Dd January 12, 2024 +.Dt GIT.CAUSAL.AGENCY 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm causal agency +.Nd \(dqI think some people from the Gentoo project are behind this.\(dq +. +.Sh DESCRIPTION +basically cgit (awful software) +getting hammered by web crawlers +keeps making my machine crash. +this static page will be here +until I can find a better solution. +clone urls and tarball urls are still functional. +. +.Bl -tag +.It src \(em dontfiles +.Dl git clone https://git.causal.agency/src +.It ascii.town +.Bl -tag +.It torus \(em collaborative ASCII art +.Dl git clone https://git.causal.agency/torus +.It play \(em some games for SSH +.Dl git clone https://git.causal.agency/play +.El +.It email +.Bl -tag +.It imbox \(em IMAP to mbox +.Dl git clone https://git.causal.agency/imbox +.It bubger \(em IMAP archive generator +.Dl git clone https://git.causal.agency/bubger +.It notemap \(em notemap +.Dl git clone https://git.causal.agency/notemap +.El +.It forks +.Bl -tag +.It shulker \(em Discord to vanilla Minecraft bridge +.Dl git clone https://git.causal.agency/shulker +.It cgit-pink \(em web frontend for git +.Dl git clone https://git.causal.agency/cgit-pink +.It dash \(em patched shell with cmake build +.Dl git clone https://git.causal.agency/dash +.El +.It games +.Bl -tag +.It wep \(em Windows Entertainment Pack recreations +.Dl git clone https://git.causal.agency/wep +.It cards \(em CARDS.DLL loader for SDL +.Dl git clone https://git.causal.agency/cards +.El +.It irc +.Bl -tag +.It scooper \(em web interface for litterbox +.Dl git clone https://git.causal.agency/scooper +.It litterbox \(em IRC logger +.Dl git clone https://git.causal.agency/litterbox +.It pounce \(em IRC bouncer +.Dl git clone https://git.causal.agency/pounce +.It catgirl \(em IRC client +.Dl git clone https://git.causal.agency/catgirl +.El +.It ports +.Bl -tag +.It jorts \(em my own ports tree for macOS +.Dl git clone https://git.causal.agency/jorts +.It exman \(em manuals for other systems +.Dl git clone https://git.causal.agency/exman +.It libretls \(em libtls for OpenSSL +.Dl git clone https://git.causal.agency/libretls +.It ports \(em Fx and Ox ports for this software +.Dl git clone https://git.causal.agency/ports +.El +.It system +.Bl -tag +.It kitd \(em process supervisor for OpenBSD +.Dl git clone https://git.causal.agency/kitd +.It catsit \(em (deprecated) process supervisor +.Dl git clone https://git.causal.agency/catsit +.El +.El diff --git a/www/photo.causal.agency/.gitignore b/www/photo.causal.agency/.gitignore new file mode 100644 index 00000000..a5f66a9d --- /dev/null +++ b/www/photo.causal.agency/.gitignore @@ -0,0 +1,2 @@ +static/ +*.JPG diff --git a/2024-04-10/IMG_0832.txt b/www/photo.causal.agency/2024-04-10/IMG_0832.txt index 65724024..65724024 100644 --- a/2024-04-10/IMG_0832.txt +++ b/www/photo.causal.agency/2024-04-10/IMG_0832.txt diff --git a/2024-04-10/IMG_0850.txt b/www/photo.causal.agency/2024-04-10/IMG_0850.txt index 4cbb3def..4cbb3def 100644 --- a/2024-04-10/IMG_0850.txt +++ b/www/photo.causal.agency/2024-04-10/IMG_0850.txt diff --git a/2024-04-10/IMG_0852.txt b/www/photo.causal.agency/2024-04-10/IMG_0852.txt index 707d7cd6..707d7cd6 100644 --- a/2024-04-10/IMG_0852.txt +++ b/www/photo.causal.agency/2024-04-10/IMG_0852.txt diff --git a/2024-04-10/IMG_0858.txt b/www/photo.causal.agency/2024-04-10/IMG_0858.txt index 42f243e4..42f243e4 100644 --- a/2024-04-10/IMG_0858.txt +++ b/www/photo.causal.agency/2024-04-10/IMG_0858.txt diff --git a/2024-04-10/IMG_0859.txt b/www/photo.causal.agency/2024-04-10/IMG_0859.txt index ca33d7e0..ca33d7e0 100644 --- a/2024-04-10/IMG_0859.txt +++ b/www/photo.causal.agency/2024-04-10/IMG_0859.txt diff --git a/2024-04-10/IMG_0865.txt b/www/photo.causal.agency/2024-04-10/IMG_0865.txt index 7a955fc2..7a955fc2 100644 --- a/2024-04-10/IMG_0865.txt +++ b/www/photo.causal.agency/2024-04-10/IMG_0865.txt diff --git a/2024-04-10/IMG_0890.txt b/www/photo.causal.agency/2024-04-10/IMG_0890.txt index 9d2cdc43..9d2cdc43 100644 --- a/2024-04-10/IMG_0890.txt +++ b/www/photo.causal.agency/2024-04-10/IMG_0890.txt diff --git a/2024-04-14/IMG_1054.txt b/www/photo.causal.agency/2024-04-14/IMG_1054.txt index f4803ee2..f4803ee2 100644 --- a/2024-04-14/IMG_1054.txt +++ b/www/photo.causal.agency/2024-04-14/IMG_1054.txt diff --git a/2024-04-14/IMG_1058.txt b/www/photo.causal.agency/2024-04-14/IMG_1058.txt index 21aeb189..21aeb189 100644 --- a/2024-04-14/IMG_1058.txt +++ b/www/photo.causal.agency/2024-04-14/IMG_1058.txt diff --git a/2024-04-14/IMG_1066.txt b/www/photo.causal.agency/2024-04-14/IMG_1066.txt index 81747287..81747287 100644 --- a/2024-04-14/IMG_1066.txt +++ b/www/photo.causal.agency/2024-04-14/IMG_1066.txt diff --git a/generate.sh b/www/photo.causal.agency/generate.sh index 4b30db92..4b30db92 100644 --- a/generate.sh +++ b/www/photo.causal.agency/generate.sh diff --git a/rsync.sh b/www/photo.causal.agency/rsync.sh index 957911d2..957911d2 100644 --- a/rsync.sh +++ b/www/photo.causal.agency/rsync.sh diff --git a/www/temp.causal.agency/.gitignore b/www/temp.causal.agency/.gitignore new file mode 100644 index 00000000..e31ee94e --- /dev/null +++ b/www/temp.causal.agency/.gitignore @@ -0,0 +1 @@ +up diff --git a/www/temp.causal.agency/Makefile b/www/temp.causal.agency/Makefile new file mode 100644 index 00000000..a69a2b48 --- /dev/null +++ b/www/temp.causal.agency/Makefile @@ -0,0 +1,15 @@ +CGI_BIN = /var/www/cgi-bin + +CFLAGS += -std=c11 -Wall -Wextra -Wpedantic $$(pkg-config --cflags kcgi) +LDLIBS = -static $$(pkg-config --static --libs kcgi-html) + +up: + +clean: + rm -f up + +install: up + install up ${CGI_BIN}/up + +uninstall: + rm -f ${CGI_BIN}/up diff --git a/www/temp.causal.agency/up.c b/www/temp.causal.agency/up.c new file mode 100644 index 00000000..561a8901 --- /dev/null +++ b/www/temp.causal.agency/up.c @@ -0,0 +1,193 @@ +/* Copyright (C) 2020 June McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sysexits.h> +#include <time.h> +#include <unistd.h> + +#include <kcgi.h> +#include <kcgihtml.h> + +static const char *Page = "up"; +static const struct kvalid Key = { NULL, "file" }; + +static enum kcgi_err head(struct kreq *req, enum khttp http, enum kmime mime) { + return khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http]) + || khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[mime]); +} + +static enum kcgi_err fail(struct kreq *req, enum khttp http) { + return head(req, http, KMIME_TEXT_PLAIN) + || khttp_body(req) + || khttp_printf(req, "%s\n", khttps[http]); +} + +static int dir = -1; +static const char *upload(const char *ext, void *ptr, size_t len) { + static char name[256]; + snprintf( + name, sizeof(name), "%jx%08x%s%s", + (intmax_t)time(NULL), arc4random(), + (ext && ext[0] != '.' ? "." : ""), (ext ? ext : "") + ); + int fd = openat(dir, name, O_CREAT | O_EXCL | O_WRONLY, 0644); + if (fd < 0) { + warn("%s", name); + return NULL; + } + ssize_t n = write(fd, ptr, len); + int error = close(fd); + if (n < 0 || error) { + warn("%s", name); + return NULL; + } + return name; +} + +static enum kcgi_err handle(struct kreq *req) { + if (req->page) return fail(req, KHTTP_404); + + if (req->method == KMETHOD_GET) { + struct khtmlreq html; + struct khtmlreq *h = &html; + return head(req, KHTTP_200, KMIME_TEXT_HTML) + || khttp_body(req) + || khtml_open(h, req, 0) + || khtml_elem(h, KELEM_DOCTYPE) + || khtml_elem(h, KELEM_TITLE) + || khtml_puts(h, "Upload") + || khtml_closeelem(h, 1) + || khtml_attr( + h, KELEM_FORM, + KATTR_METHOD, "post", + KATTR_ACTION, "", + KATTR_ENCTYPE, "multipart/form-data", + KATTR__MAX + ) + || khtml_attr( + h, KELEM_INPUT, + KATTR_TYPE, "file", + KATTR_NAME, Key.name, + KATTR__MAX + ) + || khtml_attr( + h, KELEM_INPUT, + KATTR_TYPE, "submit", + KATTR_VALUE, "Upload", + KATTR__MAX + ) + || khtml_close(h); + + } else if (req->method == KMETHOD_POST) { + struct kpair *field = req->fieldmap[0]; + if (!field || !field->valsz) return fail(req, KHTTP_400); + + const char *ext = strrchr(field->file, '.'); + const char *name = upload(ext, field->val, field->valsz); + if (!name) return fail(req, KHTTP_507); + + return head(req, KHTTP_303, KMIME_TEXT_PLAIN) + || khttp_head(req, kresps[KRESP_LOCATION], "/%s", name) + || khttp_body(req) + || khttp_puts(req, name); + + } else if (req->method == KMETHOD_PUT) { + struct kpair *field = req->fields; + if (!field || !field->valsz) return fail(req, KHTTP_400); + + const char *ext = req->suffix; + if (!ext[0]) ext = strrchr(field->file, '.'); + const char *name = upload(ext, field->val, field->valsz); + if (!name) return fail(req, KHTTP_507); + + return head(req, KHTTP_200, KMIME_TEXT_PLAIN) + || khttp_body(req) + || khttp_printf( + req, "%s://%s/%s\n", kschemes[req->scheme], req->host, name + ); + + } else { + return fail(req, KHTTP_405); + } +} + +int main(int argc, char *argv[]) { + int error; + const char *path = (argc > 1 ? argv[1] : "."); + dir = open(path, O_DIRECTORY); + if (dir < 0) err(EX_NOINPUT, "%s", path); + +#ifdef __OpenBSD__ + error = unveil(path, "wc"); + if (error) err(EX_OSERR, "unveil"); +#endif + + if (!khttp_fcgi_test()) { +#ifdef __OpenBSD__ + error = pledge("stdio wpath cpath proc", NULL); + if (error) err(EX_OSERR, "pledge"); +#endif + + struct kreq req; + error = khttp_parse(&req, &Key, 1, &Page, 1, 0); + if (error) errx(EX_PROTOCOL, "khttp_parse: %s", kcgi_strerror(error)); + +#ifdef __OpenBSD__ + error = pledge("stdio wpath cpath", NULL); + if (error) err(EX_OSERR, "pledge"); +#endif + + error = handle(&req); + if (error) errx(EX_PROTOCOL, "%s", kcgi_strerror(error)); + khttp_free(&req); + return EX_OK; + } + +#ifdef __OpenBSD__ + error = pledge("stdio wpath cpath unix sendfd recvfd proc", NULL); + if (error) err(EX_OSERR, "pledge"); +#endif + + struct kfcgi *fcgi; + error = khttp_fcgi_init(&fcgi, &Key, 1, &Page, 1, 0); + if (error) errx(EX_CONFIG, "khttp_fcgi_init: %s", kcgi_strerror(error)); + +#ifdef __OpenBSD__ + error = pledge("stdio wpath cpath recvfd", NULL); + if (error) err(EX_OSERR, "pledge"); +#endif + + for ( + struct kreq req; + !(error = khttp_fcgi_parse(fcgi, &req)); + khttp_free(&req) + ) { + error = handle(&req); + if (error && error != KCGI_HUP) break; + } + if (error != KCGI_EXIT) { + errx(EX_PROTOCOL, "khttp_fcgi_parse: %s", kcgi_strerror(error)); + } + khttp_fcgi_free(fcgi); +} diff --git a/www/text.causal.agency/.gitignore b/www/text.causal.agency/.gitignore new file mode 100644 index 00000000..66b3e637 --- /dev/null +++ b/www/text.causal.agency/.gitignore @@ -0,0 +1,4 @@ +*.txt +colb +feed.atom +igp diff --git a/www/text.causal.agency/001-make.7 b/www/text.causal.agency/001-make.7 new file mode 100644 index 00000000..b4805729 --- /dev/null +++ b/www/text.causal.agency/001-make.7 @@ -0,0 +1,159 @@ +.Dd September 17, 2018 +.Dt MAKE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Using Make +.Nd writing less Makefile +. +.Sh DESCRIPTION +Let's talk about +.Xr make 1 . +I think an important thing to know about +.Xr make 1 +is that you don't need to write a +.Pa Makefile +to use it. +There are default rules +for C, C++ and probably Fortran. +To build +.Pa foo +from +.Pa foo.c , +just run: +. +.Pp +.Dl make foo +. +.Pp +The default rule for C files uses the +.Ev CFLAGS +variable, +so you can set that in the environment +to pass flags to the C compiler: +. +.Pp +.Dl CFLAGS=-Wall make foo +. +.Pp +It also uses +.Ev LDLIBS +for linking, +so you can add libraries with: +. +.Pp +.Dl LDLIBS=-lcurses make foo +. +.Pp +Obviously writing this every time +would become tedious, +so it might be time to write a +.Pa Makefile . +But it really doesn't need much: +. +.Bd -literal -offset indent +CFLAGS += -Wall -Wextra +LDLIBS = -lcurses + +foo: +.Ed +. +.Pp +Assigning +.Ev CFLAGS +with +.Ql += +preserves the system default +or anything passed in the environment. +Declaring +.Pa foo +as the first rule +makes it the default when +.Ql make +is run without a target. +Note that the rule doesn't need a definition; +the default will still be used. +. +.Pp +If +.Pa foo +is built from serveral source files, +unfortunately a rule definition is required: +. +.Bd -literal -offset indent +OBJS = foo.o bar.o baz.o + +foo: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ +.Ed +. +.Pp +This rule uses +.Ev LDFLAGS +for passing linker flags, +which is what the default rule does. +The +.Ql $@ +variable here expands to +.Ql foo , +so this rule can be copied easily +for other binary targets. +. +.Pp +If some sources depend on a header file, +they can be automatically rebuilt +when the header changes +by declaring a dependency rule: +. +.Pp +.Dl foo.o bar.o: foo.h +. +.Pp +Note that several files can appear +either side of the +.Ql ":" . +. +.Pp +Lastly, +it's always nice to add a +.Cm clean +target: +. +.Bd -literal -offset indent +clean: + rm -f $(OBJS) foo +.Ed +. +.Pp +I hope this helps getting started with +.Xr make 1 +without writing too much +.Pa Makefile ! +. +.Sh EXAMPLES +The example +.Pa Makefile +in its entirety: +. +.Bd -literal -offset indent +CFLAGS += -Wall -Wextra +LDLIBS = -lcurses +OBJS = foo.o bar.o baz.o + +foo: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ + +foo.o bar.o: foo.h + +clean: + rm -f $(OBJS) foo +.Ed +. +.Sh AUTHORS +.An Mt june@causal.agency +. +.Pp +This document is produced from +.Xr mdoc 7 +source available from +.Lk https://git.causal.agency/src/tree/www/text.causal.agency diff --git a/www/text.causal.agency/002-writing-mdoc.7 b/www/text.causal.agency/002-writing-mdoc.7 new file mode 100644 index 00000000..b377d364 --- /dev/null +++ b/www/text.causal.agency/002-writing-mdoc.7 @@ -0,0 +1,138 @@ +.Dd September 27, 2018 +.Dt WRITING-MDOC 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Writing mdoc +.Nd semantic markup +. +.Sh DESCRIPTION +I recently learned how to write man pages +so that I could document +a bunch of little programs I've written. +Modern man pages are written in +.Xr mdoc 7 , +whose documentation is also available from +.Lk http://mandoc.bsd.lv . +. +.Pp +.Xr mdoc 7 +differs from many other markup languages +by providing +.Dq semantic markup +rather than just +.Dq physical markup. +What this means is that +the markup indicates what something is, +not how to format it. +For example, +the +.Ql \&Ar +macro is used to indicate +command-line arguments +rather than one of the macros +for bold, italic or underline. +This frees each author of having to choose +and enables consistent presentation +across different man pages. +. +.Pp +Another advantage of semantic markup +is that information can be extracted from it. +For example, +.Xr makewhatis 8 +can easily extract the name and short description +from each man page +thanks to the +.Ql \&Nm +and +.Ql \&Nd +macros. +I use the same information +to generate an Atom feed for these documents, +though in admittedly a much less robust way than +.Xr mandoc 1 . +. +.Pp +When it comes to actually writing +.Xr mdoc 7 , +it can take some getting used to. +The language is of +.Xr roff 7 +lineage +so its syntax is very particular. +Macros cannot appear inline, +but must start on new lines +beginning with +.Ql \&. . +Sentences should likewise +always start on a new line. +Since I'm in the habit of writing with +semantic line breaks, +I actually find these requirements +fit in well. +. +.Pp +The more frustrating syntax limitation to me +is the rule against empty lines. +Without them, +it can be quite difficult to edit a lengthy document. +Thankfully, +lines with only a +.Ql \&. +on them are allowed, +but this still causes visual noise. +To alleviate that, +I have a +.Xr vim 1 +syntax file for +.Xr mdoc 7 +which conceals the lone dots: +. +.Bd -literal -offset indent +if exists("b:current_syntax") + finish +endif + +runtime! syntax/nroff.vim +unlet! b:current_syntax + +setlocal sections+=ShSs +syntax match mdocBlank /^\\.$/ conceal +setlocal conceallevel=2 + +let b:current_syntax = "mdoc" +.Ed +. +.Pp +It also adds the +.Xr mdoc 7 +section header and subsection header macros to the +.Cm sections +option to make +.Xr vim 1 Ap s +.Ic { +and +.Ic } +motions +aware of them. +. +.Pp +With that, +I've found writing man pages pleasant and rewarding. +I've started writing other documents with +.Xr mdoc 7 +as well, +as you can see here. +. +.Sh SEE ALSO +.Lk http://rhodesmill.org/brandon/2012/one-sentence-per-line/ "Semantic Linefeeds" +. +.Sh AUTHORS +.An Mt june@causal.agency +. +.Pp +This document is produced from +.Xr mdoc 7 +source available from +.Lk https://git.causal.agency/src/tree/www/text.causal.agency diff --git a/www/text.causal.agency/003-pleasant-c.7 b/www/text.causal.agency/003-pleasant-c.7 new file mode 100644 index 00000000..16030b7e --- /dev/null +++ b/www/text.causal.agency/003-pleasant-c.7 @@ -0,0 +1,120 @@ +.Dd September 30, 2018 +.Dt PLEASANT-C 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Pleasant C +.Nd it's good, actually +. +.Sh DESCRIPTION +I've been writing a lot of C lately +and actually find it very pleasant. +I want to talk about some of its ergonomic features. +These are C99 features unless otherwise noted. +. +.Ss Initializer syntax +Struct and union initializer syntax +is well generalized. +Designators can be chained, +making initializing nested structs easy, +and all uninitialized fields are zeroed. +. +.Bd -literal -offset indent +struct { + struct pollfd fds[2]; +} loop = { + .fds[0].fd = STDIN_FILENO, + .fds[1].fd = STDOUT_FILENO, + .fds[0].events = POLLIN, + .fds[1].events = POLLOUT, +}; +.Ed +. +.Ss Variable-length arrays +VLAs can be multi-dimensional, +which can avoid manual stride multiplications +needed to index a flat +.Xr malloc 3 Ap d +array. +. +.Bd -literal -offset indent +uint8_t glyphs[len][height][width]; +fread(glyphs, height * width, len, stdin); +.Ed +. +.Ss Incomplete array types +The last field of a struct can be an +.Dq incomplete +array type, +which means it doesn't have a length. +A variable amount of space for the struct can be +.Xr malloc 3 Ap d , +or the struct can be used as +a sort of pointer with fields. +. +.Bd -literal -offset indent +struct Line { + enum Filter type; + uint8_t data[]; +} *line = &png.data[1 + lineSize()]; +.Ed +. +.Ss Anonymous struct and union fields (C11) +Members of structs or unions +which are themselves structs or unions +can be unnamed. +In that case, +each of the inner fields +is treated as a member of the outer struct or union. +This makes working with tagged unions nicer. +. +.Bd -literal -offset indent +struct Message { + enum { Foo, Bar } type; + union { + uint8_t foo; + uint32_t bar; + }; +} msg = { .type = Foo, .foo = 0xFF }; +.Ed +. +.Ss Static assert (C11) +Assertions can be made at compile time. +Most useful for checking sizes of structs. +. +.Bd -literal -offset indent +static_assert(13 == sizeof(struct PNGHeader), "PNG IHDR size"); +.Ed +. +.Ss Leading-break switch +This one is just an odd style choice +I came across that C happens to allow. +To prevent accidental fall-through +in switch statements, +you can put breaks before the case labels. +. +.Bd -literal -offset indent +while (0 < (opt = getopt(argc, argv, "h:w:"))) { + switch (opt) { + break; case 'h': height = optarg; + break; case 'w': width = optarg; + break; default: return EX_USAGE; + } +} +.Ed +. +.Sh AUTHORS +.An Mt june@causal.agency +. +.Pp +This document is produced from +.Xr mdoc 7 +source available from +.Lk https://git.causal.agency/src/tree/www/text.causal.agency +. +.Sh CAVEATS +This isn't meant to be advice. +It's just how I like to write C, +and I don't +.Dq ship +software in C. diff --git a/www/text.causal.agency/004-uloc.7 b/www/text.causal.agency/004-uloc.7 new file mode 100644 index 00000000..edd78d80 --- /dev/null +++ b/www/text.causal.agency/004-uloc.7 @@ -0,0 +1,64 @@ +.Dd December 14, 2018 +.Dt ULOC 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm ULOC +.Nd unique lines of code +. +.Sh DESCRIPTION +There are many tools available +which measure SLOC: source lines of code. +These tools are strangely complex +for what they intend to do, +which is to estimate the relative sizes of projects. +They perform some amount of parsing +in order to discount comments in various languages, +and for reasons unknown each format their ouput +in some oddly encumbered way. +. +.Pp +I propose a much simpler method +of estimating relative sizes of projects: +unique lines of code. +ULOC can be calculated with standard tools as follows: +. +.Bd -literal -offset indent +sort -u *.h *.c | wc -l +.Ed +. +.Pp +In my opinion, +the number this produces +should be a better estimate of +the complexity of a project. +Compared to SLOC, +not only are blank lines discounted, +but so are close-brace lines +and other repetitive code +such as common includes. +On the other hand, +ULOC counts comments, +which require just as much maintenance +as the code around them does, +while avoiding inflating the result +with license headers which appear in every file, +for example. +. +.Pp +It can also be amusing +to read all of your code sorted alphabetically. +. +.Sh AUTHORS +.An Mt june@causal.agency +. +.Pp +This document is produced from +.Xr mdoc 7 +source available from +.Lk https://git.causal.agency/src/tree/www/text.causal.agency +. +.Sh CAVEATS +Estimates such as these +should not be used for decision making +as if they were data. diff --git a/www/text.causal.agency/005-testing-c.7 b/www/text.causal.agency/005-testing-c.7 new file mode 100644 index 00000000..d0c636ff --- /dev/null +++ b/www/text.causal.agency/005-testing-c.7 @@ -0,0 +1,73 @@ +.Dd December 21, 2018 +.Dt TESTING-C 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Testing C +.Nd a simple unit testing setup +. +.Sh DESCRIPTION +This is a simple approach +to unit testing in C +that I've used in a couple projects. +At the bottom of a C file +with some code I want to test, +I add: +. +.Bd -literal -offset indent +#ifdef TEST +#include <assert.h> + +int main(void) { + assert(...); + assert(...); +} + +#endif +.Ed +. +.Pp +This file normally produces a +.Pa .o +to be linked into the main binary. +For testing, +I produce separate binaries +and run them with +.Xr make 1 : +. +.Bd -literal -offset indent +TESTS = foo.t bar.t + +\&.SUFFIXES: .t + +\&.c.t: + $(CC) $(CFLAGS) -DTEST $(LDFLAGS) $< $(LDLIBS) -o $@ + +test: $(TESTS) + set -e; $(TESTS:%=./%;) +.Ed +. +.Pp +Note that the test binaries +aren't linked with the rest of the code, +so there is potential for simple stubbing or mocking. +. +.Pp +To get the best output +from C's simple +.Xr assert 3 , +it's best to assert the result +of a helper function +which takes the expected output +and the test input, +rather than calling +.Xr assert 3 +inside the helper function. +This way, +the message printed by the assert failure +contains a useful line number +and the expected output +rather than just variable names. +. +.Sh AUTHORS +.An Mt june@causal.agency diff --git a/www/text.causal.agency/006-some-libs.7 b/www/text.causal.agency/006-some-libs.7 new file mode 100644 index 00000000..5af65404 --- /dev/null +++ b/www/text.causal.agency/006-some-libs.7 @@ -0,0 +1,96 @@ +.Dd December 11, 2019 +.Dt SOME-LIBS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Some Libraries +.Nd good ones +. +.Sh DESCRIPTION +This is a little list of C libraries +I've had good experiences using. +. +.Bl -tag -width Ds +.It Fl lcurl +The library behind the +.Xr curl 1 +command. +It downloads or uploads things on the internet +through a number of protocols, +not just HTTP. +It has an easy-to-use library API, +appropriately named +.Xr libcurl-easy 3 . +I've used it to implement a +.Lk https://causal.agency/bin/title.html "page title fetcher" . +. +.It Fl lcurses +Okay so this one really isn't great. +Its interfaces can seem archaic +and its documentation is often poor. +However, it gets the job done +and is commonly available pretty much everywhere. +Interesting to note that +.Nx +uses its own implementation of curses +that is not GNU ncurses, +unlike +.Fx . +. +.It Fl ledit +This is a BSD line editing library, +similar to GNU readline. +It supports right-aligned prompts, +which I prefer for variable-length +information in shells. +. +.It Fl lkcgi +A CGI and FastCGI library +for web applications in C. +Don't worry, +it isolates HTTP parsing and input validation +from application logic +in sandboxed processes. +I think it's an excellent example +of how to design an API for C. +I used it to implement the +.Lk https://ascii.town/explore.html "torus web viewer" . +. +.It Fl lsqlite3 +An embedded relational database engine. +It's amazing what you can do with this, +and it's super easy to use! +My one gripe with it is that the library and SQL documentation +are not available as +.Xr man 1 +pages. +I'm currently working on a project using SQLite, +but it hasn't gotten very far yet. +. +.It Fl ltls +This is a new library in LibreSSL +which provides a much simpler interface for TLS sockets +compared to +.Fl lssl . +It's much more like what you'd expect +from other TLS socket wrappers, +with calls like +.Xr tls_connect 3 , +.Xr tls_read 3 +and +.Xr tls_write 3 . +I've used this for IRC clients, bouncers and bots. +. +.It Fl lz +An implementation of the DEFLATE compression algorithm +and gzip format. +It's all documented in comments in +.In zlib.h , +which isn't bad, +but for my own use I copied the docs into +.Lk https://code.causal.agency/june/zlib-man-pages "manual pages" . +I've used this for decoding and encoding PNG images. +.El +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/007-cgit-setup.7 b/www/text.causal.agency/007-cgit-setup.7 new file mode 100644 index 00000000..44fb436a --- /dev/null +++ b/www/text.causal.agency/007-cgit-setup.7 @@ -0,0 +1,271 @@ +.Dd December 15, 2019 +.Dt CGIT-SETUP 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm cgit setup +.Nd configuration notes +. +.Sh DESCRIPTION +I just set up cgit on +.Lk https://git.causal.agency +to replace an instance of gitea. +After 30 days of uptime, +gitea had accumulated over 11 hours of CPU time +and was using hundreds of megabytes of memory. +cgit is much more lightweight +and much more in line with my aesthetic. +I'm documenting how I set it up here +mostly to remind myself in the future. +. +.Ss slowcgi +cgit is CGI software, +but +.Xr nginx 8 +only supports FastCGI. +I used +.Xr slowcgi 8 +as a compatibility layer +by adding the following to +.Pa /etc/rc.conf : +.Bd -literal -offset indent +slowcgi_enable="YES" +slowcgi_flags="-p / -s /var/run/slowcgi.sock" +.Ed +. +.Ss nginx +I added the following in a new +.Cm server +block to +.Pa /usr/local/etc/nginx/nginx.conf : +.Bd -literal -offset indent +root /usr/local/www/cgit; +location / { + try_files $uri @cgit; +} +location @cgit { + fastcgi_pass unix:/var/run/slowcgi.sock; + fastcgi_param SCRIPT_FILENAME $document_root/cgit.cgi; + fastcgi_param SCRIPT_NAME /; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $query_string; + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param CONTENT_TYPE $content_type; + fastcgi_param CONTENT_LENGTH $content_length; + fastcgi_param HTTPS $https if_not_empty; + fastcgi_param SERVER_PORT $server_port; + fastcgi_param SERVER_NAME $server_name; +} +.Ed +. +.Pp +The +.Cm try_files +directive causes +.Xr nginx 8 +to first try to serve static files from +.Pa /usr/local/www/cgit +before passing anything else on to FastCGI. +. +.Pp +The +.Va SCRIPT_FILENAME +parameter tells +.Xr slowcgi 8 +the path of the CGI binary to run. +Setting +.Va SCRIPT_NAME +to +.Pa / +tells cgit its root URL +and avoids it using query strings for everything. +. +.Ss cgit +cgit doesn't provide any configuration to start from, +so you have to just read +.Xr cgitrc 5 . +I added the following to +.Pa /usr/local/etc/cgitrc : +.Bd -literal -offset indent +cache-size=1024 +clone-url=https://$HTTP_HOST/$CGIT_REPO_URL +snapshots=tar.gz zip +remove-suffix=1 +enable-git-config=1 +scan-path=/home/june/pub +.Ed +. +.Pp +The +.Cm cache-size +option enables caching, +which by default is stored in +.Pa /var/cache/cgit , +so I made sure that directory exists +and is writable by the +.Sy www +user. +The +.Cm clone-url +option sets the clone URL to advertise. +cgit will automatically serve git over HTTP. +The +.Cm snapshots +option makes tarballs available for tags and commits. +. +.Pp +The +.Cm scan-path +option causes cgit to scan the given path +for git repositories. +I'm putting mine in +.Pa ~/pub . +The +.Cm remove-suffix +option causes cgit to remove the +.Pa .git +suffix from the URLs it uses +for the repositories it finds, +so that +.Pa ~/pub/pounce.git +is served at +.Pa /pounce . +The +.Cm enable-git-config +option allows controlling some cgit options +from the +.Xr git-config 1 +of each repository. +See +.Sx git +below. +. +.Pp +I also set up a filter to render +.Xr mdoc 7 +files +and do syntax highlighting +by adding the following to +.Pa cgitrc : +.Bd -literal -offset indent +readme=:README.7 +readme=:README +about-filter=/usr/local/libexec/cgit-filter +source-filter=/usr/local/libexec/cgit-filter +.Ed +. +.Pp +The +.Cm readme +options tell cgit which files to look for +to render the +.Dq about +page. +The colon prefix causes it to look for them +in the git tree. +The +.Pa /usr/local/libexec/cgit-filter +script contains the following: +.Bd -literal -offset indent +#!/bin/sh +case "$1" in + (*.[1-9]) + /usr/bin/mandoc -T utf8 | /usr/local/libexec/ttpre + ;; + (*) + exec /usr/local/libexec/hi -t -n "$1" -f html -o anchor + ;; +esac +.Ed +. +.Pp +Filter scripts are run with the filename as their first argument +and the contents of the file on standard input. +The +.Xr ttpre 1 +command is my own utility to convert +.Xr man 1 +output to HTML. +The +.Xr hi 1 +command is my own +.Lk https://causal.agency/bin/hi.html "syntax highlighter" . +. +.Ss git +I create my repositories in +.Pa ~/pub +with +.Ql git init --bare +and use +.Pa git.causal.agency:pub/example.git +locally as the remote. +Descriptions are set by editing the +.Pa description +file in each repository. +The section and homepage can be set with +.Xr git-config 1 +through the keys +.Cm cgit.section +and +.Cm cgit.homepage , +respectively, +thanks to the +.Cm enable-git-config +option above. +. +.Ss Redirects +I added the following to the +.Cm server +block that used to serve gitea in +.Pa nginx.conf : +.Bd -literal -offset indent +location ~* /june/([^.]+)[.]git(.*) { + return 301 https://git.causal.agency/$1$2?$query_string; +} +location ~* /june/([^/]+) { + return 301 https://git.causal.agency/$1; +} +location / { + return 301 https://git.causal.agency; +} +.Ed +. +.Pp +This redirects any links to my gitea repos +to the corresponding repo in cgit. +The first +.Sy location +block also redirects gitea HTTP clone URLs to cgit +so that +.Xr git-pull 1 +continues to work on existing clones. +. +.Ss Update: fast HTTPS clones +Someone pointed out that cloning my repos +over HTTPS was incredibly slow, +and this is because cgit only implements the +.Dq dumb +HTTP git transport. +To speed up cloning, +I send the URLs used by the +.Dq smart +HTTP transport to +.Xr git-http-backend 1 +instead: +.Bd -literal -offset indent +location ~ /.+/(info/refs|git-upload-pack) { + fastcgi_pass unix:/var/run/slowcgi.sock; + fastcgi_param SCRIPT_NAME /usr/local/libexec/git-core/git-http-backend; + fastcgi_param GIT_HTTP_EXPORT_ALL 1; + fastcgi_param GIT_PROJECT_ROOT /home/june/pub; + include fastcgi_params; +} +.Ed +. +.Pp +I factored out the FastCGI parameters +I'm using with cgit +to be included here as well. +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/008-how-irc.7 b/www/text.causal.agency/008-how-irc.7 new file mode 100644 index 00000000..aba1bbf9 --- /dev/null +++ b/www/text.causal.agency/008-how-irc.7 @@ -0,0 +1,193 @@ +.Dd March 8, 2020 +.Dt HOW-IRC 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm How I Relay Chat +.Nd in code +. +.Sh DESCRIPTION +I've been writing a lot of IRC software lately +.Pq Sx SEE ALSO , +and developed some nice code patterns +that I've been reusing. +Here they are. +. +.Ss Parsing +I use fixed size buffers almost everywhere, +so it's necessary to know IRC's size limits. +A traditional IRC message is a maximum of 512 bytes, +but the IRCv3 message-tags spec adds +(unreasonably, in my opinion) +8191 bytes for tags. +IRC messages also have a maximum of 15 command parameters. +.Bd -literal -offset indent +enum { MessageCap = 8191 + 512 }; +enum { ParamCap = 15 }; +.Ed +. +.Pp +If I'm using tags, +I'll use X macros +to declare the set I care about. +X macros are a way of maintaining parallel arrays, +or in this case an enum and an array. +.Bd -literal -offset indent +#define ENUM_TAG \e + X("msgid", TagMsgid) \e + X("time", TagTime) + +enum Tag { +#define X(name, id) id, + ENUM_TAG +#undef X + TagCap, +}; + +static const char *TagNames[TagCap] = { +#define X(name, id) [id] = name, + ENUM_TAG +#undef X +}; +.Ed +. +.Pp +The TagNames array is used by the parsing function +to assign tag values into the message structure, +which looks like this: +.Bd -literal -offset indent +struct Message { + char *tags[TagCap]; + char *nick; + char *user; + char *host; + char *cmd; + char *params[ParamCap]; +}; +.Ed +. +.Pp +I'm a fan of using +.Xr strsep 3 +for simple parsing. +Although it modifies its input +(replacing delimiters with NUL terminators), +since the raw message is in a static buffer, +it is ideal for so-called zero-copy parsing. +I'm not going to include the whole parsing function here, +but I will at least include the part that many get wrong, +which is dealing with the colon-prefixed trailing parameter: +.Bd -literal -offset indent +msg.cmd = strsep(&line, " "); +for (int i = 0; line && i < ParamCap; ++i) { + if (line[0] == ':') { + msg.params[i] = &line[1]; + break; + } + msg.params[i] = strsep(&line, " "); +} +.Ed +. +.Ss Handling +To handle IRC commands and replies +I add handler functions to a big array. +I usually have some form of helper as well +to check the number of expected parameters. +.Bd -literal -offset indent +typedef void HandlerFn(struct Message *msg); + +static const struct Handler { + const char *cmd; + HandlerFn *fn; +} Handlers[] = { + { "001", handleReplyWelcome }, + { "PING", handlePing }, + { "PRIVMSG", handlePrivmsg }, +}; +.Ed +. +.Pp +Since I keep these arrays sorted anyway, +I started using the standard +.Xr bsearch 3 +function, +but a basic for loop probably works just as well. +I do wish I could compile-time assert +that the array really is sorted, though. +.Bd -literal -offset indent +static int compar(const void *cmd, const void *_handler) { + const struct Handler *handler = _handler; + return strcmp(cmd, handler->cmd); +} + +void handle(struct Message msg) { + if (!msg.cmd) return; + const struct Handler *handler = bsearch( + msg.cmd, + Handlers, ARRAY_LEN(Handlers), + sizeof(*handler), compar + ); + if (handler) handler->fn(&msg); +} +.Ed +. +.Ss Capabilities +For IRCv3 capabilties +I use X macros again, +this time with another handy macro +for declaring bit flag enums. +.Bd -literal -offset indent +#define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit + +#define ENUM_CAP \e + X("message-tags", CapMessageTags) \e + X("sasl", CapSASL) \e + X("server-time", CapServerTime) + +enum Cap { +#define X(name, id) BIT(id), + ENUM_CAP +#undef X +}; + +static const char *CapNames[] = { +#define X(name, id) [id##Bit] = name, + ENUM_CAP +#undef X +}; +.Ed +. +.Pp +The +.Fn BIT +macro declares, for example, +.Dv CapSASL +as the bit flag and +.Dv CapSASLBit +as the corresponding index. +The +.Vt "enum Cap" +is used as a set, +for example checking if SASL is enabled with +.Ql caps & CapSASL . +. +.Pp +These patterns are serving my IRC software well, +and my IRC projects are serving me well. +It is immensely satisfying +to be (near) constantly using software +that I wrote myself and am happy with, +regardless of how niche it may be. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://git.causal.agency/pounce/about "IRC bouncer" +.It +.Lk https://git.causal.agency/litterbox/about "IRC logger" +.It +.Lk https://git.causal.agency/catgirl/about "IRC client" +.El +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/009-casual-update.7 b/www/text.causal.agency/009-casual-update.7 new file mode 100644 index 00000000..0548436a --- /dev/null +++ b/www/text.causal.agency/009-casual-update.7 @@ -0,0 +1,127 @@ +.Dd May 6, 2020 +.Dt CASUAL-UPDATE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm casual update +.Nd software developments +. +.Sh DESCRIPTION +I've been figuring out more of IMAP +and Internet Messages in general +while working on a new project +so I've revisited some older ones. +I've copied my somewhat more proper +IMAP parsing code into them, +so they should be more robust. +. +.Pp +.Xr imbox 1 +is my tool to export messages +in mboxrd format directly from IMAP. +It's mostly for applying patches sent by email +without having any kind of local mail setup. +For that, +it includes the +.Xr git-fetch-email 1 +wrapper which works very similarly to +.Xr git-send-email 1 . +I learned by reading the source of +.Xr git-subtree 1 +that +.Xr git-rev-parse 1 +can be used by shell scripts +to parse long options, +so I added those. +I also added the +.Fl Fl apply +flag to automatically pipe to +.Xr git-am 1 +with the right flags for mboxrd. +. +.Pp +.Xr notemap 1 +is a tool for mirroring text files +to an IMAP Notes mailbox, +which is used by FastMail's web UI +and the macOS/iOS Notes app. +Its original parsing code +was particularly ad-hoc. +Since I've now learned +how UTF-8 headers are encoded, +I updated it to properly encode +the file name in the Subject line. +. +.Pp +I also got distracted by +a conversation about UNIX-domain sockets +where I was comparing the macOS and FreeBSD +.Xr unix 4 +pages and the Linux +.Xr unix 7 +page. +This lead me to make +.Xr exman 1 , +a tool to locally install and read +manual pages for Linux, POSIX, +.Fx , +.Nx +and +.Ox . +I've already gotten quite a bit of use out of it. +. +.Pp +In yet another IRC distraction, +I was talking about some further plans for my IRC software, +and realized it might be time to write +my future projects list down. +I opened a +.Pa .plan +file, +immediately wondered how anyone can write plain text, +then switched to a +.Pa plan.7 +file. +There's nothing I won't use +.Xr mdoc 7 +for. +After a little setup, +I can now be fingered, +and make jokes about this silly little protocol +from the days of old. +.Xr finger 1 Ap s +default output fills me with joy: +.Bd -unfilled -offset indent +No Mail. +No Plan. +.Ed +. +.Pp +And speaking of IRC and plans, +I've been meaning to tag +.Xr catgirl 1 +version 1.0 for a while now. +I've been using it as my main client +and my commits to it have really slowed down. +When I do tag it, +I'm planning on writing another post +about my whole +.Dq suite +of IRC software +and how the parts work together. +Watch this space. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://git.causal.agency/imbox "imbox" +.It +.Lk https://git.causal.agency/notemap "notemap" +.It +.Lk https://git.causal.agency/exman "exman" +.It +.Lk https://git.causal.agency/catgirl "catgirl" +.El +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/010-irc-suite.7 b/www/text.causal.agency/010-irc-suite.7 new file mode 100644 index 00000000..515a30ab --- /dev/null +++ b/www/text.causal.agency/010-irc-suite.7 @@ -0,0 +1,409 @@ +.Dd June 19, 2020 +.Dt IRC-SUITE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm IRC suite +.Nd my own IRC software +. +.Sh DESCRIPTION +Over the past months +.Po +eight of them, according to +.Xr git-log 1 +.Pc +I developed a new +.Dq suite +of IRC software +that I now use full-time, +consisting of a bouncer, +a new logging and search solution, +and a terminal client. +These new programs share some characteristics: +they are all TLS-only +and use the libtls API from LibreSSL, +they can all be entirely configured from the command line +or with equivalent configuration files, +they are all designed as +a one process to one IRC connection mapping, +and they all take advantage of IRCv3 features. +. +.Pp +For context, +I was previously running +the znc IRC bouncer +and using the Textual IRC client +with its plain text logs. +I also continue to use +the Palaver IRC client for iOS. +. +.Ss Background +A bouncer is a piece of server software +that stays connected to IRC at all times +and acts as a relay +between your client and the IRC server. +When the client is disconnected, +the bouncer buffers incoming messages +to send to the client when it reconnects. +. +.Pp +Aside from this, +bouncers have another advantage: +client multiplexing. +Several clients, +for instance on different computers +or a phone, +should be able to connect to the same bouncer, +and send and receive messages under the same nick. +Unfortunately, +znc does not handle this use-case well at all. +Out of the box it offers two options: +either any client connection totally clears the buffer, +causing other clients to miss chat history; +or the buffer is never cleared, +causing every client connection +to be repeatedly spammed with redundant history. +There is also a znc wiki page +that suggests one way to solve this issue +is to connect znc to itself multiple times over. +Yikes. +. +.Ss pounce +My dissatisfaction with +connecting multiple clients to znc +directly motivated me to start working +on a new multi-client-focused IRC bouncer. +The result is +.Xr pounce 1 , +based on a rather straightforward +single-producer (the IRC server) +multiple-consumer (each IRC client) +ring buffer. +Each client has its own +independent position in the buffer +so can properly be brought up to date +whenever it connects. +. +.Pp +Additionally, +by assuming support for the IRCv3 +.Sy server-time +extension, +all IRC events can be accurately +relayed to clients at any time, +and the internals of +.Xr pounce 1 +can be kept very simple. +In fact, +it completely avoids parsing most IRC messages, +simply pushing them into the buffer +with an associated timestamp. +. +.Pp +The usernames sent by clients during registration +are used as opaque identifiers for buffer consumers. +This was chosen since most clients +can be configured to send an arbitrary username, +and those that don't often default +to the name of the client itself, +making it an appropriate identifier. +. +.Pp +Later, +I added a way for clients +to be informed of their own buffer positions +using a vendor-specific IRCv3 capability. +This means a client +can save the position +of the last message it actually received, +and request to set its position +when it reconnects, +ensuring no messages are lost +to network issues +or software crashes. +. +.Ss calico +Due to the simple design of mapping +one process to one IRC (server) connection, +it is necessary to run several instances of +.Xr pounce 1 . +Initially I simply used different ports for each, +but as I connected to more networks +and even ran some instances for friends, +it became less feasible. +. +.Pp +The solution I came up with +was to dispatch incoming connections +using Server Name Indication, or SNI. +This way, +multiple domains pointing to the same host +could be used with only one port +to connect to different instances of +.Xr pounce 1 . +For example, +I use a +.Li *.irc.causal.agency +wildcard DNS entry +and a subdomain for each IRC network, +all on port 6697. +. +.Pp +The +.Xr calico 1 +daemon included with pounce +accomplishes this dispatch +using the +.Dv MSG_PEEK +flag of +.Xr recvmsg(2) +on incoming connections. +Since SNI is immediately sent by TLS clients +as part of the ClientHello message in clear-text, +it can be processed +without doing any actual TLS. +The connection itself is then +sent to the corresponding +.Xr pounce 1 +instance +over UNIX-domain socket, +which handles TLS as normal. +This means that +.Xr calico 1 +and +.Xr pounce 1 +operate entirely independently of each other. +. +.Ss litterbox +Based on the multiple-consumer ring buffer design, +I realized it would be easy +to implement additional functionality +as independent purpose-built clients +which connect to +.Xr pounce 1 +alongside regular clients. +This could allow dedicated OTR or DCC software +to operate in parallel with a basic client, +or for more passive software +to provide notifications +or dedicated logging. +. +.Pp +For the latter, +I wanted to do better than +plain text log files. +.Xr grep 1 +over files works fine, +but search could be faster and smarter, +and the text format is +more lossy and less structured +than I'd like it to be. +Conveniently, +SQLite provides an extension +(actually two) +for full-text search. +. +.Pp +The litterbox project +is my dedicated logging solution +using SQLite FTS5. +It consists of three tools: +the +.Xr litterbox 1 +daemon itself which connects to pounce +and logs messages to SQLite, +the +.Xr scoop 1 +command line query tool, +and the +.Xr unscoop 1 +plain text import tool. +The +.Xr scoop 1 +tool constructs SQL queries +and formats the results for viewing, +with coloured nicks +and piped to a pager +by default. +. +.Pp +The +.Xr litterbox 1 +daemon +can also provide a simple +.Dq online +.Pq over IRC +search query interface +to other connected clients. +The simplest way to allow different +.Xr pounce 1 +clients to talk to each other +was to route private messages to self +internally without sending them to the IRC server. +So from any client +I can simply message myself +a full-text search query +and +.Xr litterbox 1 +responds with the results. +. +.Pp +Along with routing self-messages, +.Xr pounce 1 +also provides a vendor-specific IRCv3 capability +for passive clients such as +.Xr litterbox 1 +to indicate that they should not influence +the automatic away status, +which is normally only set +when no clients are connected. +. +.Pp +An advantage of this architecture +of dedicated clients +rather than bouncer modules +is that they need not run +on the same host. +I run my bouncers on a VPS, +but I'd rather not store my private logs there, +so +.Xr litterbox 1 +runs instead on a Raspberry Pi +in my apartment. +Also, +since it is essentially +just a regular IRC bot, +it could be used independently +for keeping public logs for a channel. +. +.Ss catgirl +There's not really that much to say +about the client, +.Xr catgirl 1 . +Of the three projects +it contains the most code +but is also the least interesting, +in my opinion. +It just does what I want a client to do, +and gets the details right. +. +.Pp +Tab complete is ordered by most recently seen or used, +and completing several nicks +inserts commas between them +as well as the colon following the final nick. +In the input line, +the prompt is updated +to reflect whether the input +will be interpreted as a command or as a message. +Messages are automatically scanned for URLs, +which can be opened or copied with commands +specifying the nick or a substring of the URL. +. +.Pp +Scrolling in a window creates a split view, +keeping the latest messages visible. +Nick colours are based instead on usernames, +keeping them more stable across renames, +and mentions in messages are coloured +to make the conversation easier to follow. +The visibility of ignored messages +can be toggled at any time. +Channels can be muted +so their activity is hidden +from the status line +unless you are pinged. +. +.Pp +.Xr catgirl 1 +is configured entirely on the command line +or in equivalent simple configuration files. +There's no dynamic manipulation of state +using complex +.Ql / +commands like in some other clients. +. +.Pp +The major caveat is that +.Xr catgirl 1 +connects to only one network at a time. +This keeps the configuration, the interface +and the code much simpler. +.Xr tmux 1 , +.Xr screen 1 +or a tabbed terminal emulator +are good options to run several instances. +. +.Pp +If you're interested in giving +.Xr catgirl 1 +a quick (and necessarily limited) try, +you can +.Li ssh chat@ascii.town . +. +.Ss Future +I think I'm done with IRC software for now. +As mentioned above, +there are a few more pieces +that could fit in to this setup, +but I don't really want or need them right now. +One thing I definitely want to try +at some point +is adding a litterbox component +to index the contents of URLs +to make finding previously shared links easier. +. +.Pp +If you try any of this software +and have feedback, +let me know in +.Li #ascii.town +on tilde.chat +or by email. +And of course, +patches are always welcome. +. +.Ss Update: scooper +Somehow I had the motivation +to create a web interface for litterbox: +.Xr scooper 1 . +It can be used either as CGI +or as a FastCGI worker, +and I used the excellent +.Xr kcgi 3 +library for it. +. +.Pp +The main advantage of this interface +is that you can click on a search result +to be brought to its context in the log viewer. +I also added an option to +.Xr litterbox 1 +to provide a corresponding scooper link +in response to its query interface. +. +.Pp +A small demo of scooper is hosted at +.Aq Lk "https://causal.agency/scooper/" . +It publicly logs the +.Li #litterbox +channel on tilde.chat. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk "https://git.causal.agency/pounce" pounce +.It +.Lk "https://git.causal.agency/litterbox" litterbox +.It +.Lk "https://git.causal.agency/catgirl" catgirl +.It +.Lk "https://www.sqlite.org/fts5.html" "SQLite FTS5 Extension" +.It +.Lk "https://git.causal.agency/scooper" scooper +.It +.Lk "https://kristaps.bsd.lv/kcgi/" kcgi +.El +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/011-libretls.7 b/www/text.causal.agency/011-libretls.7 new file mode 100644 index 00000000..c29c325e --- /dev/null +++ b/www/text.causal.agency/011-libretls.7 @@ -0,0 +1,220 @@ +.Dd August 9, 2020 +.Dt LIBRETLS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm LibreTLS +.Nd libtls for OpenSSL +. +.Sh DESCRIPTION +This is a sort of announcement post about LibreTLS, +my port of libtls from LibreSSL to OpenSSL. +If you've wanted to try any of my software +but have been unable to because of LibreSSL, +LibreTLS is an option that will likely work for you. +I'm including instructions +for building it and my IRC software +on Debian as an example, +since manually installing libraries +is less straightforward than it could be. +. +.Pp +libtls is +.Do +a new TLS library, +designed to make it easier to write foolproof applications +.Dc . +It was developed as part of LibreSSL, +.Ox Ap s +fork of OpenSSL, +and is implemented against their version of libssl. +It provides a nice high-level API +for TLS sockets, +with functions like +.Xr tls_connect 3 , +.Xr tls_read 3 +and +.Xr tls_write 3 . +This is a vast improvement over libssl's +confusing mess of an API! +Its relative obscurity is a real shame +for C programmers. +. +.Pp +An obvious cause of its obscurity +is that it is tied to LibreSSL. +Although LibreSSL is available +for platforms other than +.Ox , +it conflicts with OpenSSL +so is difficult to install alongside it +and is often not packaged at all. +Additionally, +even if a user manually installs LibreSSL, +libtls is likely not to work on some distros +due to its hardcoded CA bundle file path. +. +.Pp +Since libtls is implemented against libssl, +which originates in OpenSSL, +it should be possible to use libtls with it. +This is what I set out to do in LibreTLS. +I started by importing the sources +from a LibreSSL-portable release, +then worked on porting the portions +that were incompatible with OpenSSL. +. +.Pp +The simpler changes just involved +replacing internal struct field accesses +with public APIs. +libtls accesses libssl internals +using a hack to get the header files +to declare private struct fields, +and for basically no reason. +The bigger changes involved +reimplementing some functions +which only exist in LibreSSL, +but these were still quite small. +I also imported the necessary compatibility functions +from LibreSSL's libcrypto +and adapated the autotools build files +to produce only a libtls +which depends on OpenSSL. +. +.Pp +Along the way +I decided to make one small behavioural change +in order for LibreTLS to be more likely +to work for everyone. +I removed the hardcoded CA file path +and changed the default configuration +to use OpenSSL's default CA paths, +which include a CA directory. +This seems to be the preferred CA source +on systems such as Debian, +where the default CA file path doesn't exist. +. +.Pp +I think the reason LibreSSL +wants to avoid using a CA directory +is so that it can fully load the CA file +once before being sandboxed. +However, +using OpenSSL's default configuration, +the CA file will still be loaded immediately +if it exists. +If it doesn't exist, +sandboxed applications +will fail when trying to +load certificates from the directory, +but unsandboxed applications +will work just fine. +Since LibreSSL's libtls +would fail either way, +I think the new behaviour +is an improvement. +. +.Pp +Another advantage of separating libtls from LibreSSL +is that it is unencumbered by OpenSSL's +awkward double-license, +both of which are incompatible with the GPL. +libtls is all new ISC-licensed code, +and future versions of OpenSSL (3.0) +will be released under the Apache 2.0 license, +which is compatible with GPLv3. +In the future, +GPL software will be able to link with +libtls and OpenSSL without additional permissions. +. +.Pp +It's also worth noting that LibreSSL +likely will not be able to import any code +from future versions of OpenSSL, +since Apache 2.0 is on +.Ox Ap s +license shitlist. +LLVM is also slowly changing their license +to Apache 2.0, +so it'll be interesting to see what +.Ox +does. +. +.Ss Installing Manually +To install LibreTLS on Debian, +for example, +fetch a release tarball from +.Lk https://causal.agency/libretls/ +and install the build dependencies: +.Bd -literal -offset indent +sudo apt-get install build-essential libssl-dev pkgconf +.Ed +. +.Pp +.Xr pkgconf 1 +isn't a dependency of LibreTLS itself, +but it's how my software +configures its build +for a dependency on libtls. +The usual build steps +will install the library: +.Bd -literal -offset indent +\&./configure +make all +sudo make install +.Ed +. +.Pp +The library will be installed in +.Pa /usr/local/lib +by default, +and you need to make sure +the dynamic linker +will be able to find it there. +On Debian, +.Pa /usr/local/lib +already appears in +.Pa /etc/ld.so.conf.d/libc.conf , +but on other systems +you'll probably need to add it to either +.Pa /etc/ld.so.conf +or a new file such as +.Pa /etc/ld.so.conf.d/local.conf . +Once the library is installed +and the path is configured, +the linker cache needs to be refreshed: +.Bd -literal -offset indent +sudo ldconfig +.Ed +. +.Pp +You'll probably also need to set +.Ev PKG_CONFIG_PATH +for the configure scripts +of my software: +.Bd -literal -offset indent +PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure +.Ed +. +.Pp +On +.Fx , +LibreTLS and some of my IRC software +can be installed from my own +.Lk https://git.causal.agency/ports/ "ports tree" +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://git.causal.agency/libretls/about LibreTLS +.It +.Lk https://man.openbsd.org/tls_init.3 "libtls API documentation" +.El +. +.Pp +Another alternative libtls implementation, +.Lk https://sr.ht/~mcf/libtls-bearssl/ "libtls-bearssl" +. +.Sh AUTHORS +.An June Bug Aq Mt june@causal.agency diff --git a/www/text.causal.agency/012-inability.7 b/www/text.causal.agency/012-inability.7 new file mode 100644 index 00000000..d352143b --- /dev/null +++ b/www/text.causal.agency/012-inability.7 @@ -0,0 +1,39 @@ +.Dd November 26, 2020 +.Dt INABILITY 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Inability +.Nd losing the ability to create +. +.Sh DESCRIPTION +For often weeks, sometimes months at a time, +I lose the ability to write new code. +I can still make fixes +and little cleanups +in my existing projects, +but if I try to work on something new, +nothing happens. +I can't get anything done. +. +.Pp +I think it's now been +over 3 months +since I've created anything. +I don't know what to do about it. +In the past I've eventually +regained the ability to code, +but it's unclear to me how or why. +I also don't know what +I should be doing instead. +Writing code is the only hobby +I've ever really developed, +so without it I basically +don't do anything. +. +.Pp +Does this happen to anyone else? +How do you cope? +. +.Sh AUTHORS +.Mt june@causal.agency diff --git a/www/text.causal.agency/013-hot-tips.7 b/www/text.causal.agency/013-hot-tips.7 new file mode 100644 index 00000000..63b6e353 --- /dev/null +++ b/www/text.causal.agency/013-hot-tips.7 @@ -0,0 +1,156 @@ +.Dd December 2, 2020 +.Dt HOT-TIPS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm hot tips +.Nd from my files +. +.Sh DESCRIPTION +This is a short list of tips +from my configuration files and code +that might be useful. +. +.Ss Shell +.Bl -tag -width Ds +.It CDPATH=:~ +This is useful if you sometimes type, +for example, +.Ql cd src/bin +wanting to go to +.Pa ~/src/bin +but you aren't in +.Pa ~ . +If the path doesn't exist +in the current directory, +.Ic cd +will try it in +.Pa ~ +as well. +. +.It alias ls='LC_COLLATE=C ls' +This makes it so that +.Xr ls 1 +lists files in ASCIIbetical order, +which puts capitalized names like +.Pa README +and +.Pa Makefile +first. +. +.It git config --global commit.verbose true +Not shell but close enough. +This makes it so the entire diff is shown +below the usual summary +in the editor for a +.Xr git-commit(1) +message. +Useful for doing a quick review +of what you're committing. +.El +. +.Ss (neo)vim +.Bl -tag -width Ds +.It set inccommand=nosplit +This is the only +.Xr nvim 1 +feature I really care about +aside from the improved defaults. +This provides a live preview of what a +.Ic :s +substitution command will do. +It makes it much easier to +write complex substitutions. +. +.It nmap <leader>s vip:sort<CR> +This mapping sorts the lines of a paragraph, +or block of text separated by blank lines. +I use this a lot to sort +#include directives. +. +.It nmap <leader>S $vi{:sort<CR> +Similar to the last mapping, +this one sorts lines inside braces. +I use this to sort +switch statement cases +or array initializers. +. +.It nmap <leader>a m':0/^#include <<CR>:nohlsearch<CR>O#include < +I use this mapping to add new +#include directives, +usually followed by +.Ic <leader>s +and +.Ic '' +to sort them +and return to where I was. +. +.It nmap <leader>d :0delete<CR>:0read !date +'.Dd \e%B \e%e, \e%Y'<CR> +I use this to replace the first line of +.Xr mdoc 7 +files with the current date. +.El +. +.Ss C +.Bl -tag -width Ds +.It #define Q(...) #__VA_ARGS__ +This is what I've started using +to quote things like SQL statements +or HTML fragments in C. +Anything that happens to be valid C tokens, +which is most code, +can be quoted this way. +Macros are not expanded +inside the quoted part. +You can embed (matched) quotes +without having to escape them. +Whitespace gets collapsed, +so you can write nicely formatted multi-line SQL +that doesn't mess up your debug logging, +for example. +.Bd -literal -offset indent +const char *sql = Q( + INSERT OR IGNORE INTO names (nick, user, host) + VALUES (:nick, :user, :host); +); +.Ed +. +.It #define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit +I use this macro to declare bitflag enums. +It takes advantage of +auto-incrementing enum items +so you don't need to set the values manually. +You also get constants +for both the bit index +and the flag value +for each item. +.Bd -literal -offset indent +enum Attr { + BIT(Bold), + BIT(Reverse), + BIT(Italic), + BIT(Underline), +}; +.Ed +.Pp +For example, +defines +.Sy ItalicBit = 2 +and +.Sy Italic = 1 << 2 . +Ignore the extraneous constants. +. +.It typedef int FnType(const char *str, size_t len); +You can just typedef function types! +It annoys me more than it probably should +that everyone writes ugly +function pointer typedefs. +Just stick +.Sy typedef +on the front of a function declaration +and use +.Vt FnType * . +.El +. +.Sh AUTHORS +.Mt june@causal.agency diff --git a/www/text.causal.agency/014-using-vi.7 b/www/text.causal.agency/014-using-vi.7 new file mode 100644 index 00000000..e6a6a7a0 --- /dev/null +++ b/www/text.causal.agency/014-using-vi.7 @@ -0,0 +1,135 @@ +.Dd January 11, 2021 +.Dt USING-VI 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Using vi +.Nd simpler tools +. +.Sh DESCRIPTION +Happy new year +and hello from +.Xr vi 1 ! +I'm in the mood to post something +but not in the mood for +.Dq social +media. +This one will probably be short. +. +.Pp +Yesterday I was trying to work on sandboxing +.Xr catgirl 1 +(that's the IRC client I work on) +with +.Xr pledge 2 +and +.Xr unveil 2 +on +.Ox , +as suggested by the maintainer of its port. +I've done similar things before, +but only on server software +rather than user software. +. +.Pp +Anyway I was in +.Xr ssh 1 +to my +.Ox +VM +.Po +sadly I don't currently have any hardware to run +.Ox +on +.Pc +using my usual editor, +which is +.Xr nvim 1 . +I'm honestly not very thrilled +with what neovim is doing lately, +but the cleaned up defaults +make my configuration files happier. +. +.Pp +The real problem with +.Xr nvim 1 , +though, +is that it's laggy as hell on +.Ox . +There is significant delay +on every single keystroke, +as if I'm typing remotely to a server +on the other side of the world, +but this is on a local VM! +. +.Pp +So I did the only reasonable thing: +I typed +.Sy :qa +followed by +.Sy vi . +The difference was astonishing. +Typing and editing suddenly felt +.Em physical +again. +(I put that in italics even though I know it won't render.) +Not only was it a vast improvement over +.Xr nvim 1 +in +.Xr ssh 1 +in a VM, +it was a marked improvement over +.Xr nvim 1 +running locally and natively. +. +.Pp +Now obviously +.Xr vi 1 +doesn't have all the bells and whistles +of newer editors, +but of course the core editing model +that makes +.Xr vim 1 +and +.Xr nvim 1 +so good is there, +and in purer form, +I think. +The +.Xr vi 1 +manual page +is feasible to just sit down and read, +and learn everything there is to know about the editor. +I set up a basic configuration +and got coding. +.Bd -literal -offset indent +export EXINIT='set ai ic sm sw=4 ts=4' +.Ed +. +.Pp +After I finished my +.Xr pledge 2 +and +.Xr unveil 2 +patch, +I was so pleased with +.Xr vi 1 +that I kept on using it +yesterday and today +for other work, +and obviously to write this post. +Despite the lack of editor amenities, +its responsiveness and simplicity +are enough to make using it +.Em comfortable +and perhaps +.Em cosy . +I'm not sure I'll ever use +.Xr vi 1 +full-time, +but for now I am much less likely +to launch +.Xr nvim 1 . +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/015-reusing-tags.7 b/www/text.causal.agency/015-reusing-tags.7 new file mode 100644 index 00000000..19546496 --- /dev/null +++ b/www/text.causal.agency/015-reusing-tags.7 @@ -0,0 +1,155 @@ +.Dd January 17, 2021 +.Dt REUSING-TAGS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm reusing tags +.Nd beyond ctags +. +.Sh DESCRIPTION +I've tried to start writing this post a couple times now +and I keep getting bogged down in explanations, +so I'm just going to tell you +about some cool things I did +and hope they make sense. +. +.Pp +When I wrote my first syntax highlighter, +I decided that function definitions +should have anchor links, +because line number anchor links +are entirely useless +if you expect the file to change at all. +Since the syntax highlighter +was somewhat deliberately just a big pile of regex, +I hacked in more regex to try +to identify function and type definitions. +It wasn't elegant and it didn't always work well. +It did work though, +and I found the links very useful. +. +.Pp +Recently I was thinking about +the lexer generator +.Xr lex 1 +and decided to +rewrite the syntax highlighter +using it. +Really syntax highlighting +is no different than lexical analysis. +I ran into a problem though, +trying to preserve my anchor link function, +because really that should involve +some amount of parsing. +Trying to port my regex hacks to +.Xr lex 1 +made the lexers way more complicated +and less reliable, +so I gave up on it for a while. +. +.Pp +And then, +probably in the shower, +I realized I was approaching it +completely from the wrong direction. +There's already a tool that does what I want, +and I already use it: +.Xr ctags 1 . +All I need to do is use its output +to insert anchor links +into my syntax highlighter output. +In an afternoon I wrote +.Xr htagml 1 , +which loads tag definitions for its input file, +then scans through the input for where they match. +It can either HTML-escape +the input as it goes, +or use already formatted HTML +being piped into it from a syntax highlighter. +. +.Pp +The result is three simple tools +working together to accomplish +what a more complex tool +couldn't reliably achieve. +I'm very pleased with it, +and I've updated my site and cgit +to use the new +.Xr lex 1 Ns -based +highlighter, +.Xr ctags 1 +and +.Xr htagml 1 . +I'm currently missing a lexer for +.Xr sh 1 , +but I plan to write it eventually. +I also want to write a tool +to generate tags for +.Xr make 1 , +.Xr mdoc 7 +and perhaps +.Xr sh 1 . +The cool thing about generating more kinds of tags +is that they'll not only improve +the HTML output, +they'll also be usable in my editor. +. +.Pp +Speaking of generating different kinds of tags, +I also wrote some scripts not too long ago +for reading IETF RFCs offline. +The plain text files are available to +.Xr rsync 1 , +but they're hard to navigate on their own. +By scanning the files for headings +and generating tags, +it allows jumping to sections using +.Ic :ta +or +.Ic ^] +in +.Xr vi 1 . +For +.Xr nvim 1 +I also added an +.Ic :RFC +command to open an RFC by number +and set up +.Ic ^] +to work optimally for them. +. +.Pp +I'm still using +.Xr vi 1 +for most of my editing, +by the way. +And of course +.Xr ctags 1 +was made to work with it! +Simple old tools +are really doing it for me lately. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://causal.agency/bin/htagml.html htagml +.It +.Lk https://causal.agency/bin/hilex.html hilex +.It +.Lk https://git.causal.agency/src/tree/doc/rfc rfctags +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Sh ADDENDUM +.Xr catgirl 1 , +.Xr pounce 1 , +.Xr litterbox 1 +and +.Xr scooper 1 +all have new releases, +if you're using any of them. +Also, this space is now +available over gopher, +if that's your sort of thing. diff --git a/www/text.causal.agency/016-using-openbsd.7 b/www/text.causal.agency/016-using-openbsd.7 new file mode 100644 index 00000000..b843e3c3 --- /dev/null +++ b/www/text.causal.agency/016-using-openbsd.7 @@ -0,0 +1,505 @@ +.Dd February 14, 2021 +.Dt USING-OPENBSD 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Using OpenBSD +.Nd for real +. +.Sh DESCRIPTION +Hello from +.Ox ! +After wishing one too many times +that I had a real BSD +on a physical machine, +I finally got around to +just installing one on my +mid-2014 MacBook Pro. +I hadn't done it sooner +because I didn't realize +how easy it would be. +It helped that I already had a +.Dq Boot Camp +partition with a disused Windows 8 install +that I could replace. +. +.Pp +I roughly followed an old jcs gist +along with the +.Ox +Disk Setup guide. +I'm once again happy +that I bought a printer\(em +they're very useful for instructions +to install an operating system +on your only usable computer. +I set up encrypted softraid +and the operating system +installed smoothly. +. +.Pp +Next I had to install rEFInd, +since the default Mac boot manager +is really not keen on booting much. +Installing it requires using the +macOS recovery partition these days. +But there was a problem +with my new boot menu: +I was promised a picture of Puffy, +and instead I just got some abstract coloured circles! +Turns out a bunch of OS icons +got removed from rEFInd at some point, +and I had to rescue Puffy +from the git history. +. +.Pp +So I could happily boot +.Ox +by selecting Puffy, +but I had no networking. +I thought the wifi chip might be supported by +.Xr bwfm 4 , +but I got unlucky and it's a BCM4360, +which everything hates. +Based on the jcs gist, +I checked the list of hardware +supported by the +.Xr urtwn 4 +driver for a wifi dongle to order. +Just having a clear list +in the driver manual is wonderful. +I went with the Edimax EW-7811Un v2, +which I could get for around $20. +It's nice and tiny, +though it has a piercing blue LED +(destroy all blue LEDs) +which I had to cover with electrical tape. +. +.Pp +I had to do one other thing +before I could get it all working, though. +When I had checked the +.Xr urtwn 4 +hardware list, +I had been looking at +.Ox Ns -current , +but I had installed +.Ox 6.8 , +and support for the v2 hardware +I had bought was added after that release. +So I downloaded a snapshot +.Pa bsd.rd +along with the +.Xr urtwn 4 +firmware file +to a USB drive +and upgraded the system. +. +.Pp +Connecting to wifi with +.Xr ifconfig 8 +is a breeze, by the way, +and then you just write the same thing to a +.Xr hostname.if 5 +file to make it automatic. +I wanted to use +.Ox +for exactly this reason: +simple, consistent, cohesive, well-documented tools. +. +.Pp +Finally, I got to configuring. +The console is configured with +.Xr wsconsctl 8 , +and similarly you can put the commands in +.Xr wsconsctl.conf 5 +to have them run at boot. +I added +.Li display.brightness=50% +to tone down the brightness, +which is initially 100%, +and +.Li keyboard.backlight=0% +to turn off those annoying lights. +.Xr wsconsctl.conf 5 +is also where you can set +trackpad settings if you're not using +.Xr synaptics 4 . +I ended up using: +.Bd -literal -offset indent +mouse1.tp.tapping=1 +mouse1.tp.scaling=0.2 +mouse1.reverse_scrolling=1 +.Ed +.Pp +This enables tapping with several fingers +to simulate different mouse buttons, +makes the cursor move at a reasonable speed +and scrolling move in the right direction. +I also set up my usual modified QWERTY layout. +. +.Pp +For +.Xr X 7 +I had enabled +.Xr xenodm 1 , +which seems quite nice. +It automatically prompts you to add your +.Xr ssh 1 +keys to +.Xr ssh-agent 1 +when you log in. +One of the reasons I had not wanted +to set up another graphical system +is that I thought +I would have to make too many choices, +and that I would have to choose least bad options +rather than actually good options, +but +.Ox +already includes reasonable choices. +I wanted to use +.Xr cwm 1 , +so I started a basic +.Pa .xsession +file: +.Bd -literal -offset indent +\&. ~/.profile +export LC_CTYPE=en_US.UTF-8 +xset r rate 175 m 5/4 0 +xmodmap ~/.config/X/modmap +xrdb -load ~/.config/X/resources +exec cwm -c ~/.config/cwm/cwmrc +.Ed +. +.Pp +The +.Xr xset 1 +command sets keyboard repeat rate +and mouse acceleration. +I spent some time going through +.Xr cwm 1 Ap s +functions and writing up bindings +that would get me something close enough +to what I'm used to in macOS. +Most importantly, +putting everything on the 4 modifier (command key). +. +.Pp +I also added key bindings on F1 and F2 +to adjust the brightness with +.Xr xbacklight 1 , +and on F10, F11 and F12 +to adjust volume with +.Xr sndioctl 1 . +I'm not sure why the F keys +just send regular F1, F2, etc.\& +regardless of the Fn key. +I don't use F keys for anything else though, +so I'm not too concerned. +Once again, +.Xr sndioctl 1 +is such an easy straightforward tool: +.Bd -literal -offset indent +bind-key F10 "sndioctl output.mute=!" +bind-key F11 "sndioctl output.level=-0.05" +bind-key F12 "sndioctl output.level=+0.05" +.Ed +. +.Pp +For aesthetic configuration, +I added a new output to my +.Xr scheme 1 +colour scheme tool for +.Xr X 7 Ns -style +RGB and +.Xr xterm 1 +resources. +Normally I use the +.Em Go Mono +font, +but since +.Ox +already includes +.Em Luxi Mono , +which +.Em Go Mono +is based on, +I used that. +The most important configuration +to make anything readable on a high-DPI display is: +.Bd -literal -offset indent +Xft.dpi: 144 +Xft.antialias: true +Xft.hinting: false +.Ed +. +.Pp +I'm annoyed that I haven't found +where these resources are actually documented. +I would hope they'd be in +.Xr Xft 3 +or something, +but they're not. +Anyway, +turning off hinting +seems absolutely necessary +to prevent text from looking like garbage. +. +.Pp +It seems that to get a reasonably sized cursor +I need to install +.Sy xcursor-dmz . +I'd prefer if there wasn't this one +extra package that I needed +for a reasonable setup. +Tangentially, +I've never understood why +the black versions of dmz cursors +are called +.Dq aa +when it seems like that +would stand for antialiasing +or something. +.Bd -literal -offset indent +Xcursor.size: 64 +Xcursor.theme: dmz-aa +.Ed +. +.Pp +For a desktop background, +I found a cute bitmap (little picture) +of snowflakes already in the system +and used colours from my usual scheme: +.Bd -literal -offset indent +xsetroot -bitmap /usr/X11R6/include/X11/bitmaps/xsnow \e + -bg rgb:14/13/0E -fg rgb:7A/49/55 +.Ed +. +.Pp +Since I'd rather not install anything +I don't have to, +I went with the default +.Xr xterm 1 . +It seems more than adequate, honestly. +I read through its RESOURCES +section to configure it how I like. +The important settings are +.Sy XTerm*utf8 +and +.Sy XTerm*metaSendsEscape . +Since I'm used to copying and pasting on macOS, +I added equivalent +.Dq translations : +.Bd -literal -offset indent +XTerm*VT100*translations: #override \en\e + Super <Key>C: copy-selection(CLIPBOARD) \en\e + Super <Key>V: insert-selection(CLIPBOARD) +.Ed +. +.Pp +The next thing I needed +was a clock and battery indicator. +I actually had my battery die on me +while I was doing all this, +which reminded me. +.Xr xclock 1 +would be perfect, +but then I'd need something else +for battery. +There are a couple basic battery indicators +for X in ports, +but they're terribly ugly. +I wanted something as simple as +.Xr xclock 1 , +but that I could add some other text to. +Then I realized I could just use +.Xr xterm 1 +for that. +To my +.Pa xsession +I added: +.Bd -literal -offset indent +xterm -name clock -geometry 14x1-0+0 -sl 0 -e clock & +.Ed +.Pp +This places a little terminal +in the top-right corner of the screen +with no scrollback lines, +running a script called +.Pa clock . +To have +.Xr cwm 1 +treat it like a +.Dq panel +and show it on every desktop, +I added this to my +.Pa cwmrc : +.Bd -literal -offset indent +ignore clock +autogroup 0 clock,XTerm +.Ed +.Pp +The +.Pa clock +script simply uses +.Xr date 1 +and +.Xr apm 8 +to print the time and battery charge +every minute: +.Bd -literal -offset indent +tput civis +sleep=$(( 60 - $(date +'%S' | sed 's/^0//') )) +while :; do + if [ $(apm -a) -eq 1 ]; then + printf '%3s%%' "$(apm -l)" + else + test $(apm -b) -eq 2 && tput setaf 1 bold + printf '%3.3sm' "$(apm -m)" + tput sgr0 + fi + printf ' %s\r' "$(date +'%a %H:%M')" + sleep $sleep + sleep=60 +done +.Ed +.Pp +The initial setting of +.Va sleep +is to align the updates +with the minute ticking over. +I made the battery output +a bit fancier by showing +percentage while charging, +minutes left while discharging, +and highlighting in red +when the battery is +.Dq critical . +. +.Pp +Now is a good time to mention adding +.Ql apmd_flags=-A +to +.Pa /etc/rc.conf.local +to enable +.Dq automatic performance adjustment , +or not running your battery flat +as fast as possible mode. +It seems like I can get up to 3 hours +of battery life depending on the screen brightness, +but this is quite an old battery by now. +. +.Pp +The other thing I needed +was something to tone down +that awful, evil blue light from the screen. +I asked around and someone told me about +.Xr sct 1 , +originally written by tedu. +The package also includes a little +.Xr sctd 1 +script that you can add to your +.Pa .xsession +to have it automatically adjust +the colour temperature throughout the day. +My eyes are no longer being assaulted. +. +.Pp +While I was doing all this, +I of course needed to talk about it on IRC, +and it was very nice to be able to +install my own IRC client with: +.Bd -literal -offset indent +doas pkg_add catgirl +.Ed +.Pp +I don't plan to do +general Web Browsing on +.Ox , +and there is definitely +no good choice for browser, +so I just installed +.Xr imv 1 , +.Xr mpv 1 , +.Xr youtube-dl 1 +and +.Xr w3m 1 . +I wrote a script +to open images by piping +.Xr curl 1 +into +.Xr imv 1 , +videos with +.Xr mpv 1 , +and everything else with +.Xr w3m 1 +in a new +.Xr xterm 1 . +Annoyingly, +.Xr mpv 1 +seems incapable of exiting +without segfaulting. +That's quality. +. +.Pp +One thing I am still missing +is automatic brightness adjustment +based on ambient light +like macOS can do. +I can read the sensor with +.Xr sysctl 8 +.Cm hw.sensors.asmc0.illuminance0 , +which is measured in lux. +I tried doing something with it in a script, +but it seems tricky to map its value +to brightness adjustments +and to play nice with manual brightness changes, +so I'll just keep doing it manually for now. +. +.Pp +Update: +prx sent mail to let me know about +.Aq Lk https://github.com/jcs/xdimmer . +I should've guessed jcs had written something. +. +.Pp +And that's my current +.Ox +setup after a week of using it. +I'm quite enjoying it, +and still being pleasantly surprised +by the quality-of-life from +.Ox +tools and documentation. +For a small example, +I can jump to sections +or flag definitions in +.Xr man 1 +using +.Ic :t . +Systems without basic usability like that +should be ashamed. +. +.Pp +I would post a screenshot, +but this is +.Li text.causal.agency +;) +. +.Sh SEE ALSO +.Lk https://gist.github.com/jcs/5573685 +.Pp +My full configurations are in +.Aq Lk https://git.causal.agency/src . +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Sh BUGS +There's a red LED +inside the headphone jack +that is always on +and I have no idea how to turn off. +If anyone knows +please send me an email. diff --git a/www/text.causal.agency/017-unpasswords.7 b/www/text.causal.agency/017-unpasswords.7 new file mode 100644 index 00000000..f9643f2f --- /dev/null +++ b/www/text.causal.agency/017-unpasswords.7 @@ -0,0 +1,153 @@ +.Dd February 20, 2021 +.Dt UNPASSWORDS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Unpasswords +.Nd password anti-management +. +.Sh DESCRIPTION +Right away I want to say +that I'm not trying to tell anyone +how to manage their online authentication. +This is just how I do it, +and I haven't seen anyone else write about it. +. +.Pp +I don't use a password manager. +It's not a type of software +I want to deal with. +For the small handful of sites +that I use regularly +and that actually matter, +I use strong passwords +(stored in my noggin) +and TOTP. +For everything else, +I simply do not know the password, +and neither does any software. +. +.Pp +I think I started doing this one time +when I had legitimately forgotten +the password to some old account. +I clicked on +.Dq forgot my password +and opened the email, +but I didn't want to +come up with a new password +I would just forget again. +Instead I set a random one +.Po +I usually use +.Ql openssl rand -base64 33 +for this +.Pc +and immediately used that to log in +while it was still in my clipboard. +Next time I wanted to log in, +I could use +.Dq forgot my password +again. +. +.Pp +Thinking about it, +I realized that any web authentication +with an email password reset flow +is only ever as strong as +the authentication for your email account. +So what is the point of having +all these passwords set on different sites? +They all answer to your email account, +and storing them in a password manager +seems to add another potential point of failure. +May as well have no other passwords at all, +or as close as possible. +.Po +Shout out to sites like Liberapay +and asciienema +which let me not set a password at all. +.Pc +. +.Pp +So I started doing that for any site +that I don't regularly log in to. +Going through the password reset flow +can be a bit slow, +but it doesn't need to be done often. +And I can do it from anywhere +I have access to my email, +which I feel is more easily reliable +than syncing password management databases. +It's quite stress-free. +. +.Pp +After doing this manually for years, +this week I finally got around to +writing some automation for it. +A while ago I had written +.Xr imbox 1 , +a tool to directly export mail +in mboxrd format from IMAP, +along with +.Xr git-fetch-email 1 , +a wrapper which offloads configuration to +.Xr git-config 1 . +It can match emails by +Subject, From, To and Cc. +This week I added a flag +to use IMAP IDLE +to wait for a matching message +if there isn't one already, +and a flag to move matching messages +(for example to Trash) +after exporting them. +. +.Pp +With those two new flags, +I started writing some shell scripts +to automate the password reset flow +using +.Xr curl 1 +to submit forms and +.Xr git-fetch-email 1 +with +.Xr sed 1 +to pull the reset tokens +from my inbox. +At the end of the script, +the random password it set +is copied to the clipboard +and the login page for the site is opened. +So now logging in is as simple +as running a command, +waiting for the login page to open, +and pasting. +. +.Pp +The script isn't sophisticated, +but I don't think it needs to be. +I've written functions +for a couple different sites already, +and they all work in mostly the same way. +Writing a new one is just a matter +of identifying the form URLs and fields +along with where the token is in the email. +I'm not going to turn this automation +into any kind of generally usable project, +because I don't want to have to +maintain functions for tonnes of different services. +If you're interested in this idea, +I encourage you to use my script as a template +and implement the functions for services you use. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://git.causal.agency/imbox +.It +.Lk https://causal.agency/bin/sup.html +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/018-operating-systems.7 b/www/text.causal.agency/018-operating-systems.7 new file mode 100644 index 00000000..691102e2 --- /dev/null +++ b/www/text.causal.agency/018-operating-systems.7 @@ -0,0 +1,86 @@ +.Dd February 22, 2021 +.Dt OPERATING-SYSTEMS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Operating systems +.Nd criteria +. +.Sh DESCRIPTION +Sometimes in conversation +I use the term +.Dq real operating system +which people, +perhaps rightfully, +take as inflammatory. +But I have actually thought about +what I mean when I say +.Dq real operating system +and come up with +this list of criteria. +. +.Pp +An operating system should be... +.Bl -bullet +.It +Consistent and cohesive: +all parts of the system should have similar +usage, configuration, documentation and so on. +Parts of the system should naturally work together, +because they were designed to do so. +. +.It +Documented: +the system should include its own documentation. +A user should not have to +search some external wiki +to learn how the system works. +It should be obvious +where to find documentation +on a particular topic. +. +.It +Programmable: +the system should provide +a way to program the computer. +A computer which cannot be programmed +is not a computer at all. +Usually this takes the form +of a C compiler +and the tools that go with it. +In earlier times, +it might have been +a BASIC interpreter. +. +.It +Examinable and modifiable: +the full source tree +for the system should be included, +or easily obtainable +through official means. +A user should have no trouble +finding the corresponding source +for a part of the system. +Together with the previous point, +the source tree should be +compiled by the included toolchain, +allowing local modification. +.El +. +.Pp +Some things that may be parts +of real operating systems, +but are not themselves operating systems: +a kernel, +a package manager, +a collection of packages. +. +.Pp +I will leave it as an +.Dq exercise for the reader +to guess which operating systems +meet these criteria +and which don't. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/019-mailing-list.7 b/www/text.causal.agency/019-mailing-list.7 new file mode 100644 index 00000000..b3490a94 --- /dev/null +++ b/www/text.causal.agency/019-mailing-list.7 @@ -0,0 +1,286 @@ +.Dd March 4, 2021 +.Dt MAILING-LIST 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Mailing List +.Nd a small-scale approach +. +.Sh DESCRIPTION +When I initially published +some software I expected +other people to use, +I just asked that patches +be mailed directly to me, +but I figured that +if more people were interested, +it would be better +to have a mailing list. +Unfortunately +email software, +mailing list options in particular, +are quite daunting. +I wanted a light-weight option +that would require me to host +as little software as possible. +. +.Pp +My regular email is hosted by Fastmail, +and I poked around its settings +to see what I could do. +It turns out Fastmail lets you +configure address aliases to +.Dq also send to all contacts in +a contacts group. +That's a mailing list! +I created a group called +.Dq List +and an alias called +.Mt list@causal.agency +configured to deliver to that group. +So it's really just an alias +for my regular address +that happens to also +deliver to another group of people. +. +.Pp +It's easier to just configure +and manage one mailing list, +so what I do is ask patches and feedback +to be sent to +.Mt list+catgirl@causal.agency , +for example. +Fastmail treats any +.Ar +suffix +the same as the base address, +but the full address can be used +by subscribers to filter mail by topic +if they wish. +. +.Pp +To subscribe someone to the list, +I add their contact to the group. +For a long time I was planning +to write some software +to manage these subscriptions. +It should be possible +to process subscription requests from IMAP +and manipulate the contact group with CardDAV. +When I went to start implementing this, +however, +I found CardDAV (and WebDAV in general) +completely inscrutable. +It's the kind of protocol +that is split across like 20 +different RFCs +and you can't understand anything +by just reading +the one you actually care about. +So I've given up on that +and will keep manually subscribing people +on request. +. +.Pp +The only thing missing, then, +is a way for people to read +mail sent to the list +while they aren't subscribed. +All the existing +mailing list archive software +I know of +expects to have the mail locally, +but I'd rather keep all my mail in IMAP. +First, +in order to make sure +I keep a complete archive +of the mailing list in IMAP, +I added a small amount +of Sieve code +to my Fastmail filters configuration: +.Bd -literal -offset indent +if address :matches ["To", "Cc"] "list*@causal.agency" { + fileinto :copy :flags "\e\eSeen" "INBOX.List"; +} +.Ed +. +.Pp +Sieve is a small standard language +specifically for filtering mail. +This bit of code matches +anything sent to the list +and adds a copy of it +(the original is going into my inbox) +to the +.Dq List +folder +and marks the copy as read. +. +.Pp +With a pristine IMAP mailbox +to export from, +I wrote a new archive generator. +It's called +.Xr bubger 1 +kirg (have it in a way). +My goal was to render directly from IMAP +and produce only static files as output, +making it not only easy to serve, +but also to run in one place +and copy the files elsewhere. +That's important to me +because it has access to my email, +so I'd rather run it +on my local network and +.Xr rsync 1 +its output into The Cloud. +The static files are in +HTML, Atom and mboxrd formats. +. +.Pp +The architecture of +.Xr bubger 1 +is that for each piece of mail, +identified by its UID in the mailbox, +HTML and Atom fragments +are exported along with the mboxrd. +Those fragments are then stitched together +using the IMAP SORT and THREAD extensions +to make full pages and feeds +for each thread. +The fragments act as a cache +for subsequent runs. +. +.Pp +I admit I did some +pretty questionable things +to achieve this. +Namely, +I wrote a small string templating engine in C. +I use it to produce the HTML +and XML for Atom, +as well as to generate URLs +and paths. +I'm really happy with how it works, actually. +This is also where +I really started using +one of my favourite C hacks: +.Bd -literal -offset indent +#define Q(...) #__VA_ARGS__ +.Ed +. +.Pp +I quote all my HTML/XML templates +with this and it's lovely. +. +.Pp +I've been working on +.Xr bubger 1 +on and off for almost a year now, +and it's been interesting. +I learned a lot about how email +works from having to deal with +all the ways a message can be. +Thankfully a lot of that dealing +is done by the IMAP server. +. +.Pp +As for running it, +I initially just ran it with +.Xr cron 8 , +and that's still a good way to go. +To hook it up to +.Xr rsync 1 , +pipe it like so: +.Bd -literal -offset indent +bubger -C list [...] | rsync -a --files-from=- list remote:list +.Ed +. +.Pp +Later, +I got a little annoyed +with having to wait +for the next run +if I wanted to link +to some mail I just received. +I added an option +to use IMAP IDLE +to wait for new mail continuously +and I started running it +under my process supervisor, +.Xr catsitd 8 . +. +.Pp +The setup is a little more complex +to feed the list of updated files to +.Xr rsync 1 . +I added the +.Xr catsit-watch 1 +utility to run a command +when a file changes, +and in my +.Xr catsit.conf 5 +I have the following: +.Bd -literal -offset indent +bubger ~/.local/libexec/bubger +rsync catsit-watch -i -f ~/list/UIDNEXT ~/.local/libexec/rsync +.Ed +. +.Pp +The +.Pa ~/.local/libexec/bubger +script runs +.Xr bubger 1 , +writing the list of updated paths to +.Pa ~/list/FILES : +.Bd -literal -offset indent +exec bubger -i -C ~/list [...] >~/list/FILES +.Ed +. +.Pp +And the +.Pa ~/.local/libexec/rsync +script gets run each time a +.Xr bubger 1 +update completes +.Po +.Pa UIDNEXT +is always the last file written +.Pc +and copies the listed files +to the remote host: +.Bd -literal -offset indent +exec rsync -a --files-from=$HOME/list/FILES ~/list remote:list +.Ed +. +.Pp +I haven't tagged any +.Xr bubger 1 +releases yet +because it hasn't gotten +a huge amount of testing, +and I'm not sure anyone but me +would even want to use it. +But I'm happy +with how it's working right now, +so I might tag 1.0 soon +just for fun. +. +.Sh SEE ALSO +.Bl -item -compact +.It +.Lk https://causal.agency/list/ +.It +.Lk https://git.causal.agency/bubger/about +.It +.Lk https://git.causal.agency/catsit/about +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Sh BUGS +Almost every time +I try to type +.Dq mailing list +I instead type +.Dq mailist list . diff --git a/www/text.causal.agency/020-c-style.7 b/www/text.causal.agency/020-c-style.7 new file mode 100644 index 00000000..9816dbc3 --- /dev/null +++ b/www/text.causal.agency/020-c-style.7 @@ -0,0 +1,172 @@ +.Dd March 16, 2021 +.Dt C-STYLE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm C Style +.Nd a rough description +. +.Sh DESCRIPTION +This is a rough description +of the style in which I write C, +since it's uncommon +but some people seem to like it. +I don't have any hard rules, +it just needs to look right. +. +.Ss Superficialities +I use tabs +and they're set to 4 characters wide +in my editor. +I keep my lines shorter than 80 columns, +which I enforce by +not resizing my terminal's width. +I use block indentation only, +meaning I write long function calls +like this: +.Bd -literal -offset indent +fprintf( + imap.w, "%s UID THREAD %s UTF-8 %s\er\en", + Atoms[thread], algo, search +); +.Ed +.Pp +Anything that can be sorted +should be sorted, +with trailing commas where possible. +This and block indentation +make for simpler diffs. +.Pp +I either write single-line ifs +or always use braces. +I put parentheses +around ternary expressions. +I use camelCase +for functions and variables, +and PascalCase for types and constants. +When an acronym appears +in an identifier, +it's in either all lower case +or all upper case. +The despicable SCREAMING_SNAKE_CASE +is reserved for macros. +I don't set globals or statics to zero +since that is already the default. +I don't compare against zero or NULL +unnecessarily. +. +.Ss \&No side-effects in control flow +I never write a function call +with side-effects +inside the condition of an if statement. +I find this makes following the +.Dq happy path +through functions +much easier. +I write things like this: +.Bd -literal -offset indent +pidFile = open(pidPath, O_WRONLY | O_CREAT | O_CLOEXEC, 0600); +if (pidFile < 0) err(EX_CANTCREAT, "%s", pidPath); + +error = flock(pidFile, LOCK_EX | LOCK_NB); +if (error && errno != EWOULDBLOCK) err(EX_IOERR, "%s", pidPath); +if (error) errx(EX_CANTCREAT, "%s: file is locked", pidPath); +.Ed +.Pp +I do write side-effects +inside for and while statement heads, +since that's generally expected. +For some reason +I like to write the constant first +if I'm comparing the result of an assignment +with a side-effect. +.Bd -literal -offset indent +for (ssize_t len; 0 <= (len = getline(&buf, &cap, file)); ++line) +.Ed +. +.Ss Paragraphs +I leave blank lines +between logical chunks of +.Dq things happening . +This is usually between side-effects +with their related error handling, +or between groups of closely related side-effects. +I try to keep variable declarations +glued to the top of the bit of code +they're used in. +. +.Ss Leading break +I've mentioned this previously. +I write my switch statement breaks +before each case label. +Doing this aligns nicely, +and being in the habit +means I always avoid +accidental fallthrough. +.Bd -literal -offset indent +switch (opt) { + break; case 'a': append = 1; + break; case 'd': delay = strtol(optarg, NULL, 10); + break; case 'f': watch(kq, optarg); + break; case 'i': init = 1; + break; default: return EX_USAGE; +} +.Ed +. +.Ss Function type definitions +Function types are always typedef'd, +and it's the function type itself +that is defined, +not a function pointer type! +I put the typedef above any functions +that are supposed to be of that type +so it's clear what the pattern is. +.Bd -literal -offset indent +typedef void Action(struct Service *service); +Action *fn = NULL; +.Ed +. +.Ss Constants +I prefer enums over #defines +for integer constants, +and static const strings over #defines +unless I want to do concatenation. +.Bd -literal -offset indent +enum { Cap = 1024 }; +.Ed +.Pp +I avoid the preprocessor +wherever possible, +with the notable exception of X macros, +which I've talked about previously. +Doing things in the actual language +makes for easier debugging. +. +.Ss Organization +I usually use only one header file +in each project. +The dependency is easy to declare +and the complete rebuild +when the header changes +isn't a problem for small projects. +Unless it's a single-file program, +I name the file which contains main +something generic, +since the name of the project +isn't relevant to its function. +I name functions like +.Ar nounVerb , +and all the functions for a +.Ar noun +are defined in +.Pa noun.c . +Not really to do with C, +but I always put a FILES section +in my README pages +to briefly describe +the layout of the code +for anyone looking to +read or make changes to it. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/021-time-machine.7 b/www/text.causal.agency/021-time-machine.7 new file mode 100644 index 00000000..93d35c1e --- /dev/null +++ b/www/text.causal.agency/021-time-machine.7 @@ -0,0 +1,144 @@ +.Dd April 25, 2021 +.Dt TIME-MACHINE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Time Machine +.Nd an awful one +. +.Sh DESCRIPTION +If, like me, +you have a Raspberry Pi 3 at home +that you've just upgraded to +.Fx 13.0 +which has a hard drive +from an old laptop +attached to it by USB adapter +with ZFS on it +and you want to +use that as a Time Machine +backup destination +over SMB using +.Xr samba 8 , +despite +.Xr samba 8 +being awful software +and using ZFS on a system +with only 1 GB of RAM +being a terrible idea, +this is how to do it. +. +.Pp +In +.Pa /usr/local/etc/smb4.conf : +.Bd -literal -offset indent +[global] +vfs objects = zfsacl catia fruit streams_xattr +fruit:metadata = stream +fruit:model = Macmini + +[TimeMachine] +read only = no +path = /media/zhdd/backup/TimeMachine +fruit:time machine = yes +fruit:time machine max size = 250G +.Ed +. +.Pp +The important thing here is +.Sy zfsacl +in the vfs objects list. +Most pages will tell you about the others, +but without +.Sy zfsacl +Time Machine will just fail to +create the backup +and not provide any useful error. +I'm not actually sure if the +.Sy fruit:metadata +setting is required, +but a bunch of pages recommend it. +The +.Sy fruit:model +just makes it look nice in Finder. +The rest creates an SMB share called +.Dq TimeMachine +that macOS will be willing to use. +You can limit the size of the share that +.Xr samba 8 +reports so that Time Machine +doesn't fill up the whole drive. +. +.Pp +The other important thing to do +is to create some swap space. +When I first tried backing up +to this share, +it stopped after a while +because +.Xr smbd 8 +got killed +when there was nowhere to swap pages to. +A wiki page told me to +create swap on ZFS like this: +.Bd -literal -offset indent +zfs create -V 2G \e + -o org.freebsd:swap=on \e + -o checksum=off \e + -o compression=off \e + -o dedup=off \e + -o sync=disabled \e + -o primarycache=none \e + zhdd/swap +swapon /dev/zvol/zhdd/swap +.Ed +. +.Pp +To be fair to +.Xr samba 8 , +most of the memory +is being used by the ZFS ARC +.Po +which you can see in +.Xr top 1 +.Pc , +but +.Xr smbd 8 +still seems to be using +far more memory than is reasonable. +It's interesting seeing processes +with 0 RES in +.Xr htop 1 +because they're all being swapped out +while the ARC takes half the available RAM. +And having to wait for my shell +to be paged back in when I quit +.Xr htop 1 . +. +.Pp +Anyway, +as expected this whole thing +is terribly slow. +On my initial backup, +I'm currently at 26.49 GB +of 104.22 GB +with an estimate of 8 hours remaining. +Normally transfer time estimates +are wildly inaccurate, +but I think in this case it's right. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Sh BUGS +.Fx +doesn't seem to want to mount +the ZFS volumes on the hard-drive-over-USB +automatically at boot. +I have to +.Xr zpool-import 8 +the drive manually each time. +I don't know if there's a workaround for this, +but I don't have anything essential +to the system on the drive, +and it doesn't need to reboot often. diff --git a/www/text.causal.agency/022-swans-are-dead.7 b/www/text.causal.agency/022-swans-are-dead.7 new file mode 100644 index 00000000..8664e886 --- /dev/null +++ b/www/text.causal.agency/022-swans-are-dead.7 @@ -0,0 +1,164 @@ +.Dd May 5, 2021 +.Dt SWANS-ARE-DEAD 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Swans Are Dead +.Nd album by Swans +. +.Sh DESCRIPTION +Swans Are Dead +is the best Swans album. +Among my favourites are +Soundtracks for the Blind, +To Be Kind +and Love of Life, +but Swans Are Dead +is the one I come back to +most consistently. +I'm always in the mood +to listen to these tunes. +. +.Pp +It's interesting to me +that I enjoy it so much, +I think because I had the expectation +that live albums +are not of the same quality +as studio albums, +but that's just completely untrue +in the case of Swans. +The performances are excellent +and the recording is +for the most part perfect. +The album feels live, +without any distracting deficiencies +of live recording +that would take you out +of just enjoying the music. +. +.Bl -ohang +.It Dq Feel Happiness +This track feels kind of special +since it's the only song on the album +that was never released +as part of another project. +I absolutely love this format of song. +It's like 10 minutes of build +before any lyrics happen, +which you only get after +the wave of the first part +of the song collapses. +It bookends the first disc nicely with +.Dq Blood Promise, +I think, +which is sort of the reverse. +. +.It Dq Blood On Yr Hands +This is such a great start +to the Jarboe-focused +section of the black disc. +A cappella apart from the hum +of the equipment on stage, +I love this vocal performance. +I sing this song, +terribly, +in the shower. +The lack of instrumental +seems to make it stick in my mind even more. +. +.It Dq I Crawled +This is another great vocal performance +by Jarboe. +It's so much more dynamic and intense +than the version of this song +released much earlier on Young God +with Gira's vocals. +I remember seeing a bad comment +somewhere online +from someone who couldn't stand +any Swans song Jarboe sang on. +They must have never heard +this version of +.Dq I Crawled. +Incredible. +. +.It Dq Blood Promise +My favourite track on +Swans Are Dead, +by far. +I had actually never heard of +.Dq The Whiffenpoof Song +until I looked up +the recording they use +to introduce this song +and indicate it's the last of the show. +Anyway, +this track highlights +what makes Swans live albums +so interesting. +This performance of the song +has evolved so much +from the studio recording on +The Great Annihilator. +They share the same lyrics, +but the earlier version is only 4:15, +to the live version's fifteen and a half minutes! +And it sucks me in the whole time. +As the song winds down +you can hear an audience member yell, +.Dq Don't stop! +and I agree. +. +.It Dq The Sound +One of my all-time favourite songs. +It's the one that got me to listen to +Soundtracks for the Blind, +and might've gotten me into Swans altogether. +I don't quite remember +what order I started listening to things in. +This version of it is great. +I don't think I could choose +between this and the studio recording. +There are just +two ways to enjoy it. +I love how frantic the guitars get +at the height of this track. +. +.It Dq I See Them All Lined Up +This version of the song +is way more harsh +than the version on Soundtracks. +It loses some contrast +between the verses +and the explosions of sound +punctuating them, +it just hits hard +the whole time. +. +.It Dq Yum Yab +An absolute banger. +The drums sound so good on this +and they really get me moving. +The whole thing is delightfully nasty and fun. +.El +. +.Pp +Everything else on the album +is good too, +of course, +I just don't have as much to say. +There's almost two and a half hours of music +on this thing! +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +I want to try writing +about different kinds of things here, +and this is my first attempt +at doing so. +There's more music +I want to write about, +and maybe some other +non-computer topics. diff --git a/www/text.causal.agency/023-sparse-checkout.7 b/www/text.causal.agency/023-sparse-checkout.7 new file mode 100644 index 00000000..925bc043 --- /dev/null +++ b/www/text.causal.agency/023-sparse-checkout.7 @@ -0,0 +1,144 @@ +.Dd June 9, 2021 +.Dt SPARSE-CHECKOUT 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Sparse Checkout +.Nd a cool git feature +. +.Sh DESCRIPTION +I was going to write a post about +.Xr git-subtree 1 +(and I still plan to!) +but while talking about it +with a friend +I came across another command: +.Xr git-sparse-checkout 1 . +I got pretty excited because +I already had a use case for it. +. +.Pp +.Xr git-sparse-checkout 1 +does pretty much what it sounds like. +It lets you only have +a subset of files in the repository actually +.Dq checked out . +This is really useful +for huge respositories +where you are only interested in +some part of it. +Any operation touching the working tree +is much faster because +it can skip all the files you don't care about. +. +.Pp +My use case is with the +.Fx +.Xr ports 7 +tree, +which recently moved to git +and contains almost 14 thousand files. +Working with the whole repository +was super painful. +.Xr git-status 1 , +which I run as a habit +when my shell is idle, +would take dozens of seconds +to check the whole working tree +and report back. +(I didn't get any real time measurements +before enabling +.Xr git-sparse-checkout 1 , +and I'm not about to disable it now, +since it'd have to check out +all those files again.) +I'm only actually working on +a small handful of ports, +so all that work is wasted. +Time to turn on sparse checkout: +.Bd -literal -offset indent +git sparse-checkout init --cone +.Ed +. +.Pp +The +.Fl \-cone +option here +(which I keep reading as +.Dq clone +because it's git) +restricts the kinds of patterns +you can use to select files to check out, +but makes the calculation more efficient. +Basically it means you can only select +paths along with everything below them, +which I think is pretty much +always what you want anyway. +Enabling sparse checkout +can take quite a while +because it has to do a lot of un-checking-out. +I should mention +that you can pass +.Fl \-sparse +to +.Xr git-clone 1 +to avoid ever checking out +the whole tree. +. +.Pp +The default selection when you run +.Cm init +is to check out all the files +at the root of the repository, +but none of the subdirectories. +For +.Xr ports 7 , +I also want to check out +the shared scripts and Makefiles: +.Bd -literal -offset indent +git sparse-checkout add Keywords Mk Templates Tools +.Ed +. +.Pp +And then I can selectively check out +just the ports I'm working on: +.Bd -literal -offset indent +git sparse-checkout add irc/catgirl irc/pounce +.Ed +. +.Pp +After enabling sparse checkout, +.Xr git-status 1 +takes what I'd call +a normal amount of time. +I also did this on +a couple-weeks-out-of-date copy of the +.Xr ports 7 +tree, +and when I ran +.Xr git-pull 1 +it was also really quick, +because it didn't have to bother +updating all those files +I'm not interested in. +It still downloads all the git objects, +of course, +and you can just add any new paths you need +to the sparse checkout list. +My disk usage also went down +by about a gigabyte. +. +.Pp +I'm super pleased to discover this part of git, +because it makes working with huge +and/or monorepo-style repositories +so much more feasible! +You can see how I came across it, +since +.Xr git-subtree 1 +is also a useful tool for monorepos. +Stay tuned for that post, +I guess :) +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/024-seprintf.7 b/www/text.causal.agency/024-seprintf.7 new file mode 100644 index 00000000..d1af2e1a --- /dev/null +++ b/www/text.causal.agency/024-seprintf.7 @@ -0,0 +1,137 @@ +.Dd June 12, 2021 +.Dt SEPRINTF 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm seprintf +.Nd an snprintf alternative +. +.Sh SYNOPSIS +.Ft "char *" +.Fn seprintf "char *ptr" "char *end" "const char *fmt" "..." +. +.Sh DESCRIPTION +While discussing string building in C recently, +mcf pointed out +.Xr seprint 2 +from Plan 9, +and it kind of blew my mind. +I had implemented my own function in +.Xr catgirl 1 +for building up strings using +.Xr snprintf 3 +and a struct containing +pointer, length and capacity, +but it felt out of place. +.Fn seprintf +(I add the +.Dq f , +Plan 9 doesn't) +is a much simpler +and more +.Dq C-like +interface with really nice usage patterns. +. +.Pp +The obvious difference from +.Xr snprintf 3 +is that +.Fn seprintf +takes an +.Fa end +pointer +rather than a size. +This means you need only calculate it +once for each buffer, +rather than subtracting +the running length from the buffer size. +.Fn seprintf Ap s +return value is a pointer +to the terminating null +of the string it wrote, +so you can pass that back in +to continue appending +to the same buffer. +. +.Pp +I'm not sure of the exact behaviour on Plan 9, +but my implementation indicates truncation occurred +by returning the +.Fa end +pointer. +That makes it both easy to check, +and perfectly fine to keep calling +.Fn seprintf +anyway. +It just won't write anything if +.Fa ptr +== +.Fa end . +. +.Pp +In the case of formatting failure +(which should be prevented by +compile-time format string checking, +but should still be considered), +.Fn seprintf +returns +.Dv NULL . +I'm again not sure if this matches Plan 9. +I like this a lot better than +.Xr snprintf 3 +returning -1, +because an unchecked +.Dv NULL +is likely to quickly cause a crash, +while blindly adding +.Xr snprintf 3 Ap s +return value +to your running length +is a non-obvious logic error. +. +.Sh EXAMPLES +Here's an example of what some code using +.Fn seprintf +might look like: +.Bd -literal -offset indent +char buf[4096]; +char *ptr = buf, *end = &buf[sizeof(buf)]; +ptr = seprintf(ptr, end, "argv: "); +for (int i = 1; i < argc; ++i) { + ptr = seprintf( + ptr, end, "%s%s", + (i > 1 ? ", " : ""), argv[i] + ); +} +if (ptr == end) errx(1, "truncation occurred :("); +.Ed +. +.Pp +And here is the very short implementation of it against +.Xr vsnprintf 3 +which I copy into my project header files: +.Bd -literal -offset indent +#include <stdarg.h> +#include <stdio.h> +static inline char * +seprintf(char *ptr, char *end, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +static inline char * +seprintf(char *ptr, char *end, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(ptr, end - ptr, fmt, ap); + va_end(ap); + if (n < 0) return NULL; + if (n > end - ptr) return end; + return ptr + n; +} +.Ed +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +Another short one before +.Xr git-subtree 1 . +I just think this function +is really neat. diff --git a/www/text.causal.agency/025-v6-pwd.7 b/www/text.causal.agency/025-v6-pwd.7 new file mode 100644 index 00000000..90bfd6ac --- /dev/null +++ b/www/text.causal.agency/025-v6-pwd.7 @@ -0,0 +1,330 @@ +.Dd September 1, 2021 +.Dt V6-PWD 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm V6 pwd +.Nd deciphering old code +. +.Sh DESCRIPTION +We were talking about +.Xr wall 1 +on IRC +and how long it had been annoying users. +My manual page says +.Xr wall 1 +appeared in +.At v6 , +which means that +.Xr wall 1 +has been annoying users for 46 years! +. +.Pp +The Wikipedia page links to the source for +.At v6 , +so I was curious to see how the very first +.Xr wall 1 +was implemented. +It's not that surprising, +except that it is hardcoded +to handle only 50 logins, +and it forks to write to each tty, +waiting one second between each. +I think the forking must be to avoid +any of the terminals being opened +from becoming the controlling terminal +of the original +.Xr wall 1 +process. +. +.Pp +Then I started looking +at some of the other source files +and found the implementation of +.Xr pwd 1 , +which was surprising. +There's no +.Xr getcwd 3 +function +(the earlier form of which, +.Xr getwd 3 , +appeared in +.Bx 4.0 ) , +so +.Xr pwd 1 +has to figure out +the path to the working directory itself. +It took me a while to figure out how it works. +. +.Pp +To make it easy to talk about, +I'm just going to include the whole thing here: +.Bd -literal +char dot[] "."; +char dotdot[] ".."; +char root[] "/"; +char name[512]; +int file, off -1; +struct statb {int devn, inum, i[18];}x; +struct entry { int jnum; char name[16];}y; + +main() { + int n; + +loop0: + stat(dot, &x); + if((file = open(dotdot,0)) < 0) prname(); +loop1: + if((n = read(file,&y,16)) < 16) prname(); + if(y.jnum != x.inum)goto loop1; + close(file); + if(y.jnum == 1) ckroot(); + cat(); + chdir(dotdot); + goto loop0; +} +ckroot() { + int i, n; + + if((n = stat(y.name,&x)) < 0) prname(); + i = x.devn; + if((n = chdir(root)) < 0) prname(); + if((file = open(root,0)) < 0) prname(); +loop: + if((n = read(file,&y,16)) < 16) prname(); + if(y.jnum == 0) goto loop; + if((n = stat(y.name,&x)) < 0) prname(); + if(x.devn != i) goto loop; + x.i[0] =& 060000; + if(x.i[0] != 040000) goto loop; + if(y.name[0]=='.')if(((y.name[1]=='.') && (y.name[2]==0)) || + (y.name[1] == 0)) goto pr; + cat(); +pr: + write(1,root,1); + prname(); +} +prname() { + if(off<0)off=0; + name[off] = '\en'; + write(1,name,off+1); + exit(); +} +cat() { + int i, j; + + i = -1; + while(y.name[++i] != 0); + if((off+i+2) > 511) prname(); + for(j=off+1; j>=0; --j) name[j+i+1] = name[j]; + off=i+off+1; + name[i] = root[0]; + for(--i; i>=0; --i) name[i] = y.name[i]; +} +.Ed +. +.Pp +First, some syntax trivia: +it seems you don't need +.Sy = +to give globals values. +I guess that makes sense. +I also noticed that +it avoids giving +.Va inum +and +.Va jnum +the same name. +I think that's because in old C, +struct field names all shared the same namespace. +The last difference I noticed +is the operator +.Sy =& +rather than +.Sy &= . +Honestly I think the former makes more sense, +but I can see that the one we have now +is less ambiguous. +. +.Pp +To get +.Fn prname +and +.Fn cat +out of the way, +it's building up a path from the bottom. +At first I thought it must be +starting at the end of its buffer +and moving back as it adds components, +but no, +it moves the entire path-so-far over +every time it adds a new component +onto the front. +.Fn cat +is just a bunch of manual string copying. +It also gives up +if the new component +would make the path longer than 511 characters. +Fair enough. +. +.Pp +So how does it build up the path? +The loop in +.Fn main +first calls +.Xr stat 2 +on the current directory +.Pa \&. +in order to get its inode number. +I love that +.Vt struct statb +is just declared at the top of this file. +Clearly this code predates the C preprocessor. +. +.Pp +It then opens the parent directory +.Pa .. +and reads directory entries from it. +The inner loop is looking for +a directory entry with the same inode number +as the current directory, +to figure out what the current directory is called. +Curiously, +it reads 16-byte directory entries, +despite declaring a larger struct. +The preprocessor can't be invented soon enough. +. +.Pp +Once it finds the matching directory entry, +it adds the name of the entry +onto the front of the path, +changes directory to +.Pa .. +and starts over. +It stops when the current directory +has an inode number of 1, +which must be the root of a file system, +but then it does something else. +It took me a while to decipher what +.Fn ckroot +is doing. +. +.Pp +The loop in +.Fn main +stops when it gets to the root +of a file system, +but that's not necessarily +.Pa / . +I think what +.Fn ckroot +is doing is trying to figure out +where that file system is mounted. +It starts by checking the device number +that the current directory is on. +Or really it calls +.Xr stat 2 +on the name of the directory entry that +.Fn main +just found, +which I think must be +.Pa \&. +at this point anyway since it's at a root. +. +.Pp +Anyway, +it then changes directory to and opens +.Pa / +and starts reading directory entries from that, +calling +.Xr stat 2 +on each of them +and checking for a matching device number. +I think this implies that file systems +can only be mounted in +.Pa / +and not at any lower level, +at least not if you want +.Xr pwd 1 +to understand it. +I'm not sure what the check for +an inode number of 0 is skipping over +in this loop. +Some kind of special entry in +.Pa / +perhaps. +. +.Pp +Once it finds an entry +with a matching device number, +it checks the flags +to make sure the entry is a directory. +It does so with hardcoded constants, +but it seems they haven't changed +in all these years. +According to +.Xr stat 2 , +040000 is +.Dv S_IFDIR . +The number of file types +clearly has grown since then though, +since +.Dv S_IFMT +is now 0170000 rather than 060000. +. +.Pp +I think the reason it checks +that the entry is a directory +is because if it actually is +on the root file system already, +then any regular file +would have a matching device number. +If the entry is indeed a directory, +it then checks if the entry is +.Pa \&. +or +.Pa \&.. , +which indicates that it really is already at +.Pa / . +If it's not, +it adds the mount point that it found +to the front of the path. +. +.Pp +Finally, +it prints +.Pa / +followed by the path it built up. +If it failed at any point before that, +it would print the path it had built so far +with no leading +.Pa / . +Better than nothing! +. +.Pp +So that's how I think +.Xr pwd 1 +works in +.At v6 . +It was a fun puzzle to work through, +and it was interesting to see +the assumptions it makes. +How simple things were back then... +Actually I find it really cool +that code from 1975 +can still be read and understood +using knowledge of modern C and UNIX-likes. +. +.Sh SEE ALSO +.Lk https://minnie.tuhs.org/cgi-bin/utree.pl?file=V6 +.Pp +.Pa pwd.c +appears in +.Pa V6/usr/source/s2 . +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +I regret saying in two previous posts +what I planned to write next, +because this is still not that. diff --git a/www/text.causal.agency/026-git-comment.7 b/www/text.causal.agency/026-git-comment.7 new file mode 100644 index 00000000..fefb497e --- /dev/null +++ b/www/text.causal.agency/026-git-comment.7 @@ -0,0 +1,190 @@ +.Dd September 10, 2021 +.Dt GIT-COMMENT 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm git-comment +.Nd add comments from commit messages +. +.Sh SYNOPSIS +.Nm git comment +.Op Fl \-all +.Op Fl \-comment-start Ar string +.Op Fl \-comment-lead Ar string +.Op Fl \-comment-end Ar string +.Op Fl \-min-group Ar lines +.Op Fl \-min-repeat Ar lines +.Op Fl \-no-repeat +.Op Fl \-pretty Ar format +.Op Ar options ... +.Op Fl \- +.Ar file +. +.Sh DESCRIPTION +The +.Nm +command +adds comments to a file +showing the commit messages +which last modified +each group of lines. +By default only commit messages with bodies +and which modified groups of at least 2 lines +are added. +Each comment contains +the abbreviated commit hash +and the commit summary, +followed by the commit body. +. +.Pp +.Nm +accepts all the options of +.Xr git-blame 1 +in addition to the following: +.Bl -tag -width Ds +.It Fl \-all +Include all commit messages. +The default is to include +only commit messages with bodies +(lines after the summary). +. +.It Fl \-comment-start Ar string +Start comments with +.Ar string . +The default is the value of +.Cm comment.start +or +.Ql /* . +. +.It Fl \-comment-lead Ar string +Continue comments with the leading +.Ar string . +The default is the value of +.Cm comment.lead +or +.Ql " *" . +. +.It Fl \-comment-end Ar string +End comments with +.Ar string . +The default is the value of +.Cm comment.end +or +.Ql " */" . +. +.It Fl \-min-group Ar lines +Add comments only for groups of at least +.Ar lines . +The default is 2 lines. +. +.It Fl \-min-repeat Ar lines +Avoid repeating a comment +if it occurred in the last +.Ar lines . +The default is 30 lines. +. +.It Fl \-no-repeat +Avoid repeating comments entirely. +. +.It Fl \-pretty Ar format +Set the pretty-print format +to use for commit messages. +The default is the value of +.Cm comment.pretty +or +.Ql format:%h\ %s%n%n%-b . +See +.Xr git-show 1 . +.El +. +.Sh EXAMPLES +For files with +.Ql # +comments: +.Bd -literal -offset indent +git config comment.start '#' +git config comment.lead '#' +git config comment.end '' +.Ed +. +.Pp +Add as many comments as possible: +.Bd -literal -offset indent +git comment --all --min-group 1 --min-repeat 1 +.Ed +. +.Pp +Some examples of output from +.Xr catgirl 1 : +.Bd -literal +/* 347e2b4 Don't apply uiThreshold to Network and Debug + * + * Messages don't really need to be hidden from <network> and I think + * it could be confusing. Debug messages are all Cold so everything + * would be hidden, and I want to keep them that way so that <debug> + * doesn't clutter the status line needlessly. + */ +if (id == Network || id == Debug) { + window->thresh = Cold; +} else { + window->thresh = uiThreshold; +} + +/* b4c26a2 Measure timestamp width using ncurses + * + * This allows for non-ASCII characters in timestamps, and simplifies + * things by including the trailing space in the width. + */ +int y; +char buf[TimeCap]; +struct tm *time = localtime(&(time_t) { -22100400 }); +size_t len = strftime(buf, sizeof(buf), uiTime.format, time); +if (!len) errx(EX_CONFIG, "invalid timestamp format: %s", uiTime.format); +waddstr(main, buf); +waddch(main, ' '); +getyx(main, y, uiTime.width); +(void)y; + +/* 43b1dba Restore toggling ignore with M-- + * + * So that pressing M-- repeatedly maintains the previous behavior. + */ +if (n < 0 && window->thresh == Ice) { + window->thresh = Cold; +} else { + window->thresh += n; +} + +/* 1891c77 Preserve colon from previous tab-complete + * + * This fixes the case when pinging multiple nicks and one of them needs to + * be cycled through. + */ +bool colon = (tab.len >= 2 && buf[tab.pos + tab.len - 2] == L':'); +.Ed +. +.Sh SEE ALSO +.Lk https://git.causal.agency/src/tree/bin/git-comment.pl +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Pp +In case it's unclear, +this is a +.Xr git 1 +subcommand I wrote. +Did you know you can add new +.Xr git 1 +subcommands just by +adding executables named +.Pa git-* +to somewhere in +.Ev PATH ? +. +.Pp +This is also, +I think, +my third Perl script ever. +It's an interestingly shaped language. +Quite neat. diff --git a/www/text.causal.agency/027-openbsd-linode.7 b/www/text.causal.agency/027-openbsd-linode.7 new file mode 100644 index 00000000..9f40de42 --- /dev/null +++ b/www/text.causal.agency/027-openbsd-linode.7 @@ -0,0 +1,202 @@ +.Dd September 26, 2021 +.Dt OPENBSD-LINODE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Installing OpenBSD on Linode +.Nd a guide +. +.Sh DESCRIPTION +I've been thinking for a while +about moving my servers to Linode, +and also about moving them to +.Ox . +I actually originally got into +.Fx +(and from there, +.Ox ) +only because DigitalOcean +started offering it as a +.Dq droplet +image. +I've been running those servers fine for years, +but now I prefer to run +.Ox , +and some recent DigitalOcean outages +had me thinking about it more, +so I'm giving it a shot. +. +.Pp +As an aside, +running +.Ox +on DigitalOcean +is not really a good option. +It seems more awkward to install your own OS there, +and if you do, +I've heard that IPv6 won't work +because they don't know how to run SLAAC. +Also, +now that I've used +the Linode control panel and LISH a bit, +DigitalOcean kind of feels like a toy +in comparison. +. +.Pp +Here's what I did to install +.Ox +on Linode: +.Bl -enum +.It +Create a Linode with the +.Dq Choose a Distribution +box blank. +. +.It +Under the Storage tab, +create a disk called +.Dq miniroot +of type raw +with size 8 MB. +This will hold the install image. +. +.It +Create another disk called +.Dq root +of type raw +using the remaining available storage. +. +.It +Boot the Linode in rescue mode +from the option in the three-dots menu +next to +.Dq Power On . +Attach +.Dq miniroot +to +.Pa /dev/sda . +. +.It +Log into the LISH console +and obtain the install image: +.Bd -literal +curl -O https://cdn.openbsd.org/pub/OpenBSD/6.9/amd64/miniroot69.img +dd if=miniroot69.img of=/dev/sda +.Ed +.Pp +Power off the Linode. +. +.It +Under the Configurations tab, +create a configuration called +.Dq install +in full virtualization mode. +Paravirtualization works fine once installed, +but for some reason the installer +can't see the root disk +without full virtualization. +Under boot settings, +select direct disk. +Attach +.Dq root +to +.Pa /dev/sda , +.Dq miniroot +to +.Pa /dev/sdb +and set the root device to +.Pa /dev/sdb . +. +.It +Create a similar configuration called +.Dq boot +but using paravirtualiztion +and without +.Dq miniroot +attached. +Set the root device to +.Pa /dev/sda . +. +.It +Boot the +.Dq install +configuration, +launch the LISH console +and switch to Glish. +It's possible +to have the installer use serial console, +but it requires entering commands +at the boot prompt +before the timeout, +and I never managed it. +If you do manage it, +run: +.Bd -literal +stty com0 9600 +set tty com0 +boot +.Ed +. +.It +Proceed through the +.Ox +installer. +When asked to +change the default console to com0, +answer yes +so that regular LISH will work. +Power off the Linode. +. +.It +Boot the +.Dq boot +configuration +and log in to LISH. +Since the installer configured networking +in full virtualization, +rename the file to the paravirtualized interface: +.Bd -literal +mv /etc/hostname.em0 /etc/hostname.vio0 +.Ed +.Pp +In order to get the right public IPv6 address, +disable privacy extensions +by changing the inet6 line of +.Pa hostname.vio0 +to: +.Bd -literal +inet6 autoconf -temporary -soii +.Ed +. +.It +Bring networking up +and run +.Xr syspatch 8 +since +.Pa rc.firsttime +couldn't do it: +.Bd -literal +sh /etc/netstart +syspatch +.Ed +. +.It +Reboot. +.El +. +.Pp +I guess I'll be slowly moving things over +to the new servers +for the next little while. +With any luck the next post here +will not say +.Fx +in its header! +. +.Sh SEE ALSO +I learned the basic idea +of how to do this from +.Lk https://www.subgeniuskitty.com/notes/openbsd_on_linode . +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/028-names.7 b/www/text.causal.agency/028-names.7 new file mode 100644 index 00000000..de47c074 --- /dev/null +++ b/www/text.causal.agency/028-names.7 @@ -0,0 +1,81 @@ +.Dd October 30, 2021 +.Dt NAMES 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Names +.Nd three types +. +.Sh DESCRIPTION +There are (at least) three +different types of names +a person has. +. +.Pp +First, there are normie names. +These are names usually made up +of several words +each of which is capitalized. +Most people have one of these, +but it's possible to have more. +They're names that might appear on +various types of Documents. +A +.Dq legal name +(dubious) +is a normie name, +but normie names need not be +.Dq legal +(dubious). +I list this category first +not because it's more important, +but because it is by far the most boring. +. +.Pp +Next, there are Real Names. +Most people have at least a few +and will probably go through +different ones over time. +Your Real Names are anything people +use to refer to you. +On the internet these are often not capitalized. +Sometimes that is the only distinction +between a Real Name +and a normie name. +. +.Pp +There was a period of time +when I was playing a lot of TF2 +and not really leaving my apartment. +I had set my steam name to +.Dq gluten product +(yeah, from that dril tweet) +and I talked in the game's voice chat +quite a bit. +Naturally other Gamers in voice chat +called me +.Dq gluten +and at some point I realized +that over the span of months +I had been refered to as +.Dq gluten +more often than any other name. +So that was a Real Name of mine. +People used it and I responded to it. +. +.Pp +Last, there are the True Names. +The kind of name that knowledge of +gives one power over a person. +I don't think any humans +know their own True Names, +but I do believe they exist. +It's possible that other animals +know theirs. +It's probably best not to know though, right? +I think if I knew mine +I would always worry +about accidentally revealing it. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/029-topics.7 b/www/text.causal.agency/029-topics.7 new file mode 100644 index 00000000..d071eb67 --- /dev/null +++ b/www/text.causal.agency/029-topics.7 @@ -0,0 +1,116 @@ +.Dd January 8, 2022 +.Dt TOPICS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Topics +.Nd a bit of a mess +. +.Sh DESCRIPTION +Shortly after my last post +I started writing another one +but I never finished it. +I don't think I had enough to say, +or if I did it meant going into +a whole extra thing. +I also had a list in my mind +of other things to write about, +but inspiration hasn't really struck +for any of them. +I'm currently in the mood +to write something anyway, +so I'm just going to write a bit +about the topics I have in mind. +I may or may not ever +write more about any of them. +. +.Pp +The post I had started writing +(twice, actually) +was about voices. +I like them a lot +and I'm fascinated by them. +The problem is +I don't actually have much +to say about it +without getting into Gender, +which as I say is a whole extra thing, +and not something I've written about here before. +. +.Pp +When I started writing here, +I didn't want to blog about +personal topics or LGBTQ stuff. +But more recently +I want to move away from +only writing about computers. +Or maybe away from +writing about computers entirely. +There are more interesting things, +but I don't have experience +writing about them. +Yet, +I should say. +. +.Pp +I'm honestly still not sure +if writing about gender here +is at all a good idea. +But it turns out to feel like +a bit of a prerequisite +for other things. +I find gender perception +in particular +to be fascinating. +It's interesting. +It's neat. +And I don't know if I can +write anything coherent about it. +. +.Pp +Related to that, +I've been thinking of writing +about how the pandemic +has had a strangely positive effect +on my life. +Or at least, +I've made a lot of positive changes +during it. +I'm in a better place emotionally now +than ever before, +and that obviously runs counter +to most people's experiences. +Additionally with that positive outlook +I want to write about the meaning +of my domain name. +I'm proud of it. +. +.Pp +This week the topic of fetish +has been on my mind. +That actually feels +a bit less risky +to write about than gender. +And it may honestly be more interesting. +I don't know. +There's not enough sex +on computer blogs, +or whatever this is. +Although my main ideas +are not about sex at all. +. +.Pp +Just this turned out to be +harder to write than I thought it would be. +I think I want to populate this space +with more short posts like the previous one. +I wrote that while very sleepy +after 3 AM though, +and I don't exactly +want to repeat that regularly. +We'll see. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +Listening to Kate Bush \(em Hounds of Love. diff --git a/www/text.causal.agency/030-discs.7 b/www/text.causal.agency/030-discs.7 new file mode 100644 index 00000000..df73a750 --- /dev/null +++ b/www/text.causal.agency/030-discs.7 @@ -0,0 +1,114 @@ +.Dd January 8, 2022 +.Dt DISCS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Desert Island Discs +.Nd we're doing three in this one +. +.Sh DESCRIPTION +In typical fashion +I'm going to write about something +completely different instead. +Something short and simple. +I got thinking about this +after reading a little interview thing +this week. +The question is +which three albums would you want to have +if you were stranded on a desert island. +What could you listen to +for the rest of time? +It's surprisingly easy +to take this question very seriously. +. +.Pp +My immediate thought was +.Em Music for 18 Musicians. +I've literally said this about it +in conversation before. +That's an album +I'd want to have on a desert island. +I find it incredibly soothing, +almost hypnotic. +I really do feel like +I could listen to it forever. +And then maybe I could finally determine +which of its eleven sections +is the best. +. +.Pp +My next thought was +.Em Soundtracks for the Blind . +We already know I'm a huge SWANS fan. +Despite what I've written about +.Em Swans Are Dead , +I instead jumped to SFTB. +I still think that +.Em Dead +has better tunes, +but +.Em Soundtracks +is definitely the better cohesive album. +It has such atmosphere and mood on it. +Like +.Em 18 , +it's an album that sucks me in. +Also, +either SWANS album +is an economical choice +in this hypothetical +since they're each 2 hours and 20 minutes long. +. +.Pp +Choosing a third album is a lot harder. +There's so much other music I like +and only one slot left. +There's no other single album +that stands out above the rest +like the previous two, +for me. +.Em Wildlife , +maybe? +Or +.Em Jane Doe ? +Perhaps a classic like +.Em Aeroplane , +or a boomer classic like +.Em The Wall . +But would I really want to +listen to any of those +to the exclusion of everything else? +They're too mood-dependent. +. +.Pp +Then I realized the perfect choice +for third album. +.Em Mouth Moods . +A mashup album is the perfect wildcard, +and +.Em Moods +is just fun as hell to listen to. +I get songs from it stuck in my head +instead of the originals. +The final track, +.Em Shit , +always gets me moving. +It's a masterpiece. +. +.Bl -enum +.It +Steve Reich Ensemble \(em +.Em Music for 18 Musicians +.It +SWANS \(em +.Em Soundtracks for the Blind +.It +Neil Cicierega \(em +.Em Mouth Moods +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +Listening to Steve Reich Ensemble \(em Music for 18 Musicians. diff --git a/www/text.causal.agency/031-books-2021.7 b/www/text.causal.agency/031-books-2021.7 new file mode 100644 index 00000000..d7b46f17 --- /dev/null +++ b/www/text.causal.agency/031-books-2021.7 @@ -0,0 +1,127 @@ +.Dd January 12, 2022 +.Dt BOOKS-2021 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Books 2021 +.Nd a review, I guess +. +.Sh DESCRIPTION +In 2021 I read 26 books. +Finished the 26th right on December 31st. +It's not a lot but it's more than last year. +Here are the ones I loved +(in the order I read them). +I will avoid spoilers, +of course. +. +.Ss Network Effect by Martha Wells +I've been reading the +.Em Murderbot Diaries +series for a while. +They're fun stories. +I liked this full-length novel entry a lot. +I guess it felt like it had more room +for the characters to develop. +This is probably when I started +asking my friends if they'd read it +because I wanted to talk about +Murderbot gender vibes. +.Pp +You may like if: you're trans. +. +.Ss The Once and Future Witches by Alix E. Harrow +Um, +it's about witches! +One of them has the same name as me. +Kind of has some similar vibes to +.%T The Future of Another Timeline , +which was my favourite book I read in 2020. +.Pp +You may like if: you like women. +. +.Ss A Desolation Called Peace by Arkady Martine +I was so excited for this sequel to +.%T A Memory Called Empire , +another previous favourite +and something I've been wanting more of. +I kinda wish there was more fucking in it though honestly. +.Pp +You may like if: you like women. +. +.Ss Piranesi by Susanna Clarke +Really something different. +It turned out to be a different story +than I expected +from reading the first few pages. +.Pp +You may like if: you like statues, I guess? +. +.Ss A Psalm for the Wild-Built by Becky Chambers +Ok yes I do give 3/3 stars +to every Becky Chambers book. +They're so fucking good. +I'm looking forward to +more entries in this novella series. +(Also I'm currently reading +the fourth +.Em Wayfarers +book +and loving it too!) +.Pp +You may like if: your pronouns are they/them <3 +. +.Sh HONOURABLE MENTIONS +.Ss Her Body and Other Parties by Carmen Maria Machado +I really enjoyed the short story +.Dq Especially Heinous: 272 Views of Law & Order SVU +in this collection. +It goes on a bit too long +but the format is unique. +You can read that one online, +actually. +. +.Ss The Hobbit by J. R. R. Tolkien +Yeah I hadn't read this until last year. +I borrowed it after marathoning +the extended editions of the +.%T Lord of the Rings +trilogy during a heat wave. +As I said at the time, +pretty good for something +written by a man +like a hundred years ago. +Kind of hilarious that women +just don't exist +in the world of +.%T The Hobbit . +. +.Ss Earthlings by Sayaka Murata +Pretty fucking wild. +I'd recommend it, +but I have to say it +.Em extremely +needs a child sexual abuse content warning on it. +. +.Ss Six Months, Three Days, Five Others by Charlie Jane Anders +A surprising number of these short stories +are actual stories! +They have beginnings, +middles +and ends! +. +.Ss The City in the Middle of the Night by Charlie Jane Anders +It's got some +.Em Xenogenesis +series vibes. +Sophie is a goddamn lesbian idiot though +and she never even realizes it. +. +.Sh SEE ALSO +.Lk https://git.causal.agency/src/tree/txt/books.txt +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +Listening to +.Em Ear Massage with Latex Gloves 100% Sensitivity 40 minute (No Talking) . diff --git a/www/text.causal.agency/032-albums-2021.7 b/www/text.causal.agency/032-albums-2021.7 new file mode 100644 index 00000000..72c1d0d2 --- /dev/null +++ b/www/text.causal.agency/032-albums-2021.7 @@ -0,0 +1,173 @@ +.Dd January 13, 2022 +.Dt ALBUMS-2021 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Albums 2021 +.Nd a review +. +.Sh DESCRIPTION +Every year I create a new playlist +in iTunes +(Music dot app, whatever) +for the albums I listen to that year. +It's usually embarrassingly short. +I don't listen to new music +as much as I'd like, +and usually only one or two +are actually from the current year. +Not that the playlist +is limited to new (to me) music. +If I get really into an album +I've heard before, +more than before, +I also add it to the list. +Anyway, +this is a review +of my 2021 albums playlist. +. +.Ss Black Country, New Road \(em For the First Time (2021) +I first heard the single +.Em Sunglasses +from someone sharing it on IRC, +and I loved it, +so I was looking forward to this album. +What a let down though. +The version of +.Em Sunglasses +on the album is just plain worse +than the single version. +I still got some decent listening +out of the album, +but that just sours it for me. +.Pp +Favourite track: +.Em Track X . +. +.Ss Black Dresses \(em Forever \&In Your Heart (2021) +I fucking love Black Dresses. +.Em Peaceful as Hell +is one of my all-time favourite albums. +I'm glad they put out another one +after it looked like they wouldn't. +The sounds are just so good. +Exactly what my ears crave. +The texture of it +tickles my brain clit. +.Pp +Favourite tracks: +.Em Waiting42moro , +.Em Mistake . +. +.Ss Low \(em Drums and Guns (2007) +I've long loved the song +.Em Breaker +and its music video, +but I only listened to the album +it's on last year. +Something I didn't realize, +I guess because I usually pulled up +the music video +without headphones on, +is how aggressively this album +uses stereo panning. +Vocals are generally +panned hard right throughout, +with much of the instrumentation +panned centre or hard left. +It's bold +and it really works for me. +I especially love the vocal harmony on +.Em Breaker +all the way on the opposite channel. +Bring back stereo separation! +.Pp +Favourite tracks: +.Em Breaker , +.Em Murderer , +.Em Violent Past . +. +.Ss The Armed \(em Ultrapop (2021) +I have to admit +I didn't actually listen to this one much. +I listened to the previous album, +.Em Only Love , +a lot in 2020. +I think this album is good, +but I'll probably only really get into it +in some future year. +. +.Ss Lingua Ignota \(em Caligula (2019) +Dear lord, +why did I wait so long +to listen to this one. +I had heard +.Em "Do You Doubt Me Traitor" +back when it came out, +but somehow I didn't realize +just how much this album +would be my shit. +Fucking incredible vocals. +Lovely sometimes minimal, +sometimes extreme +instrumentals +and exquisite percussion. +The sound of, +I believe, +a lightbulb rolling around on the floor on +.Em Fragrant +is such an interesting addition. +.Pp +Favourite tracks: +.Em "Do You Doubt Me Traitor" , +.Em "Fragrant Is My Many Flower'd Crown" , +.Em "If the Poison Won't Take You My Dogs Will" . +. +.Ss Black Dresses \(em LOVE AND AFFECTION FOR STUPID LITTLE BITCHES (2019) +I wanted even more Black Dresses +and fortunately there was still more +I hadn't yet listened to! +I've already gushed about Black Dresses +so I'll spare you. +They're so good though. +.Pp +Favourite tracks: +.Em STATIC , +.Em HERTZ , +.Em MY HEART BEATS OUT OF TIME . +. +.Ss Barenaked Ladies \(em All Their Greatest Hits: Disc One 1991-2001 +What? +Yeah, +late last year I decided to revisit BNL. +My parents listened to them a lot +when I was growing up, +and I liked them too. +The first show I ever went to was the +.Dq Barenaked for the Holidays +tour with my parents. +It turns out +I still think their '90s stuff +is pretty darn good! +Steven Page is really a great singer. +This is also the first time +I'm listening to these tunes +with fancy headphones +and it sounds great. +Honestly +.Em The Old Apartment +can totally compete +with the favourites +I've accumulated more recently. +\&'90s alt rock was good actually? +.Pp +Favourite tracks: +.Em The Old Apartment , +.Em Brian Wilson , +.Em What a Good Boy , +.Em Too Little Too Late . +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +Listening to all my favourite tracks :) diff --git a/www/text.causal.agency/033-jorts.7 b/www/text.causal.agency/033-jorts.7 new file mode 100644 index 00000000..001f877c --- /dev/null +++ b/www/text.causal.agency/033-jorts.7 @@ -0,0 +1,485 @@ +.Dd February 2, 2022 +.Dt JORTS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Introducing Jorts +.Nd june's ports +. +.Sh DESCRIPTION +Alright so I've gone off the deep end, +maybe. +After continual frustration with MacPorts +culminating in not being able to install +.Xr nvi 1 +on my work MacBook, +I have just gone ahead +and started my own personal ports tree +for macOS. +After a couple of weeks, +I have 32 ports in my tree +and only two remaining requested ports +installed from MacPorts. +. +.Pp +I set out with a couple ideas in mind: +.Bl -bullet +.It +This will be my own personal ports tree. +It only has to work for me. +Since I'm using it on both +my personal Intel MacBook Pro +still running Catalina +and my work M1 MacBook Pro +running Monterey, +it is at least that portable. +. +.It +It's ok to rely on +system libraries and tools +provided by macOS. +I'm not creating a distro, +so it doesn't need to be totally isolated. +This lets me skip really annoying things +like compiler toolchains. +. +.It +Sources get vendored, +either from release tarballs +or with +.Xr git-subtree 1 . +This allows totally pain-free +local patching, +and boy has this paid off. +I can just do what I need to do +to get the thing to build how I want +and commit it in git like anything else. +.Pp +It also means that the tree itself +is entirely self-contained +and doesn't rely on any external sources +or network access. +Honestly with some old and obscure software +it feels like upstream could disappear at any moment, +so this gives me peace of mind too. +.Pp +Another advantage of vendoring upstream sources +is that all of the code installed on my system +(in +.Pa /usr/local +anyway) +is easily inspected, +much like +.Pa /usr/src +on a BSD. +This can be super useful for debugging +or just for reference. +. +.It +Produce simple package tarballs. +They're just the contents of +.Ev DESTDIR +after a staged install. +They get installed for real +by untarring them in +.Pa / . +They can then be uninstalled +(or upgraded) +by removing the paths contained +in the tarball from the system. +. +.It +Track installed packages with symbolic links +to specific package tarballs. +Keep old tarballs around for rollbacks. +This means I can see what's installed +with plain old +.Xr ls 1 ! +.Bd -literal +$ ls */Installed +\&... +libretls/Installed toilet/Installed +mandoc/Installed tree/Installed + +$ ls -l toilet/Installed +lrwxr-xr-x 1 root staff 19 17 Jan 21:45 toilet/Installed -> toilet-0.3~1.tar.gz +.Ed +. +.It +Use +.Xr bmake 1 . +It's scrutable. +It also knows how to bootstrap itself +pretty well. +Since +.Xr bmake 1 +is itself a port in my tree +that would require +.Xr bmake 1 +to build and install, +I wrote a small +.Pa Bootstrap +shell script +to install +.Xr bmake 1 +.Dq manually +then use that +.Xr bmake 1 +to build and install its own port. +It also requires a bit of care +when upgrading the +.Xr bmake 1 +port since macOS +rather doesn't like a binary +deleting itself while it's running. +. +.It +No GNU software. +I simply refuse to do it. +To that end, +prefer configuring/building with +.Xr cmake 1 +where at all possible. +I fell into this early on +since I originally just wanted to install +.Xr nvi 1 +and +.Sy lichray/nvi2 +is a better upstream source these days +that uses +.Xr cmake 1 . +.Pp +With a port and support for +.Xr cmake 1 +in +.Pa Port.mk , +I can make changes to +.Pa CMakeLists.txt +files without issue. +I can also vendor upstreams +directly from git +rather than having to find +release tarballs with generated +.Pa configure +scripts and so on. +When I need to make changes +to the build systems of projects using autotools, +I either have to have autotools installed +(from outside my tree) +or painstakingly reflect my edits by hand +in the generated files, +both of which suck hard. +.El +. +.Pp +Ok so that's actually quite a number of ideas. +But they have come together +into something surprisingly usable +surprisingly quickly! +Like I said, +this is only intended to be +my own personal ports tree, +but I hope that some of these ideas +are interesting +and maybe inspire others +to explore similar approaches. +. +.Pp +But wait, +I'm not done yet! +There are some other interesting things +that I came up with along the way, +and also some complaints +about some upstreams, +but I'll try to keep those to a minimum. +. +.Pp +So it turns out that dependencies are hard. +Who knew? +It's easy enough to enforce +direct dependencies +at build time +by just checking for the required +.Pa Installed +symlinks. +It's less straightforward +to do this recursively, +which you need if +you want to be able to say, +.Do +Install +.Xr nvi +for me! +.Dc +and get +.Xr ncurses 3 , +.Xr cmake 1 +and +.Xr pkgconf 1 +installed first +if they aren't already. +. +.Pp +Rather than trying to do all that in +.Xr bmake 1 , +I wrote a shell script called +.Pa Plan , +which itself produces a shell script. +Given a list of ports +to install or upgrade, +it recursively gathers their dependencies +and feeds them to +.Xr tsort 1 , +which is a neat utility +which topologically sorts a graph. +In other words, +it determines the order +in which the graph of dependencies +should be installed. +The +.Pa Plan +script produces a list of +.Xr bmake 1 +commands to make that happen +on standard output, +which can be piped to +.Xr sh 1 . +So, +the way to say the above is: +.Bd -literal -offset ident +$ ./Plan -j4 nvi | sh -e +.Ed +. +.Pp +Now, +what's missing from this approach +is the ability to automatically +uninstall no-longer-needed dependencies. +It's something I've criticized Homebrew for lacking +and one of the reasons I started using MacPorts, +so it's somewhat ironic that +my own system lacks it as well. +However, +I don't think it's much of a problem, +since I'm only packaging +what I actually want installed +in the first place. +On my personal computer, +I have all 32 of my ports installed, +and I expect that to continue. +I can always keep using MacPorts +to install things I only intend +to use temporarily. +. +.Pp +Another thing I was slightly concerned about +from the beginning was disk usage. +I think the benefits of vendoring sources +far outweigh the cost in storage, +but it would be nice to at least minimize that cost. +Previously, +I wrote about +.Xr git-sparse-checkout 1 , +which allows you to only have certain paths +checked out in your git working tree. +Since port sources aren't always interesting +and only +.Em required +while actually building the port, +it makes sense to not have them always checked out. +. +.Pp +Rather than manipulate +.Xr git-sparse-checkout 1 +myself, +I added support for it +directly into +.Pa Port.mk . +If sparse checkout is enabled, +building a port will automatically +add its source tree to the checkout list, +and cleaning that port will +remove it from the list. +At rest, +only the port system itself +and the package tarballs +need to be present on the file system. +. +.Pp +It turns out that upstream +build system behaviour +is super inconsistent, +even among projects using +the same tools. +I started collecting a list of checks +to perform on the output of my port builds +to make sure they didn't do anything weird. +They live in +.Pa Check.sh , +which gets run +when a package tarball is created. +The current list of checks is: +.Bl -bullet +.It +Check for directories not included by +.Ev PACKAGE_DIRS . +In other words, +make sure the port isn't +trying to install anything +outside of +.Pa /usr/local . +Sometimes this makes sense, +though, +which is what +.Ev PACKAGE_DIRS +is for. +.It +Check for references to PWD, +i.e. the build directory. +This can mean the build +didn't understand +.Ev PREFIX +and +.Ev DESTDIR +correctly, +or that it built with debug info. +.It +Check for binaries without manuals. +If your software installs an executable in +.Pa bin +but not a manual page, +your software is incomplete! +Sometimes this just means +I missed an extra documentation install target. +.It +Check for dynamic linking to outside objects. +In other words, +if something ended up linking to +a library installed by MacPorts +rather than the one from +.Nm jorts +or macOS. +.It +Check for dynamic linking +to system libraries +.Nm jorts +provides instead. +Similar to the last one, +if both macOS and +.Nm jorts +provide a library, +check that ports link with the latter. +.It +Check for scripts with outside interpreters. +This is analogous to the linking checks +but for scripts, +checking that their shebang lines +refer to interpreters installed +by macOS or +.Nm jorts . +.El +. +.Pp +A number of my ports +still fail some of these checks, +but I have fixed a lot of problems +the script called out. +. +.Pp +Speaking of problem ports... +git's build system is truly awful. +I'm sorry, +it's just really disappointing. +On the upside though, +I did manage to patch it +to use +.Xr asciidoctor 1 +directly to generate manual pages +from asciidoc source, +rather than generating docbook or whatever +then converting that. +One less build dependency! +I also fixed up curl's +.Pa CMakeLists.txt +(which I guess are normally only used on Windows) +to build and install documentation properly. +And I got libcaca's Cocoa driver working again! +Very important to be able to run +.Xr cacafire 1 +in a Cocoa window. +. +.Pp +Shout out to SDL2, +which didn't require any patching +or extra options beyond +.Ev USE_CMAKE=yes . +Model upstream. +. +.Pp +Some other odds and ends: +I like being able to name ports how I want +(for example, +.Sy ag ) +and use my own port version convention, +using +.Ql + +to append VCS revisions +and +.Ql ~ +to append port revisions. +I don't think those are likely +to ever clash with upstream versioning schemes. +Not that I even need to follow upstream versioning. +There is no reason the version number of +.Xr dash 1 +should start with a zero. +. +.Pp +Speaking of versions, +a big downside of maintaining your own ports tree +is that you actually need to update it. +Thankfully, +once I packaged +.Xr curl 1 +and +.Xr jq 1 +(which needs a new release dammit, +it's been 4 years and the build is broken +on macOS), +I could use the Repology API +to check if I'm behind everyone else. +Far more reliable than +trying to automate checking upstreams +for new versions. +That lives in the +.Pa Outdated +shell script. +. +.Pp +Phew! +I wrote a lot about this. +It feels a little self-indulgent, +but I've had fun working on this +and want to share. +If anyone else tries anything similar, +or is weird enough to give +.Nm jorts +a try themselves, +I'd love to hear about it! +. +.Sh SEE ALSO +.Lk https://git.causal.agency/jorts/ +.Pp +.Lk https://youtu.be/Sx3ORAO1Y6s +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +Listening to +.Em Arcade Fire \(em Arcade Fire (EP) , +.Em Arcade Fire \(em The Suburbs . +.Pp +Typed on a brand new +Leopold FC660M +with Cherry MX Red switches. +Lovely keyboard. diff --git a/www/text.causal.agency/034-voices.7 b/www/text.causal.agency/034-voices.7 new file mode 100644 index 00000000..4990295d --- /dev/null +++ b/www/text.causal.agency/034-voices.7 @@ -0,0 +1,56 @@ +.Dd March 5, 2022 +.Dt VOICES 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Voices +.Nd more kinds of them +. +.Sh DESCRIPTION +Welcome to the third time +I started writing this post! +I think the first time +was after watching a jan Misali video +that had clips of audio interviews in it. +It got me thinking about +how interesting it was +to hear someone's voice +without knowing anything else about them. +. +.Pp +That's pretty much all I managed to write +the first two times I started this. +If I get past this next sentence, +then I can probably finish the post. +What stopped me was that +all my thoughts and feelings about voices +are influenced by being trans +(and being a fan of other trans people), +and I thought, +.Dq I don't write about that here, +but why don't I? +I don't have to come out to my blog. +. +.Pp +So really what I have been wanting to say is this: +every trans woman's voice that I have heard +has sounded genuinely wonderful to me. +Especially if you're reading this +and we've been on a voice call before. +I know, +voices are the object of so much self-consciousness, +but I really wish they didn't have to be. +Most of us do not sound like cis women +and to me that is fine. +Good, actually. +Trans women sound like trans women. +As a voice appreciator, +I am so happy to hear more kinds. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +I've been watching some Vektroid streams lately, +and I love her voice. +It was another thing +reminding me to write this. diff --git a/www/text.causal.agency/035-addendum-2021.7 b/www/text.causal.agency/035-addendum-2021.7 new file mode 100644 index 00000000..262f2178 --- /dev/null +++ b/www/text.causal.agency/035-addendum-2021.7 @@ -0,0 +1,111 @@ +.Dd March 18, 2022 +.Dt ADDENDUM-2021 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Addendum 2021 +.Nd missed music +. +.Sh DESCRIPTION +I just realized that I totally forgot +some important music for me from last year +in my Albums 2021 post, +because it wasn't in my playlist. +Last year I watched +the Berserk anime from 1997, +and its soundtrack is incredible. +. +.Pp +Actually the only reason +I started watching it at all +was because of the music. +I was watching the wayneradiotv stream, +.Do +Mon repas durant un temps de tristesse; +un pizza je n'oublierai jamais +.Dc +and I was mesmerized by the Guts theme. +I had to find out what it was from. +This was also around the time +that Kentaro Miura died +so people were really talking about it. +Anyway just hearing +that part of the soundtrack +got me to start watching the anime, +since you can find it all on youtube. +. +.Pp +The anime in general did not disappoint. +Actually it's really fucking good, +and so is the rest of the soundtrack. +The title sequence and credits tracks +are so good that I let them play +every episode even though +I watched the series over only like 2 days. +. +.Pp +I absolutely love whatever genre this stuff is. +Is '90s anime intros its own genre? +Something about combining +acoustic and electric guitars, +maybe. +I'm also fond of +the poorly written english lyrics. +They're poetic in a distinctive way. +I feel the same about +that Shinsei Kamattechan +song that was used for the credits of +Attack on Titan season 2. +Honestly awesome to write lyrics +in a second language you haven't mastered. +. +.Pp +So, +the intro track, +.Em Tell Me Why . +First off, +that sword sound effect +near the beginning rules. +Put that in more songs. +What I really can't get enough of +on this track are +the quiet shouty vocals +a bit off to the left +during the chorus. +It's such a cool idea +to have clean lead vocals +and shouting in the background. +. +.Pp +And the credits track, +.Em "Waiting So Long" . +That first low note is so good. +This is really a perfect credits song +for the atmosphere of the show. +It's creeping. +The dual vocals +the whole way through +are such an interesting texture. +Both of these tracks +have really cool vocal sounds. +And that dirty final guitar chord +is a great sound to end on. +. +.Sh SEE ALSO +These aren't great quality uploads +but this stuff is sadly hard to find. +.Bl -tag -width Ds +.It "Guts" +.Lk https://youtu.be/vZa0Yh6e7dw +.It "Earth" +.Lk https://youtu.be/5iAViNf9Z4Y +.It "Penpals \(em Tell Me Why" +.Lk https://youtu.be/I2rV8oKWSdM +.It "Silver Fins \(em Waiting So Long" +.Lk https://youtu.be/70GD2SBCq64 +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +.Dq I like swinging my sword in battle. diff --git a/www/text.causal.agency/036-compassion.7 b/www/text.causal.agency/036-compassion.7 new file mode 100644 index 00000000..9d0d887d --- /dev/null +++ b/www/text.causal.agency/036-compassion.7 @@ -0,0 +1,105 @@ +.Dd March 31, 2022 +.Dt COMPASSION 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Compassion +.Nd better world fiction +. +.Sh DESCRIPTION +Recently I watched the film +.Em Margarita With a Straw . +I'm not sure how to feel +about some aspects of it, +but it tries to do a lot, +and I was still thinking about it +a couple days later. +. +.Pp +What really sticks out about it, +to me, +is that it is +better world fiction, +for lack of a better term. +It's a film about two characters +with disabilities, +but it doesn't play into tropes. +There's no big dramatic scene +where a character gets treated unfairly. +It doesn't really happen. +In the world of the movie, +most people are accepting, +patient +and compassionate. +That's not to say +there is no conflict. +The film is just telling a different story. +. +.Pp +The story takes place +in a better world. +Or maybe it takes place +in a world that exists +within our own, +hidden between the worse parts. +It's wonderfully subversive. +Because I went into the film +expecting at least one deeply upsetting +scene of discrimination. +What else would you expect +of a story like this one, +right? +But instead of being upset, +I was warmed. +It was so nice to see +the characters work through +their own problems +surrounded by simple kindness. +And when it was over, +I was left wanting +to move our world +closer to that one. +. +.Pp +That's what I love about this kind of fiction. +It's why I love the books of Becky Chambers so much. +They give me hope, +and guidance. +I count the +.Em Murderbot Diaries +series in this as well, +which shows a sort of bad world, +and an alternative. +I think it's so important +to see the good that exists +and the good that could exist. +Rather than something to fight against, +these stories show something to fight for. +A more compassionate world. +. +.Pp +I know, +one person can't change the world. +But they can change their own world, +and the worlds of those around them. +And slowly, +good things can spread. +I'll strive to be +more patient, +more understanding, +more compassionate, +and I hope you will too. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Pp +I can't help but worry, +when I write something like this, +that someone I know will read it +and think that I'm lying +because I've hurt them. +If that's the case, +I am sorry, +and I promise +I am trying to do better. diff --git a/www/text.causal.agency/037-care.7 b/www/text.causal.agency/037-care.7 new file mode 100644 index 00000000..052a4727 --- /dev/null +++ b/www/text.causal.agency/037-care.7 @@ -0,0 +1,167 @@ +.Dd April 3, 2022 +.Dt CARE 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Care +.Nd trans stuff in Montreal +. +.Sh DESCRIPTION +This kind of info +is frustratingly hard to find +even from support orgs +and the like. +I think it's unlikely +that anyone in my blog's audience +is also someone who needs this info, +and my blog isn't easy to find either, +but I want to at least +make it available somewhere. +Really this is just like +the posts where I figure out +how to do something with a computer +then I write it down. +. +.Pp +Prices obviously change, +by which I mean they inevitably go up, +but I'm gonna give the amounts I paid +in 2021\(en2022. +Also if you want more details +about any of this +please email me. +I will be happy to tell you all about it. +. +.Ss Medication +I get HRT through +Dr. Gabrielle Landry +at La Clinique A, +which is a private clinic. +I've done everything over the phone. +After the first consultation, +I signed an informed consent form +and had a prescription the next day, +which I could start +after I got an initial blood test. +The information I found +said to contact a specific person +at the clinic with a direct phone number, +which is what I did. +Email me if you want that number. +. +.Pp +I paid $300 for the first consult, +$195 for the first followup, +and $75 for further followups. +I think annual appointments +are more expensive +than the followups. +I've been getting blood tests done at a CLSC, +which is free. +On the public drug insurance plan, +I paid $30-$35 +for my prescriptions +as my dosage increased. +I have private insurance now +that entirely covers prescriptions, +so I'm not sure what I'd be paying +for my current prescription +on the public plan. +. +.Ss Hair removal +I tried laser hair removal, +for longer than I should have. +It was a waste of time and money. +Do not believe any arguments about +its convenience over electrolysis. +. +.Pp +I've started getting electrolysis done +with Dimi. +Again, +feel free to email me for contact info. +He is very good and can do long sessions. +I really don't find it very painful, +which I think is partly my own pain tolerance +and partly good equipment and skill. +I've also found that taking acetaminophen beforehand +and dressing warmly to keep my body relaxed help. +I've paid $85 for hour-long sessions +and $160 for two-hour sessions. +I'm still early in treatment, +but I'm really happy with the results so far! +. +.Ss Sex & name change +The form for this is +.Do +Application to Change the Sex Designation +of a Person 18 Years of Age and Over +.Dc +from the +.Em Directeur de l'\('etat civil . +It's self-ID, +but you have to get it signed by +someone you know +and a commissioner for oaths. +Julien at P10 is qualified for that +and was super nice. +We did it over Zoom. +It's a free service, +so I made a donation to P10. +. +.Pp +I paid $144 to file mine +but it's now FREE +the first time you do it. +Also $17 to mail it. +Surprisingly, +I got an acknowledgment letter +.Po +just saying they got it +and would start looking at it +.Dq shortly +.Pc +like a week and a half +after I mailed the application. +My cheque was cashed +39 days after the date +on the acknowledgment, +and I got a +.Dq favourable decision +a week later. +It takes another 30 days +to get the certificate of change, +after which you can +order a new birth certificate +and RAMQ will (slowly) send you a form +to get a new card. +In all it took about 4 months +from when I mailed the application +to having ID with my name on it. +. +.Ss Therapy +I'm not seeking therapy +for gender specifically, +but I would like to find a good therapist +that's aware of it. +I'll update this +if I find one. +. +.Ss Piercings +Ok I know this isn't trans-specific +but at least for me getting piercings +was gender-affirming. +Cuz I got nipple piercings lol. +Anyway, +I went to Mauve. +They're super nice, +really know what they're doing, +and their website has lots of info. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Pp +If somehow you did find this useful, +I'd love for you to email me +and let me know how things went for you. diff --git a/www/text.causal.agency/038-agency.7 b/www/text.causal.agency/038-agency.7 new file mode 100644 index 00000000..f99a070b --- /dev/null +++ b/www/text.causal.agency/038-agency.7 @@ -0,0 +1,85 @@ +.Dd April 14, 2022 +.Dt AGENCY 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Agency +.Nd origin of a name +. +.Sh DESCRIPTION +When I registered this domain name, +it was aspirational. +Intentionally so. +I wanted a new domain +for a new identity, +and I was thinking about personhood. +That's what causal agency means. +. +.Pp +It really was aspirational +for me at the time. +I spent a lot of time +wishing I could be a person, +because I didn't feel like one. +I didn't feel real, +like everyone else was. +I didn't have any power +over my own life. +Things just happened to me, +and I watched. +There wasn't really a +.Dq me +there. +The world was something that happened +but that I couldn't interact with. +I felt like that +for most of my life. +. +.Pp +But at some point +I decided that, +even if I wasn't now, +one day I hoped to be an actual real life person. +Like most programmers +I am dreadful at naming things, +so I didn't come up +with this clever domain name +myself. +I typed +.Dq person +into some thesaurus, +and it gave back +.Dq causal agent , +and I realized +agency is a TLD now. +. +.Pp +Maybe it's a little dramatic +to label myself with the thing +I didn't think I had. +But who knows, +maybe it helped. +Because it took a few years, +but I did become a person. +I feel real now. +I can change my own life +and the world around me. +I have causal agency. +. +.Pp +I am really proud of this domain name. +I'm proud to put it on everything I make. +Every instance of it +is a reminder +that I did what I set out to do, +and that I'm still doing it. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +. +.Pp +If anything in this post resonates with you, +I want you to know that, +whatever you think you can't do, +it is possible, +and you'll get there one day. diff --git a/www/text.causal.agency/039-apologies.7 b/www/text.causal.agency/039-apologies.7 new file mode 100644 index 00000000..1b15076a --- /dev/null +++ b/www/text.causal.agency/039-apologies.7 @@ -0,0 +1,81 @@ +.Dd September 19, 2022 +.Dt APOLOGIES 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Apologies +.Nd making them +. +.Sh DESCRIPTION +Apologies are very important to me. +Unfortunately +I've only recently realized +how valuable they are. +I've tried to think about +what makes a good apology, +since it's not something +I was ever taught. +This is the advice +I came up with for myself, +on how to apologize. +. +.Bl -enum +.It +Make the apology. +This is the most important part. +If you feel guilty +for something you've done, +or think you might have hurt someone, +apologize. +Even if they don't need an apology, +saying sorry won't hurt. +And start with that. +Literally say +.Dq I'm sorry . +Sometimes people forget that. +.Pp +On the other side, +if you've been hurt by someone, +and you trust them, +let them know. +Give them a chance to apologize. +People don't always realize +they've made a mistake. +. +.It +Explain what you did wrong. +I think it's important +for the other person +to know you understand +how you've messed up. +Really think about this! +It's what will help you learn. +If you know you've hurt someone +but you're not sure why, +you can try asking them. +Take their answer seriously. +. +.It +Don't make excuses. +Do not talk about yourself. +Don't even mention +how you were feeling stressed that day, +or whatever. +It's not relevant. +We all make mistakes, +we all have bad days. +. +.It +Commit to doing better. +Try to learn from your mistakes. +Say it won't happen again. +Literally say +.Dq I won't do that again . +And then try your hardest to make that true. +An apology is a commitment, +not something you're done with +once you've said it. +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/040-sound-memory.7 b/www/text.causal.agency/040-sound-memory.7 new file mode 100644 index 00000000..c995de08 --- /dev/null +++ b/www/text.causal.agency/040-sound-memory.7 @@ -0,0 +1,165 @@ +.Dd November 14, 2022 +.Dt SOUND-MEMORY 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm Sound Memory +.Nd associations +. +.Sh DESCRIPTION +.Ss Talking Heads \(em "Remain In Light" +The first time I gave this album a serious listen +was when I was going for several-hour walks +at 4 in the morning in, +I think, +fall 2020. +I would stay up all night, +go out walking at 4am +for a couple hours, +come home, +eat +.Dq breakfast +and go to sleep. +I listened to this album +walking on completely empty +big city streets +in the dark. +. +.Ss Buffy Sainte-Marie \(em Up Where We Belong +I started listening to this album +after hearing it many mornings +walking into the cafe on my block +back in 2019. +I could tell Vincent was working +if I heard this when I opened the door. +. +.Ss Molasses \(em Trilogie: Toil & Peaceful Life +I listened to this when I had 8am classes +in CEGEP. +In particular my first semester philosophy course, +which was in the forum. +I usually got there even earlier +because of how the bus schedules worked out. +There was another girl in my class, +who I always sat next to, +who also got there early, +but we never spoke outside of class. +. +.Ss Arcade Fire \(em Funeral +This album just feels like walking outside +in fresh snow in early winter, +you know? +. +.Ss Molasses \(em Trouble at Jinx Hotel +I listened to this when I was looking for an apartment. +I specifically remember listening to it +walking down Clark toward my new place +to pick up my keys. +. +.Ss Arcade Fire \(em Neon Bible +The song +.Dq "No Cars Go" +is strongly associated for me +with my earliest gender feelings. +It's how I date when I first +started to feel like something was wrong. +The Suburbs was released in 2010, +so I was probably listening to Neon Bible +in 2011. +Ten years between that +and coming out. +. +.Ss "Do Make Say Think" \(em "You You're a History In Rust" +I remember hearing +.Dq "A Tender History In Rust" +for the first time +at the office of my first job. +Me and my coworkers stayed late, +probably on a Friday night, +drinking free tech startup booze. +. +.Ss mewithoutYou \(em It's All Crazy! It's All False! It's All a Dream! It's Alright +I exclusively listened to this album +on a high school trip to Europe. +Every morning when we got on the bus, +I heard +.Dq Every Thought a Thought of You +and every night before bed +I listened to +.Dq The King Beetle on a Coconut Estate . +. +.Ss Arcade Fire \(em The Suburbs +I listened to this album a tonne +when I was playing +Minecraft and Urban Terror +with my online friends +while I was in high school. +In particular I remember +a backyard shed World of Padman map +and the apartments Minecraft world. +. +.Ss Arcade Fire \(em Reflektor +I associate +.Dq Afterlife +with the walk between Laurier metro +and my first job, +in the winter. +Must've just been how the timing worked out +with my commute at the time. +. +.Ss Swans \(em To Be Kind +I listened to this on one of my playthroughs +of Half-Life 2. +In particular I associate +.Dq Bring the Sun / Toussaint L'Ouverture +with the Water Hazard chapter. +. +.Ss Wrekmeister Harmonies \(em Light Falls +For a while I put this on whenever I +left my apartment to go somewhere +and it was already dark, +so probably winter. +. +.Ss St. Vincent \(em MASSEDUCTION +This, +along with the next one, +I think were all I listened to +on a family vacation +to Quebec City and New Brunswick +some years ago. +. +.Ss SOPHIE \(em Oil of Every Pearl's Un-Insides +Many hours on the road +on that family vacation. +Two albums on repeat. +. +.Ss Julia Holter \(em Aviary +This is another album +I listened to when I was taking +walks at 4am. +I wasn't in a good place. +Yet. +. +.Ss Beep Test \(em Laugh Track +A tape from the first act +at one of my favourite shows +I've ever been to, +at La Sotterenea +in Suoni 2019. +I wish I had been out already. +. +.Ss The Armed \(em Only Love +The third of the albums I listened to +on those dark walks. +I listened to it loud, +this album's mixing needs it. +. +.Ss Eliza Kavtion \(em The Rez That Summer +A favourite local artist. +I remember vividly the first time +I heard her play, +opening for Wrekmeister Harmonies +at La Vitrola in 2018. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/041-albums-2022.7 b/www/text.causal.agency/041-albums-2022.7 new file mode 100644 index 00000000..48bd3c3d --- /dev/null +++ b/www/text.causal.agency/041-albums-2022.7 @@ -0,0 +1,185 @@ +.Dd December 21, 2022 +.Dt ALBUMS-2022 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm albums 2022 +.Nd review +. +.Sh DESCRIPTION +it's the year-end review +of albums I listened to. +same as last year, +I added any albums I got into +this year to a playlist. +I've actually done that +every year since 2018. +maybe I'll review +those old playlists some time. +. +.Ss ZHAOZE \(em SUMMER INSECTS TALK ABOUT ICE (2021) +it's a five-and-a-half-minute album! +you can loop it however long you want. +it's really lovely. +.Pp +favourite track: +ON HORSEBACK, TO FARAWAY +. +.Ss KATE BUSH \(em HOUNDS OF LOVE (1985) +first of all I do not watch that one show. +I've known that track for a while actually. +I mean I probably first heard the CHROMATICS cover. +but anyway, +I think someone mentioned this album +on IRC at just the right time +and I put it on. +the second half really shines tbh. +love a concept album. +.Pp +favourite tracks: +RUNNING UP THAT HILL, +HOUNDS OF LOVE, +AND DREAM OF SHEEP, +WATCHING YOU WITHOUT ME, +THE MORNING FOG. +. +.Ss GODSPEED YOU! BLACK EMPEROR \(em ALL LIGHTS FUCKED ON THE HAIRY AMP DROOLING (1994) +didn't expect to hear this probably ever. +still wild that it finally got uploaded. +and to be honest I'm a little mad +that it's actually good. +like yeah it's not a godspeed album +but it holds up as a tape on its own. +it's the kind of shit I listen to. +also can't believe some people +still thought it was fake. +like have you not heard +any other efrim menuck projects? +.Pp +favourite tracks: +$13.13, +DIMINISHING SHINE, +DADMOMDADDY, +333 FRAMES PER SECOND, +ALL ANGELS GONE. +. +.Ss BLACK DRESSES \(em FORGET YOUR OWN FACE (2022) +woops I think I only listened to this like twice. +will need to revisit it later for sure. +I'll like it. +. +.Ss BACKXWASH \(em I LIE HERE BURIED WITH MY RINGS AND MY DRESSES (2021) +only got into this album +after hearing it live this summer. +was the first show I went to in years +and it was really fucking good. +gotta listen to this shit loud. +sampling godspeed for a beat fucks. +honestly back to back bangers. +.Pp +favourite tracks: +I LIE HERE BURIED WITH MY RINGS AND MY DRESSES, +TERROR PACKETS, +SONG OF SINNERS, +BURN TO ASHES. +. +.Ss PHILIP GLASS ENSEMBLE \(em EINSTEIN ON THE BEACH (1979) +actually just the knee plays +because I can't be bothered +listening to all of it. +and I'm embarrassed by how much +I enjoy this avant-garde bullshit. +like ok just sing repeating numbers at me +and my brain is happy. +what is this? +my kink? +anyway I also have kind of an obsession +with the +.Dq story of love +in knee 5. +I fucking hate it. +but it's delivered so well. +and that violin though! +.Pp +favourite tracks: +KNEE 1, +KNEE 5. +. +.Ss KANYE WEST \(em YEEZUS (2013) +ok look I listened to this +before recent events. +what the fuck. +it's a really good album though? +pretty sure I listened to it +because bound 2 kept getting in my head, +because of that minecraft parody parody +wayne did ages ago. +.Pp +favourite tracks: +BLACK SKINHEAD, +HOLD MY LIQUOR, +BLOOD ON THE LEAVES, +BOUND 2. +. +.Ss FLYING RACCOON SUIT \(em AFTERGLOW (2021) +I've listened to the whole album +a few times +but I'm mostly just here +for the title track. +this also happened to be +dropped in IRC at just the right time. +good ska-punk-type shit. +and I like lisps ok. +.Pp +favourite track: +AFTERGLOW. +. +.Ss RAMSHACKLE GLORY \(em LIVE THE DREAM (2011) +one of those albums +I don't know why I took so long +to get to. +I've been listening to johnny hobo +since I was like in high school. +ramshackle is a little more hopeful +and I love that. +your heart is a muscle the size of your fist. +keep on loving. +keep on fighting. +.Pp +favourite tracks: +WE ARE ALL COMPOST IN TRAINING, +NEVER COMING HOME, +YOUR HEART IS A MUSCLE THE SIZE OF YOUR FIST. +. +.Ss LES RALLIZES D\('ENUD\('ES \(em THE OZ TAPES (2022) +a pleasant surprise in someone's playlist. +lately I've been listening to this +in the metro to or from electrolysis. +it's good listening for that. +bold to have two versions +of the same 24-minute song +on the same release. +.Pp +favourite tracks: +A SHADOW ON OUR JOY, +THE LAST ONE_1970 (ver.2). +. +.Ss LINGUA IGNOTA \(em SINNER GET READY (2021) +another I'm only getting into +after hearing it live. +just last sunday actually. +was a good show. +people will go wild +to hear a cover live for real. +.Pp +favourite tracks: +I WHO BEND THE TALL GRASSES, +PENNSYLVANIA FURNACE, +PERPETUAL FLAME OF CENTRALIA. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +I started writing this +before I saw LINGUA IGNOTA. +good thing I waited. diff --git a/www/text.causal.agency/042-comfort-music.7 b/www/text.causal.agency/042-comfort-music.7 new file mode 100644 index 00000000..445e04c3 --- /dev/null +++ b/www/text.causal.agency/042-comfort-music.7 @@ -0,0 +1,62 @@ +.Dd February 23, 2024 +.Dt COMFORT-MUSIC 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm comfort music +.Nd feel better +. +.Sh DESCRIPTION +it's been a while. +and I'm on almost no sleep +and haven't eaten a real meal +since noon. +which is a state I've written +at least a couple posts in before, +so what better time +to return to what has apparently +become this blog's format: +lists of some music I like. +. +.Pp +this is a list of music that comforts me. +. +.Bl -bullet +.It +knee play 5, from einstein on the beach. +I like the organ and the counting and the cadence of the story. +.It +low \(em words. +and I'm tired. +.It +godspeed you! black emperor \(em storm. +this is like my original comfort music. +been listening to it since I was teenage. +the grooves are worn deep in my mind. +.It +set fire to flames \(em love song for 15 ontario (w/ singing police car). +I like how it ends. +.It +va, from the beginner's guide. +I think that's the whole point. +though maybe it's too sad +to be truly comforting. +.It +undertale, from undertale. +what can I say? +.It +wrekmeister harmonies \(em covered in blood from invisible wounds. +I find quite a bit of the album comforting really. +I'm picking this one because I like the cadence +of the lyrics. +.It +lingua ignota \(em pennsylvania furnace and perpetual flame of centralia. +these are really my go to in recent times. +I like waiting for the next line. +.El +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency +.Pp +I don't think I've said anything +very interesting here. diff --git a/www/text.causal.agency/043-little-blessings.7 b/www/text.causal.agency/043-little-blessings.7 new file mode 100644 index 00000000..957c6289 --- /dev/null +++ b/www/text.causal.agency/043-little-blessings.7 @@ -0,0 +1,78 @@ +.Dd March 24, 2024 +.Dt LITTLE-BLESSINGS 7 +.Os "Causal Agency" +. +.Sh NAME +.Nm little blessings +.Nd life's +. +.Sh DESCRIPTION +today I went out to go around. +run some errands and do some shopping. +along the way I was given +several of life's little blessings. +. +.Pp +while walking on ste-cath +between berri and complexe desjardins, +there was a somewhat disheveled man +walking in the same direction and singing. +he had a beautiful voice. +he was singing a sad song in french, +and he sung it well and enunciated every word. +. +.Pp +in the mcdonald's at complexe desjardins, +while waiting for my order, +there were what appeared to be +a teenager and her younger brother, +who must have been +looking at the display of +current happy meal toys. +the teenager was playing smash or pass, +to the amusement of the younger one. +they got ice cream +and ate it across the room from me downstairs. +. +.Pp +later, +taking the 24 home from atwater +carrying my new vacuum cleaner, +the bus got lost. +I think the driver missed the stop +and tried to compensate +by turning north onto peel +and stopping there. +but then he had to keep going up peel. +he turned right onto docteur-penfield, +which just brings you further up the mountain. +when it met des pins, +he turned left and pulled over, +asking for guidance over the radio. +we got moving again, +back towards peel. +that's how I ended up +on a 24 +.Dq sherbrooke +east, +facing west on des pins. +it was actually quite scenic. +and amusing. +I was in no rush. +. +.Pp +after getting back onto sherbrooke, +the bus had to take another detour, +this one planned. +so my ride on the 24, +which normally only drives on sherbrooke, +ended up going on peel, +docteur-penfield, +des pins, +de bleury, +ren\('e-l\('evesque +and saint-laurent. +it was a very exciting bus trip. +. +.Sh AUTHORS +.An june Aq Mt june@causal.agency diff --git a/www/text.causal.agency/Makefile b/www/text.causal.agency/Makefile new file mode 100644 index 00000000..a8683a20 --- /dev/null +++ b/www/text.causal.agency/Makefile @@ -0,0 +1,66 @@ +WEBROOT = /var/www/text.causal.agency + +TXTS += 001-make.txt +TXTS += 002-writing-mdoc.txt +TXTS += 003-pleasant-c.txt +TXTS += 004-uloc.txt +TXTS += 005-testing-c.txt +TXTS += 006-some-libs.txt +TXTS += 007-cgit-setup.txt +TXTS += 008-how-irc.txt +TXTS += 009-casual-update.txt +TXTS += 010-irc-suite.txt +TXTS += 011-libretls.txt +TXTS += 012-inability.txt +TXTS += 013-hot-tips.txt +TXTS += 014-using-vi.txt +TXTS += 015-reusing-tags.txt +TXTS += 016-using-openbsd.txt +TXTS += 017-unpasswords.txt +TXTS += 018-operating-systems.txt +TXTS += 019-mailing-list.txt +TXTS += 020-c-style.txt +TXTS += 021-time-machine.txt +TXTS += 022-swans-are-dead.txt +TXTS += 023-sparse-checkout.txt +TXTS += 024-seprintf.txt +TXTS += 025-v6-pwd.txt +TXTS += 026-git-comment.txt +TXTS += 027-openbsd-linode.txt +TXTS += 028-names.txt +TXTS += 029-topics.txt +TXTS += 030-discs.txt +TXTS += 031-books-2021.txt +TXTS += 032-albums-2021.txt +TXTS += 033-jorts.txt +TXTS += 034-voices.txt +TXTS += 035-addendum-2021.txt +TXTS += 036-compassion.txt +TXTS += 037-care.txt +TXTS += 038-agency.txt +TXTS += 039-apologies.txt +TXTS += 040-sound-memory.txt +TXTS += 041-albums-2022.txt +TXTS += 042-comfort-music.txt +TXTS += 043-little-blessings.txt + +all: colb ${TXTS} + +.SUFFIXES: .7 .fmt .txt + +.7.txt: + mandoc -T utf8 $< | ./colb > $@ + touch -m -r $< $@ + +.fmt.txt: + fmt $< | sed '1,/^$$/d' > $@ + touch -m -r $< $@ + +feed.atom: feed.sh colb ${TXTS} + sh feed.sh > feed.atom + +clean: + rm -f colb ${TXTS} feed.atom + +install: colb ${TXTS} feed.atom + install -p -m 644 ${TXTS} feed.atom ${WEBROOT} diff --git a/www/text.causal.agency/colb.c b/www/text.causal.agency/colb.c new file mode 100644 index 00000000..5faabc3a --- /dev/null +++ b/www/text.causal.agency/colb.c @@ -0,0 +1,16 @@ +#include <locale.h> +#include <stdio.h> +#include <wchar.h> +int main(void) { + setlocale(LC_CTYPE, "en_US.UTF-8"); + wint_t next, prev = WEOF; + while (WEOF != (next = getwchar())) { + if (next == L'\b') { + prev = WEOF; + } else { + if (prev != WEOF) putwchar(prev); + prev = next; + } + } + if (prev != WEOF) putwchar(prev); +} diff --git a/www/text.causal.agency/feed.sh b/www/text.causal.agency/feed.sh new file mode 100644 index 00000000..71bbf662 --- /dev/null +++ b/www/text.causal.agency/feed.sh @@ -0,0 +1,58 @@ +#!/bin/sh +set -eu + +readonly Root='https://text.causal.agency' + +updated=$(date -u '+%FT%TZ') +cat <<-EOF + <?xml version="1.0" encoding="utf-8"?> + <feed xmlns="http://www.w3.org/2005/Atom"> + <title>Causal Agency</title> + <author><name>June</name><email>june@causal.agency</email></author> + <link href="${Root}"/> + <link rel="self" href="${Root}/feed.atom"/> + <id>${Root}/</id> + <updated>${updated}</updated> +EOF + +encode() { + sed ' + s/&/\&/g + s/</\</g + s/"/\"/g + ' "$@" +} + +set -- *.txt +shift $(( $# - 20 )) +for txt; do + entry="${txt%.txt}.7" + test -f "$entry" || entry="${txt%.txt}.fmt" + date=$(grep '^[.]Dd' "$entry" | cut -c 5-) + title=$(grep -m 1 '^[.]Nm' "$entry" | cut -c 5- | encode) + summary=$(grep '^[.]Nd' "$entry" | cut -c 5- | encode) + published=$(date -ju -f '%B %d, %Y %T' "${date} 00:00:00" '+%FT%TZ') + mtime=$(stat -f '%m' "$entry") + updated=$(date -ju -f '%s' "$mtime" '+%FT%TZ') + cat <<-EOF + <entry> + <title>${title}</title> + <summary>${summary}</summary> + <link href="${Root}/${txt}"/> + <id>${Root}/${txt}</id> + <published>${published}</published> + <updated>${updated}</updated> + <content type="xhtml"> + <div xmlns="http://www.w3.org/1999/xhtml"> + EOF + printf '<pre>' + encode "$txt" + cat <<-EOF + </pre> + </div> + </content> + </entry> + EOF +done + +echo '</feed>' |