Writing a Gemini client
Posted on
I have officially gone off the deep end. I am writing a C program just for fun. And it has been fun! I will admit, the experience of writing and debugging the code is much more frustrating than Rust, but the results are very rewarding. My client is currently clocking at 99KB, dynamically linking against libssl, libcrypto, and SDL2. And it’s a fully visual Gemini client! It’s always pleasant to write dense software.
Find it here, though at the time of writing, it’s not usable yet:
=> https://git.sr.ht/~neon/nemini
Here’s a bag of assorted thoughts I came across development.
Oops, apparently I’m not the only one who thought this is a great idea
About a week into development, I learned of Lagrange.
=> https://gmi.skyjake.fi/lagrange/
It uses SDL and OpenSSL, is written in C, and made by a finnish person as well. It’s one thing to make “yet another program to do X”, and another thing to make “yet another program to do X based on Y written in Z”. Oh well. Good thing I’m writing this client for fun, then ;)
TCC scripting
Here’s a trick I learned many moons ago, which I finally got to use during this project:
#!/usr/bin/tcc -run
You know, if you want to write a quick little script, but want to do it in style, with C. I wrote a short script to encode a binary file into a C source file with this:
#!/usr/bin/tcc -run
/* Reads bytes from stdin and prints them out as C source code to stdout. */
#include <stdio.h>
#include <unistd.h>
int main(void) {
int bytes_read;
unsigned char buf[75 / 5]; /* 5 chars per byte: 0xFF, */
printf("const unsigned char data[] = {");
do {
printf("\n");
bytes_read = read(STDIN_FILENO, buf, sizeof(buf));
for (int i = 0; i < bytes_read; i++) {
unsigned char byte = buf[i];
printf("0x%02X,", byte);
}
} while (bytes_read != 0);
printf("};\n");
return 0;
}
Usage:
clangifyer.c <binaryfile.bin >cfilewiththedata.c
There might be bugs, but it seemed to work for me. I included the Atkinson Hyperlegible Font into my client with it!
=> https://www.brailleinstitute.org/freefont
You might ask: is this really the first time you had the opportunity to use C for scripting, after having learned of this “trick” “many” “moons” ago? No, not really, but I had C on my mind this time.
OpenSSL isn’t that bad
It’s actually quite a simple library to use. I based my code completely on gmni’s TLS code, and I’m not sure I know how the whole thing works yet, so I won’t write a tutorial here. But it wasn’t that much code! Don’t be intimidated by the fact that it’s a big scary system library like I was initially.
=> https://git.sr.ht/~sircmpwn/gmni
C woes
Writing C sure has been an adventure. Here’s some highlights:
- malloc/freeing things has been surprisingly easy to remember. I say, having not checked my program for memory leaks.
- Reading the manpages carefully is important. They always contain neat little tidbits, such as “you absolutely must free() this yourself” or “do not free() this, or everything will break”. Rust is pretty neat in how it encodes this stuff as types, in the language, so they can be checked, at compile time. >:[
- Did you know, you can do “man <pretty much any function from OpenSSL>” and it opens up the documentation for that function, with explanations, possible return values, and everything else relevant? It’s pretty neat, though I never learnt this in programming school(tm). I came across it accidentally by calling “man getaddrinfo” during the dark hours of the morning.
- The “char” type is not defined as signed or unsigned. Madness! I guess it doesn’t come up often, since you generally don’t do arithmetic on letters. But considering that there isn’t another “number type” for 8-bit values, this really salted my fields. The “i32/u32” convention is a lot easier to program with.
- Use-after-frees are painful to debug, t. spent-like-six-hours-trying-to-figure-out-why-libssl-crashed-and-then-found-out-I-wasn’t-supposed-to-free-the-cert-myself.
This will be on my gemlog as well
Do you have a Gemini client handy? Are you from the future? You could read this blog post on my gemlog too:
If there’s nothing there, I haven’t set it up yet. Likely because I’m still hacking away at Nemini instead. I’ll probably make another post when I set that up, so sign up to my Atom feed if you’re interested.