👾 Tentaclar Aliens' Epic Extraterrestrial Jungle Dance Party 👾
14 May 2026

Pimp your catgirl(1)

⣿⡟⠙⠛⠋⠩⠭⣉⡛⢛⠫⠭⠄⠒⠄⠄⠄⠈⠉⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⡇⠄⠄⠄⠄⣠⠖⠋⣀⡤⠄⠒⠄⠄⠄⠄⠄⠄⠄⠄⠄⣈⡭⠭⠄⠄⠄⠉⠙
⣿⡇⠄⠄⢀⣞⣡⠴⠚⠁⠄⠄⢀⠠⠄⠄⠄⠄⠄⠄⠄⠉⠄⠄⠄⠄⠄⠄⠄⠄
⣿⡇⠄⡴⠁⡜⣵⢗⢀⠄⢠⡔⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
⣿⡇⡜⠄⡜⠄⠄⠄⠉⣠⠋⠠⠄⢀⡄⠄⠄⣠⣆⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢸
⣿⠸⠄⡼⠄⠄⠄⠄⢰⠁⠄⠄⠄⠈⣀⣠⣬⣭⣛⠄⠁⠄⡄⠄⠄⠄⠄⠄⢀⣿
⣏⠄⢀⠁⠄⠄⠄⠄⠇⢀⣠⣴⣶⣿⣿⣿⣿⣿⣿⡇⠄⠄⡇⠄⠄⠄⠄⢀⣾⣿
⣿⣸⠈⠄⠄⠰⠾⠴⢾⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⢁⣾⢀⠁⠄⠄⠄⢠⢸⣿⣿
⣿⣿⣆⠄⠆⠄⣦⣶⣦⣌⣿⣿⣿⣿⣷⣋⣀⣈⠙⠛⡛⠌⠄⠄⠄⠄⢸⢸⣿⣿
⣿⣿⣿⠄⠄⠄⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠈⠄⠄⠄⠄⠄⠈⢸⣿⣿
⣿⣿⣿⠄⠄⠄⠘⣿⣿⣿⡆⢀⣈⣉⢉⣿⣿⣯⣄⡄⠄⠄⠄⠄⠄⠄⠄⠈⣿⣿
⣿⣿⡟⡜⠄⠄⠄⠄⠙⠿⣿⣧⣽⣍⣾⣿⠿⠛⠁⠄⠄⠄⠄⠄⠄⠄⠄⠃⢿⣿
⣿⡿⠰⠄⠄⠄⠄⠄⠄⠄⠄⠈⠉⠩⠔⠒⠉⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠐⠘⣿
⣿⠃⠃⠄⠄⠄⠄⠄⠄⣀⢀⠄⠄⡀⡀⢀⣤⣴⣤⣤⣀⣀⠄⠄⠄⠄⠄⠄⠁⢹

So you want to use (or go back to using) IRC after testing the waters via your favorite website's KiwiIRC or TheLounge webclient. You've lurked in the main chat channel long enough for people to mention "clients". Actual native clients - none of that web stuff.

You scour the web until you find a minimalist terminal client which is 98% of what you want. It's easy to configure. It works. It looks fine in your terminal emulator. You're mostly happy with it - but you have a few things you'd like to fix. You want that last 2%. You want to make it comfy. But it's written in C, and you haven't touched that for a long time - so you hold off on that plan for a while.

Eventually, the missing 2% irks you enough that you decide the time investment is worth it. The quest for a comfy catgirl(1) is the subject of today's devlog.

Note: If you are interested in the finished product, deb packages are provided here

Setting up the git(1) Repository

This will be a "soft fork." I expect to rebase my changes onto upstream periodically, or possibly even push some of them back. Therefore, adding the upstream remote with git remote add ... is a good start.

Ensuring Everything Really Works

catgirl(1) is a C program. I've been using it for months without any crashes or segfaults, so it might be safe to assume that most of the common code paths do not have egregious memory access bugs.

However, one can't be certain unless some form of allocator or memory-access instrumentation is used. Before performing more drastic modifications to the code, it's worth inspecting it for easily-solvable memory bugs.

valgrind(1) is often used for this; it works by hooking into libc's malloc() and free() to catch leaks and use-after-free bugs in heap allocations. However, newer versions of GCC and Clang offer a more broadly effective solution: AddressSanitizer (ASan). ASan instruments all memory access operations, inserting bounds checks for any object access, regardless of whether the memory is stack- or heap-allocated. Since ASan is included with gcc, adding -g -fsanitize=address to CFLAGS when make(1)-ing the project is all that's needed to produce an instrumented binary.

Does running the program under ASan find anything? Yes! Switching buffers terminates the program, with ASan barfing out a trace complaining about a use-after-free. The trace is straightforward and pinpoints the exact location of the initial free() and the subsequent access. This made for an easy fix.

Adding Custom Macros

catgirl supports simple macro substitution by typing \macro and pressing C-x. While this is fine, there are two issues with this approach:

  1. catgirl uses a hardcoded macro table (Fine if you're happy with the predefined macro set, but we want more bling 💎✨)
  2. The backslash is actually an allowed character in IRC nicknames (See the <nick> rule in the RFC1459 pseudo-BNF)

To address #1, I implement a mechanism for loading macros from a simple two-column <macro> <substitution> configuration file.

My initial version of this was overly complicated and attempted to parse the file by hand via character comparisons and iswspace(). While wading through manpages in search of a better solution, I discovered scanf() scansets. Neat! Not only do these functions have support for scanning wide character strings, but they allow encoding some of the scanning constraints within the format string via scansets.

The original code used a linear search for macro table lookups, so I replaced that with a binary search. This is intended to be more of a simplification rather than an optimization - lsearch(3p), although not part of standard C, would've been a possible alternative.

I also added a /macros command to display the macro expansion table and (re)load new macro files. As for #2, I picked a new prefix character to replace / - according to the RFC1459 pseudo-BNF, . is a possible choice - it's not a valid nickname character (as it's reserved for hostnames), so it won't interfere with nick completion.

Fixing Minor Annoyances

While testing the macro implementation, I ran into a couple of minor bugs - sometimes, macros weren't being expanded. This was harder to fix than expected because it seemed to happen sporadically without a clear trigger. Naturally, I assumed the new code was the culprit.

The next time this happened, attaching to the process via gdb(1) and inspecting the line editor's state offered some insight into the cause: Sure enough, there's a subtle off-by-one error in the macro expansion logic. After reproducing this on the upstream sources, After reproducing this on the upstream source, I was able to apply a permanent fix.

Next was an issue with the overflow marker bleeding into the prompt area, caused by a missing color pair (pen) reset when updating the input state.

Finally, I constrained command completion within the network (server) buffer to slash-prefixed entries. This prevents the client from completing "N" to "NickServ" when you're just trying to type a command. It's mostly a correctness fix; in practice, erroneous completions are just rejected as invalid commands.

WALLOPS

During testing, the Libera.Chat admins sent a Wallops. Apparently, a new Linux LPE was discovered and people were talking about it. I didn't get it.

It turns out catgirl(1) didn't support receiving Wallops. Luckily, the message format is simple. An implementation only needs to echo the message to the network buffer. Implementing a /wallops command to send a Wallops is only marginally more difficult due to the similarity to PRIVMSG.

I was too lazy to set up my own ircd for testing this. Luckily, the RektIRC IRCops agreed to send me a couple of Wallops so I can test this. They worked! Nice.

IRCv3 echo-message

Libera.Chat and other IRC networks support the IRCv3 echo-message extension. This echoes sent messages back to the client, which may seem redundant at first until you consider that:

  1. Many channels transform received messages (e.g., stripping control code via Libera's +c chanmode)
  2. It serves as a latency measurement
  3. It acts as an acknowledgment that the server successfully received the message

The spec notes that clients may choose to disable local echoing of sent PRIVMSG and NOTICE messages altogether, so I did just that. While there is a tiny delay between sending a message and the echo appearing, I found it negligible in practice.

Input History

catgirl(1) uses the ↑ and ↓ keys to scroll the window backlog, while PgUp and PgDn scroll pagewise. C-p and C-n cycle buffers, and M-p/M-n scroll to highlighted terms.

Aside from the fact that much of the keybinding "real estate" is used for the scrolling functionality, there is no implementation of readline-like edit history commonly seen in other IRC clients.

I was able to do a fairly compact implementation of this, integrated into the input handling unit, without modifying other code. Finding an appropriate keybind for this required some research. Originally, I wanted to use M-↑ and M-↓ - however, binding to arrow keys within terminals in not portable, so eventually I settled on M-, and M-. with M-↑ and M-↓ as alternate keybindings.

Replacing the Completion Engine

As a side project, I experimented with modifying the completion module to use a Treap data structure. Originally, catgirl(1) used a doubly-linked list, which was simply traversed linearly and searched with str[n]cmp() for implementing tab completion.

While the original implementation is O(n), it’s barely noticeable in daily use. Glibc’s vectorized implementations of strcmp() and memcmp() make linear searches incredibly fast.

I did a few tests comparing the averaged lookup performance of the original O(n) implementation with the O(log n) treap-based implementation for word lists of size 1.000, 10.000, up to 50.000, and the difference between the two was around 1-5ms. In the end, I decided to retain the treap-based code in my branch since it is already tested and working, while acknowledging that it may not be worth incurring the additional complexity (I may decide to remove this later).

Wrapping up

I spent a few days testing, rebasing, and revising the various commits. Throughout the process, I set up an OBS project to build deb packages for the distros I use. I installed resulting artifacts and used the packaged client on a day-to-day basis as a form of dogfooding.

At this point, I felt that the modified client was comfy enough for my usage. Was it worth it? IMO, yes - ultimately, the modifications were fairly compact and compartmentalized, and putting the educational value aside, the client can be now considered "feature complete" from my point of view.

Tags: c programming irc
29 Mar 2024

In Praise of (modern) Vanilla Emacs (29+)

If we were to plot Emacs configurations on a geometric space based on their complexity, a simple metric to use would be the distance from vanilla Emacs, i.e. the amount of key rebinding and/or function overrides being done to the default, vanilla Emacs configuration.

In this (admittedly one-dimensional) space, sitting on one extreme (most modifications) would be Emacs distributions such as spacemacs or Doom Emacs, which seek to remold Emacs into an entirely different modal editor, with Vim-like keybindings. Some packages, such as ergoemacs, may rebind enough of the default key bindings to warrant their inclusion in this region.

On the other extreme (least modification), we have the vanilla, or "default" Emacs configuration, and adjacent "starter configurations" such as bedrock, which apply a set of modernized settings to Emacs' built-in packages.

Predictable Keybinds, Faster Startups

Vanilla, default, or "minimalist" Emacs configurations enable a faster start-up, as less elisp code gets evaluated as part of Emacs' initialization process. This eliminates the need for running Emacs in server mode, and makes starting & running several ad-hoc Emacsen viable, as each Emacs instance can be started in less than a second.

Aside from startup performance, staying close to the defaults ensures that the configuration is usable by other Emacsers without any special guidance, using key bindings that are familiar to them. Many Elisp programs, snippets, or packages also assume a close-to-default environment. Overall, on a close-to-vanilla configuration, the potential for conflicts, incompatibilies, and general breakage remains low.

Viability

Since version 29, Emacs can provide a powerful, modern editing experience with only a modest amount of custom configuration. Bedrock consists of ~119 non-comment lines. And my own configuration is around ~173 lines long. Both only pull a minimum amount of external packages (Bedrock pulls which-key for discoverability, and my configuration pulls markdown-mode for documentation buffer formatting when using eglot).

In my opinion, a number of factors contributed to this:

  • (f)ido-mode, icomplete and variations thereof such as fido-vertical-mode (Emacs 28+) are built-in into Emacs, and are viable replacements for vertico and other 3rd-party minibuffer completion packages.
  • undo-no-redo (Emacs 28+) provides a traditional undo functionality, although it is disabled by default.
  • The built-in completion-at-point function, and the \*Completions\* buffer have both gotten various usability upgrades in Emacs 29.
  • tree-sitter is part of core Emacs (29+), enabling simple structural editing.
  • eglot is part of core Emacs, providing Emacs with language server support out of the box.
  • project.el is built-in and automatically detects projects under VCS by default.
  • The modus and leuven themes are bundled with Emacs, both offering usable dark themes.

Other niceties such as use-package (built-in as of Emacs 29+) may not directly affect the editing experience, but allow for succinct and less-verbose configuration.

The Optimum

New Emacs users often make the mistake of assuming that a modern editing experience requires a full-fledged distro like spacemacs or Doom. This may have been true to an extent is the past – indeed, installing a full distribution like spacemacs used to save a considerable amount of customization time, configuring and installing packages.

However, I would argue that the last two major Emacs releases (28, 29) make simpler, more minimalist Emacs setups more viable than ever. In the very least, for most people, the number of external packages they need to install should be reduced, so that only a handful of external packages are necessary. In that sense, the optimum (for most people) should be somewhere around the vanilla cluster with less complex, mostly-straightforward customizations.

Tags: emacs editors
23 Mar 2024

Going Back To The Roots, Taking Back Control: Escaping the Enshittified Web

If you're reading this article coming from reddit, HN, slashdot, or some other social news aggregator, and have been browsing the web for ≥10 years, you might have experienced the following signs of degradation in your web browsing experience:

The above phenomena can be considered symptoms of enshittification or "platform decay". Many articles explore this trend more comprehensively, and explain how/why we got here, as well as possible (proposed) regulatory mechanisms to control it.

But this is not an article about enshittification. This is an article about individual, autonomous actions and behavioral changes we can undertake in order to counteract it, at least on a personal level. Most of these actions involving giving up certain "creature comforts" and habits in order to regain a certain level of control, customizability, and functionality. These trade-offs might not be worth it for some at this point – but eventually, as the phenomenon of platform decay accelerates, the advantages of these trade-offs will eventually outweigh the disadvantages.

News

Doomscrolling may be (possibly) linked to a decline in mental and physical health. No really, it's better if you put down the live feed (on whatever platform you're reading it on) and opt for a slower-moving alternative. The dynamics of social news feeds incentivize presenting the most rage-baiting, vitriolic, divisive content at the expense of … everything else (including accuracy).

This is not an invitation to "quit news" wholesale, it is an invitation to quit social news in favor of a small selection of quality, self-curated sources. To that end, the following tools can be used, in order of increasing complexity:

If you follow only a handful of magazines, blogs, or news sources and/or read news occasionally, a handful of browser bookmarks are entirely sufficient. Alternatively, the most portable solution remains RSS, which is still supported by popular blogware.

RSS is a hypermedia format that embraces the decentralized nature of the web. RSS readers or clients (also known as aggregators) maintain a library of links to RSS documents, which are refreshed periodically to check for updates. Blogs, fediverse applications, CMSes, public/private broadcasters, and many other types of websites with updatable content support RSS.

A smaller number of programs (Gnus) support reading a large number of other "news sources" in addition to RSS. These programs offer a more uniform interface to reading news content from a wide variety of sources, including NNTP newsgroups, mailing lists (via e-mail), or even web searches.

"Domain Experts"

A number of "domain experts" and/or public figures you may want to follow may be only available on walled-garden platforms that do not support RSS (or any form of public access). In these cases, you might be faced with the following options:

  • Asking the person to move to a publicly-accessible alternative (e.g. a (self-)hosted blog, or a federated alternative)
  • Using a public access gateway/proxy
  • Stop "following" said individuals

A "public access" gateway or proxy application may exist. Such applications (usually in the form of a website) allow anonymous reading of platform profiles and/or content, circumventing the need for an account. However, it should be noted that platform providers perceive these applications as a threat, and deliberately introduce changes and/or incompatibilities to break their functionality.

"Interacting" with the "News"

An often-praised, but these days increasingly maligned (mis)feature of "social" news is the ability to interact with news items by commenting (often creating threaded conversations). If you're reading news to be informed, this is something you decidedly do not want.

But this does not preclude interactivity entirely. The level of interactivity is determined by the author of the news article: An author may choose to embed into the article a commenting mechanism, a service like disqus, or may simply opt to offer an e-mail address where feedback can be sent.

Communication

In their attempt to construct monetizable content silos, many platforms embarked on a process of horizontal integration where the platform becomes a communication provider (among other things) in addition to a content aggregator, integrating conferencing or chat services in addition to the usual submission or (micro-)blogging core feature set.

Platform-provided communications should be avoided whenever possible. Not only do they facilitate surveillance and data mining, they create further dependence on the platform and force participants to sign up in order to participate in communication, thus encouraging further vendor lock-in.

The following tools do not suffer from these drawbacks:

Note that if you use a private e-mail provider, governing authorities (or malicious actors with similar capabilities) likely have the ability to eavesdrop on your correspondence. Therefore, it is imperative that you secure your e-mail exchanges with GPG, this is especially important if your e-mail is hosted domestically.

IRC and XMPP are older protocols that are still used to this day. An IRC or XMPP server can be self-hosted at a low cost. IRC, being a text-oriented protocol, is less prone to predatory advertising, and a number of non-profit IRC networks still exist, boasting tens of thousands of users – still a lot, although a far cry from the activity level during the peak period of IRC. On most networks, registering a nickname (which is not mandatory but offers some benefits) on an IRC network can be done without providing any personal information.

Note that the text-oriented nature of IRC does not prevent IRC users from sharing rich media content: IRC users embrace hypermedia and the decentralized web, and share media content by embedding into their messages hyperlinks to the media resources they want to share (which may be even self hosted). Some IRC clients even have the ability to transparently fetch and preview such resources inline.

If IRC users wish to initiate a conference, they may choose to post a hyperlink to a Jitsi meeting in their channel (which, again, may point to a self-hosted jitsi instance). Clicking a hyperlink may be less convenient than clicking on a voice chat room in the "sidebar", but it decouples the conferencing provider from the messaging provider and allows participants more control as to the choice of their conferencing/VoIP solution. Of course, IRC clients may choose to integrate jitsi support into their UI as with image previews, adding functionality to create ad-hoc meetings on a specific instance at the press of a button, or by opening a tab embedding the meeting within a client if a link to a conference is clicked. As most IRC clients are extensible via plugins, a user may even write such functionality themselves. A sufficiently featureful IRC client can make an IRC channel look as "modern" or "dated" as it wants it to be, depending on the level of hypermedia integration supported by the client.

A common misconception about IRC is that a persistent connection to IRC (or a bouncer enabling the latter) is a necessity for things like offline messaging. While bouncers offer some benefits to a user, most IRC networks provide a MemoServ service to send offline messages to a registered user, and some networks (e.g. Libera.Chat) offer mail forwarding for received memos as an additional convenience feature. For chat history, some channels perform public logging (often including a hyperlink to the log archive in the channel topic), and any client may choose when/what/how to log as it sees fit.

Cross-linking your website to your IRC, XMPP, and E-Mail identities via a vCard should be sufficient for covering all your communication needs, and can be done without relying on a single commercialized or closed platform.

Hypermedia Maximalism

While you're reading this article, you've probably noticed that it's replete with hyperlinks to other sites. These hyperlinks point to references in the form of wiki articles, other blog articles posted on external blogs, research papers, or web applications. Most of these hyperlinks point to publicly-accessible external websites.

And that's fine.

This is how the web is supposed to operate. Platform providers nowadays attempt to control, curtail, or suppress web linking through various strategies, e.g. using modal scare-dialogs warning users of "risks" associated with "external websites", by displaying an inline preview of the content in their own "viewer", or by discouraging web linking altogether in favor of their own integrated content hosting.

The latter option is by far, the worst, since it also usually implicitly gives the platform provider control (and certain rights) to your own content. If you want control, the best choice remains: Weblink away.

If you're worried that weblinked content is "ephemeral" and may disappear, or get reseated somewhere else, then you may choose to rehost the content (check if the license allows this first), link to the content via the internet archive, or pick a mirror or alternative resource. Broken links may be annoying, but are not entirely useless - if you encounter a broken link in this article, I invite you to copy the URL and paste it into archive.org, where you will likely find a readable (archived) version of the target resource (Also, please inform me of this by sending an e-mail to alcor@tilde.club mentioning the broken link, so I can update this article).

Note that the e-mail address in the last paragraph is also a hyperlink (following the mailto: scheme). Clicking on it usually opens up a mail client in composition mode (on most operating systems), with the recipient prefilled with the e-mail-address.

Web linking should be encouraged in all web contexts. Be a hyperlink maximalist! Instead of doing a "self-post" or hosting your content/article on a siloed platform, consider whether you can post a link to your article/content (self-hosted, or hosted elsewhere) instead. Platforms that exhibit hostility to web linking should not be trusted.

If you're a web developer, consider adopting architectures and methodologies that enable and maximize the usage of hypermedia. htmx is a good example of this. A server-side hypermedia application can be more performant and less resource-intensive than an SPA, on top of being more extensible, and in many cases, simpler!

Back to Web 1.0?

If you grew up in the late 90s/early 00s, all of this may seem very familiar. This is how most of internet in the "Web 1.0" era used to work, and these are all "old" technologies and protocols. Is this all worth revisiting?

I'm inclined to think that the answer is yes. A number of developments have happened in the meantime that might make such an effort worthwhile:

  1. Web standards are more expansive, more mature, and much richer in functionality than 20 years ago. A website can be anything from a simple article like this one, to a full-blown 3D MMORPG, to an entire office suite, to a webconferencing program, all entirely served via the web and rendered by a web browser. It is now possible to hyperlink anything (since nearly anything can run on the web platform), anywhere (provided the linked application has sufficient support for that).
  2. Computing resources have increased to an extravagant level – hyperlink maximalism, high decentralization, and elaborate modes of hypermedia integration are possible for the same reason it's possible to have 100 browser tabs open on a phone.
  3. The increased centralization of content on big "platform provider" turf, and the latter's subsequent decay in quality make it so that such a development would qualitatively improve things.

Revisiting "older" technologies while armed with greater knowledge and a considerably greater availability of resources is not a return to a Web 1.0, and should not be characterized as such. It should be viewed in terms of revisiting paths of development not taken, checking if the same limitations leading towards that outcome still apply, taking what works (hypermedia, decentralization, the web as a rich, portable application platform) and abandoning what doesn't (centralized, horizontally-integrated platforms) – that is, the goal should be a "Web 1.1" rather than a regression back to "Web 1.0".

A Retrotopia for the Web

In J.M. Greer's Retrotopia, a fictional state achieves prosperity in what appears to be a scarcity-dominated, desolate world by revisiting, and making more efficient use, of "older" technologies, trading off some comfort for reliability, efficiency, resilience, and control.

Could something similar be done for the web? Probably, and most likely, without sacrificing nearly as much comfort.

Barring regulatory action, this hinges on individuals abandoning larger platforms, if not fully, then at least in part, by refusing to adopt the "integrated services" offered by the platform provider. If the trajectory of platform decay continues, then users may be incentivized to do that anyway – a trend which should be encouraged.

Tags: enshittification capitalism web
Other posts
Other posts
Creative Commons License
tilde.club/–alcor by Alcor's Tentaclar Aliens' Epic Extraterrestrial Jungle Dance Party is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.